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, DiffHunkSecondaryStatus, 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, CompletionSettings,
   20        LanguageSettingsContent, PrettierSettings,
   21    },
   22    BracketPairConfig,
   23    Capability::ReadWrite,
   24    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   25    Override, Point,
   26};
   27use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   28use multi_buffer::{IndentGuide, PathKey};
   29use parking_lot::Mutex;
   30use pretty_assertions::{assert_eq, assert_ne};
   31use project::project_settings::{LspSettings, ProjectSettings};
   32use project::FakeFs;
   33use serde_json::{self, json};
   34use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   35use std::{
   36    iter,
   37    sync::atomic::{self, AtomicUsize},
   38};
   39use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   40use text::ToPoint as _;
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq, path,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45    uri,
   46};
   47use workspace::{
   48    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   49    NavigationEntry, ViewId,
   50};
   51
   52#[gpui::test]
   53fn test_edit_events(cx: &mut TestAppContext) {
   54    init_test(cx, |_| {});
   55
   56    let buffer = cx.new(|cx| {
   57        let mut buffer = language::Buffer::local("123456", cx);
   58        buffer.set_group_interval(Duration::from_secs(1));
   59        buffer
   60    });
   61
   62    let events = Rc::new(RefCell::new(Vec::new()));
   63    let editor1 = cx.add_window({
   64        let events = events.clone();
   65        |window, cx| {
   66            let entity = cx.entity().clone();
   67            cx.subscribe_in(
   68                &entity,
   69                window,
   70                move |_, _, event: &EditorEvent, _, _| match event {
   71                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   72                    EditorEvent::BufferEdited => {
   73                        events.borrow_mut().push(("editor1", "buffer edited"))
   74                    }
   75                    _ => {}
   76                },
   77            )
   78            .detach();
   79            Editor::for_buffer(buffer.clone(), None, window, cx)
   80        }
   81    });
   82
   83    let editor2 = cx.add_window({
   84        let events = events.clone();
   85        |window, cx| {
   86            cx.subscribe_in(
   87                &cx.entity().clone(),
   88                window,
   89                move |_, _, event: &EditorEvent, _, _| match event {
   90                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   91                    EditorEvent::BufferEdited => {
   92                        events.borrow_mut().push(("editor2", "buffer edited"))
   93                    }
   94                    _ => {}
   95                },
   96            )
   97            .detach();
   98            Editor::for_buffer(buffer.clone(), None, window, cx)
   99        }
  100    });
  101
  102    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  103
  104    // Mutating editor 1 will emit an `Edited` event only for that editor.
  105    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor1", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Mutating editor 2 will emit an `Edited` event only for that editor.
  116    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor2", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  138    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor1", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  160    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  161    assert_eq!(
  162        mem::take(&mut *events.borrow_mut()),
  163        [
  164            ("editor2", "edited"),
  165            ("editor1", "buffer edited"),
  166            ("editor2", "buffer edited"),
  167        ]
  168    );
  169
  170    // No event is emitted when the mutation is a no-op.
  171    _ = editor2.update(cx, |editor, window, cx| {
  172        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  173
  174        editor.backspace(&Backspace, window, cx);
  175    });
  176    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  177}
  178
  179#[gpui::test]
  180fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  181    init_test(cx, |_| {});
  182
  183    let mut now = Instant::now();
  184    let group_interval = Duration::from_millis(1);
  185    let buffer = cx.new(|cx| {
  186        let mut buf = language::Buffer::local("123456", cx);
  187        buf.set_group_interval(group_interval);
  188        buf
  189    });
  190    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  191    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  192
  193    _ = editor.update(cx, |editor, window, cx| {
  194        editor.start_transaction_at(now, window, cx);
  195        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  196
  197        editor.insert("cd", window, cx);
  198        editor.end_transaction_at(now, cx);
  199        assert_eq!(editor.text(cx), "12cd56");
  200        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  201
  202        editor.start_transaction_at(now, window, cx);
  203        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  204        editor.insert("e", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cde6");
  207        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  208
  209        now += group_interval + Duration::from_millis(1);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  211
  212        // Simulate an edit in another editor
  213        buffer.update(cx, |buffer, cx| {
  214            buffer.start_transaction_at(now, cx);
  215            buffer.edit([(0..1, "a")], None, cx);
  216            buffer.edit([(1..1, "b")], None, cx);
  217            buffer.end_transaction_at(now, cx);
  218        });
  219
  220        assert_eq!(editor.text(cx), "ab2cde6");
  221        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  222
  223        // Last transaction happened past the group interval in a different editor.
  224        // Undo it individually and don't restore selections.
  225        editor.undo(&Undo, window, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  228
  229        // First two transactions happened within the group interval in this editor.
  230        // Undo them together and restore selections.
  231        editor.undo(&Undo, window, cx);
  232        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  233        assert_eq!(editor.text(cx), "123456");
  234        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  235
  236        // Redo the first two transactions together.
  237        editor.redo(&Redo, window, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  240
  241        // Redo the last transaction on its own.
  242        editor.redo(&Redo, window, cx);
  243        assert_eq!(editor.text(cx), "ab2cde6");
  244        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  245
  246        // Test empty transactions.
  247        editor.start_transaction_at(now, window, cx);
  248        editor.end_transaction_at(now, cx);
  249        editor.undo(&Undo, window, cx);
  250        assert_eq!(editor.text(cx), "12cde6");
  251    });
  252}
  253
  254#[gpui::test]
  255fn test_ime_composition(cx: &mut TestAppContext) {
  256    init_test(cx, |_| {});
  257
  258    let buffer = cx.new(|cx| {
  259        let mut buffer = language::Buffer::local("abcde", cx);
  260        // Ensure automatic grouping doesn't occur.
  261        buffer.set_group_interval(Duration::ZERO);
  262        buffer
  263    });
  264
  265    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  266    cx.add_window(|window, cx| {
  267        let mut editor = build_editor(buffer.clone(), window, cx);
  268
  269        // Start a new IME composition.
  270        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  271        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  272        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  273        assert_eq!(editor.text(cx), "äbcde");
  274        assert_eq!(
  275            editor.marked_text_ranges(cx),
  276            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  277        );
  278
  279        // Finalize IME composition.
  280        editor.replace_text_in_range(None, "ā", window, cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // IME composition edits are grouped and are undone/redone at once.
  285        editor.undo(&Default::default(), window, cx);
  286        assert_eq!(editor.text(cx), "abcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288        editor.redo(&Default::default(), window, cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition.
  293        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  294        assert_eq!(
  295            editor.marked_text_ranges(cx),
  296            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  297        );
  298
  299        // Undoing during an IME composition cancels it.
  300        editor.undo(&Default::default(), window, cx);
  301        assert_eq!(editor.text(cx), "ābcde");
  302        assert_eq!(editor.marked_text_ranges(cx), None);
  303
  304        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  305        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  306        assert_eq!(editor.text(cx), "ābcdè");
  307        assert_eq!(
  308            editor.marked_text_ranges(cx),
  309            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  310        );
  311
  312        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  313        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  314        assert_eq!(editor.text(cx), "ābcdę");
  315        assert_eq!(editor.marked_text_ranges(cx), None);
  316
  317        // Start a new IME composition with multiple cursors.
  318        editor.change_selections(None, window, cx, |s| {
  319            s.select_ranges([
  320                OffsetUtf16(1)..OffsetUtf16(1),
  321                OffsetUtf16(3)..OffsetUtf16(3),
  322                OffsetUtf16(5)..OffsetUtf16(5),
  323            ])
  324        });
  325        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  326        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(0)..OffsetUtf16(3),
  331                OffsetUtf16(4)..OffsetUtf16(7),
  332                OffsetUtf16(8)..OffsetUtf16(11)
  333            ])
  334        );
  335
  336        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  337        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  338        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  339        assert_eq!(
  340            editor.marked_text_ranges(cx),
  341            Some(vec![
  342                OffsetUtf16(1)..OffsetUtf16(2),
  343                OffsetUtf16(5)..OffsetUtf16(6),
  344                OffsetUtf16(9)..OffsetUtf16(10)
  345            ])
  346        );
  347
  348        // Finalize IME composition with multiple cursors.
  349        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  350        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  351        assert_eq!(editor.marked_text_ranges(cx), None);
  352
  353        editor
  354    });
  355}
  356
  357#[gpui::test]
  358fn test_selection_with_mouse(cx: &mut TestAppContext) {
  359    init_test(cx, |_| {});
  360
  361    let editor = cx.add_window(|window, cx| {
  362        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  363        build_editor(buffer, window, cx)
  364    });
  365
  366    _ = editor.update(cx, |editor, window, cx| {
  367        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  368    });
  369    assert_eq!(
  370        editor
  371            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  374    );
  375
  376    _ = editor.update(cx, |editor, window, cx| {
  377        editor.update_selection(
  378            DisplayPoint::new(DisplayRow(3), 3),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            window,
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  391    );
  392
  393    _ = editor.update(cx, |editor, window, cx| {
  394        editor.update_selection(
  395            DisplayPoint::new(DisplayRow(1), 1),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            window,
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |editor, window, cx| {
  411        editor.end_selection(window, cx);
  412        editor.update_selection(
  413            DisplayPoint::new(DisplayRow(3), 3),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            window,
  417            cx,
  418        );
  419    });
  420
  421    assert_eq!(
  422        editor
  423            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  424            .unwrap(),
  425        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  426    );
  427
  428    _ = editor.update(cx, |editor, window, cx| {
  429        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  430        editor.update_selection(
  431            DisplayPoint::new(DisplayRow(0), 0),
  432            0,
  433            gpui::Point::<f32>::default(),
  434            window,
  435            cx,
  436        );
  437    });
  438
  439    assert_eq!(
  440        editor
  441            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  442            .unwrap(),
  443        [
  444            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  445            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  446        ]
  447    );
  448
  449    _ = editor.update(cx, |editor, window, cx| {
  450        editor.end_selection(window, cx);
  451    });
  452
  453    assert_eq!(
  454        editor
  455            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  456            .unwrap(),
  457        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  458    );
  459}
  460
  461#[gpui::test]
  462fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  463    init_test(cx, |_| {});
  464
  465    let editor = cx.add_window(|window, cx| {
  466        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  467        build_editor(buffer, window, cx)
  468    });
  469
  470    _ = editor.update(cx, |editor, window, cx| {
  471        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  472    });
  473
  474    _ = editor.update(cx, |editor, window, cx| {
  475        editor.end_selection(window, cx);
  476    });
  477
  478    _ = editor.update(cx, |editor, window, cx| {
  479        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  480    });
  481
  482    _ = editor.update(cx, |editor, window, cx| {
  483        editor.end_selection(window, cx);
  484    });
  485
  486    assert_eq!(
  487        editor
  488            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  489            .unwrap(),
  490        [
  491            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  492            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  493        ]
  494    );
  495
  496    _ = editor.update(cx, |editor, window, cx| {
  497        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  498    });
  499
  500    _ = editor.update(cx, |editor, window, cx| {
  501        editor.end_selection(window, cx);
  502    });
  503
  504    assert_eq!(
  505        editor
  506            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  507            .unwrap(),
  508        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  509    );
  510}
  511
  512#[gpui::test]
  513fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  514    init_test(cx, |_| {});
  515
  516    let editor = cx.add_window(|window, cx| {
  517        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  518        build_editor(buffer, window, cx)
  519    });
  520
  521    _ = editor.update(cx, |editor, window, cx| {
  522        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  523        assert_eq!(
  524            editor.selections.display_ranges(cx),
  525            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  526        );
  527    });
  528
  529    _ = editor.update(cx, |editor, window, cx| {
  530        editor.update_selection(
  531            DisplayPoint::new(DisplayRow(3), 3),
  532            0,
  533            gpui::Point::<f32>::default(),
  534            window,
  535            cx,
  536        );
  537        assert_eq!(
  538            editor.selections.display_ranges(cx),
  539            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  540        );
  541    });
  542
  543    _ = editor.update(cx, |editor, window, cx| {
  544        editor.cancel(&Cancel, window, cx);
  545        editor.update_selection(
  546            DisplayPoint::new(DisplayRow(1), 1),
  547            0,
  548            gpui::Point::<f32>::default(),
  549            window,
  550            cx,
  551        );
  552        assert_eq!(
  553            editor.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  555        );
  556    });
  557}
  558
  559#[gpui::test]
  560fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  561    init_test(cx, |_| {});
  562
  563    let editor = cx.add_window(|window, cx| {
  564        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  565        build_editor(buffer, window, cx)
  566    });
  567
  568    _ = editor.update(cx, |editor, window, cx| {
  569        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  570        assert_eq!(
  571            editor.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  573        );
  574
  575        editor.move_down(&Default::default(), window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  579        );
  580
  581        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  585        );
  586
  587        editor.move_up(&Default::default(), window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  591        );
  592    });
  593}
  594
  595#[gpui::test]
  596fn test_clone(cx: &mut TestAppContext) {
  597    init_test(cx, |_| {});
  598
  599    let (text, selection_ranges) = marked_text_ranges(
  600        indoc! {"
  601            one
  602            two
  603            threeˇ
  604            four
  605            fiveˇ
  606        "},
  607        true,
  608    );
  609
  610    let editor = cx.add_window(|window, cx| {
  611        let buffer = MultiBuffer::build_simple(&text, cx);
  612        build_editor(buffer, window, cx)
  613    });
  614
  615    _ = editor.update(cx, |editor, window, cx| {
  616        editor.change_selections(None, window, cx, |s| {
  617            s.select_ranges(selection_ranges.clone())
  618        });
  619        editor.fold_creases(
  620            vec![
  621                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  622                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  623            ],
  624            true,
  625            window,
  626            cx,
  627        );
  628    });
  629
  630    let cloned_editor = editor
  631        .update(cx, |editor, _, cx| {
  632            cx.open_window(Default::default(), |window, cx| {
  633                cx.new(|cx| editor.clone(window, cx))
  634            })
  635        })
  636        .unwrap()
  637        .unwrap();
  638
  639    let snapshot = editor
  640        .update(cx, |e, window, cx| e.snapshot(window, cx))
  641        .unwrap();
  642    let cloned_snapshot = cloned_editor
  643        .update(cx, |e, window, cx| e.snapshot(window, cx))
  644        .unwrap();
  645
  646    assert_eq!(
  647        cloned_editor
  648            .update(cx, |e, _, cx| e.display_text(cx))
  649            .unwrap(),
  650        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  651    );
  652    assert_eq!(
  653        cloned_snapshot
  654            .folds_in_range(0..text.len())
  655            .collect::<Vec<_>>(),
  656        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  657    );
  658    assert_set_eq!(
  659        cloned_editor
  660            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  661            .unwrap(),
  662        editor
  663            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  664            .unwrap()
  665    );
  666    assert_set_eq!(
  667        cloned_editor
  668            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  669            .unwrap(),
  670        editor
  671            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  672            .unwrap()
  673    );
  674}
  675
  676#[gpui::test]
  677async fn test_navigation_history(cx: &mut TestAppContext) {
  678    init_test(cx, |_| {});
  679
  680    use workspace::item::Item;
  681
  682    let fs = FakeFs::new(cx.executor());
  683    let project = Project::test(fs, [], cx).await;
  684    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  685    let pane = workspace
  686        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  687        .unwrap();
  688
  689    _ = workspace.update(cx, |_v, window, cx| {
  690        cx.new(|cx| {
  691            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  692            let mut editor = build_editor(buffer.clone(), window, cx);
  693            let handle = cx.entity();
  694            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  695
  696            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  697                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  698            }
  699
  700            // Move the cursor a small distance.
  701            // Nothing is added to the navigation history.
  702            editor.change_selections(None, window, cx, |s| {
  703                s.select_display_ranges([
  704                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  705                ])
  706            });
  707            editor.change_selections(None, window, cx, |s| {
  708                s.select_display_ranges([
  709                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  710                ])
  711            });
  712            assert!(pop_history(&mut editor, cx).is_none());
  713
  714            // Move the cursor a large distance.
  715            // The history can jump back to the previous position.
  716            editor.change_selections(None, window, cx, |s| {
  717                s.select_display_ranges([
  718                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  719                ])
  720            });
  721            let nav_entry = pop_history(&mut editor, cx).unwrap();
  722            editor.navigate(nav_entry.data.unwrap(), window, cx);
  723            assert_eq!(nav_entry.item.id(), cx.entity_id());
  724            assert_eq!(
  725                editor.selections.display_ranges(cx),
  726                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  727            );
  728            assert!(pop_history(&mut editor, cx).is_none());
  729
  730            // Move the cursor a small distance via the mouse.
  731            // Nothing is added to the navigation history.
  732            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  733            editor.end_selection(window, cx);
  734            assert_eq!(
  735                editor.selections.display_ranges(cx),
  736                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  737            );
  738            assert!(pop_history(&mut editor, cx).is_none());
  739
  740            // Move the cursor a large distance via the mouse.
  741            // The history can jump back to the previous position.
  742            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  743            editor.end_selection(window, cx);
  744            assert_eq!(
  745                editor.selections.display_ranges(cx),
  746                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  747            );
  748            let nav_entry = pop_history(&mut editor, cx).unwrap();
  749            editor.navigate(nav_entry.data.unwrap(), window, cx);
  750            assert_eq!(nav_entry.item.id(), cx.entity_id());
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  754            );
  755            assert!(pop_history(&mut editor, cx).is_none());
  756
  757            // Set scroll position to check later
  758            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  759            let original_scroll_position = editor.scroll_manager.anchor();
  760
  761            // Jump to the end of the document and adjust scroll
  762            editor.move_to_end(&MoveToEnd, window, cx);
  763            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  764            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  765
  766            let nav_entry = pop_history(&mut editor, cx).unwrap();
  767            editor.navigate(nav_entry.data.unwrap(), window, cx);
  768            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  769
  770            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  771            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  772            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  773            let invalid_point = Point::new(9999, 0);
  774            editor.navigate(
  775                Box::new(NavigationData {
  776                    cursor_anchor: invalid_anchor,
  777                    cursor_position: invalid_point,
  778                    scroll_anchor: ScrollAnchor {
  779                        anchor: invalid_anchor,
  780                        offset: Default::default(),
  781                    },
  782                    scroll_top_row: invalid_point.row,
  783                }),
  784                window,
  785                cx,
  786            );
  787            assert_eq!(
  788                editor.selections.display_ranges(cx),
  789                &[editor.max_point(cx)..editor.max_point(cx)]
  790            );
  791            assert_eq!(
  792                editor.scroll_position(cx),
  793                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  794            );
  795
  796            editor
  797        })
  798    });
  799}
  800
  801#[gpui::test]
  802fn test_cancel(cx: &mut TestAppContext) {
  803    init_test(cx, |_| {});
  804
  805    let editor = cx.add_window(|window, cx| {
  806        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  807        build_editor(buffer, window, cx)
  808    });
  809
  810    _ = editor.update(cx, |editor, window, cx| {
  811        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  812        editor.update_selection(
  813            DisplayPoint::new(DisplayRow(1), 1),
  814            0,
  815            gpui::Point::<f32>::default(),
  816            window,
  817            cx,
  818        );
  819        editor.end_selection(window, cx);
  820
  821        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  822        editor.update_selection(
  823            DisplayPoint::new(DisplayRow(0), 3),
  824            0,
  825            gpui::Point::<f32>::default(),
  826            window,
  827            cx,
  828        );
  829        editor.end_selection(window, cx);
  830        assert_eq!(
  831            editor.selections.display_ranges(cx),
  832            [
  833                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  834                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  835            ]
  836        );
  837    });
  838
  839    _ = editor.update(cx, |editor, window, cx| {
  840        editor.cancel(&Cancel, window, cx);
  841        assert_eq!(
  842            editor.selections.display_ranges(cx),
  843            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  844        );
  845    });
  846
  847    _ = editor.update(cx, |editor, window, cx| {
  848        editor.cancel(&Cancel, window, cx);
  849        assert_eq!(
  850            editor.selections.display_ranges(cx),
  851            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  852        );
  853    });
  854}
  855
  856#[gpui::test]
  857fn test_fold_action(cx: &mut TestAppContext) {
  858    init_test(cx, |_| {});
  859
  860    let editor = cx.add_window(|window, cx| {
  861        let buffer = MultiBuffer::build_simple(
  862            &"
  863                impl Foo {
  864                    // Hello!
  865
  866                    fn a() {
  867                        1
  868                    }
  869
  870                    fn b() {
  871                        2
  872                    }
  873
  874                    fn c() {
  875                        3
  876                    }
  877                }
  878            "
  879            .unindent(),
  880            cx,
  881        );
  882        build_editor(buffer.clone(), window, cx)
  883    });
  884
  885    _ = editor.update(cx, |editor, window, cx| {
  886        editor.change_selections(None, window, cx, |s| {
  887            s.select_display_ranges([
  888                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  889            ]);
  890        });
  891        editor.fold(&Fold, window, cx);
  892        assert_eq!(
  893            editor.display_text(cx),
  894            "
  895                impl Foo {
  896                    // Hello!
  897
  898                    fn a() {
  899                        1
  900                    }
  901
  902                    fn b() {⋯
  903                    }
  904
  905                    fn c() {⋯
  906                    }
  907                }
  908            "
  909            .unindent(),
  910        );
  911
  912        editor.fold(&Fold, window, cx);
  913        assert_eq!(
  914            editor.display_text(cx),
  915            "
  916                impl Foo {⋯
  917                }
  918            "
  919            .unindent(),
  920        );
  921
  922        editor.unfold_lines(&UnfoldLines, window, cx);
  923        assert_eq!(
  924            editor.display_text(cx),
  925            "
  926                impl Foo {
  927                    // Hello!
  928
  929                    fn a() {
  930                        1
  931                    }
  932
  933                    fn b() {⋯
  934                    }
  935
  936                    fn c() {⋯
  937                    }
  938                }
  939            "
  940            .unindent(),
  941        );
  942
  943        editor.unfold_lines(&UnfoldLines, window, cx);
  944        assert_eq!(
  945            editor.display_text(cx),
  946            editor.buffer.read(cx).read(cx).text()
  947        );
  948    });
  949}
  950
  951#[gpui::test]
  952fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  953    init_test(cx, |_| {});
  954
  955    let editor = cx.add_window(|window, cx| {
  956        let buffer = MultiBuffer::build_simple(
  957            &"
  958                class Foo:
  959                    # Hello!
  960
  961                    def a():
  962                        print(1)
  963
  964                    def b():
  965                        print(2)
  966
  967                    def c():
  968                        print(3)
  969            "
  970            .unindent(),
  971            cx,
  972        );
  973        build_editor(buffer.clone(), window, cx)
  974    });
  975
  976    _ = editor.update(cx, |editor, window, cx| {
  977        editor.change_selections(None, window, cx, |s| {
  978            s.select_display_ranges([
  979                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  980            ]);
  981        });
  982        editor.fold(&Fold, window, cx);
  983        assert_eq!(
  984            editor.display_text(cx),
  985            "
  986                class Foo:
  987                    # Hello!
  988
  989                    def a():
  990                        print(1)
  991
  992                    def b():⋯
  993
  994                    def c():⋯
  995            "
  996            .unindent(),
  997        );
  998
  999        editor.fold(&Fold, window, cx);
 1000        assert_eq!(
 1001            editor.display_text(cx),
 1002            "
 1003                class Foo:⋯
 1004            "
 1005            .unindent(),
 1006        );
 1007
 1008        editor.unfold_lines(&UnfoldLines, window, cx);
 1009        assert_eq!(
 1010            editor.display_text(cx),
 1011            "
 1012                class Foo:
 1013                    # Hello!
 1014
 1015                    def a():
 1016                        print(1)
 1017
 1018                    def b():⋯
 1019
 1020                    def c():⋯
 1021            "
 1022            .unindent(),
 1023        );
 1024
 1025        editor.unfold_lines(&UnfoldLines, window, cx);
 1026        assert_eq!(
 1027            editor.display_text(cx),
 1028            editor.buffer.read(cx).read(cx).text()
 1029        );
 1030    });
 1031}
 1032
 1033#[gpui::test]
 1034fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1035    init_test(cx, |_| {});
 1036
 1037    let editor = cx.add_window(|window, cx| {
 1038        let buffer = MultiBuffer::build_simple(
 1039            &"
 1040                class Foo:
 1041                    # Hello!
 1042
 1043                    def a():
 1044                        print(1)
 1045
 1046                    def b():
 1047                        print(2)
 1048
 1049
 1050                    def c():
 1051                        print(3)
 1052
 1053
 1054            "
 1055            .unindent(),
 1056            cx,
 1057        );
 1058        build_editor(buffer.clone(), window, cx)
 1059    });
 1060
 1061    _ = editor.update(cx, |editor, window, cx| {
 1062        editor.change_selections(None, window, cx, |s| {
 1063            s.select_display_ranges([
 1064                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1065            ]);
 1066        });
 1067        editor.fold(&Fold, window, cx);
 1068        assert_eq!(
 1069            editor.display_text(cx),
 1070            "
 1071                class Foo:
 1072                    # Hello!
 1073
 1074                    def a():
 1075                        print(1)
 1076
 1077                    def b():⋯
 1078
 1079
 1080                    def c():⋯
 1081
 1082
 1083            "
 1084            .unindent(),
 1085        );
 1086
 1087        editor.fold(&Fold, window, cx);
 1088        assert_eq!(
 1089            editor.display_text(cx),
 1090            "
 1091                class Foo:⋯
 1092
 1093
 1094            "
 1095            .unindent(),
 1096        );
 1097
 1098        editor.unfold_lines(&UnfoldLines, window, cx);
 1099        assert_eq!(
 1100            editor.display_text(cx),
 1101            "
 1102                class Foo:
 1103                    # Hello!
 1104
 1105                    def a():
 1106                        print(1)
 1107
 1108                    def b():⋯
 1109
 1110
 1111                    def c():⋯
 1112
 1113
 1114            "
 1115            .unindent(),
 1116        );
 1117
 1118        editor.unfold_lines(&UnfoldLines, window, cx);
 1119        assert_eq!(
 1120            editor.display_text(cx),
 1121            editor.buffer.read(cx).read(cx).text()
 1122        );
 1123    });
 1124}
 1125
 1126#[gpui::test]
 1127fn test_fold_at_level(cx: &mut TestAppContext) {
 1128    init_test(cx, |_| {});
 1129
 1130    let editor = cx.add_window(|window, cx| {
 1131        let buffer = MultiBuffer::build_simple(
 1132            &"
 1133                class Foo:
 1134                    # Hello!
 1135
 1136                    def a():
 1137                        print(1)
 1138
 1139                    def b():
 1140                        print(2)
 1141
 1142
 1143                class Bar:
 1144                    # World!
 1145
 1146                    def a():
 1147                        print(1)
 1148
 1149                    def b():
 1150                        print(2)
 1151
 1152
 1153            "
 1154            .unindent(),
 1155            cx,
 1156        );
 1157        build_editor(buffer.clone(), window, cx)
 1158    });
 1159
 1160    _ = editor.update(cx, |editor, window, cx| {
 1161        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1162        assert_eq!(
 1163            editor.display_text(cx),
 1164            "
 1165                class Foo:
 1166                    # Hello!
 1167
 1168                    def a():⋯
 1169
 1170                    def b():⋯
 1171
 1172
 1173                class Bar:
 1174                    # World!
 1175
 1176                    def a():⋯
 1177
 1178                    def b():⋯
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1186        assert_eq!(
 1187            editor.display_text(cx),
 1188            "
 1189                class Foo:⋯
 1190
 1191
 1192                class Bar:⋯
 1193
 1194
 1195            "
 1196            .unindent(),
 1197        );
 1198
 1199        editor.unfold_all(&UnfoldAll, window, cx);
 1200        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1201        assert_eq!(
 1202            editor.display_text(cx),
 1203            "
 1204                class Foo:
 1205                    # Hello!
 1206
 1207                    def a():
 1208                        print(1)
 1209
 1210                    def b():
 1211                        print(2)
 1212
 1213
 1214                class Bar:
 1215                    # World!
 1216
 1217                    def a():
 1218                        print(1)
 1219
 1220                    def b():
 1221                        print(2)
 1222
 1223
 1224            "
 1225            .unindent(),
 1226        );
 1227
 1228        assert_eq!(
 1229            editor.display_text(cx),
 1230            editor.buffer.read(cx).read(cx).text()
 1231        );
 1232    });
 1233}
 1234
 1235#[gpui::test]
 1236fn test_move_cursor(cx: &mut TestAppContext) {
 1237    init_test(cx, |_| {});
 1238
 1239    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1240    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1241
 1242    buffer.update(cx, |buffer, cx| {
 1243        buffer.edit(
 1244            vec![
 1245                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1246                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1247            ],
 1248            None,
 1249            cx,
 1250        );
 1251    });
 1252    _ = editor.update(cx, |editor, window, cx| {
 1253        assert_eq!(
 1254            editor.selections.display_ranges(cx),
 1255            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1256        );
 1257
 1258        editor.move_down(&MoveDown, window, cx);
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1262        );
 1263
 1264        editor.move_right(&MoveRight, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1268        );
 1269
 1270        editor.move_left(&MoveLeft, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1274        );
 1275
 1276        editor.move_up(&MoveUp, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1280        );
 1281
 1282        editor.move_to_end(&MoveToEnd, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1286        );
 1287
 1288        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1292        );
 1293
 1294        editor.change_selections(None, window, cx, |s| {
 1295            s.select_display_ranges([
 1296                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1297            ]);
 1298        });
 1299        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1300        assert_eq!(
 1301            editor.selections.display_ranges(cx),
 1302            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1303        );
 1304
 1305        editor.select_to_end(&SelectToEnd, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1309        );
 1310    });
 1311}
 1312
 1313// TODO: Re-enable this test
 1314#[cfg(target_os = "macos")]
 1315#[gpui::test]
 1316fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1317    init_test(cx, |_| {});
 1318
 1319    let editor = cx.add_window(|window, cx| {
 1320        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1321        build_editor(buffer.clone(), window, cx)
 1322    });
 1323
 1324    assert_eq!('🟥'.len_utf8(), 4);
 1325    assert_eq!('α'.len_utf8(), 2);
 1326
 1327    _ = editor.update(cx, |editor, window, cx| {
 1328        editor.fold_creases(
 1329            vec![
 1330                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1331                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1332                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1333            ],
 1334            true,
 1335            window,
 1336            cx,
 1337        );
 1338        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1339
 1340        editor.move_right(&MoveRight, window, cx);
 1341        assert_eq!(
 1342            editor.selections.display_ranges(cx),
 1343            &[empty_range(0, "🟥".len())]
 1344        );
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥🟧".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧⋯".len())]
 1354        );
 1355
 1356        editor.move_down(&MoveDown, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(1, "ab⋯e".len())]
 1360        );
 1361        editor.move_left(&MoveLeft, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "a".len())]
 1375        );
 1376
 1377        editor.move_down(&MoveDown, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(2, "α".len())]
 1381        );
 1382        editor.move_right(&MoveRight, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "αβ".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ⋯".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯ε".len())]
 1396        );
 1397
 1398        editor.move_up(&MoveUp, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(1, "ab⋯e".len())]
 1402        );
 1403        editor.move_down(&MoveDown, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβ⋯ε".len())]
 1407        );
 1408        editor.move_up(&MoveUp, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(1, "ab⋯e".len())]
 1412        );
 1413
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(0, "🟥🟧".len())]
 1418        );
 1419        editor.move_left(&MoveLeft, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "".len())]
 1428        );
 1429    });
 1430}
 1431
 1432#[gpui::test]
 1433fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1434    init_test(cx, |_| {});
 1435
 1436    let editor = cx.add_window(|window, cx| {
 1437        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1438        build_editor(buffer.clone(), window, cx)
 1439    });
 1440    _ = editor.update(cx, |editor, window, cx| {
 1441        editor.change_selections(None, window, cx, |s| {
 1442            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1443        });
 1444
 1445        // moving above start of document should move selection to start of document,
 1446        // but the next move down should still be at the original goal_x
 1447        editor.move_up(&MoveUp, window, cx);
 1448        assert_eq!(
 1449            editor.selections.display_ranges(cx),
 1450            &[empty_range(0, "".len())]
 1451        );
 1452
 1453        editor.move_down(&MoveDown, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(1, "abcd".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(2, "αβγ".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(3, "abcd".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1475        );
 1476
 1477        // moving past end of document should not change goal_x
 1478        editor.move_down(&MoveDown, window, cx);
 1479        assert_eq!(
 1480            editor.selections.display_ranges(cx),
 1481            &[empty_range(5, "".len())]
 1482        );
 1483
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_up(&MoveUp, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(3, "abcd".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(2, "αβγ".len())]
 1506        );
 1507    });
 1508}
 1509
 1510#[gpui::test]
 1511fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1512    init_test(cx, |_| {});
 1513    let move_to_beg = MoveToBeginningOfLine {
 1514        stop_at_soft_wraps: true,
 1515        stop_at_indent: true,
 1516    };
 1517
 1518    let delete_to_beg = DeleteToBeginningOfLine {
 1519        stop_at_indent: false,
 1520    };
 1521
 1522    let move_to_end = MoveToEndOfLine {
 1523        stop_at_soft_wraps: true,
 1524    };
 1525
 1526    let editor = cx.add_window(|window, cx| {
 1527        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1528        build_editor(buffer, window, cx)
 1529    });
 1530    _ = editor.update(cx, |editor, window, cx| {
 1531        editor.change_selections(None, window, cx, |s| {
 1532            s.select_display_ranges([
 1533                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1534                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1535            ]);
 1536        });
 1537    });
 1538
 1539    _ = editor.update(cx, |editor, window, cx| {
 1540        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1541        assert_eq!(
 1542            editor.selections.display_ranges(cx),
 1543            &[
 1544                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1545                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1546            ]
 1547        );
 1548    });
 1549
 1550    _ = editor.update(cx, |editor, window, cx| {
 1551        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1552        assert_eq!(
 1553            editor.selections.display_ranges(cx),
 1554            &[
 1555                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1556                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1557            ]
 1558        );
 1559    });
 1560
 1561    _ = editor.update(cx, |editor, window, cx| {
 1562        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1563        assert_eq!(
 1564            editor.selections.display_ranges(cx),
 1565            &[
 1566                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1567                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1568            ]
 1569        );
 1570    });
 1571
 1572    _ = editor.update(cx, |editor, window, cx| {
 1573        editor.move_to_end_of_line(&move_to_end, window, cx);
 1574        assert_eq!(
 1575            editor.selections.display_ranges(cx),
 1576            &[
 1577                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1578                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1579            ]
 1580        );
 1581    });
 1582
 1583    // Moving to the end of line again is a no-op.
 1584    _ = editor.update(cx, |editor, window, cx| {
 1585        editor.move_to_end_of_line(&move_to_end, window, cx);
 1586        assert_eq!(
 1587            editor.selections.display_ranges(cx),
 1588            &[
 1589                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1590                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1591            ]
 1592        );
 1593    });
 1594
 1595    _ = editor.update(cx, |editor, window, cx| {
 1596        editor.move_left(&MoveLeft, window, cx);
 1597        editor.select_to_beginning_of_line(
 1598            &SelectToBeginningOfLine {
 1599                stop_at_soft_wraps: true,
 1600                stop_at_indent: true,
 1601            },
 1602            window,
 1603            cx,
 1604        );
 1605        assert_eq!(
 1606            editor.selections.display_ranges(cx),
 1607            &[
 1608                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1609                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1610            ]
 1611        );
 1612    });
 1613
 1614    _ = editor.update(cx, |editor, window, cx| {
 1615        editor.select_to_beginning_of_line(
 1616            &SelectToBeginningOfLine {
 1617                stop_at_soft_wraps: true,
 1618                stop_at_indent: true,
 1619            },
 1620            window,
 1621            cx,
 1622        );
 1623        assert_eq!(
 1624            editor.selections.display_ranges(cx),
 1625            &[
 1626                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1627                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1628            ]
 1629        );
 1630    });
 1631
 1632    _ = editor.update(cx, |editor, window, cx| {
 1633        editor.select_to_beginning_of_line(
 1634            &SelectToBeginningOfLine {
 1635                stop_at_soft_wraps: true,
 1636                stop_at_indent: true,
 1637            },
 1638            window,
 1639            cx,
 1640        );
 1641        assert_eq!(
 1642            editor.selections.display_ranges(cx),
 1643            &[
 1644                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1645                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1646            ]
 1647        );
 1648    });
 1649
 1650    _ = editor.update(cx, |editor, window, cx| {
 1651        editor.select_to_end_of_line(
 1652            &SelectToEndOfLine {
 1653                stop_at_soft_wraps: true,
 1654            },
 1655            window,
 1656            cx,
 1657        );
 1658        assert_eq!(
 1659            editor.selections.display_ranges(cx),
 1660            &[
 1661                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1662                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1663            ]
 1664        );
 1665    });
 1666
 1667    _ = editor.update(cx, |editor, window, cx| {
 1668        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1669        assert_eq!(editor.display_text(cx), "ab\n  de");
 1670        assert_eq!(
 1671            editor.selections.display_ranges(cx),
 1672            &[
 1673                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1674                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1675            ]
 1676        );
 1677    });
 1678
 1679    _ = editor.update(cx, |editor, window, cx| {
 1680        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1681        assert_eq!(editor.display_text(cx), "\n");
 1682        assert_eq!(
 1683            editor.selections.display_ranges(cx),
 1684            &[
 1685                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1686                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1687            ]
 1688        );
 1689    });
 1690}
 1691
 1692#[gpui::test]
 1693fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1694    init_test(cx, |_| {});
 1695    let move_to_beg = MoveToBeginningOfLine {
 1696        stop_at_soft_wraps: false,
 1697        stop_at_indent: false,
 1698    };
 1699
 1700    let move_to_end = MoveToEndOfLine {
 1701        stop_at_soft_wraps: false,
 1702    };
 1703
 1704    let editor = cx.add_window(|window, cx| {
 1705        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1706        build_editor(buffer, window, cx)
 1707    });
 1708
 1709    _ = editor.update(cx, |editor, window, cx| {
 1710        editor.set_wrap_width(Some(140.0.into()), cx);
 1711
 1712        // We expect the following lines after wrapping
 1713        // ```
 1714        // thequickbrownfox
 1715        // jumpedoverthelazydo
 1716        // gs
 1717        // ```
 1718        // The final `gs` was soft-wrapped onto a new line.
 1719        assert_eq!(
 1720            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1721            editor.display_text(cx),
 1722        );
 1723
 1724        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1725        // Start the cursor at the `k` on the first line
 1726        editor.change_selections(None, window, cx, |s| {
 1727            s.select_display_ranges([
 1728                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1729            ]);
 1730        });
 1731
 1732        // Moving to the beginning of the line should put us at the beginning of the line.
 1733        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1734        assert_eq!(
 1735            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1736            editor.selections.display_ranges(cx)
 1737        );
 1738
 1739        // Moving to the end of the line should put us at the end of the line.
 1740        editor.move_to_end_of_line(&move_to_end, window, cx);
 1741        assert_eq!(
 1742            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1743            editor.selections.display_ranges(cx)
 1744        );
 1745
 1746        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1747        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1748        editor.change_selections(None, window, cx, |s| {
 1749            s.select_display_ranges([
 1750                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1751            ]);
 1752        });
 1753
 1754        // Moving to the beginning of the line should put us at the start of the second line of
 1755        // display text, i.e., the `j`.
 1756        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1757        assert_eq!(
 1758            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1759            editor.selections.display_ranges(cx)
 1760        );
 1761
 1762        // Moving to the beginning of the line again should be a no-op.
 1763        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1764        assert_eq!(
 1765            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1766            editor.selections.display_ranges(cx)
 1767        );
 1768
 1769        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1770        // next display line.
 1771        editor.move_to_end_of_line(&move_to_end, window, cx);
 1772        assert_eq!(
 1773            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1774            editor.selections.display_ranges(cx)
 1775        );
 1776
 1777        // Moving to the end of the line again should be a no-op.
 1778        editor.move_to_end_of_line(&move_to_end, window, cx);
 1779        assert_eq!(
 1780            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1781            editor.selections.display_ranges(cx)
 1782        );
 1783    });
 1784}
 1785
 1786#[gpui::test]
 1787fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1788    init_test(cx, |_| {});
 1789
 1790    let move_to_beg = MoveToBeginningOfLine {
 1791        stop_at_soft_wraps: true,
 1792        stop_at_indent: true,
 1793    };
 1794
 1795    let select_to_beg = SelectToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let delete_to_beg = DeleteToBeginningOfLine {
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let move_to_end = MoveToEndOfLine {
 1805        stop_at_soft_wraps: false,
 1806    };
 1807
 1808    let editor = cx.add_window(|window, cx| {
 1809        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1810        build_editor(buffer, window, cx)
 1811    });
 1812
 1813    _ = editor.update(cx, |editor, window, cx| {
 1814        editor.change_selections(None, window, cx, |s| {
 1815            s.select_display_ranges([
 1816                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1817                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1818            ]);
 1819        });
 1820
 1821        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1822        // and the second cursor at the first non-whitespace character in the line.
 1823        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1824        assert_eq!(
 1825            editor.selections.display_ranges(cx),
 1826            &[
 1827                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1828                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1829            ]
 1830        );
 1831
 1832        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1833        // and should move the second cursor to the beginning of the line.
 1834        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1835        assert_eq!(
 1836            editor.selections.display_ranges(cx),
 1837            &[
 1838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1839                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1840            ]
 1841        );
 1842
 1843        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1844        // and should move the second cursor back to the first non-whitespace character in the line.
 1845        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1846        assert_eq!(
 1847            editor.selections.display_ranges(cx),
 1848            &[
 1849                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1850                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1851            ]
 1852        );
 1853
 1854        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1855        // and to the first non-whitespace character in the line for the second cursor.
 1856        editor.move_to_end_of_line(&move_to_end, window, cx);
 1857        editor.move_left(&MoveLeft, window, cx);
 1858        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1859        assert_eq!(
 1860            editor.selections.display_ranges(cx),
 1861            &[
 1862                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1863                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1864            ]
 1865        );
 1866
 1867        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1868        // and should select to the beginning of the line for the second cursor.
 1869        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1870        assert_eq!(
 1871            editor.selections.display_ranges(cx),
 1872            &[
 1873                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1874                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1875            ]
 1876        );
 1877
 1878        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1879        // and should delete to the first non-whitespace character in the line for the second cursor.
 1880        editor.move_to_end_of_line(&move_to_end, window, cx);
 1881        editor.move_left(&MoveLeft, window, cx);
 1882        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1883        assert_eq!(editor.text(cx), "c\n  f");
 1884    });
 1885}
 1886
 1887#[gpui::test]
 1888fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1889    init_test(cx, |_| {});
 1890
 1891    let editor = cx.add_window(|window, cx| {
 1892        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1893        build_editor(buffer, window, cx)
 1894    });
 1895    _ = editor.update(cx, |editor, window, cx| {
 1896        editor.change_selections(None, window, cx, |s| {
 1897            s.select_display_ranges([
 1898                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1899                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1900            ])
 1901        });
 1902
 1903        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1904        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1905
 1906        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1907        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1919        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1922        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1926
 1927        editor.move_right(&MoveRight, window, cx);
 1928        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1929        assert_selection_ranges(
 1930            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1931            editor,
 1932            cx,
 1933        );
 1934
 1935        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1936        assert_selection_ranges(
 1937            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1938            editor,
 1939            cx,
 1940        );
 1941
 1942        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1943        assert_selection_ranges(
 1944            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1945            editor,
 1946            cx,
 1947        );
 1948    });
 1949}
 1950
 1951#[gpui::test]
 1952fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1953    init_test(cx, |_| {});
 1954
 1955    let editor = cx.add_window(|window, cx| {
 1956        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1957        build_editor(buffer, window, cx)
 1958    });
 1959
 1960    _ = editor.update(cx, |editor, window, cx| {
 1961        editor.set_wrap_width(Some(140.0.into()), cx);
 1962        assert_eq!(
 1963            editor.display_text(cx),
 1964            "use one::{\n    two::three::\n    four::five\n};"
 1965        );
 1966
 1967        editor.change_selections(None, window, cx, |s| {
 1968            s.select_display_ranges([
 1969                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1970            ]);
 1971        });
 1972
 1973        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1974        assert_eq!(
 1975            editor.selections.display_ranges(cx),
 1976            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1977        );
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1995        );
 1996
 1997        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2007        );
 2008    });
 2009}
 2010
 2011#[gpui::test]
 2012async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2013    init_test(cx, |_| {});
 2014    let mut cx = EditorTestContext::new(cx).await;
 2015
 2016    let line_height = cx.editor(|editor, window, _| {
 2017        editor
 2018            .style()
 2019            .unwrap()
 2020            .text
 2021            .line_height_in_pixels(window.rem_size())
 2022    });
 2023    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2024
 2025    cx.set_state(
 2026        &r#"ˇone
 2027        two
 2028
 2029        three
 2030        fourˇ
 2031        five
 2032
 2033        six"#
 2034            .unindent(),
 2035    );
 2036
 2037    cx.update_editor(|editor, window, cx| {
 2038        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2039    });
 2040    cx.assert_editor_state(
 2041        &r#"one
 2042        two
 2043        ˇ
 2044        three
 2045        four
 2046        five
 2047        ˇ
 2048        six"#
 2049            .unindent(),
 2050    );
 2051
 2052    cx.update_editor(|editor, window, cx| {
 2053        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2054    });
 2055    cx.assert_editor_state(
 2056        &r#"one
 2057        two
 2058
 2059        three
 2060        four
 2061        five
 2062        ˇ
 2063        sixˇ"#
 2064            .unindent(),
 2065    );
 2066
 2067    cx.update_editor(|editor, window, cx| {
 2068        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2069    });
 2070    cx.assert_editor_state(
 2071        &r#"one
 2072        two
 2073
 2074        three
 2075        four
 2076        five
 2077
 2078        sixˇ"#
 2079            .unindent(),
 2080    );
 2081
 2082    cx.update_editor(|editor, window, cx| {
 2083        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2084    });
 2085    cx.assert_editor_state(
 2086        &r#"one
 2087        two
 2088
 2089        three
 2090        four
 2091        five
 2092        ˇ
 2093        six"#
 2094            .unindent(),
 2095    );
 2096
 2097    cx.update_editor(|editor, window, cx| {
 2098        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2099    });
 2100    cx.assert_editor_state(
 2101        &r#"one
 2102        two
 2103        ˇ
 2104        three
 2105        four
 2106        five
 2107
 2108        six"#
 2109            .unindent(),
 2110    );
 2111
 2112    cx.update_editor(|editor, window, cx| {
 2113        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2114    });
 2115    cx.assert_editor_state(
 2116        &r#"ˇone
 2117        two
 2118
 2119        three
 2120        four
 2121        five
 2122
 2123        six"#
 2124            .unindent(),
 2125    );
 2126}
 2127
 2128#[gpui::test]
 2129async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2130    init_test(cx, |_| {});
 2131    let mut cx = EditorTestContext::new(cx).await;
 2132    let line_height = cx.editor(|editor, window, _| {
 2133        editor
 2134            .style()
 2135            .unwrap()
 2136            .text
 2137            .line_height_in_pixels(window.rem_size())
 2138    });
 2139    let window = cx.window;
 2140    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2141
 2142    cx.set_state(
 2143        r#"ˇone
 2144        two
 2145        three
 2146        four
 2147        five
 2148        six
 2149        seven
 2150        eight
 2151        nine
 2152        ten
 2153        "#,
 2154    );
 2155
 2156    cx.update_editor(|editor, window, cx| {
 2157        assert_eq!(
 2158            editor.snapshot(window, cx).scroll_position(),
 2159            gpui::Point::new(0., 0.)
 2160        );
 2161        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 3.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 6.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 3.)
 2175        );
 2176
 2177        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 1.)
 2181        );
 2182        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 3.)
 2186        );
 2187    });
 2188}
 2189
 2190#[gpui::test]
 2191async fn test_autoscroll(cx: &mut TestAppContext) {
 2192    init_test(cx, |_| {});
 2193    let mut cx = EditorTestContext::new(cx).await;
 2194
 2195    let line_height = cx.update_editor(|editor, window, cx| {
 2196        editor.set_vertical_scroll_margin(2, cx);
 2197        editor
 2198            .style()
 2199            .unwrap()
 2200            .text
 2201            .line_height_in_pixels(window.rem_size())
 2202    });
 2203    let window = cx.window;
 2204    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2205
 2206    cx.set_state(
 2207        r#"ˇone
 2208            two
 2209            three
 2210            four
 2211            five
 2212            six
 2213            seven
 2214            eight
 2215            nine
 2216            ten
 2217        "#,
 2218    );
 2219    cx.update_editor(|editor, window, cx| {
 2220        assert_eq!(
 2221            editor.snapshot(window, cx).scroll_position(),
 2222            gpui::Point::new(0., 0.0)
 2223        );
 2224    });
 2225
 2226    // Add a cursor below the visible area. Since both cursors cannot fit
 2227    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2228    // allows the vertical scroll margin below that cursor.
 2229    cx.update_editor(|editor, window, cx| {
 2230        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2231            selections.select_ranges([
 2232                Point::new(0, 0)..Point::new(0, 0),
 2233                Point::new(6, 0)..Point::new(6, 0),
 2234            ]);
 2235        })
 2236    });
 2237    cx.update_editor(|editor, window, cx| {
 2238        assert_eq!(
 2239            editor.snapshot(window, cx).scroll_position(),
 2240            gpui::Point::new(0., 3.0)
 2241        );
 2242    });
 2243
 2244    // Move down. The editor cursor scrolls down to track the newest cursor.
 2245    cx.update_editor(|editor, window, cx| {
 2246        editor.move_down(&Default::default(), window, cx);
 2247    });
 2248    cx.update_editor(|editor, window, cx| {
 2249        assert_eq!(
 2250            editor.snapshot(window, cx).scroll_position(),
 2251            gpui::Point::new(0., 4.0)
 2252        );
 2253    });
 2254
 2255    // Add a cursor above the visible area. Since both cursors fit on screen,
 2256    // the editor scrolls to show both.
 2257    cx.update_editor(|editor, window, cx| {
 2258        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2259            selections.select_ranges([
 2260                Point::new(1, 0)..Point::new(1, 0),
 2261                Point::new(6, 0)..Point::new(6, 0),
 2262            ]);
 2263        })
 2264    });
 2265    cx.update_editor(|editor, window, cx| {
 2266        assert_eq!(
 2267            editor.snapshot(window, cx).scroll_position(),
 2268            gpui::Point::new(0., 1.0)
 2269        );
 2270    });
 2271}
 2272
 2273#[gpui::test]
 2274async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2275    init_test(cx, |_| {});
 2276    let mut cx = EditorTestContext::new(cx).await;
 2277
 2278    let line_height = cx.editor(|editor, window, _cx| {
 2279        editor
 2280            .style()
 2281            .unwrap()
 2282            .text
 2283            .line_height_in_pixels(window.rem_size())
 2284    });
 2285    let window = cx.window;
 2286    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2287    cx.set_state(
 2288        &r#"
 2289        ˇone
 2290        two
 2291        threeˇ
 2292        four
 2293        five
 2294        six
 2295        seven
 2296        eight
 2297        nine
 2298        ten
 2299        "#
 2300        .unindent(),
 2301    );
 2302
 2303    cx.update_editor(|editor, window, cx| {
 2304        editor.move_page_down(&MovePageDown::default(), window, cx)
 2305    });
 2306    cx.assert_editor_state(
 2307        &r#"
 2308        one
 2309        two
 2310        three
 2311        ˇfour
 2312        five
 2313        sixˇ
 2314        seven
 2315        eight
 2316        nine
 2317        ten
 2318        "#
 2319        .unindent(),
 2320    );
 2321
 2322    cx.update_editor(|editor, window, cx| {
 2323        editor.move_page_down(&MovePageDown::default(), window, cx)
 2324    });
 2325    cx.assert_editor_state(
 2326        &r#"
 2327        one
 2328        two
 2329        three
 2330        four
 2331        five
 2332        six
 2333        ˇseven
 2334        eight
 2335        nineˇ
 2336        ten
 2337        "#
 2338        .unindent(),
 2339    );
 2340
 2341    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2342    cx.assert_editor_state(
 2343        &r#"
 2344        one
 2345        two
 2346        three
 2347        ˇfour
 2348        five
 2349        sixˇ
 2350        seven
 2351        eight
 2352        nine
 2353        ten
 2354        "#
 2355        .unindent(),
 2356    );
 2357
 2358    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2359    cx.assert_editor_state(
 2360        &r#"
 2361        ˇone
 2362        two
 2363        threeˇ
 2364        four
 2365        five
 2366        six
 2367        seven
 2368        eight
 2369        nine
 2370        ten
 2371        "#
 2372        .unindent(),
 2373    );
 2374
 2375    // Test select collapsing
 2376    cx.update_editor(|editor, window, cx| {
 2377        editor.move_page_down(&MovePageDown::default(), window, cx);
 2378        editor.move_page_down(&MovePageDown::default(), window, cx);
 2379        editor.move_page_down(&MovePageDown::default(), window, cx);
 2380    });
 2381    cx.assert_editor_state(
 2382        &r#"
 2383        one
 2384        two
 2385        three
 2386        four
 2387        five
 2388        six
 2389        seven
 2390        eight
 2391        nine
 2392        ˇten
 2393        ˇ"#
 2394        .unindent(),
 2395    );
 2396}
 2397
 2398#[gpui::test]
 2399async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2400    init_test(cx, |_| {});
 2401    let mut cx = EditorTestContext::new(cx).await;
 2402    cx.set_state("one «two threeˇ» four");
 2403    cx.update_editor(|editor, window, cx| {
 2404        editor.delete_to_beginning_of_line(
 2405            &DeleteToBeginningOfLine {
 2406                stop_at_indent: false,
 2407            },
 2408            window,
 2409            cx,
 2410        );
 2411        assert_eq!(editor.text(cx), " four");
 2412    });
 2413}
 2414
 2415#[gpui::test]
 2416fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2417    init_test(cx, |_| {});
 2418
 2419    let editor = cx.add_window(|window, cx| {
 2420        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2421        build_editor(buffer.clone(), window, cx)
 2422    });
 2423
 2424    _ = editor.update(cx, |editor, window, cx| {
 2425        editor.change_selections(None, window, cx, |s| {
 2426            s.select_display_ranges([
 2427                // an empty selection - the preceding word fragment is deleted
 2428                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2429                // characters selected - they are deleted
 2430                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2431            ])
 2432        });
 2433        editor.delete_to_previous_word_start(
 2434            &DeleteToPreviousWordStart {
 2435                ignore_newlines: false,
 2436            },
 2437            window,
 2438            cx,
 2439        );
 2440        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2441    });
 2442
 2443    _ = editor.update(cx, |editor, window, cx| {
 2444        editor.change_selections(None, window, cx, |s| {
 2445            s.select_display_ranges([
 2446                // an empty selection - the following word fragment is deleted
 2447                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2448                // characters selected - they are deleted
 2449                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2450            ])
 2451        });
 2452        editor.delete_to_next_word_end(
 2453            &DeleteToNextWordEnd {
 2454                ignore_newlines: false,
 2455            },
 2456            window,
 2457            cx,
 2458        );
 2459        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2460    });
 2461}
 2462
 2463#[gpui::test]
 2464fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2465    init_test(cx, |_| {});
 2466
 2467    let editor = cx.add_window(|window, cx| {
 2468        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2469        build_editor(buffer.clone(), window, cx)
 2470    });
 2471    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2472        ignore_newlines: false,
 2473    };
 2474    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2475        ignore_newlines: true,
 2476    };
 2477
 2478    _ = editor.update(cx, |editor, window, cx| {
 2479        editor.change_selections(None, window, cx, |s| {
 2480            s.select_display_ranges([
 2481                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2482            ])
 2483        });
 2484        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2485        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2486        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2487        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2496    });
 2497}
 2498
 2499#[gpui::test]
 2500fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2501    init_test(cx, |_| {});
 2502
 2503    let editor = cx.add_window(|window, cx| {
 2504        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2505        build_editor(buffer.clone(), window, cx)
 2506    });
 2507    let del_to_next_word_end = DeleteToNextWordEnd {
 2508        ignore_newlines: false,
 2509    };
 2510    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2511        ignore_newlines: true,
 2512    };
 2513
 2514    _ = editor.update(cx, |editor, window, cx| {
 2515        editor.change_selections(None, window, cx, |s| {
 2516            s.select_display_ranges([
 2517                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2518            ])
 2519        });
 2520        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2521        assert_eq!(
 2522            editor.buffer.read(cx).read(cx).text(),
 2523            "one\n   two\nthree\n   four"
 2524        );
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2537        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2538        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2539        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2541    });
 2542}
 2543
 2544#[gpui::test]
 2545fn test_newline(cx: &mut TestAppContext) {
 2546    init_test(cx, |_| {});
 2547
 2548    let editor = cx.add_window(|window, cx| {
 2549        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2550        build_editor(buffer.clone(), window, cx)
 2551    });
 2552
 2553    _ = editor.update(cx, |editor, window, cx| {
 2554        editor.change_selections(None, window, cx, |s| {
 2555            s.select_display_ranges([
 2556                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2557                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2558                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2559            ])
 2560        });
 2561
 2562        editor.newline(&Newline, window, cx);
 2563        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2564    });
 2565}
 2566
 2567#[gpui::test]
 2568fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2569    init_test(cx, |_| {});
 2570
 2571    let editor = cx.add_window(|window, cx| {
 2572        let buffer = MultiBuffer::build_simple(
 2573            "
 2574                a
 2575                b(
 2576                    X
 2577                )
 2578                c(
 2579                    X
 2580                )
 2581            "
 2582            .unindent()
 2583            .as_str(),
 2584            cx,
 2585        );
 2586        let mut editor = build_editor(buffer.clone(), window, cx);
 2587        editor.change_selections(None, window, cx, |s| {
 2588            s.select_ranges([
 2589                Point::new(2, 4)..Point::new(2, 5),
 2590                Point::new(5, 4)..Point::new(5, 5),
 2591            ])
 2592        });
 2593        editor
 2594    });
 2595
 2596    _ = editor.update(cx, |editor, window, cx| {
 2597        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2598        editor.buffer.update(cx, |buffer, cx| {
 2599            buffer.edit(
 2600                [
 2601                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2602                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2603                ],
 2604                None,
 2605                cx,
 2606            );
 2607            assert_eq!(
 2608                buffer.read(cx).text(),
 2609                "
 2610                    a
 2611                    b()
 2612                    c()
 2613                "
 2614                .unindent()
 2615            );
 2616        });
 2617        assert_eq!(
 2618            editor.selections.ranges(cx),
 2619            &[
 2620                Point::new(1, 2)..Point::new(1, 2),
 2621                Point::new(2, 2)..Point::new(2, 2),
 2622            ],
 2623        );
 2624
 2625        editor.newline(&Newline, window, cx);
 2626        assert_eq!(
 2627            editor.text(cx),
 2628            "
 2629                a
 2630                b(
 2631                )
 2632                c(
 2633                )
 2634            "
 2635            .unindent()
 2636        );
 2637
 2638        // The selections are moved after the inserted newlines
 2639        assert_eq!(
 2640            editor.selections.ranges(cx),
 2641            &[
 2642                Point::new(2, 0)..Point::new(2, 0),
 2643                Point::new(4, 0)..Point::new(4, 0),
 2644            ],
 2645        );
 2646    });
 2647}
 2648
 2649#[gpui::test]
 2650async fn test_newline_above(cx: &mut TestAppContext) {
 2651    init_test(cx, |settings| {
 2652        settings.defaults.tab_size = NonZeroU32::new(4)
 2653    });
 2654
 2655    let language = Arc::new(
 2656        Language::new(
 2657            LanguageConfig::default(),
 2658            Some(tree_sitter_rust::LANGUAGE.into()),
 2659        )
 2660        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2661        .unwrap(),
 2662    );
 2663
 2664    let mut cx = EditorTestContext::new(cx).await;
 2665    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2666    cx.set_state(indoc! {"
 2667        const a: ˇA = (
 2668 2669                «const_functionˇ»(ˇ),
 2670                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2671 2672        ˇ);ˇ
 2673    "});
 2674
 2675    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        ˇ
 2678        const a: A = (
 2679            ˇ
 2680            (
 2681                ˇ
 2682                ˇ
 2683                const_function(),
 2684                ˇ
 2685                ˇ
 2686                ˇ
 2687                ˇ
 2688                something_else,
 2689                ˇ
 2690            )
 2691            ˇ
 2692            ˇ
 2693        );
 2694    "});
 2695}
 2696
 2697#[gpui::test]
 2698async fn test_newline_below(cx: &mut TestAppContext) {
 2699    init_test(cx, |settings| {
 2700        settings.defaults.tab_size = NonZeroU32::new(4)
 2701    });
 2702
 2703    let language = Arc::new(
 2704        Language::new(
 2705            LanguageConfig::default(),
 2706            Some(tree_sitter_rust::LANGUAGE.into()),
 2707        )
 2708        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2709        .unwrap(),
 2710    );
 2711
 2712    let mut cx = EditorTestContext::new(cx).await;
 2713    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2714    cx.set_state(indoc! {"
 2715        const a: ˇA = (
 2716 2717                «const_functionˇ»(ˇ),
 2718                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2719 2720        ˇ);ˇ
 2721    "});
 2722
 2723    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2724    cx.assert_editor_state(indoc! {"
 2725        const a: A = (
 2726            ˇ
 2727            (
 2728                ˇ
 2729                const_function(),
 2730                ˇ
 2731                ˇ
 2732                something_else,
 2733                ˇ
 2734                ˇ
 2735                ˇ
 2736                ˇ
 2737            )
 2738            ˇ
 2739        );
 2740        ˇ
 2741        ˇ
 2742    "});
 2743}
 2744
 2745#[gpui::test]
 2746async fn test_newline_comments(cx: &mut TestAppContext) {
 2747    init_test(cx, |settings| {
 2748        settings.defaults.tab_size = NonZeroU32::new(4)
 2749    });
 2750
 2751    let language = Arc::new(Language::new(
 2752        LanguageConfig {
 2753            line_comments: vec!["//".into()],
 2754            ..LanguageConfig::default()
 2755        },
 2756        None,
 2757    ));
 2758    {
 2759        let mut cx = EditorTestContext::new(cx).await;
 2760        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2761        cx.set_state(indoc! {"
 2762        // Fooˇ
 2763    "});
 2764
 2765        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2766        cx.assert_editor_state(indoc! {"
 2767        // Foo
 2768        //ˇ
 2769    "});
 2770        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2771        cx.set_state(indoc! {"
 2772        ˇ// Foo
 2773    "});
 2774        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2775        cx.assert_editor_state(indoc! {"
 2776
 2777        ˇ// Foo
 2778    "});
 2779    }
 2780    // Ensure that comment continuations can be disabled.
 2781    update_test_language_settings(cx, |settings| {
 2782        settings.defaults.extend_comment_on_newline = Some(false);
 2783    });
 2784    let mut cx = EditorTestContext::new(cx).await;
 2785    cx.set_state(indoc! {"
 2786        // Fooˇ
 2787    "});
 2788    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        // Foo
 2791        ˇ
 2792    "});
 2793}
 2794
 2795#[gpui::test]
 2796fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2797    init_test(cx, |_| {});
 2798
 2799    let editor = cx.add_window(|window, cx| {
 2800        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2801        let mut editor = build_editor(buffer.clone(), window, cx);
 2802        editor.change_selections(None, window, cx, |s| {
 2803            s.select_ranges([3..4, 11..12, 19..20])
 2804        });
 2805        editor
 2806    });
 2807
 2808    _ = editor.update(cx, |editor, window, cx| {
 2809        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2810        editor.buffer.update(cx, |buffer, cx| {
 2811            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2812            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2813        });
 2814        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2815
 2816        editor.insert("Z", window, cx);
 2817        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2818
 2819        // The selections are moved after the inserted characters
 2820        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2821    });
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_tab(cx: &mut TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.tab_size = NonZeroU32::new(3)
 2828    });
 2829
 2830    let mut cx = EditorTestContext::new(cx).await;
 2831    cx.set_state(indoc! {"
 2832        ˇabˇc
 2833        ˇ🏀ˇ🏀ˇefg
 2834 2835    "});
 2836    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838           ˇab ˇc
 2839           ˇ🏀  ˇ🏀  ˇefg
 2840        d  ˇ
 2841    "});
 2842
 2843    cx.set_state(indoc! {"
 2844        a
 2845        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2846    "});
 2847    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2848    cx.assert_editor_state(indoc! {"
 2849        a
 2850           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852}
 2853
 2854#[gpui::test]
 2855async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2856    init_test(cx, |_| {});
 2857
 2858    let mut cx = EditorTestContext::new(cx).await;
 2859    let language = Arc::new(
 2860        Language::new(
 2861            LanguageConfig::default(),
 2862            Some(tree_sitter_rust::LANGUAGE.into()),
 2863        )
 2864        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2865        .unwrap(),
 2866    );
 2867    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2868
 2869    // cursors that are already at the suggested indent level insert
 2870    // a soft tab. cursors that are to the left of the suggested indent
 2871    // auto-indent their line.
 2872    cx.set_state(indoc! {"
 2873        ˇ
 2874        const a: B = (
 2875            c(
 2876                d(
 2877        ˇ
 2878                )
 2879        ˇ
 2880        ˇ    )
 2881        );
 2882    "});
 2883    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2884    cx.assert_editor_state(indoc! {"
 2885            ˇ
 2886        const a: B = (
 2887            c(
 2888                d(
 2889                    ˇ
 2890                )
 2891                ˇ
 2892            ˇ)
 2893        );
 2894    "});
 2895
 2896    // handle auto-indent when there are multiple cursors on the same line
 2897    cx.set_state(indoc! {"
 2898        const a: B = (
 2899            c(
 2900        ˇ    ˇ
 2901        ˇ    )
 2902        );
 2903    "});
 2904    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        const a: B = (
 2907            c(
 2908                ˇ
 2909            ˇ)
 2910        );
 2911    "});
 2912}
 2913
 2914#[gpui::test]
 2915async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2916    init_test(cx, |settings| {
 2917        settings.defaults.tab_size = NonZeroU32::new(4)
 2918    });
 2919
 2920    let language = Arc::new(
 2921        Language::new(
 2922            LanguageConfig::default(),
 2923            Some(tree_sitter_rust::LANGUAGE.into()),
 2924        )
 2925        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2926        .unwrap(),
 2927    );
 2928
 2929    let mut cx = EditorTestContext::new(cx).await;
 2930    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2931    cx.set_state(indoc! {"
 2932        fn a() {
 2933            if b {
 2934        \t ˇc
 2935            }
 2936        }
 2937    "});
 2938
 2939    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        fn a() {
 2942            if b {
 2943                ˇc
 2944            }
 2945        }
 2946    "});
 2947}
 2948
 2949#[gpui::test]
 2950async fn test_indent_outdent(cx: &mut TestAppContext) {
 2951    init_test(cx, |settings| {
 2952        settings.defaults.tab_size = NonZeroU32::new(4);
 2953    });
 2954
 2955    let mut cx = EditorTestContext::new(cx).await;
 2956
 2957    cx.set_state(indoc! {"
 2958          «oneˇ» «twoˇ»
 2959        three
 2960         four
 2961    "});
 2962    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2963    cx.assert_editor_state(indoc! {"
 2964            «oneˇ» «twoˇ»
 2965        three
 2966         four
 2967    "});
 2968
 2969    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2970    cx.assert_editor_state(indoc! {"
 2971        «oneˇ» «twoˇ»
 2972        three
 2973         four
 2974    "});
 2975
 2976    // select across line ending
 2977    cx.set_state(indoc! {"
 2978        one two
 2979        t«hree
 2980        ˇ» four
 2981    "});
 2982    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2983    cx.assert_editor_state(indoc! {"
 2984        one two
 2985            t«hree
 2986        ˇ» four
 2987    "});
 2988
 2989    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2990    cx.assert_editor_state(indoc! {"
 2991        one two
 2992        t«hree
 2993        ˇ» four
 2994    "});
 2995
 2996    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2997    cx.set_state(indoc! {"
 2998        one two
 2999        ˇthree
 3000            four
 3001    "});
 3002    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3003    cx.assert_editor_state(indoc! {"
 3004        one two
 3005            ˇthree
 3006            four
 3007    "});
 3008
 3009    cx.set_state(indoc! {"
 3010        one two
 3011        ˇ    three
 3012            four
 3013    "});
 3014    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3015    cx.assert_editor_state(indoc! {"
 3016        one two
 3017        ˇthree
 3018            four
 3019    "});
 3020}
 3021
 3022#[gpui::test]
 3023async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3024    init_test(cx, |settings| {
 3025        settings.defaults.hard_tabs = Some(true);
 3026    });
 3027
 3028    let mut cx = EditorTestContext::new(cx).await;
 3029
 3030    // select two ranges on one line
 3031    cx.set_state(indoc! {"
 3032        «oneˇ» «twoˇ»
 3033        three
 3034        four
 3035    "});
 3036    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3037    cx.assert_editor_state(indoc! {"
 3038        \t«oneˇ» «twoˇ»
 3039        three
 3040        four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        \t\t«oneˇ» «twoˇ»
 3045        three
 3046        four
 3047    "});
 3048    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        \t«oneˇ» «twoˇ»
 3051        three
 3052        four
 3053    "});
 3054    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3055    cx.assert_editor_state(indoc! {"
 3056        «oneˇ» «twoˇ»
 3057        three
 3058        four
 3059    "});
 3060
 3061    // select across a line ending
 3062    cx.set_state(indoc! {"
 3063        one two
 3064        t«hree
 3065        ˇ»four
 3066    "});
 3067    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3068    cx.assert_editor_state(indoc! {"
 3069        one two
 3070        \tt«hree
 3071        ˇ»four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        one two
 3076        \t\tt«hree
 3077        ˇ»four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        one two
 3082        \tt«hree
 3083        ˇ»four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        one two
 3088        t«hree
 3089        ˇ»four
 3090    "});
 3091
 3092    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3093    cx.set_state(indoc! {"
 3094        one two
 3095        ˇthree
 3096        four
 3097    "});
 3098    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3099    cx.assert_editor_state(indoc! {"
 3100        one two
 3101        ˇthree
 3102        four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        \tˇthree
 3108        four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        ˇthree
 3114        four
 3115    "});
 3116}
 3117
 3118#[gpui::test]
 3119fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3120    init_test(cx, |settings| {
 3121        settings.languages.extend([
 3122            (
 3123                "TOML".into(),
 3124                LanguageSettingsContent {
 3125                    tab_size: NonZeroU32::new(2),
 3126                    ..Default::default()
 3127                },
 3128            ),
 3129            (
 3130                "Rust".into(),
 3131                LanguageSettingsContent {
 3132                    tab_size: NonZeroU32::new(4),
 3133                    ..Default::default()
 3134                },
 3135            ),
 3136        ]);
 3137    });
 3138
 3139    let toml_language = Arc::new(Language::new(
 3140        LanguageConfig {
 3141            name: "TOML".into(),
 3142            ..Default::default()
 3143        },
 3144        None,
 3145    ));
 3146    let rust_language = Arc::new(Language::new(
 3147        LanguageConfig {
 3148            name: "Rust".into(),
 3149            ..Default::default()
 3150        },
 3151        None,
 3152    ));
 3153
 3154    let toml_buffer =
 3155        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3156    let rust_buffer =
 3157        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3158    let multibuffer = cx.new(|cx| {
 3159        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3160        multibuffer.push_excerpts(
 3161            toml_buffer.clone(),
 3162            [ExcerptRange {
 3163                context: Point::new(0, 0)..Point::new(2, 0),
 3164                primary: None,
 3165            }],
 3166            cx,
 3167        );
 3168        multibuffer.push_excerpts(
 3169            rust_buffer.clone(),
 3170            [ExcerptRange {
 3171                context: Point::new(0, 0)..Point::new(1, 0),
 3172                primary: None,
 3173            }],
 3174            cx,
 3175        );
 3176        multibuffer
 3177    });
 3178
 3179    cx.add_window(|window, cx| {
 3180        let mut editor = build_editor(multibuffer, window, cx);
 3181
 3182        assert_eq!(
 3183            editor.text(cx),
 3184            indoc! {"
 3185                a = 1
 3186                b = 2
 3187
 3188                const c: usize = 3;
 3189            "}
 3190        );
 3191
 3192        select_ranges(
 3193            &mut editor,
 3194            indoc! {"
 3195                «aˇ» = 1
 3196                b = 2
 3197
 3198                «const c:ˇ» usize = 3;
 3199            "},
 3200            window,
 3201            cx,
 3202        );
 3203
 3204        editor.tab(&Tab, window, cx);
 3205        assert_text_with_selections(
 3206            &mut editor,
 3207            indoc! {"
 3208                  «aˇ» = 1
 3209                b = 2
 3210
 3211                    «const c:ˇ» usize = 3;
 3212            "},
 3213            cx,
 3214        );
 3215        editor.backtab(&Backtab, window, cx);
 3216        assert_text_with_selections(
 3217            &mut editor,
 3218            indoc! {"
 3219                «aˇ» = 1
 3220                b = 2
 3221
 3222                «const c:ˇ» usize = 3;
 3223            "},
 3224            cx,
 3225        );
 3226
 3227        editor
 3228    });
 3229}
 3230
 3231#[gpui::test]
 3232async fn test_backspace(cx: &mut TestAppContext) {
 3233    init_test(cx, |_| {});
 3234
 3235    let mut cx = EditorTestContext::new(cx).await;
 3236
 3237    // Basic backspace
 3238    cx.set_state(indoc! {"
 3239        onˇe two three
 3240        fou«rˇ» five six
 3241        seven «ˇeight nine
 3242        »ten
 3243    "});
 3244    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3245    cx.assert_editor_state(indoc! {"
 3246        oˇe two three
 3247        fouˇ five six
 3248        seven ˇten
 3249    "});
 3250
 3251    // Test backspace inside and around indents
 3252    cx.set_state(indoc! {"
 3253        zero
 3254            ˇone
 3255                ˇtwo
 3256            ˇ ˇ ˇ  three
 3257        ˇ  ˇ  four
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        zero
 3262        ˇone
 3263            ˇtwo
 3264        ˇ  threeˇ  four
 3265    "});
 3266
 3267    // Test backspace with line_mode set to true
 3268    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3269    cx.set_state(indoc! {"
 3270        The ˇquick ˇbrown
 3271        fox jumps over
 3272        the lazy dog
 3273        ˇThe qu«ick bˇ»rown"});
 3274    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3275    cx.assert_editor_state(indoc! {"
 3276        ˇfox jumps over
 3277        the lazy dogˇ"});
 3278}
 3279
 3280#[gpui::test]
 3281async fn test_delete(cx: &mut TestAppContext) {
 3282    init_test(cx, |_| {});
 3283
 3284    let mut cx = EditorTestContext::new(cx).await;
 3285    cx.set_state(indoc! {"
 3286        onˇe two three
 3287        fou«rˇ» five six
 3288        seven «ˇeight nine
 3289        »ten
 3290    "});
 3291    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3292    cx.assert_editor_state(indoc! {"
 3293        onˇ two three
 3294        fouˇ five six
 3295        seven ˇten
 3296    "});
 3297
 3298    // Test backspace with line_mode set to true
 3299    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3300    cx.set_state(indoc! {"
 3301        The ˇquick ˇbrown
 3302        fox «ˇjum»ps over
 3303        the lazy dog
 3304        ˇThe qu«ick bˇ»rown"});
 3305    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3306    cx.assert_editor_state("ˇthe lazy dogˇ");
 3307}
 3308
 3309#[gpui::test]
 3310fn test_delete_line(cx: &mut TestAppContext) {
 3311    init_test(cx, |_| {});
 3312
 3313    let editor = cx.add_window(|window, cx| {
 3314        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3315        build_editor(buffer, window, cx)
 3316    });
 3317    _ = editor.update(cx, |editor, window, cx| {
 3318        editor.change_selections(None, window, cx, |s| {
 3319            s.select_display_ranges([
 3320                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3321                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3322                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3323            ])
 3324        });
 3325        editor.delete_line(&DeleteLine, window, cx);
 3326        assert_eq!(editor.display_text(cx), "ghi");
 3327        assert_eq!(
 3328            editor.selections.display_ranges(cx),
 3329            vec![
 3330                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3331                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3332            ]
 3333        );
 3334    });
 3335
 3336    let editor = cx.add_window(|window, cx| {
 3337        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3338        build_editor(buffer, window, cx)
 3339    });
 3340    _ = editor.update(cx, |editor, window, cx| {
 3341        editor.change_selections(None, window, cx, |s| {
 3342            s.select_display_ranges([
 3343                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3344            ])
 3345        });
 3346        editor.delete_line(&DeleteLine, window, cx);
 3347        assert_eq!(editor.display_text(cx), "ghi\n");
 3348        assert_eq!(
 3349            editor.selections.display_ranges(cx),
 3350            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3351        );
 3352    });
 3353}
 3354
 3355#[gpui::test]
 3356fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3357    init_test(cx, |_| {});
 3358
 3359    cx.add_window(|window, cx| {
 3360        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3361        let mut editor = build_editor(buffer.clone(), window, cx);
 3362        let buffer = buffer.read(cx).as_singleton().unwrap();
 3363
 3364        assert_eq!(
 3365            editor.selections.ranges::<Point>(cx),
 3366            &[Point::new(0, 0)..Point::new(0, 0)]
 3367        );
 3368
 3369        // When on single line, replace newline at end by space
 3370        editor.join_lines(&JoinLines, window, cx);
 3371        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3372        assert_eq!(
 3373            editor.selections.ranges::<Point>(cx),
 3374            &[Point::new(0, 3)..Point::new(0, 3)]
 3375        );
 3376
 3377        // When multiple lines are selected, remove newlines that are spanned by the selection
 3378        editor.change_selections(None, window, cx, |s| {
 3379            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3380        });
 3381        editor.join_lines(&JoinLines, window, cx);
 3382        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3383        assert_eq!(
 3384            editor.selections.ranges::<Point>(cx),
 3385            &[Point::new(0, 11)..Point::new(0, 11)]
 3386        );
 3387
 3388        // Undo should be transactional
 3389        editor.undo(&Undo, window, cx);
 3390        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3391        assert_eq!(
 3392            editor.selections.ranges::<Point>(cx),
 3393            &[Point::new(0, 5)..Point::new(2, 2)]
 3394        );
 3395
 3396        // When joining an empty line don't insert a space
 3397        editor.change_selections(None, window, cx, |s| {
 3398            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3399        });
 3400        editor.join_lines(&JoinLines, window, cx);
 3401        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3402        assert_eq!(
 3403            editor.selections.ranges::<Point>(cx),
 3404            [Point::new(2, 3)..Point::new(2, 3)]
 3405        );
 3406
 3407        // We can remove trailing newlines
 3408        editor.join_lines(&JoinLines, window, cx);
 3409        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3410        assert_eq!(
 3411            editor.selections.ranges::<Point>(cx),
 3412            [Point::new(2, 3)..Point::new(2, 3)]
 3413        );
 3414
 3415        // We don't blow up on the last line
 3416        editor.join_lines(&JoinLines, window, cx);
 3417        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3418        assert_eq!(
 3419            editor.selections.ranges::<Point>(cx),
 3420            [Point::new(2, 3)..Point::new(2, 3)]
 3421        );
 3422
 3423        // reset to test indentation
 3424        editor.buffer.update(cx, |buffer, cx| {
 3425            buffer.edit(
 3426                [
 3427                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3428                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3429                ],
 3430                None,
 3431                cx,
 3432            )
 3433        });
 3434
 3435        // We remove any leading spaces
 3436        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3437        editor.change_selections(None, window, cx, |s| {
 3438            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3439        });
 3440        editor.join_lines(&JoinLines, window, cx);
 3441        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3442
 3443        // We don't insert a space for a line containing only spaces
 3444        editor.join_lines(&JoinLines, window, cx);
 3445        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3446
 3447        // We ignore any leading tabs
 3448        editor.join_lines(&JoinLines, window, cx);
 3449        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3450
 3451        editor
 3452    });
 3453}
 3454
 3455#[gpui::test]
 3456fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    cx.add_window(|window, cx| {
 3460        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3461        let mut editor = build_editor(buffer.clone(), window, cx);
 3462        let buffer = buffer.read(cx).as_singleton().unwrap();
 3463
 3464        editor.change_selections(None, window, cx, |s| {
 3465            s.select_ranges([
 3466                Point::new(0, 2)..Point::new(1, 1),
 3467                Point::new(1, 2)..Point::new(1, 2),
 3468                Point::new(3, 1)..Point::new(3, 2),
 3469            ])
 3470        });
 3471
 3472        editor.join_lines(&JoinLines, window, cx);
 3473        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3474
 3475        assert_eq!(
 3476            editor.selections.ranges::<Point>(cx),
 3477            [
 3478                Point::new(0, 7)..Point::new(0, 7),
 3479                Point::new(1, 3)..Point::new(1, 3)
 3480            ]
 3481        );
 3482        editor
 3483    });
 3484}
 3485
 3486#[gpui::test]
 3487async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3488    init_test(cx, |_| {});
 3489
 3490    let mut cx = EditorTestContext::new(cx).await;
 3491
 3492    let diff_base = r#"
 3493        Line 0
 3494        Line 1
 3495        Line 2
 3496        Line 3
 3497        "#
 3498    .unindent();
 3499
 3500    cx.set_state(
 3501        &r#"
 3502        ˇLine 0
 3503        Line 1
 3504        Line 2
 3505        Line 3
 3506        "#
 3507        .unindent(),
 3508    );
 3509
 3510    cx.set_head_text(&diff_base);
 3511    executor.run_until_parked();
 3512
 3513    // Join lines
 3514    cx.update_editor(|editor, window, cx| {
 3515        editor.join_lines(&JoinLines, window, cx);
 3516    });
 3517    executor.run_until_parked();
 3518
 3519    cx.assert_editor_state(
 3520        &r#"
 3521        Line 0ˇ Line 1
 3522        Line 2
 3523        Line 3
 3524        "#
 3525        .unindent(),
 3526    );
 3527    // Join again
 3528    cx.update_editor(|editor, window, cx| {
 3529        editor.join_lines(&JoinLines, window, cx);
 3530    });
 3531    executor.run_until_parked();
 3532
 3533    cx.assert_editor_state(
 3534        &r#"
 3535        Line 0 Line 1ˇ Line 2
 3536        Line 3
 3537        "#
 3538        .unindent(),
 3539    );
 3540}
 3541
 3542#[gpui::test]
 3543async fn test_custom_newlines_cause_no_false_positive_diffs(
 3544    executor: BackgroundExecutor,
 3545    cx: &mut TestAppContext,
 3546) {
 3547    init_test(cx, |_| {});
 3548    let mut cx = EditorTestContext::new(cx).await;
 3549    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3550    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3551    executor.run_until_parked();
 3552
 3553    cx.update_editor(|editor, window, cx| {
 3554        let snapshot = editor.snapshot(window, cx);
 3555        assert_eq!(
 3556            snapshot
 3557                .buffer_snapshot
 3558                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3559                .collect::<Vec<_>>(),
 3560            Vec::new(),
 3561            "Should not have any diffs for files with custom newlines"
 3562        );
 3563    });
 3564}
 3565
 3566#[gpui::test]
 3567async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3568    init_test(cx, |_| {});
 3569
 3570    let mut cx = EditorTestContext::new(cx).await;
 3571
 3572    // Test sort_lines_case_insensitive()
 3573    cx.set_state(indoc! {"
 3574        «z
 3575        y
 3576        x
 3577        Z
 3578        Y
 3579        Xˇ»
 3580    "});
 3581    cx.update_editor(|e, window, cx| {
 3582        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3583    });
 3584    cx.assert_editor_state(indoc! {"
 3585        «x
 3586        X
 3587        y
 3588        Y
 3589        z
 3590        Zˇ»
 3591    "});
 3592
 3593    // Test reverse_lines()
 3594    cx.set_state(indoc! {"
 3595        «5
 3596        4
 3597        3
 3598        2
 3599        1ˇ»
 3600    "});
 3601    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3602    cx.assert_editor_state(indoc! {"
 3603        «1
 3604        2
 3605        3
 3606        4
 3607        5ˇ»
 3608    "});
 3609
 3610    // Skip testing shuffle_line()
 3611
 3612    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3613    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3614
 3615    // Don't manipulate when cursor is on single line, but expand the selection
 3616    cx.set_state(indoc! {"
 3617        ddˇdd
 3618        ccc
 3619        bb
 3620        a
 3621    "});
 3622    cx.update_editor(|e, window, cx| {
 3623        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3624    });
 3625    cx.assert_editor_state(indoc! {"
 3626        «ddddˇ»
 3627        ccc
 3628        bb
 3629        a
 3630    "});
 3631
 3632    // Basic manipulate case
 3633    // Start selection moves to column 0
 3634    // End of selection shrinks to fit shorter line
 3635    cx.set_state(indoc! {"
 3636        dd«d
 3637        ccc
 3638        bb
 3639        aaaaaˇ»
 3640    "});
 3641    cx.update_editor(|e, window, cx| {
 3642        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3643    });
 3644    cx.assert_editor_state(indoc! {"
 3645        «aaaaa
 3646        bb
 3647        ccc
 3648        dddˇ»
 3649    "});
 3650
 3651    // Manipulate case with newlines
 3652    cx.set_state(indoc! {"
 3653        dd«d
 3654        ccc
 3655
 3656        bb
 3657        aaaaa
 3658
 3659        ˇ»
 3660    "});
 3661    cx.update_editor(|e, window, cx| {
 3662        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3663    });
 3664    cx.assert_editor_state(indoc! {"
 3665        «
 3666
 3667        aaaaa
 3668        bb
 3669        ccc
 3670        dddˇ»
 3671
 3672    "});
 3673
 3674    // Adding new line
 3675    cx.set_state(indoc! {"
 3676        aa«a
 3677        bbˇ»b
 3678    "});
 3679    cx.update_editor(|e, window, cx| {
 3680        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3681    });
 3682    cx.assert_editor_state(indoc! {"
 3683        «aaa
 3684        bbb
 3685        added_lineˇ»
 3686    "});
 3687
 3688    // Removing line
 3689    cx.set_state(indoc! {"
 3690        aa«a
 3691        bbbˇ»
 3692    "});
 3693    cx.update_editor(|e, window, cx| {
 3694        e.manipulate_lines(window, cx, |lines| {
 3695            lines.pop();
 3696        })
 3697    });
 3698    cx.assert_editor_state(indoc! {"
 3699        «aaaˇ»
 3700    "});
 3701
 3702    // Removing all lines
 3703    cx.set_state(indoc! {"
 3704        aa«a
 3705        bbbˇ»
 3706    "});
 3707    cx.update_editor(|e, window, cx| {
 3708        e.manipulate_lines(window, cx, |lines| {
 3709            lines.drain(..);
 3710        })
 3711    });
 3712    cx.assert_editor_state(indoc! {"
 3713        ˇ
 3714    "});
 3715}
 3716
 3717#[gpui::test]
 3718async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3719    init_test(cx, |_| {});
 3720
 3721    let mut cx = EditorTestContext::new(cx).await;
 3722
 3723    // Consider continuous selection as single selection
 3724    cx.set_state(indoc! {"
 3725        Aaa«aa
 3726        cˇ»c«c
 3727        bb
 3728        aaaˇ»aa
 3729    "});
 3730    cx.update_editor(|e, window, cx| {
 3731        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3732    });
 3733    cx.assert_editor_state(indoc! {"
 3734        «Aaaaa
 3735        ccc
 3736        bb
 3737        aaaaaˇ»
 3738    "});
 3739
 3740    cx.set_state(indoc! {"
 3741        Aaa«aa
 3742        cˇ»c«c
 3743        bb
 3744        aaaˇ»aa
 3745    "});
 3746    cx.update_editor(|e, window, cx| {
 3747        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3748    });
 3749    cx.assert_editor_state(indoc! {"
 3750        «Aaaaa
 3751        ccc
 3752        bbˇ»
 3753    "});
 3754
 3755    // Consider non continuous selection as distinct dedup operations
 3756    cx.set_state(indoc! {"
 3757        «aaaaa
 3758        bb
 3759        aaaaa
 3760        aaaaaˇ»
 3761
 3762        aaa«aaˇ»
 3763    "});
 3764    cx.update_editor(|e, window, cx| {
 3765        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3766    });
 3767    cx.assert_editor_state(indoc! {"
 3768        «aaaaa
 3769        bbˇ»
 3770
 3771        «aaaaaˇ»
 3772    "});
 3773}
 3774
 3775#[gpui::test]
 3776async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3777    init_test(cx, |_| {});
 3778
 3779    let mut cx = EditorTestContext::new(cx).await;
 3780
 3781    cx.set_state(indoc! {"
 3782        «Aaa
 3783        aAa
 3784        Aaaˇ»
 3785    "});
 3786    cx.update_editor(|e, window, cx| {
 3787        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3788    });
 3789    cx.assert_editor_state(indoc! {"
 3790        «Aaa
 3791        aAaˇ»
 3792    "});
 3793
 3794    cx.set_state(indoc! {"
 3795        «Aaa
 3796        aAa
 3797        aaAˇ»
 3798    "});
 3799    cx.update_editor(|e, window, cx| {
 3800        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3801    });
 3802    cx.assert_editor_state(indoc! {"
 3803        «Aaaˇ»
 3804    "});
 3805}
 3806
 3807#[gpui::test]
 3808async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3809    init_test(cx, |_| {});
 3810
 3811    let mut cx = EditorTestContext::new(cx).await;
 3812
 3813    // Manipulate with multiple selections on a single line
 3814    cx.set_state(indoc! {"
 3815        dd«dd
 3816        cˇ»c«c
 3817        bb
 3818        aaaˇ»aa
 3819    "});
 3820    cx.update_editor(|e, window, cx| {
 3821        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3822    });
 3823    cx.assert_editor_state(indoc! {"
 3824        «aaaaa
 3825        bb
 3826        ccc
 3827        ddddˇ»
 3828    "});
 3829
 3830    // Manipulate with multiple disjoin selections
 3831    cx.set_state(indoc! {"
 3832 3833        4
 3834        3
 3835        2
 3836        1ˇ»
 3837
 3838        dd«dd
 3839        ccc
 3840        bb
 3841        aaaˇ»aa
 3842    "});
 3843    cx.update_editor(|e, window, cx| {
 3844        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3845    });
 3846    cx.assert_editor_state(indoc! {"
 3847        «1
 3848        2
 3849        3
 3850        4
 3851        5ˇ»
 3852
 3853        «aaaaa
 3854        bb
 3855        ccc
 3856        ddddˇ»
 3857    "});
 3858
 3859    // Adding lines on each selection
 3860    cx.set_state(indoc! {"
 3861 3862        1ˇ»
 3863
 3864        bb«bb
 3865        aaaˇ»aa
 3866    "});
 3867    cx.update_editor(|e, window, cx| {
 3868        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3869    });
 3870    cx.assert_editor_state(indoc! {"
 3871        «2
 3872        1
 3873        added lineˇ»
 3874
 3875        «bbbb
 3876        aaaaa
 3877        added lineˇ»
 3878    "});
 3879
 3880    // Removing lines on each selection
 3881    cx.set_state(indoc! {"
 3882 3883        1ˇ»
 3884
 3885        bb«bb
 3886        aaaˇ»aa
 3887    "});
 3888    cx.update_editor(|e, window, cx| {
 3889        e.manipulate_lines(window, cx, |lines| {
 3890            lines.pop();
 3891        })
 3892    });
 3893    cx.assert_editor_state(indoc! {"
 3894        «2ˇ»
 3895
 3896        «bbbbˇ»
 3897    "});
 3898}
 3899
 3900#[gpui::test]
 3901async fn test_manipulate_text(cx: &mut TestAppContext) {
 3902    init_test(cx, |_| {});
 3903
 3904    let mut cx = EditorTestContext::new(cx).await;
 3905
 3906    // Test convert_to_upper_case()
 3907    cx.set_state(indoc! {"
 3908        «hello worldˇ»
 3909    "});
 3910    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3911    cx.assert_editor_state(indoc! {"
 3912        «HELLO WORLDˇ»
 3913    "});
 3914
 3915    // Test convert_to_lower_case()
 3916    cx.set_state(indoc! {"
 3917        «HELLO WORLDˇ»
 3918    "});
 3919    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3920    cx.assert_editor_state(indoc! {"
 3921        «hello worldˇ»
 3922    "});
 3923
 3924    // Test multiple line, single selection case
 3925    cx.set_state(indoc! {"
 3926        «The quick brown
 3927        fox jumps over
 3928        the lazy dogˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «The Quick Brown
 3933        Fox Jumps Over
 3934        The Lazy Dogˇ»
 3935    "});
 3936
 3937    // Test multiple line, single selection case
 3938    cx.set_state(indoc! {"
 3939        «The quick brown
 3940        fox jumps over
 3941        the lazy dogˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| {
 3944        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3945    });
 3946    cx.assert_editor_state(indoc! {"
 3947        «TheQuickBrown
 3948        FoxJumpsOver
 3949        TheLazyDogˇ»
 3950    "});
 3951
 3952    // From here on out, test more complex cases of manipulate_text()
 3953
 3954    // Test no selection case - should affect words cursors are in
 3955    // Cursor at beginning, middle, and end of word
 3956    cx.set_state(indoc! {"
 3957        ˇhello big beauˇtiful worldˇ
 3958    "});
 3959    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3960    cx.assert_editor_state(indoc! {"
 3961        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3962    "});
 3963
 3964    // Test multiple selections on a single line and across multiple lines
 3965    cx.set_state(indoc! {"
 3966        «Theˇ» quick «brown
 3967        foxˇ» jumps «overˇ»
 3968        the «lazyˇ» dog
 3969    "});
 3970    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        «THEˇ» quick «BROWN
 3973        FOXˇ» jumps «OVERˇ»
 3974        the «LAZYˇ» dog
 3975    "});
 3976
 3977    // Test case where text length grows
 3978    cx.set_state(indoc! {"
 3979        «tschüߡ»
 3980    "});
 3981    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3982    cx.assert_editor_state(indoc! {"
 3983        «TSCHÜSSˇ»
 3984    "});
 3985
 3986    // Test to make sure we don't crash when text shrinks
 3987    cx.set_state(indoc! {"
 3988        aaa_bbbˇ
 3989    "});
 3990    cx.update_editor(|e, window, cx| {
 3991        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3992    });
 3993    cx.assert_editor_state(indoc! {"
 3994        «aaaBbbˇ»
 3995    "});
 3996
 3997    // Test to make sure we all aware of the fact that each word can grow and shrink
 3998    // Final selections should be aware of this fact
 3999    cx.set_state(indoc! {"
 4000        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4001    "});
 4002    cx.update_editor(|e, window, cx| {
 4003        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4004    });
 4005    cx.assert_editor_state(indoc! {"
 4006        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4007    "});
 4008
 4009    cx.set_state(indoc! {"
 4010        «hElLo, WoRld!ˇ»
 4011    "});
 4012    cx.update_editor(|e, window, cx| {
 4013        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4014    });
 4015    cx.assert_editor_state(indoc! {"
 4016        «HeLlO, wOrLD!ˇ»
 4017    "});
 4018}
 4019
 4020#[gpui::test]
 4021fn test_duplicate_line(cx: &mut TestAppContext) {
 4022    init_test(cx, |_| {});
 4023
 4024    let editor = cx.add_window(|window, cx| {
 4025        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4026        build_editor(buffer, window, cx)
 4027    });
 4028    _ = editor.update(cx, |editor, window, cx| {
 4029        editor.change_selections(None, window, cx, |s| {
 4030            s.select_display_ranges([
 4031                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4032                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4033                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4034                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4035            ])
 4036        });
 4037        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4038        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4039        assert_eq!(
 4040            editor.selections.display_ranges(cx),
 4041            vec![
 4042                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4043                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4044                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4045                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4046            ]
 4047        );
 4048    });
 4049
 4050    let editor = cx.add_window(|window, cx| {
 4051        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4052        build_editor(buffer, window, cx)
 4053    });
 4054    _ = editor.update(cx, |editor, window, cx| {
 4055        editor.change_selections(None, window, cx, |s| {
 4056            s.select_display_ranges([
 4057                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4058                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4059            ])
 4060        });
 4061        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4062        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4063        assert_eq!(
 4064            editor.selections.display_ranges(cx),
 4065            vec![
 4066                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4067                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4068            ]
 4069        );
 4070    });
 4071
 4072    // With `move_upwards` the selections stay in place, except for
 4073    // the lines inserted above them
 4074    let editor = cx.add_window(|window, cx| {
 4075        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4076        build_editor(buffer, window, cx)
 4077    });
 4078    _ = editor.update(cx, |editor, window, cx| {
 4079        editor.change_selections(None, window, cx, |s| {
 4080            s.select_display_ranges([
 4081                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4082                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4083                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4084                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4085            ])
 4086        });
 4087        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4088        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4089        assert_eq!(
 4090            editor.selections.display_ranges(cx),
 4091            vec![
 4092                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4093                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4094                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4095                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4096            ]
 4097        );
 4098    });
 4099
 4100    let editor = cx.add_window(|window, cx| {
 4101        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4102        build_editor(buffer, window, cx)
 4103    });
 4104    _ = editor.update(cx, |editor, window, cx| {
 4105        editor.change_selections(None, window, cx, |s| {
 4106            s.select_display_ranges([
 4107                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4108                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4109            ])
 4110        });
 4111        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4112        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4113        assert_eq!(
 4114            editor.selections.display_ranges(cx),
 4115            vec![
 4116                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4117                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4118            ]
 4119        );
 4120    });
 4121
 4122    let editor = cx.add_window(|window, cx| {
 4123        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4124        build_editor(buffer, window, cx)
 4125    });
 4126    _ = editor.update(cx, |editor, window, cx| {
 4127        editor.change_selections(None, window, cx, |s| {
 4128            s.select_display_ranges([
 4129                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4130                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4131            ])
 4132        });
 4133        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4134        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4135        assert_eq!(
 4136            editor.selections.display_ranges(cx),
 4137            vec![
 4138                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4139                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4140            ]
 4141        );
 4142    });
 4143}
 4144
 4145#[gpui::test]
 4146fn test_move_line_up_down(cx: &mut TestAppContext) {
 4147    init_test(cx, |_| {});
 4148
 4149    let editor = cx.add_window(|window, cx| {
 4150        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4151        build_editor(buffer, window, cx)
 4152    });
 4153    _ = editor.update(cx, |editor, window, cx| {
 4154        editor.fold_creases(
 4155            vec![
 4156                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4157                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4158                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4159            ],
 4160            true,
 4161            window,
 4162            cx,
 4163        );
 4164        editor.change_selections(None, window, cx, |s| {
 4165            s.select_display_ranges([
 4166                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4167                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4168                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4169                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4170            ])
 4171        });
 4172        assert_eq!(
 4173            editor.display_text(cx),
 4174            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4175        );
 4176
 4177        editor.move_line_up(&MoveLineUp, window, cx);
 4178        assert_eq!(
 4179            editor.display_text(cx),
 4180            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4181        );
 4182        assert_eq!(
 4183            editor.selections.display_ranges(cx),
 4184            vec![
 4185                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4186                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4187                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4188                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4189            ]
 4190        );
 4191    });
 4192
 4193    _ = editor.update(cx, |editor, window, cx| {
 4194        editor.move_line_down(&MoveLineDown, window, cx);
 4195        assert_eq!(
 4196            editor.display_text(cx),
 4197            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4198        );
 4199        assert_eq!(
 4200            editor.selections.display_ranges(cx),
 4201            vec![
 4202                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4206            ]
 4207        );
 4208    });
 4209
 4210    _ = editor.update(cx, |editor, window, cx| {
 4211        editor.move_line_down(&MoveLineDown, window, cx);
 4212        assert_eq!(
 4213            editor.display_text(cx),
 4214            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4215        );
 4216        assert_eq!(
 4217            editor.selections.display_ranges(cx),
 4218            vec![
 4219                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4220                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4221                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4222                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4223            ]
 4224        );
 4225    });
 4226
 4227    _ = editor.update(cx, |editor, window, cx| {
 4228        editor.move_line_up(&MoveLineUp, window, cx);
 4229        assert_eq!(
 4230            editor.display_text(cx),
 4231            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4232        );
 4233        assert_eq!(
 4234            editor.selections.display_ranges(cx),
 4235            vec![
 4236                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4237                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4238                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4239                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4240            ]
 4241        );
 4242    });
 4243}
 4244
 4245#[gpui::test]
 4246fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4247    init_test(cx, |_| {});
 4248
 4249    let editor = cx.add_window(|window, cx| {
 4250        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4251        build_editor(buffer, window, cx)
 4252    });
 4253    _ = editor.update(cx, |editor, window, cx| {
 4254        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4255        editor.insert_blocks(
 4256            [BlockProperties {
 4257                style: BlockStyle::Fixed,
 4258                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4259                height: 1,
 4260                render: Arc::new(|_| div().into_any()),
 4261                priority: 0,
 4262            }],
 4263            Some(Autoscroll::fit()),
 4264            cx,
 4265        );
 4266        editor.change_selections(None, window, cx, |s| {
 4267            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4268        });
 4269        editor.move_line_down(&MoveLineDown, window, cx);
 4270    });
 4271}
 4272
 4273#[gpui::test]
 4274async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4275    init_test(cx, |_| {});
 4276
 4277    let mut cx = EditorTestContext::new(cx).await;
 4278    cx.set_state(
 4279        &"
 4280            ˇzero
 4281            one
 4282            two
 4283            three
 4284            four
 4285            five
 4286        "
 4287        .unindent(),
 4288    );
 4289
 4290    // Create a four-line block that replaces three lines of text.
 4291    cx.update_editor(|editor, window, cx| {
 4292        let snapshot = editor.snapshot(window, cx);
 4293        let snapshot = &snapshot.buffer_snapshot;
 4294        let placement = BlockPlacement::Replace(
 4295            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4296        );
 4297        editor.insert_blocks(
 4298            [BlockProperties {
 4299                placement,
 4300                height: 4,
 4301                style: BlockStyle::Sticky,
 4302                render: Arc::new(|_| gpui::div().into_any_element()),
 4303                priority: 0,
 4304            }],
 4305            None,
 4306            cx,
 4307        );
 4308    });
 4309
 4310    // Move down so that the cursor touches the block.
 4311    cx.update_editor(|editor, window, cx| {
 4312        editor.move_down(&Default::default(), window, cx);
 4313    });
 4314    cx.assert_editor_state(
 4315        &"
 4316            zero
 4317            «one
 4318            two
 4319            threeˇ»
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Move down past the block.
 4327    cx.update_editor(|editor, window, cx| {
 4328        editor.move_down(&Default::default(), window, cx);
 4329    });
 4330    cx.assert_editor_state(
 4331        &"
 4332            zero
 4333            one
 4334            two
 4335            three
 4336            ˇfour
 4337            five
 4338        "
 4339        .unindent(),
 4340    );
 4341}
 4342
 4343#[gpui::test]
 4344fn test_transpose(cx: &mut TestAppContext) {
 4345    init_test(cx, |_| {});
 4346
 4347    _ = cx.add_window(|window, cx| {
 4348        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4349        editor.set_style(EditorStyle::default(), window, cx);
 4350        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4351        editor.transpose(&Default::default(), window, cx);
 4352        assert_eq!(editor.text(cx), "bac");
 4353        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4354
 4355        editor.transpose(&Default::default(), window, cx);
 4356        assert_eq!(editor.text(cx), "bca");
 4357        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4358
 4359        editor.transpose(&Default::default(), window, cx);
 4360        assert_eq!(editor.text(cx), "bac");
 4361        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4362
 4363        editor
 4364    });
 4365
 4366    _ = cx.add_window(|window, cx| {
 4367        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4368        editor.set_style(EditorStyle::default(), window, cx);
 4369        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4370        editor.transpose(&Default::default(), window, cx);
 4371        assert_eq!(editor.text(cx), "acb\nde");
 4372        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4373
 4374        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4375        editor.transpose(&Default::default(), window, cx);
 4376        assert_eq!(editor.text(cx), "acbd\ne");
 4377        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4378
 4379        editor.transpose(&Default::default(), window, cx);
 4380        assert_eq!(editor.text(cx), "acbde\n");
 4381        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4382
 4383        editor.transpose(&Default::default(), window, cx);
 4384        assert_eq!(editor.text(cx), "acbd\ne");
 4385        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4386
 4387        editor
 4388    });
 4389
 4390    _ = cx.add_window(|window, cx| {
 4391        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4392        editor.set_style(EditorStyle::default(), window, cx);
 4393        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4394        editor.transpose(&Default::default(), window, cx);
 4395        assert_eq!(editor.text(cx), "bacd\ne");
 4396        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4397
 4398        editor.transpose(&Default::default(), window, cx);
 4399        assert_eq!(editor.text(cx), "bcade\n");
 4400        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4401
 4402        editor.transpose(&Default::default(), window, cx);
 4403        assert_eq!(editor.text(cx), "bcda\ne");
 4404        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4405
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "bcade\n");
 4408        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4409
 4410        editor.transpose(&Default::default(), window, cx);
 4411        assert_eq!(editor.text(cx), "bcaed\n");
 4412        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4413
 4414        editor
 4415    });
 4416
 4417    _ = cx.add_window(|window, cx| {
 4418        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4419        editor.set_style(EditorStyle::default(), window, cx);
 4420        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4421        editor.transpose(&Default::default(), window, cx);
 4422        assert_eq!(editor.text(cx), "🏀🍐✋");
 4423        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4424
 4425        editor.transpose(&Default::default(), window, cx);
 4426        assert_eq!(editor.text(cx), "🏀✋🍐");
 4427        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4428
 4429        editor.transpose(&Default::default(), window, cx);
 4430        assert_eq!(editor.text(cx), "🏀🍐✋");
 4431        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4432
 4433        editor
 4434    });
 4435}
 4436
 4437#[gpui::test]
 4438async fn test_rewrap(cx: &mut TestAppContext) {
 4439    init_test(cx, |settings| {
 4440        settings.languages.extend([
 4441            (
 4442                "Markdown".into(),
 4443                LanguageSettingsContent {
 4444                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4445                    ..Default::default()
 4446                },
 4447            ),
 4448            (
 4449                "Plain Text".into(),
 4450                LanguageSettingsContent {
 4451                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4452                    ..Default::default()
 4453                },
 4454            ),
 4455        ])
 4456    });
 4457
 4458    let mut cx = EditorTestContext::new(cx).await;
 4459
 4460    let language_with_c_comments = Arc::new(Language::new(
 4461        LanguageConfig {
 4462            line_comments: vec!["// ".into()],
 4463            ..LanguageConfig::default()
 4464        },
 4465        None,
 4466    ));
 4467    let language_with_pound_comments = Arc::new(Language::new(
 4468        LanguageConfig {
 4469            line_comments: vec!["# ".into()],
 4470            ..LanguageConfig::default()
 4471        },
 4472        None,
 4473    ));
 4474    let markdown_language = Arc::new(Language::new(
 4475        LanguageConfig {
 4476            name: "Markdown".into(),
 4477            ..LanguageConfig::default()
 4478        },
 4479        None,
 4480    ));
 4481    let language_with_doc_comments = Arc::new(Language::new(
 4482        LanguageConfig {
 4483            line_comments: vec!["// ".into(), "/// ".into()],
 4484            ..LanguageConfig::default()
 4485        },
 4486        Some(tree_sitter_rust::LANGUAGE.into()),
 4487    ));
 4488
 4489    let plaintext_language = Arc::new(Language::new(
 4490        LanguageConfig {
 4491            name: "Plain Text".into(),
 4492            ..LanguageConfig::default()
 4493        },
 4494        None,
 4495    ));
 4496
 4497    assert_rewrap(
 4498        indoc! {"
 4499            // ˇ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.
 4500        "},
 4501        indoc! {"
 4502            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4503            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4504            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4505            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4506            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4507            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4508            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4509            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4510            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4511            // porttitor id. Aliquam id accumsan eros.
 4512        "},
 4513        language_with_c_comments.clone(),
 4514        &mut cx,
 4515    );
 4516
 4517    // Test that rewrapping works inside of a selection
 4518    assert_rewrap(
 4519        indoc! {"
 4520            «// 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.ˇ»
 4521        "},
 4522        indoc! {"
 4523            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4524            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4525            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4526            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4527            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4528            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4529            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4530            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4531            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4532            // porttitor id. Aliquam id accumsan eros.ˇ»
 4533        "},
 4534        language_with_c_comments.clone(),
 4535        &mut cx,
 4536    );
 4537
 4538    // Test that cursors that expand to the same region are collapsed.
 4539    assert_rewrap(
 4540        indoc! {"
 4541            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4542            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4543            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4544            // ˇ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.
 4545        "},
 4546        indoc! {"
 4547            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4548            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4549            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4550            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4551            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4552            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4553            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4554            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4555            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4556            // porttitor id. Aliquam id accumsan eros.
 4557        "},
 4558        language_with_c_comments.clone(),
 4559        &mut cx,
 4560    );
 4561
 4562    // Test that non-contiguous selections are treated separately.
 4563    assert_rewrap(
 4564        indoc! {"
 4565            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4566            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4567            //
 4568            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4569            // ˇ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.
 4570        "},
 4571        indoc! {"
 4572            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4573            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4574            // auctor, eu lacinia sapien scelerisque.
 4575            //
 4576            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4577            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4578            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4579            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4580            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4581            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4582            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4583        "},
 4584        language_with_c_comments.clone(),
 4585        &mut cx,
 4586    );
 4587
 4588    // Test that different comment prefixes are supported.
 4589    assert_rewrap(
 4590        indoc! {"
 4591            # ˇ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.
 4592        "},
 4593        indoc! {"
 4594            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4595            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4596            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4597            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4598            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4599            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4600            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4601            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4602            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4603            # accumsan eros.
 4604        "},
 4605        language_with_pound_comments.clone(),
 4606        &mut cx,
 4607    );
 4608
 4609    // Test that rewrapping is ignored outside of comments in most languages.
 4610    assert_rewrap(
 4611        indoc! {"
 4612            /// Adds two numbers.
 4613            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4614            fn add(a: u32, b: u32) -> u32 {
 4615                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ˇ
 4616            }
 4617        "},
 4618        indoc! {"
 4619            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4620            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4621            fn add(a: u32, b: u32) -> u32 {
 4622                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ˇ
 4623            }
 4624        "},
 4625        language_with_doc_comments.clone(),
 4626        &mut cx,
 4627    );
 4628
 4629    // Test that rewrapping works in Markdown and Plain Text languages.
 4630    assert_rewrap(
 4631        indoc! {"
 4632            # Hello
 4633
 4634            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.
 4635        "},
 4636        indoc! {"
 4637            # Hello
 4638
 4639            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4640            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4641            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4642            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4643            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4644            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4645            Integer sit amet scelerisque nisi.
 4646        "},
 4647        markdown_language,
 4648        &mut cx,
 4649    );
 4650
 4651    assert_rewrap(
 4652        indoc! {"
 4653            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.
 4654        "},
 4655        indoc! {"
 4656            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4657            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4658            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4659            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4660            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4661            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4662            Integer sit amet scelerisque nisi.
 4663        "},
 4664        plaintext_language,
 4665        &mut cx,
 4666    );
 4667
 4668    // Test rewrapping unaligned comments in a selection.
 4669    assert_rewrap(
 4670        indoc! {"
 4671            fn foo() {
 4672                if true {
 4673            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4674            // Praesent semper egestas tellus id dignissim.ˇ»
 4675                    do_something();
 4676                } else {
 4677                    //
 4678                }
 4679            }
 4680        "},
 4681        indoc! {"
 4682            fn foo() {
 4683                if true {
 4684            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4685                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4686                    // egestas tellus id dignissim.ˇ»
 4687                    do_something();
 4688                } else {
 4689                    //
 4690                }
 4691            }
 4692        "},
 4693        language_with_doc_comments.clone(),
 4694        &mut cx,
 4695    );
 4696
 4697    assert_rewrap(
 4698        indoc! {"
 4699            fn foo() {
 4700                if true {
 4701            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4702            // Praesent semper egestas tellus id dignissim.»
 4703                    do_something();
 4704                } else {
 4705                    //
 4706                }
 4707
 4708            }
 4709        "},
 4710        indoc! {"
 4711            fn foo() {
 4712                if true {
 4713            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4714                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4715                    // egestas tellus id dignissim.»
 4716                    do_something();
 4717                } else {
 4718                    //
 4719                }
 4720
 4721            }
 4722        "},
 4723        language_with_doc_comments.clone(),
 4724        &mut cx,
 4725    );
 4726
 4727    #[track_caller]
 4728    fn assert_rewrap(
 4729        unwrapped_text: &str,
 4730        wrapped_text: &str,
 4731        language: Arc<Language>,
 4732        cx: &mut EditorTestContext,
 4733    ) {
 4734        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4735        cx.set_state(unwrapped_text);
 4736        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4737        cx.assert_editor_state(wrapped_text);
 4738    }
 4739}
 4740
 4741#[gpui::test]
 4742async fn test_hard_wrap(cx: &mut TestAppContext) {
 4743    init_test(cx, |_| {});
 4744    let mut cx = EditorTestContext::new(cx).await;
 4745
 4746    cx.update_editor(|editor, _, cx| {
 4747        editor.set_hard_wrap(Some(14), cx);
 4748    });
 4749
 4750    cx.set_state(indoc!(
 4751        "
 4752        one two three ˇ
 4753        "
 4754    ));
 4755    cx.simulate_input("four");
 4756    cx.run_until_parked();
 4757
 4758    cx.assert_editor_state(indoc!(
 4759        "
 4760        one two three
 4761        fourˇ
 4762        "
 4763    ));
 4764}
 4765
 4766#[gpui::test]
 4767async fn test_clipboard(cx: &mut TestAppContext) {
 4768    init_test(cx, |_| {});
 4769
 4770    let mut cx = EditorTestContext::new(cx).await;
 4771
 4772    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4773    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4774    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4775
 4776    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4777    cx.set_state("two ˇfour ˇsix ˇ");
 4778    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4779    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4780
 4781    // Paste again but with only two cursors. Since the number of cursors doesn't
 4782    // match the number of slices in the clipboard, the entire clipboard text
 4783    // is pasted at each cursor.
 4784    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4785    cx.update_editor(|e, window, cx| {
 4786        e.handle_input("( ", window, cx);
 4787        e.paste(&Paste, window, cx);
 4788        e.handle_input(") ", window, cx);
 4789    });
 4790    cx.assert_editor_state(
 4791        &([
 4792            "( one✅ ",
 4793            "three ",
 4794            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4795            "three ",
 4796            "five ) ˇ",
 4797        ]
 4798        .join("\n")),
 4799    );
 4800
 4801    // Cut with three selections, one of which is full-line.
 4802    cx.set_state(indoc! {"
 4803        1«2ˇ»3
 4804        4ˇ567
 4805        «8ˇ»9"});
 4806    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4807    cx.assert_editor_state(indoc! {"
 4808        1ˇ3
 4809        ˇ9"});
 4810
 4811    // Paste with three selections, noticing how the copied selection that was full-line
 4812    // gets inserted before the second cursor.
 4813    cx.set_state(indoc! {"
 4814        1ˇ3
 4815 4816        «oˇ»ne"});
 4817    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4818    cx.assert_editor_state(indoc! {"
 4819        12ˇ3
 4820        4567
 4821 4822        8ˇne"});
 4823
 4824    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4825    cx.set_state(indoc! {"
 4826        The quick brown
 4827        fox juˇmps over
 4828        the lazy dog"});
 4829    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4830    assert_eq!(
 4831        cx.read_from_clipboard()
 4832            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4833        Some("fox jumps over\n".to_string())
 4834    );
 4835
 4836    // Paste with three selections, noticing how the copied full-line selection is inserted
 4837    // before the empty selections but replaces the selection that is non-empty.
 4838    cx.set_state(indoc! {"
 4839        Tˇhe quick brown
 4840        «foˇ»x jumps over
 4841        tˇhe lazy dog"});
 4842    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4843    cx.assert_editor_state(indoc! {"
 4844        fox jumps over
 4845        Tˇhe quick brown
 4846        fox jumps over
 4847        ˇx jumps over
 4848        fox jumps over
 4849        tˇhe lazy dog"});
 4850}
 4851
 4852#[gpui::test]
 4853async fn test_paste_multiline(cx: &mut TestAppContext) {
 4854    init_test(cx, |_| {});
 4855
 4856    let mut cx = EditorTestContext::new(cx).await;
 4857    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4858
 4859    // Cut an indented block, without the leading whitespace.
 4860    cx.set_state(indoc! {"
 4861        const a: B = (
 4862            c(),
 4863            «d(
 4864                e,
 4865                f
 4866            )ˇ»
 4867        );
 4868    "});
 4869    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4870    cx.assert_editor_state(indoc! {"
 4871        const a: B = (
 4872            c(),
 4873            ˇ
 4874        );
 4875    "});
 4876
 4877    // Paste it at the same position.
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state(indoc! {"
 4880        const a: B = (
 4881            c(),
 4882            d(
 4883                e,
 4884                f
 4885 4886        );
 4887    "});
 4888
 4889    // Paste it at a line with a lower indent level.
 4890    cx.set_state(indoc! {"
 4891        ˇ
 4892        const a: B = (
 4893            c(),
 4894        );
 4895    "});
 4896    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4897    cx.assert_editor_state(indoc! {"
 4898        d(
 4899            e,
 4900            f
 4901 4902        const a: B = (
 4903            c(),
 4904        );
 4905    "});
 4906
 4907    // Cut an indented block, with the leading whitespace.
 4908    cx.set_state(indoc! {"
 4909        const a: B = (
 4910            c(),
 4911        «    d(
 4912                e,
 4913                f
 4914            )
 4915        ˇ»);
 4916    "});
 4917    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        const a: B = (
 4920            c(),
 4921        ˇ);
 4922    "});
 4923
 4924    // Paste it at the same position.
 4925    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4926    cx.assert_editor_state(indoc! {"
 4927        const a: B = (
 4928            c(),
 4929            d(
 4930                e,
 4931                f
 4932            )
 4933        ˇ);
 4934    "});
 4935
 4936    // Paste it at a line with a higher indent level.
 4937    cx.set_state(indoc! {"
 4938        const a: B = (
 4939            c(),
 4940            d(
 4941                e,
 4942 4943            )
 4944        );
 4945    "});
 4946    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4947    cx.assert_editor_state(indoc! {"
 4948        const a: B = (
 4949            c(),
 4950            d(
 4951                e,
 4952                f    d(
 4953                    e,
 4954                    f
 4955                )
 4956        ˇ
 4957            )
 4958        );
 4959    "});
 4960
 4961    // Copy an indented block, starting mid-line
 4962    cx.set_state(indoc! {"
 4963        const a: B = (
 4964            c(),
 4965            somethin«g(
 4966                e,
 4967                f
 4968            )ˇ»
 4969        );
 4970    "});
 4971    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4972
 4973    // Paste it on a line with a lower indent level
 4974    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 4975    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4976    cx.assert_editor_state(indoc! {"
 4977        const a: B = (
 4978            c(),
 4979            something(
 4980                e,
 4981                f
 4982            )
 4983        );
 4984        g(
 4985            e,
 4986            f
 4987"});
 4988}
 4989
 4990#[gpui::test]
 4991async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4992    init_test(cx, |_| {});
 4993
 4994    cx.write_to_clipboard(ClipboardItem::new_string(
 4995        "    d(\n        e\n    );\n".into(),
 4996    ));
 4997
 4998    let mut cx = EditorTestContext::new(cx).await;
 4999    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5000
 5001    cx.set_state(indoc! {"
 5002        fn a() {
 5003            b();
 5004            if c() {
 5005                ˇ
 5006            }
 5007        }
 5008    "});
 5009
 5010    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5011    cx.assert_editor_state(indoc! {"
 5012        fn a() {
 5013            b();
 5014            if c() {
 5015                d(
 5016                    e
 5017                );
 5018        ˇ
 5019            }
 5020        }
 5021    "});
 5022
 5023    cx.set_state(indoc! {"
 5024        fn a() {
 5025            b();
 5026            ˇ
 5027        }
 5028    "});
 5029
 5030    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5031    cx.assert_editor_state(indoc! {"
 5032        fn a() {
 5033            b();
 5034            d(
 5035                e
 5036            );
 5037        ˇ
 5038        }
 5039    "});
 5040}
 5041
 5042#[gpui::test]
 5043fn test_select_all(cx: &mut TestAppContext) {
 5044    init_test(cx, |_| {});
 5045
 5046    let editor = cx.add_window(|window, cx| {
 5047        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5048        build_editor(buffer, window, cx)
 5049    });
 5050    _ = editor.update(cx, |editor, window, cx| {
 5051        editor.select_all(&SelectAll, window, cx);
 5052        assert_eq!(
 5053            editor.selections.display_ranges(cx),
 5054            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5055        );
 5056    });
 5057}
 5058
 5059#[gpui::test]
 5060fn test_select_line(cx: &mut TestAppContext) {
 5061    init_test(cx, |_| {});
 5062
 5063    let editor = cx.add_window(|window, cx| {
 5064        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5065        build_editor(buffer, window, cx)
 5066    });
 5067    _ = editor.update(cx, |editor, window, cx| {
 5068        editor.change_selections(None, window, cx, |s| {
 5069            s.select_display_ranges([
 5070                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5071                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5072                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5073                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5074            ])
 5075        });
 5076        editor.select_line(&SelectLine, window, cx);
 5077        assert_eq!(
 5078            editor.selections.display_ranges(cx),
 5079            vec![
 5080                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5081                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5082            ]
 5083        );
 5084    });
 5085
 5086    _ = editor.update(cx, |editor, window, cx| {
 5087        editor.select_line(&SelectLine, window, cx);
 5088        assert_eq!(
 5089            editor.selections.display_ranges(cx),
 5090            vec![
 5091                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5092                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5093            ]
 5094        );
 5095    });
 5096
 5097    _ = editor.update(cx, |editor, window, cx| {
 5098        editor.select_line(&SelectLine, window, cx);
 5099        assert_eq!(
 5100            editor.selections.display_ranges(cx),
 5101            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5102        );
 5103    });
 5104}
 5105
 5106#[gpui::test]
 5107async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5108    init_test(cx, |_| {});
 5109    let mut cx = EditorTestContext::new(cx).await;
 5110
 5111    #[track_caller]
 5112    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5113        cx.set_state(initial_state);
 5114        cx.update_editor(|e, window, cx| {
 5115            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5116        });
 5117        cx.assert_editor_state(expected_state);
 5118    }
 5119
 5120    // Selection starts and ends at the middle of lines, left-to-right
 5121    test(
 5122        &mut cx,
 5123        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5124        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5125    );
 5126    // Same thing, right-to-left
 5127    test(
 5128        &mut cx,
 5129        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5130        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5131    );
 5132
 5133    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5134    test(
 5135        &mut cx,
 5136        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5137        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5138    );
 5139    // Same thing, right-to-left
 5140    test(
 5141        &mut cx,
 5142        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5143        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5144    );
 5145
 5146    // Whole buffer, left-to-right, last line ends with newline
 5147    test(
 5148        &mut cx,
 5149        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5150        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5151    );
 5152    // Same thing, right-to-left
 5153    test(
 5154        &mut cx,
 5155        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5156        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5157    );
 5158
 5159    // Starts at the end of a line, ends at the start of another
 5160    test(
 5161        &mut cx,
 5162        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5163        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let editor = cx.add_window(|window, cx| {
 5172        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5173        build_editor(buffer, window, cx)
 5174    });
 5175
 5176    // setup
 5177    _ = editor.update(cx, |editor, window, cx| {
 5178        editor.fold_creases(
 5179            vec![
 5180                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5181                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5182                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5183            ],
 5184            true,
 5185            window,
 5186            cx,
 5187        );
 5188        assert_eq!(
 5189            editor.display_text(cx),
 5190            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5191        );
 5192    });
 5193
 5194    _ = editor.update(cx, |editor, window, cx| {
 5195        editor.change_selections(None, window, cx, |s| {
 5196            s.select_display_ranges([
 5197                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5198                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5199                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5200                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5201            ])
 5202        });
 5203        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5204        assert_eq!(
 5205            editor.display_text(cx),
 5206            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5207        );
 5208    });
 5209    EditorTestContext::for_editor(editor, cx)
 5210        .await
 5211        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5212
 5213    _ = editor.update(cx, |editor, window, cx| {
 5214        editor.change_selections(None, window, cx, |s| {
 5215            s.select_display_ranges([
 5216                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5217            ])
 5218        });
 5219        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5220        assert_eq!(
 5221            editor.display_text(cx),
 5222            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5223        );
 5224        assert_eq!(
 5225            editor.selections.display_ranges(cx),
 5226            [
 5227                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5228                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5229                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5230                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5231                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5232                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5233                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5234            ]
 5235        );
 5236    });
 5237    EditorTestContext::for_editor(editor, cx)
 5238        .await
 5239        .assert_editor_state(
 5240            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5241        );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249
 5250    cx.set_state(indoc!(
 5251        r#"abc
 5252           defˇghi
 5253
 5254           jk
 5255           nlmo
 5256           "#
 5257    ));
 5258
 5259    cx.update_editor(|editor, window, cx| {
 5260        editor.add_selection_above(&Default::default(), window, cx);
 5261    });
 5262
 5263    cx.assert_editor_state(indoc!(
 5264        r#"abcˇ
 5265           defˇghi
 5266
 5267           jk
 5268           nlmo
 5269           "#
 5270    ));
 5271
 5272    cx.update_editor(|editor, window, cx| {
 5273        editor.add_selection_above(&Default::default(), window, cx);
 5274    });
 5275
 5276    cx.assert_editor_state(indoc!(
 5277        r#"abcˇ
 5278            defˇghi
 5279
 5280            jk
 5281            nlmo
 5282            "#
 5283    ));
 5284
 5285    cx.update_editor(|editor, window, cx| {
 5286        editor.add_selection_below(&Default::default(), window, cx);
 5287    });
 5288
 5289    cx.assert_editor_state(indoc!(
 5290        r#"abc
 5291           defˇghi
 5292
 5293           jk
 5294           nlmo
 5295           "#
 5296    ));
 5297
 5298    cx.update_editor(|editor, window, cx| {
 5299        editor.undo_selection(&Default::default(), window, cx);
 5300    });
 5301
 5302    cx.assert_editor_state(indoc!(
 5303        r#"abcˇ
 5304           defˇghi
 5305
 5306           jk
 5307           nlmo
 5308           "#
 5309    ));
 5310
 5311    cx.update_editor(|editor, window, cx| {
 5312        editor.redo_selection(&Default::default(), window, cx);
 5313    });
 5314
 5315    cx.assert_editor_state(indoc!(
 5316        r#"abc
 5317           defˇghi
 5318
 5319           jk
 5320           nlmo
 5321           "#
 5322    ));
 5323
 5324    cx.update_editor(|editor, window, cx| {
 5325        editor.add_selection_below(&Default::default(), window, cx);
 5326    });
 5327
 5328    cx.assert_editor_state(indoc!(
 5329        r#"abc
 5330           defˇghi
 5331
 5332           jk
 5333           nlmˇo
 5334           "#
 5335    ));
 5336
 5337    cx.update_editor(|editor, window, cx| {
 5338        editor.add_selection_below(&Default::default(), window, cx);
 5339    });
 5340
 5341    cx.assert_editor_state(indoc!(
 5342        r#"abc
 5343           defˇghi
 5344
 5345           jk
 5346           nlmˇo
 5347           "#
 5348    ));
 5349
 5350    // change selections
 5351    cx.set_state(indoc!(
 5352        r#"abc
 5353           def«ˇg»hi
 5354
 5355           jk
 5356           nlmo
 5357           "#
 5358    ));
 5359
 5360    cx.update_editor(|editor, window, cx| {
 5361        editor.add_selection_below(&Default::default(), window, cx);
 5362    });
 5363
 5364    cx.assert_editor_state(indoc!(
 5365        r#"abc
 5366           def«ˇg»hi
 5367
 5368           jk
 5369           nlm«ˇo»
 5370           "#
 5371    ));
 5372
 5373    cx.update_editor(|editor, window, cx| {
 5374        editor.add_selection_below(&Default::default(), window, cx);
 5375    });
 5376
 5377    cx.assert_editor_state(indoc!(
 5378        r#"abc
 5379           def«ˇg»hi
 5380
 5381           jk
 5382           nlm«ˇo»
 5383           "#
 5384    ));
 5385
 5386    cx.update_editor(|editor, window, cx| {
 5387        editor.add_selection_above(&Default::default(), window, cx);
 5388    });
 5389
 5390    cx.assert_editor_state(indoc!(
 5391        r#"abc
 5392           def«ˇg»hi
 5393
 5394           jk
 5395           nlmo
 5396           "#
 5397    ));
 5398
 5399    cx.update_editor(|editor, window, cx| {
 5400        editor.add_selection_above(&Default::default(), window, cx);
 5401    });
 5402
 5403    cx.assert_editor_state(indoc!(
 5404        r#"abc
 5405           def«ˇg»hi
 5406
 5407           jk
 5408           nlmo
 5409           "#
 5410    ));
 5411
 5412    // Change selections again
 5413    cx.set_state(indoc!(
 5414        r#"a«bc
 5415           defgˇ»hi
 5416
 5417           jk
 5418           nlmo
 5419           "#
 5420    ));
 5421
 5422    cx.update_editor(|editor, window, cx| {
 5423        editor.add_selection_below(&Default::default(), window, cx);
 5424    });
 5425
 5426    cx.assert_editor_state(indoc!(
 5427        r#"a«bcˇ»
 5428           d«efgˇ»hi
 5429
 5430           j«kˇ»
 5431           nlmo
 5432           "#
 5433    ));
 5434
 5435    cx.update_editor(|editor, window, cx| {
 5436        editor.add_selection_below(&Default::default(), window, cx);
 5437    });
 5438    cx.assert_editor_state(indoc!(
 5439        r#"a«bcˇ»
 5440           d«efgˇ»hi
 5441
 5442           j«kˇ»
 5443           n«lmoˇ»
 5444           "#
 5445    ));
 5446    cx.update_editor(|editor, window, cx| {
 5447        editor.add_selection_above(&Default::default(), window, cx);
 5448    });
 5449
 5450    cx.assert_editor_state(indoc!(
 5451        r#"a«bcˇ»
 5452           d«efgˇ»hi
 5453
 5454           j«kˇ»
 5455           nlmo
 5456           "#
 5457    ));
 5458
 5459    // Change selections again
 5460    cx.set_state(indoc!(
 5461        r#"abc
 5462           d«ˇefghi
 5463
 5464           jk
 5465           nlm»o
 5466           "#
 5467    ));
 5468
 5469    cx.update_editor(|editor, window, cx| {
 5470        editor.add_selection_above(&Default::default(), window, cx);
 5471    });
 5472
 5473    cx.assert_editor_state(indoc!(
 5474        r#"a«ˇbc»
 5475           d«ˇef»ghi
 5476
 5477           j«ˇk»
 5478           n«ˇlm»o
 5479           "#
 5480    ));
 5481
 5482    cx.update_editor(|editor, window, cx| {
 5483        editor.add_selection_below(&Default::default(), window, cx);
 5484    });
 5485
 5486    cx.assert_editor_state(indoc!(
 5487        r#"abc
 5488           d«ˇef»ghi
 5489
 5490           j«ˇk»
 5491           n«ˇlm»o
 5492           "#
 5493    ));
 5494}
 5495
 5496#[gpui::test]
 5497async fn test_select_next(cx: &mut TestAppContext) {
 5498    init_test(cx, |_| {});
 5499
 5500    let mut cx = EditorTestContext::new(cx).await;
 5501    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5502
 5503    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5504        .unwrap();
 5505    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5506
 5507    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5508        .unwrap();
 5509    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5510
 5511    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5512    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5513
 5514    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5515    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5516
 5517    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5518        .unwrap();
 5519    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5520
 5521    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5522        .unwrap();
 5523    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5524}
 5525
 5526#[gpui::test]
 5527async fn test_select_all_matches(cx: &mut TestAppContext) {
 5528    init_test(cx, |_| {});
 5529
 5530    let mut cx = EditorTestContext::new(cx).await;
 5531
 5532    // Test caret-only selections
 5533    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5534    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5535        .unwrap();
 5536    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5537
 5538    // Test left-to-right selections
 5539    cx.set_state("abc\n«abcˇ»\nabc");
 5540    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5541        .unwrap();
 5542    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5543
 5544    // Test right-to-left selections
 5545    cx.set_state("abc\n«ˇabc»\nabc");
 5546    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5547        .unwrap();
 5548    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5549
 5550    // Test selecting whitespace with caret selection
 5551    cx.set_state("abc\nˇ   abc\nabc");
 5552    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5553        .unwrap();
 5554    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5555
 5556    // Test selecting whitespace with left-to-right selection
 5557    cx.set_state("abc\n«ˇ  »abc\nabc");
 5558    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5559        .unwrap();
 5560    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5561
 5562    // Test no matches with right-to-left selection
 5563    cx.set_state("abc\n«  ˇ»abc\nabc");
 5564    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5565        .unwrap();
 5566    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5567}
 5568
 5569#[gpui::test]
 5570async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5571    init_test(cx, |_| {});
 5572
 5573    let mut cx = EditorTestContext::new(cx).await;
 5574    cx.set_state(
 5575        r#"let foo = 2;
 5576lˇet foo = 2;
 5577let fooˇ = 2;
 5578let foo = 2;
 5579let foo = ˇ2;"#,
 5580    );
 5581
 5582    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5583        .unwrap();
 5584    cx.assert_editor_state(
 5585        r#"let foo = 2;
 5586«letˇ» foo = 2;
 5587let «fooˇ» = 2;
 5588let foo = 2;
 5589let foo = «2ˇ»;"#,
 5590    );
 5591
 5592    // noop for multiple selections with different contents
 5593    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5594        .unwrap();
 5595    cx.assert_editor_state(
 5596        r#"let foo = 2;
 5597«letˇ» foo = 2;
 5598let «fooˇ» = 2;
 5599let foo = 2;
 5600let foo = «2ˇ»;"#,
 5601    );
 5602}
 5603
 5604#[gpui::test]
 5605async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5606    init_test(cx, |_| {});
 5607
 5608    let mut cx =
 5609        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5610
 5611    cx.assert_editor_state(indoc! {"
 5612        ˇbbb
 5613        ccc
 5614
 5615        bbb
 5616        ccc
 5617        "});
 5618    cx.dispatch_action(SelectPrevious::default());
 5619    cx.assert_editor_state(indoc! {"
 5620                «bbbˇ»
 5621                ccc
 5622
 5623                bbb
 5624                ccc
 5625                "});
 5626    cx.dispatch_action(SelectPrevious::default());
 5627    cx.assert_editor_state(indoc! {"
 5628                «bbbˇ»
 5629                ccc
 5630
 5631                «bbbˇ»
 5632                ccc
 5633                "});
 5634}
 5635
 5636#[gpui::test]
 5637async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5638    init_test(cx, |_| {});
 5639
 5640    let mut cx = EditorTestContext::new(cx).await;
 5641    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5642
 5643    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5644        .unwrap();
 5645    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5646
 5647    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5648        .unwrap();
 5649    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5650
 5651    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5652    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5653
 5654    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5655    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5656
 5657    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5658        .unwrap();
 5659    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5660
 5661    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5662        .unwrap();
 5663    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5664
 5665    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5666        .unwrap();
 5667    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5668}
 5669
 5670#[gpui::test]
 5671async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5672    init_test(cx, |_| {});
 5673
 5674    let mut cx = EditorTestContext::new(cx).await;
 5675    cx.set_state("");
 5676
 5677    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5678        .unwrap();
 5679    cx.assert_editor_state("«aˇ»");
 5680    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5681        .unwrap();
 5682    cx.assert_editor_state("«aˇ»");
 5683}
 5684
 5685#[gpui::test]
 5686async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5687    init_test(cx, |_| {});
 5688
 5689    let mut cx = EditorTestContext::new(cx).await;
 5690    cx.set_state(
 5691        r#"let foo = 2;
 5692lˇet foo = 2;
 5693let fooˇ = 2;
 5694let foo = 2;
 5695let foo = ˇ2;"#,
 5696    );
 5697
 5698    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5699        .unwrap();
 5700    cx.assert_editor_state(
 5701        r#"let foo = 2;
 5702«letˇ» foo = 2;
 5703let «fooˇ» = 2;
 5704let foo = 2;
 5705let foo = «2ˇ»;"#,
 5706    );
 5707
 5708    // noop for multiple selections with different contents
 5709    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5710        .unwrap();
 5711    cx.assert_editor_state(
 5712        r#"let foo = 2;
 5713«letˇ» foo = 2;
 5714let «fooˇ» = 2;
 5715let foo = 2;
 5716let foo = «2ˇ»;"#,
 5717    );
 5718}
 5719
 5720#[gpui::test]
 5721async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5722    init_test(cx, |_| {});
 5723
 5724    let mut cx = EditorTestContext::new(cx).await;
 5725    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5726
 5727    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5728        .unwrap();
 5729    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5730
 5731    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5732        .unwrap();
 5733    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5734
 5735    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5736    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5737
 5738    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5739    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5740
 5741    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5742        .unwrap();
 5743    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5744
 5745    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5746        .unwrap();
 5747    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5748}
 5749
 5750#[gpui::test]
 5751async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5752    init_test(cx, |_| {});
 5753
 5754    let language = Arc::new(Language::new(
 5755        LanguageConfig::default(),
 5756        Some(tree_sitter_rust::LANGUAGE.into()),
 5757    ));
 5758
 5759    let text = r#"
 5760        use mod1::mod2::{mod3, mod4};
 5761
 5762        fn fn_1(param1: bool, param2: &str) {
 5763            let var1 = "text";
 5764        }
 5765    "#
 5766    .unindent();
 5767
 5768    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5769    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5770    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5771
 5772    editor
 5773        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5774        .await;
 5775
 5776    editor.update_in(cx, |editor, window, cx| {
 5777        editor.change_selections(None, window, cx, |s| {
 5778            s.select_display_ranges([
 5779                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5780                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5781                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5782            ]);
 5783        });
 5784        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5785    });
 5786    editor.update(cx, |editor, cx| {
 5787        assert_text_with_selections(
 5788            editor,
 5789            indoc! {r#"
 5790                use mod1::mod2::{mod3, «mod4ˇ»};
 5791
 5792                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5793                    let var1 = "«textˇ»";
 5794                }
 5795            "#},
 5796            cx,
 5797        );
 5798    });
 5799
 5800    editor.update_in(cx, |editor, window, cx| {
 5801        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5802    });
 5803    editor.update(cx, |editor, cx| {
 5804        assert_text_with_selections(
 5805            editor,
 5806            indoc! {r#"
 5807                use mod1::mod2::«{mod3, mod4}ˇ»;
 5808
 5809                «ˇfn fn_1(param1: bool, param2: &str) {
 5810                    let var1 = "text";
 5811 5812            "#},
 5813            cx,
 5814        );
 5815    });
 5816
 5817    editor.update_in(cx, |editor, window, cx| {
 5818        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5819    });
 5820    assert_eq!(
 5821        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5822        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5823    );
 5824
 5825    // Trying to expand the selected syntax node one more time has no effect.
 5826    editor.update_in(cx, |editor, window, cx| {
 5827        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5828    });
 5829    assert_eq!(
 5830        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5831        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5832    );
 5833
 5834    editor.update_in(cx, |editor, window, cx| {
 5835        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5836    });
 5837    editor.update(cx, |editor, cx| {
 5838        assert_text_with_selections(
 5839            editor,
 5840            indoc! {r#"
 5841                use mod1::mod2::«{mod3, mod4}ˇ»;
 5842
 5843                «ˇfn fn_1(param1: bool, param2: &str) {
 5844                    let var1 = "text";
 5845 5846            "#},
 5847            cx,
 5848        );
 5849    });
 5850
 5851    editor.update_in(cx, |editor, window, cx| {
 5852        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5853    });
 5854    editor.update(cx, |editor, cx| {
 5855        assert_text_with_selections(
 5856            editor,
 5857            indoc! {r#"
 5858                use mod1::mod2::{mod3, «mod4ˇ»};
 5859
 5860                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5861                    let var1 = "«textˇ»";
 5862                }
 5863            "#},
 5864            cx,
 5865        );
 5866    });
 5867
 5868    editor.update_in(cx, |editor, window, cx| {
 5869        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5870    });
 5871    editor.update(cx, |editor, cx| {
 5872        assert_text_with_selections(
 5873            editor,
 5874            indoc! {r#"
 5875                use mod1::mod2::{mod3, mo«ˇ»d4};
 5876
 5877                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5878                    let var1 = "te«ˇ»xt";
 5879                }
 5880            "#},
 5881            cx,
 5882        );
 5883    });
 5884
 5885    // Trying to shrink the selected syntax node one more time has no effect.
 5886    editor.update_in(cx, |editor, window, cx| {
 5887        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5888    });
 5889    editor.update_in(cx, |editor, _, cx| {
 5890        assert_text_with_selections(
 5891            editor,
 5892            indoc! {r#"
 5893                use mod1::mod2::{mod3, mo«ˇ»d4};
 5894
 5895                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5896                    let var1 = "te«ˇ»xt";
 5897                }
 5898            "#},
 5899            cx,
 5900        );
 5901    });
 5902
 5903    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5904    // a fold.
 5905    editor.update_in(cx, |editor, window, cx| {
 5906        editor.fold_creases(
 5907            vec![
 5908                Crease::simple(
 5909                    Point::new(0, 21)..Point::new(0, 24),
 5910                    FoldPlaceholder::test(),
 5911                ),
 5912                Crease::simple(
 5913                    Point::new(3, 20)..Point::new(3, 22),
 5914                    FoldPlaceholder::test(),
 5915                ),
 5916            ],
 5917            true,
 5918            window,
 5919            cx,
 5920        );
 5921        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5922    });
 5923    editor.update(cx, |editor, cx| {
 5924        assert_text_with_selections(
 5925            editor,
 5926            indoc! {r#"
 5927                use mod1::mod2::«{mod3, mod4}ˇ»;
 5928
 5929                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5930                    «let var1 = "text";ˇ»
 5931                }
 5932            "#},
 5933            cx,
 5934        );
 5935    });
 5936}
 5937
 5938#[gpui::test]
 5939async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5940    init_test(cx, |_| {});
 5941
 5942    let base_text = r#"
 5943        impl A {
 5944            // this is an uncommitted comment
 5945
 5946            fn b() {
 5947                c();
 5948            }
 5949
 5950            // this is another uncommitted comment
 5951
 5952            fn d() {
 5953                // e
 5954                // f
 5955            }
 5956        }
 5957
 5958        fn g() {
 5959            // h
 5960        }
 5961    "#
 5962    .unindent();
 5963
 5964    let text = r#"
 5965        ˇimpl A {
 5966
 5967            fn b() {
 5968                c();
 5969            }
 5970
 5971            fn d() {
 5972                // e
 5973                // f
 5974            }
 5975        }
 5976
 5977        fn g() {
 5978            // h
 5979        }
 5980    "#
 5981    .unindent();
 5982
 5983    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5984    cx.set_state(&text);
 5985    cx.set_head_text(&base_text);
 5986    cx.update_editor(|editor, window, cx| {
 5987        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5988    });
 5989
 5990    cx.assert_state_with_diff(
 5991        "
 5992        ˇimpl A {
 5993      -     // this is an uncommitted comment
 5994
 5995            fn b() {
 5996                c();
 5997            }
 5998
 5999      -     // this is another uncommitted comment
 6000      -
 6001            fn d() {
 6002                // e
 6003                // f
 6004            }
 6005        }
 6006
 6007        fn g() {
 6008            // h
 6009        }
 6010    "
 6011        .unindent(),
 6012    );
 6013
 6014    let expected_display_text = "
 6015        impl A {
 6016            // this is an uncommitted comment
 6017
 6018            fn b() {
 6019 6020            }
 6021
 6022            // this is another uncommitted comment
 6023
 6024            fn d() {
 6025 6026            }
 6027        }
 6028
 6029        fn g() {
 6030 6031        }
 6032        "
 6033    .unindent();
 6034
 6035    cx.update_editor(|editor, window, cx| {
 6036        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6037        assert_eq!(editor.display_text(cx), expected_display_text);
 6038    });
 6039}
 6040
 6041#[gpui::test]
 6042async fn test_autoindent(cx: &mut TestAppContext) {
 6043    init_test(cx, |_| {});
 6044
 6045    let language = Arc::new(
 6046        Language::new(
 6047            LanguageConfig {
 6048                brackets: BracketPairConfig {
 6049                    pairs: vec![
 6050                        BracketPair {
 6051                            start: "{".to_string(),
 6052                            end: "}".to_string(),
 6053                            close: false,
 6054                            surround: false,
 6055                            newline: true,
 6056                        },
 6057                        BracketPair {
 6058                            start: "(".to_string(),
 6059                            end: ")".to_string(),
 6060                            close: false,
 6061                            surround: false,
 6062                            newline: true,
 6063                        },
 6064                    ],
 6065                    ..Default::default()
 6066                },
 6067                ..Default::default()
 6068            },
 6069            Some(tree_sitter_rust::LANGUAGE.into()),
 6070        )
 6071        .with_indents_query(
 6072            r#"
 6073                (_ "(" ")" @end) @indent
 6074                (_ "{" "}" @end) @indent
 6075            "#,
 6076        )
 6077        .unwrap(),
 6078    );
 6079
 6080    let text = "fn a() {}";
 6081
 6082    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6083    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6084    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6085    editor
 6086        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6087        .await;
 6088
 6089    editor.update_in(cx, |editor, window, cx| {
 6090        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6091        editor.newline(&Newline, window, cx);
 6092        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6093        assert_eq!(
 6094            editor.selections.ranges(cx),
 6095            &[
 6096                Point::new(1, 4)..Point::new(1, 4),
 6097                Point::new(3, 4)..Point::new(3, 4),
 6098                Point::new(5, 0)..Point::new(5, 0)
 6099            ]
 6100        );
 6101    });
 6102}
 6103
 6104#[gpui::test]
 6105async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6106    init_test(cx, |_| {});
 6107
 6108    {
 6109        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6110        cx.set_state(indoc! {"
 6111            impl A {
 6112
 6113                fn b() {}
 6114
 6115            «fn c() {
 6116
 6117            }ˇ»
 6118            }
 6119        "});
 6120
 6121        cx.update_editor(|editor, window, cx| {
 6122            editor.autoindent(&Default::default(), window, cx);
 6123        });
 6124
 6125        cx.assert_editor_state(indoc! {"
 6126            impl A {
 6127
 6128                fn b() {}
 6129
 6130                «fn c() {
 6131
 6132                }ˇ»
 6133            }
 6134        "});
 6135    }
 6136
 6137    {
 6138        let mut cx = EditorTestContext::new_multibuffer(
 6139            cx,
 6140            [indoc! { "
 6141                impl A {
 6142                «
 6143                // a
 6144                fn b(){}
 6145                »
 6146                «
 6147                    }
 6148                    fn c(){}
 6149                »
 6150            "}],
 6151        );
 6152
 6153        let buffer = cx.update_editor(|editor, _, cx| {
 6154            let buffer = editor.buffer().update(cx, |buffer, _| {
 6155                buffer.all_buffers().iter().next().unwrap().clone()
 6156            });
 6157            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6158            buffer
 6159        });
 6160
 6161        cx.run_until_parked();
 6162        cx.update_editor(|editor, window, cx| {
 6163            editor.select_all(&Default::default(), window, cx);
 6164            editor.autoindent(&Default::default(), window, cx)
 6165        });
 6166        cx.run_until_parked();
 6167
 6168        cx.update(|_, cx| {
 6169            pretty_assertions::assert_eq!(
 6170                buffer.read(cx).text(),
 6171                indoc! { "
 6172                    impl A {
 6173
 6174                        // a
 6175                        fn b(){}
 6176
 6177
 6178                    }
 6179                    fn c(){}
 6180
 6181                " }
 6182            )
 6183        });
 6184    }
 6185}
 6186
 6187#[gpui::test]
 6188async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6189    init_test(cx, |_| {});
 6190
 6191    let mut cx = EditorTestContext::new(cx).await;
 6192
 6193    let language = Arc::new(Language::new(
 6194        LanguageConfig {
 6195            brackets: BracketPairConfig {
 6196                pairs: vec![
 6197                    BracketPair {
 6198                        start: "{".to_string(),
 6199                        end: "}".to_string(),
 6200                        close: true,
 6201                        surround: true,
 6202                        newline: true,
 6203                    },
 6204                    BracketPair {
 6205                        start: "(".to_string(),
 6206                        end: ")".to_string(),
 6207                        close: true,
 6208                        surround: true,
 6209                        newline: true,
 6210                    },
 6211                    BracketPair {
 6212                        start: "/*".to_string(),
 6213                        end: " */".to_string(),
 6214                        close: true,
 6215                        surround: true,
 6216                        newline: true,
 6217                    },
 6218                    BracketPair {
 6219                        start: "[".to_string(),
 6220                        end: "]".to_string(),
 6221                        close: false,
 6222                        surround: false,
 6223                        newline: true,
 6224                    },
 6225                    BracketPair {
 6226                        start: "\"".to_string(),
 6227                        end: "\"".to_string(),
 6228                        close: true,
 6229                        surround: true,
 6230                        newline: false,
 6231                    },
 6232                    BracketPair {
 6233                        start: "<".to_string(),
 6234                        end: ">".to_string(),
 6235                        close: false,
 6236                        surround: true,
 6237                        newline: true,
 6238                    },
 6239                ],
 6240                ..Default::default()
 6241            },
 6242            autoclose_before: "})]".to_string(),
 6243            ..Default::default()
 6244        },
 6245        Some(tree_sitter_rust::LANGUAGE.into()),
 6246    ));
 6247
 6248    cx.language_registry().add(language.clone());
 6249    cx.update_buffer(|buffer, cx| {
 6250        buffer.set_language(Some(language), cx);
 6251    });
 6252
 6253    cx.set_state(
 6254        &r#"
 6255            🏀ˇ
 6256            εˇ
 6257            ❤️ˇ
 6258        "#
 6259        .unindent(),
 6260    );
 6261
 6262    // autoclose multiple nested brackets at multiple cursors
 6263    cx.update_editor(|editor, window, cx| {
 6264        editor.handle_input("{", window, cx);
 6265        editor.handle_input("{", window, cx);
 6266        editor.handle_input("{", window, cx);
 6267    });
 6268    cx.assert_editor_state(
 6269        &"
 6270            🏀{{{ˇ}}}
 6271            ε{{{ˇ}}}
 6272            ❤️{{{ˇ}}}
 6273        "
 6274        .unindent(),
 6275    );
 6276
 6277    // insert a different closing bracket
 6278    cx.update_editor(|editor, window, cx| {
 6279        editor.handle_input(")", window, cx);
 6280    });
 6281    cx.assert_editor_state(
 6282        &"
 6283            🏀{{{)ˇ}}}
 6284            ε{{{)ˇ}}}
 6285            ❤️{{{)ˇ}}}
 6286        "
 6287        .unindent(),
 6288    );
 6289
 6290    // skip over the auto-closed brackets when typing a closing bracket
 6291    cx.update_editor(|editor, window, cx| {
 6292        editor.move_right(&MoveRight, window, cx);
 6293        editor.handle_input("}", window, cx);
 6294        editor.handle_input("}", window, cx);
 6295        editor.handle_input("}", window, cx);
 6296    });
 6297    cx.assert_editor_state(
 6298        &"
 6299            🏀{{{)}}}}ˇ
 6300            ε{{{)}}}}ˇ
 6301            ❤️{{{)}}}}ˇ
 6302        "
 6303        .unindent(),
 6304    );
 6305
 6306    // autoclose multi-character pairs
 6307    cx.set_state(
 6308        &"
 6309            ˇ
 6310            ˇ
 6311        "
 6312        .unindent(),
 6313    );
 6314    cx.update_editor(|editor, window, cx| {
 6315        editor.handle_input("/", window, cx);
 6316        editor.handle_input("*", window, cx);
 6317    });
 6318    cx.assert_editor_state(
 6319        &"
 6320            /*ˇ */
 6321            /*ˇ */
 6322        "
 6323        .unindent(),
 6324    );
 6325
 6326    // one cursor autocloses a multi-character pair, one cursor
 6327    // does not autoclose.
 6328    cx.set_state(
 6329        &"
 6330 6331            ˇ
 6332        "
 6333        .unindent(),
 6334    );
 6335    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6336    cx.assert_editor_state(
 6337        &"
 6338            /*ˇ */
 6339 6340        "
 6341        .unindent(),
 6342    );
 6343
 6344    // Don't autoclose if the next character isn't whitespace and isn't
 6345    // listed in the language's "autoclose_before" section.
 6346    cx.set_state("ˇa b");
 6347    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6348    cx.assert_editor_state("{ˇa b");
 6349
 6350    // Don't autoclose if `close` is false for the bracket pair
 6351    cx.set_state("ˇ");
 6352    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6353    cx.assert_editor_state("");
 6354
 6355    // Surround with brackets if text is selected
 6356    cx.set_state("«aˇ» b");
 6357    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6358    cx.assert_editor_state("{«aˇ»} b");
 6359
 6360    // Autoclose when not immediately after a word character
 6361    cx.set_state("a ˇ");
 6362    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6363    cx.assert_editor_state("a \"ˇ\"");
 6364
 6365    // Autoclose pair where the start and end characters are the same
 6366    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6367    cx.assert_editor_state("a \"\"ˇ");
 6368
 6369    // Don't autoclose when immediately after a word character
 6370    cx.set_state("");
 6371    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6372    cx.assert_editor_state("a\"ˇ");
 6373
 6374    // Do autoclose when after a non-word character
 6375    cx.set_state("");
 6376    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6377    cx.assert_editor_state("{\"ˇ\"");
 6378
 6379    // Non identical pairs autoclose regardless of preceding character
 6380    cx.set_state("");
 6381    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6382    cx.assert_editor_state("a{ˇ}");
 6383
 6384    // Don't autoclose pair if autoclose is disabled
 6385    cx.set_state("ˇ");
 6386    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6387    cx.assert_editor_state("");
 6388
 6389    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6390    cx.set_state("«aˇ» b");
 6391    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6392    cx.assert_editor_state("<«aˇ»> b");
 6393}
 6394
 6395#[gpui::test]
 6396async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6397    init_test(cx, |settings| {
 6398        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6399    });
 6400
 6401    let mut cx = EditorTestContext::new(cx).await;
 6402
 6403    let language = Arc::new(Language::new(
 6404        LanguageConfig {
 6405            brackets: BracketPairConfig {
 6406                pairs: vec![
 6407                    BracketPair {
 6408                        start: "{".to_string(),
 6409                        end: "}".to_string(),
 6410                        close: true,
 6411                        surround: true,
 6412                        newline: true,
 6413                    },
 6414                    BracketPair {
 6415                        start: "(".to_string(),
 6416                        end: ")".to_string(),
 6417                        close: true,
 6418                        surround: true,
 6419                        newline: true,
 6420                    },
 6421                    BracketPair {
 6422                        start: "[".to_string(),
 6423                        end: "]".to_string(),
 6424                        close: false,
 6425                        surround: false,
 6426                        newline: true,
 6427                    },
 6428                ],
 6429                ..Default::default()
 6430            },
 6431            autoclose_before: "})]".to_string(),
 6432            ..Default::default()
 6433        },
 6434        Some(tree_sitter_rust::LANGUAGE.into()),
 6435    ));
 6436
 6437    cx.language_registry().add(language.clone());
 6438    cx.update_buffer(|buffer, cx| {
 6439        buffer.set_language(Some(language), cx);
 6440    });
 6441
 6442    cx.set_state(
 6443        &"
 6444            ˇ
 6445            ˇ
 6446            ˇ
 6447        "
 6448        .unindent(),
 6449    );
 6450
 6451    // ensure only matching closing brackets are skipped over
 6452    cx.update_editor(|editor, window, cx| {
 6453        editor.handle_input("}", window, cx);
 6454        editor.move_left(&MoveLeft, window, cx);
 6455        editor.handle_input(")", window, cx);
 6456        editor.move_left(&MoveLeft, window, cx);
 6457    });
 6458    cx.assert_editor_state(
 6459        &"
 6460            ˇ)}
 6461            ˇ)}
 6462            ˇ)}
 6463        "
 6464        .unindent(),
 6465    );
 6466
 6467    // skip-over closing brackets at multiple cursors
 6468    cx.update_editor(|editor, window, cx| {
 6469        editor.handle_input(")", window, cx);
 6470        editor.handle_input("}", window, cx);
 6471    });
 6472    cx.assert_editor_state(
 6473        &"
 6474            )}ˇ
 6475            )}ˇ
 6476            )}ˇ
 6477        "
 6478        .unindent(),
 6479    );
 6480
 6481    // ignore non-close brackets
 6482    cx.update_editor(|editor, window, cx| {
 6483        editor.handle_input("]", window, cx);
 6484        editor.move_left(&MoveLeft, window, cx);
 6485        editor.handle_input("]", window, cx);
 6486    });
 6487    cx.assert_editor_state(
 6488        &"
 6489            )}]ˇ]
 6490            )}]ˇ]
 6491            )}]ˇ]
 6492        "
 6493        .unindent(),
 6494    );
 6495}
 6496
 6497#[gpui::test]
 6498async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6499    init_test(cx, |_| {});
 6500
 6501    let mut cx = EditorTestContext::new(cx).await;
 6502
 6503    let html_language = Arc::new(
 6504        Language::new(
 6505            LanguageConfig {
 6506                name: "HTML".into(),
 6507                brackets: BracketPairConfig {
 6508                    pairs: vec![
 6509                        BracketPair {
 6510                            start: "<".into(),
 6511                            end: ">".into(),
 6512                            close: true,
 6513                            ..Default::default()
 6514                        },
 6515                        BracketPair {
 6516                            start: "{".into(),
 6517                            end: "}".into(),
 6518                            close: true,
 6519                            ..Default::default()
 6520                        },
 6521                        BracketPair {
 6522                            start: "(".into(),
 6523                            end: ")".into(),
 6524                            close: true,
 6525                            ..Default::default()
 6526                        },
 6527                    ],
 6528                    ..Default::default()
 6529                },
 6530                autoclose_before: "})]>".into(),
 6531                ..Default::default()
 6532            },
 6533            Some(tree_sitter_html::LANGUAGE.into()),
 6534        )
 6535        .with_injection_query(
 6536            r#"
 6537            (script_element
 6538                (raw_text) @injection.content
 6539                (#set! injection.language "javascript"))
 6540            "#,
 6541        )
 6542        .unwrap(),
 6543    );
 6544
 6545    let javascript_language = Arc::new(Language::new(
 6546        LanguageConfig {
 6547            name: "JavaScript".into(),
 6548            brackets: BracketPairConfig {
 6549                pairs: vec![
 6550                    BracketPair {
 6551                        start: "/*".into(),
 6552                        end: " */".into(),
 6553                        close: true,
 6554                        ..Default::default()
 6555                    },
 6556                    BracketPair {
 6557                        start: "{".into(),
 6558                        end: "}".into(),
 6559                        close: true,
 6560                        ..Default::default()
 6561                    },
 6562                    BracketPair {
 6563                        start: "(".into(),
 6564                        end: ")".into(),
 6565                        close: true,
 6566                        ..Default::default()
 6567                    },
 6568                ],
 6569                ..Default::default()
 6570            },
 6571            autoclose_before: "})]>".into(),
 6572            ..Default::default()
 6573        },
 6574        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6575    ));
 6576
 6577    cx.language_registry().add(html_language.clone());
 6578    cx.language_registry().add(javascript_language.clone());
 6579
 6580    cx.update_buffer(|buffer, cx| {
 6581        buffer.set_language(Some(html_language), cx);
 6582    });
 6583
 6584    cx.set_state(
 6585        &r#"
 6586            <body>ˇ
 6587                <script>
 6588                    var x = 1;ˇ
 6589                </script>
 6590            </body>ˇ
 6591        "#
 6592        .unindent(),
 6593    );
 6594
 6595    // Precondition: different languages are active at different locations.
 6596    cx.update_editor(|editor, window, cx| {
 6597        let snapshot = editor.snapshot(window, cx);
 6598        let cursors = editor.selections.ranges::<usize>(cx);
 6599        let languages = cursors
 6600            .iter()
 6601            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6602            .collect::<Vec<_>>();
 6603        assert_eq!(
 6604            languages,
 6605            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6606        );
 6607    });
 6608
 6609    // Angle brackets autoclose in HTML, but not JavaScript.
 6610    cx.update_editor(|editor, window, cx| {
 6611        editor.handle_input("<", window, cx);
 6612        editor.handle_input("a", window, cx);
 6613    });
 6614    cx.assert_editor_state(
 6615        &r#"
 6616            <body><aˇ>
 6617                <script>
 6618                    var x = 1;<aˇ
 6619                </script>
 6620            </body><aˇ>
 6621        "#
 6622        .unindent(),
 6623    );
 6624
 6625    // Curly braces and parens autoclose in both HTML and JavaScript.
 6626    cx.update_editor(|editor, window, cx| {
 6627        editor.handle_input(" b=", window, cx);
 6628        editor.handle_input("{", window, cx);
 6629        editor.handle_input("c", window, cx);
 6630        editor.handle_input("(", window, cx);
 6631    });
 6632    cx.assert_editor_state(
 6633        &r#"
 6634            <body><a b={c(ˇ)}>
 6635                <script>
 6636                    var x = 1;<a b={c(ˇ)}
 6637                </script>
 6638            </body><a b={c(ˇ)}>
 6639        "#
 6640        .unindent(),
 6641    );
 6642
 6643    // Brackets that were already autoclosed are skipped.
 6644    cx.update_editor(|editor, window, cx| {
 6645        editor.handle_input(")", window, cx);
 6646        editor.handle_input("d", window, cx);
 6647        editor.handle_input("}", window, cx);
 6648    });
 6649    cx.assert_editor_state(
 6650        &r#"
 6651            <body><a b={c()d}ˇ>
 6652                <script>
 6653                    var x = 1;<a b={c()d}ˇ
 6654                </script>
 6655            </body><a b={c()d}ˇ>
 6656        "#
 6657        .unindent(),
 6658    );
 6659    cx.update_editor(|editor, window, cx| {
 6660        editor.handle_input(">", window, cx);
 6661    });
 6662    cx.assert_editor_state(
 6663        &r#"
 6664            <body><a b={c()d}>ˇ
 6665                <script>
 6666                    var x = 1;<a b={c()d}>ˇ
 6667                </script>
 6668            </body><a b={c()d}>ˇ
 6669        "#
 6670        .unindent(),
 6671    );
 6672
 6673    // Reset
 6674    cx.set_state(
 6675        &r#"
 6676            <body>ˇ
 6677                <script>
 6678                    var x = 1;ˇ
 6679                </script>
 6680            </body>ˇ
 6681        "#
 6682        .unindent(),
 6683    );
 6684
 6685    cx.update_editor(|editor, window, cx| {
 6686        editor.handle_input("<", window, cx);
 6687    });
 6688    cx.assert_editor_state(
 6689        &r#"
 6690            <body><ˇ>
 6691                <script>
 6692                    var x = 1;<ˇ
 6693                </script>
 6694            </body><ˇ>
 6695        "#
 6696        .unindent(),
 6697    );
 6698
 6699    // When backspacing, the closing angle brackets are removed.
 6700    cx.update_editor(|editor, window, cx| {
 6701        editor.backspace(&Backspace, window, cx);
 6702    });
 6703    cx.assert_editor_state(
 6704        &r#"
 6705            <body>ˇ
 6706                <script>
 6707                    var x = 1;ˇ
 6708                </script>
 6709            </body>ˇ
 6710        "#
 6711        .unindent(),
 6712    );
 6713
 6714    // Block comments autoclose in JavaScript, but not HTML.
 6715    cx.update_editor(|editor, window, cx| {
 6716        editor.handle_input("/", window, cx);
 6717        editor.handle_input("*", window, cx);
 6718    });
 6719    cx.assert_editor_state(
 6720        &r#"
 6721            <body>/*ˇ
 6722                <script>
 6723                    var x = 1;/*ˇ */
 6724                </script>
 6725            </body>/*ˇ
 6726        "#
 6727        .unindent(),
 6728    );
 6729}
 6730
 6731#[gpui::test]
 6732async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6733    init_test(cx, |_| {});
 6734
 6735    let mut cx = EditorTestContext::new(cx).await;
 6736
 6737    let rust_language = Arc::new(
 6738        Language::new(
 6739            LanguageConfig {
 6740                name: "Rust".into(),
 6741                brackets: serde_json::from_value(json!([
 6742                    { "start": "{", "end": "}", "close": true, "newline": true },
 6743                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6744                ]))
 6745                .unwrap(),
 6746                autoclose_before: "})]>".into(),
 6747                ..Default::default()
 6748            },
 6749            Some(tree_sitter_rust::LANGUAGE.into()),
 6750        )
 6751        .with_override_query("(string_literal) @string")
 6752        .unwrap(),
 6753    );
 6754
 6755    cx.language_registry().add(rust_language.clone());
 6756    cx.update_buffer(|buffer, cx| {
 6757        buffer.set_language(Some(rust_language), cx);
 6758    });
 6759
 6760    cx.set_state(
 6761        &r#"
 6762            let x = ˇ
 6763        "#
 6764        .unindent(),
 6765    );
 6766
 6767    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6768    cx.update_editor(|editor, window, cx| {
 6769        editor.handle_input("\"", window, cx);
 6770    });
 6771    cx.assert_editor_state(
 6772        &r#"
 6773            let x = "ˇ"
 6774        "#
 6775        .unindent(),
 6776    );
 6777
 6778    // Inserting another quotation mark. The cursor moves across the existing
 6779    // automatically-inserted quotation mark.
 6780    cx.update_editor(|editor, window, cx| {
 6781        editor.handle_input("\"", window, cx);
 6782    });
 6783    cx.assert_editor_state(
 6784        &r#"
 6785            let x = ""ˇ
 6786        "#
 6787        .unindent(),
 6788    );
 6789
 6790    // Reset
 6791    cx.set_state(
 6792        &r#"
 6793            let x = ˇ
 6794        "#
 6795        .unindent(),
 6796    );
 6797
 6798    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6799    cx.update_editor(|editor, window, cx| {
 6800        editor.handle_input("\"", window, cx);
 6801        editor.handle_input(" ", window, cx);
 6802        editor.move_left(&Default::default(), window, cx);
 6803        editor.handle_input("\\", window, cx);
 6804        editor.handle_input("\"", window, cx);
 6805    });
 6806    cx.assert_editor_state(
 6807        &r#"
 6808            let x = "\"ˇ "
 6809        "#
 6810        .unindent(),
 6811    );
 6812
 6813    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6814    // mark. Nothing is inserted.
 6815    cx.update_editor(|editor, window, cx| {
 6816        editor.move_right(&Default::default(), window, cx);
 6817        editor.handle_input("\"", window, cx);
 6818    });
 6819    cx.assert_editor_state(
 6820        &r#"
 6821            let x = "\" "ˇ
 6822        "#
 6823        .unindent(),
 6824    );
 6825}
 6826
 6827#[gpui::test]
 6828async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6829    init_test(cx, |_| {});
 6830
 6831    let language = Arc::new(Language::new(
 6832        LanguageConfig {
 6833            brackets: BracketPairConfig {
 6834                pairs: vec![
 6835                    BracketPair {
 6836                        start: "{".to_string(),
 6837                        end: "}".to_string(),
 6838                        close: true,
 6839                        surround: true,
 6840                        newline: true,
 6841                    },
 6842                    BracketPair {
 6843                        start: "/* ".to_string(),
 6844                        end: "*/".to_string(),
 6845                        close: true,
 6846                        surround: true,
 6847                        ..Default::default()
 6848                    },
 6849                ],
 6850                ..Default::default()
 6851            },
 6852            ..Default::default()
 6853        },
 6854        Some(tree_sitter_rust::LANGUAGE.into()),
 6855    ));
 6856
 6857    let text = r#"
 6858        a
 6859        b
 6860        c
 6861    "#
 6862    .unindent();
 6863
 6864    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6865    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6866    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6867    editor
 6868        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6869        .await;
 6870
 6871    editor.update_in(cx, |editor, window, cx| {
 6872        editor.change_selections(None, window, cx, |s| {
 6873            s.select_display_ranges([
 6874                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6875                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6876                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6877            ])
 6878        });
 6879
 6880        editor.handle_input("{", window, cx);
 6881        editor.handle_input("{", window, cx);
 6882        editor.handle_input("{", window, cx);
 6883        assert_eq!(
 6884            editor.text(cx),
 6885            "
 6886                {{{a}}}
 6887                {{{b}}}
 6888                {{{c}}}
 6889            "
 6890            .unindent()
 6891        );
 6892        assert_eq!(
 6893            editor.selections.display_ranges(cx),
 6894            [
 6895                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6896                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6897                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6898            ]
 6899        );
 6900
 6901        editor.undo(&Undo, window, cx);
 6902        editor.undo(&Undo, window, cx);
 6903        editor.undo(&Undo, window, cx);
 6904        assert_eq!(
 6905            editor.text(cx),
 6906            "
 6907                a
 6908                b
 6909                c
 6910            "
 6911            .unindent()
 6912        );
 6913        assert_eq!(
 6914            editor.selections.display_ranges(cx),
 6915            [
 6916                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6917                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6918                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6919            ]
 6920        );
 6921
 6922        // Ensure inserting the first character of a multi-byte bracket pair
 6923        // doesn't surround the selections with the bracket.
 6924        editor.handle_input("/", window, cx);
 6925        assert_eq!(
 6926            editor.text(cx),
 6927            "
 6928                /
 6929                /
 6930                /
 6931            "
 6932            .unindent()
 6933        );
 6934        assert_eq!(
 6935            editor.selections.display_ranges(cx),
 6936            [
 6937                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6938                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6939                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6940            ]
 6941        );
 6942
 6943        editor.undo(&Undo, window, cx);
 6944        assert_eq!(
 6945            editor.text(cx),
 6946            "
 6947                a
 6948                b
 6949                c
 6950            "
 6951            .unindent()
 6952        );
 6953        assert_eq!(
 6954            editor.selections.display_ranges(cx),
 6955            [
 6956                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6957                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6958                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6959            ]
 6960        );
 6961
 6962        // Ensure inserting the last character of a multi-byte bracket pair
 6963        // doesn't surround the selections with the bracket.
 6964        editor.handle_input("*", window, cx);
 6965        assert_eq!(
 6966            editor.text(cx),
 6967            "
 6968                *
 6969                *
 6970                *
 6971            "
 6972            .unindent()
 6973        );
 6974        assert_eq!(
 6975            editor.selections.display_ranges(cx),
 6976            [
 6977                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6978                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6979                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6980            ]
 6981        );
 6982    });
 6983}
 6984
 6985#[gpui::test]
 6986async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6987    init_test(cx, |_| {});
 6988
 6989    let language = Arc::new(Language::new(
 6990        LanguageConfig {
 6991            brackets: BracketPairConfig {
 6992                pairs: vec![BracketPair {
 6993                    start: "{".to_string(),
 6994                    end: "}".to_string(),
 6995                    close: true,
 6996                    surround: true,
 6997                    newline: true,
 6998                }],
 6999                ..Default::default()
 7000            },
 7001            autoclose_before: "}".to_string(),
 7002            ..Default::default()
 7003        },
 7004        Some(tree_sitter_rust::LANGUAGE.into()),
 7005    ));
 7006
 7007    let text = r#"
 7008        a
 7009        b
 7010        c
 7011    "#
 7012    .unindent();
 7013
 7014    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7015    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7016    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7017    editor
 7018        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7019        .await;
 7020
 7021    editor.update_in(cx, |editor, window, cx| {
 7022        editor.change_selections(None, window, cx, |s| {
 7023            s.select_ranges([
 7024                Point::new(0, 1)..Point::new(0, 1),
 7025                Point::new(1, 1)..Point::new(1, 1),
 7026                Point::new(2, 1)..Point::new(2, 1),
 7027            ])
 7028        });
 7029
 7030        editor.handle_input("{", window, cx);
 7031        editor.handle_input("{", window, cx);
 7032        editor.handle_input("_", window, cx);
 7033        assert_eq!(
 7034            editor.text(cx),
 7035            "
 7036                a{{_}}
 7037                b{{_}}
 7038                c{{_}}
 7039            "
 7040            .unindent()
 7041        );
 7042        assert_eq!(
 7043            editor.selections.ranges::<Point>(cx),
 7044            [
 7045                Point::new(0, 4)..Point::new(0, 4),
 7046                Point::new(1, 4)..Point::new(1, 4),
 7047                Point::new(2, 4)..Point::new(2, 4)
 7048            ]
 7049        );
 7050
 7051        editor.backspace(&Default::default(), window, cx);
 7052        editor.backspace(&Default::default(), window, cx);
 7053        assert_eq!(
 7054            editor.text(cx),
 7055            "
 7056                a{}
 7057                b{}
 7058                c{}
 7059            "
 7060            .unindent()
 7061        );
 7062        assert_eq!(
 7063            editor.selections.ranges::<Point>(cx),
 7064            [
 7065                Point::new(0, 2)..Point::new(0, 2),
 7066                Point::new(1, 2)..Point::new(1, 2),
 7067                Point::new(2, 2)..Point::new(2, 2)
 7068            ]
 7069        );
 7070
 7071        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7072        assert_eq!(
 7073            editor.text(cx),
 7074            "
 7075                a
 7076                b
 7077                c
 7078            "
 7079            .unindent()
 7080        );
 7081        assert_eq!(
 7082            editor.selections.ranges::<Point>(cx),
 7083            [
 7084                Point::new(0, 1)..Point::new(0, 1),
 7085                Point::new(1, 1)..Point::new(1, 1),
 7086                Point::new(2, 1)..Point::new(2, 1)
 7087            ]
 7088        );
 7089    });
 7090}
 7091
 7092#[gpui::test]
 7093async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7094    init_test(cx, |settings| {
 7095        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7096    });
 7097
 7098    let mut cx = EditorTestContext::new(cx).await;
 7099
 7100    let language = Arc::new(Language::new(
 7101        LanguageConfig {
 7102            brackets: BracketPairConfig {
 7103                pairs: vec![
 7104                    BracketPair {
 7105                        start: "{".to_string(),
 7106                        end: "}".to_string(),
 7107                        close: true,
 7108                        surround: true,
 7109                        newline: true,
 7110                    },
 7111                    BracketPair {
 7112                        start: "(".to_string(),
 7113                        end: ")".to_string(),
 7114                        close: true,
 7115                        surround: true,
 7116                        newline: true,
 7117                    },
 7118                    BracketPair {
 7119                        start: "[".to_string(),
 7120                        end: "]".to_string(),
 7121                        close: false,
 7122                        surround: true,
 7123                        newline: true,
 7124                    },
 7125                ],
 7126                ..Default::default()
 7127            },
 7128            autoclose_before: "})]".to_string(),
 7129            ..Default::default()
 7130        },
 7131        Some(tree_sitter_rust::LANGUAGE.into()),
 7132    ));
 7133
 7134    cx.language_registry().add(language.clone());
 7135    cx.update_buffer(|buffer, cx| {
 7136        buffer.set_language(Some(language), cx);
 7137    });
 7138
 7139    cx.set_state(
 7140        &"
 7141            {(ˇ)}
 7142            [[ˇ]]
 7143            {(ˇ)}
 7144        "
 7145        .unindent(),
 7146    );
 7147
 7148    cx.update_editor(|editor, window, cx| {
 7149        editor.backspace(&Default::default(), window, cx);
 7150        editor.backspace(&Default::default(), window, cx);
 7151    });
 7152
 7153    cx.assert_editor_state(
 7154        &"
 7155            ˇ
 7156            ˇ]]
 7157            ˇ
 7158        "
 7159        .unindent(),
 7160    );
 7161
 7162    cx.update_editor(|editor, window, cx| {
 7163        editor.handle_input("{", window, cx);
 7164        editor.handle_input("{", window, cx);
 7165        editor.move_right(&MoveRight, window, cx);
 7166        editor.move_right(&MoveRight, window, cx);
 7167        editor.move_left(&MoveLeft, window, cx);
 7168        editor.move_left(&MoveLeft, window, cx);
 7169        editor.backspace(&Default::default(), window, cx);
 7170    });
 7171
 7172    cx.assert_editor_state(
 7173        &"
 7174            {ˇ}
 7175            {ˇ}]]
 7176            {ˇ}
 7177        "
 7178        .unindent(),
 7179    );
 7180
 7181    cx.update_editor(|editor, window, cx| {
 7182        editor.backspace(&Default::default(), window, cx);
 7183    });
 7184
 7185    cx.assert_editor_state(
 7186        &"
 7187            ˇ
 7188            ˇ]]
 7189            ˇ
 7190        "
 7191        .unindent(),
 7192    );
 7193}
 7194
 7195#[gpui::test]
 7196async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7197    init_test(cx, |_| {});
 7198
 7199    let language = Arc::new(Language::new(
 7200        LanguageConfig::default(),
 7201        Some(tree_sitter_rust::LANGUAGE.into()),
 7202    ));
 7203
 7204    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7205    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7206    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7207    editor
 7208        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7209        .await;
 7210
 7211    editor.update_in(cx, |editor, window, cx| {
 7212        editor.set_auto_replace_emoji_shortcode(true);
 7213
 7214        editor.handle_input("Hello ", window, cx);
 7215        editor.handle_input(":wave", window, cx);
 7216        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7217
 7218        editor.handle_input(":", window, cx);
 7219        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7220
 7221        editor.handle_input(" :smile", window, cx);
 7222        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7223
 7224        editor.handle_input(":", window, cx);
 7225        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7226
 7227        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7228        editor.handle_input(":wave", window, cx);
 7229        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7230
 7231        editor.handle_input(":", window, cx);
 7232        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7233
 7234        editor.handle_input(":1", window, cx);
 7235        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7236
 7237        editor.handle_input(":", window, cx);
 7238        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7239
 7240        // Ensure shortcode does not get replaced when it is part of a word
 7241        editor.handle_input(" Test:wave", window, cx);
 7242        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7243
 7244        editor.handle_input(":", window, cx);
 7245        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7246
 7247        editor.set_auto_replace_emoji_shortcode(false);
 7248
 7249        // Ensure shortcode does not get replaced when auto replace is off
 7250        editor.handle_input(" :wave", window, cx);
 7251        assert_eq!(
 7252            editor.text(cx),
 7253            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7254        );
 7255
 7256        editor.handle_input(":", window, cx);
 7257        assert_eq!(
 7258            editor.text(cx),
 7259            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7260        );
 7261    });
 7262}
 7263
 7264#[gpui::test]
 7265async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7266    init_test(cx, |_| {});
 7267
 7268    let (text, insertion_ranges) = marked_text_ranges(
 7269        indoc! {"
 7270            ˇ
 7271        "},
 7272        false,
 7273    );
 7274
 7275    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7276    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7277
 7278    _ = editor.update_in(cx, |editor, window, cx| {
 7279        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7280
 7281        editor
 7282            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7283            .unwrap();
 7284
 7285        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7286            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7287            assert_eq!(editor.text(cx), expected_text);
 7288            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7289        }
 7290
 7291        assert(
 7292            editor,
 7293            cx,
 7294            indoc! {"
 7295            type «» =•
 7296            "},
 7297        );
 7298
 7299        assert!(editor.context_menu_visible(), "There should be a matches");
 7300    });
 7301}
 7302
 7303#[gpui::test]
 7304async fn test_snippets(cx: &mut TestAppContext) {
 7305    init_test(cx, |_| {});
 7306
 7307    let (text, insertion_ranges) = marked_text_ranges(
 7308        indoc! {"
 7309            a.ˇ b
 7310            a.ˇ b
 7311            a.ˇ b
 7312        "},
 7313        false,
 7314    );
 7315
 7316    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7317    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7318
 7319    editor.update_in(cx, |editor, window, cx| {
 7320        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7321
 7322        editor
 7323            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7324            .unwrap();
 7325
 7326        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7327            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7328            assert_eq!(editor.text(cx), expected_text);
 7329            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7330        }
 7331
 7332        assert(
 7333            editor,
 7334            cx,
 7335            indoc! {"
 7336                a.f(«one», two, «three») b
 7337                a.f(«one», two, «three») b
 7338                a.f(«one», two, «three») b
 7339            "},
 7340        );
 7341
 7342        // Can't move earlier than the first tab stop
 7343        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7344        assert(
 7345            editor,
 7346            cx,
 7347            indoc! {"
 7348                a.f(«one», two, «three») b
 7349                a.f(«one», two, «three») b
 7350                a.f(«one», two, «three») b
 7351            "},
 7352        );
 7353
 7354        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7355        assert(
 7356            editor,
 7357            cx,
 7358            indoc! {"
 7359                a.f(one, «two», three) b
 7360                a.f(one, «two», three) b
 7361                a.f(one, «two», three) b
 7362            "},
 7363        );
 7364
 7365        editor.move_to_prev_snippet_tabstop(window, cx);
 7366        assert(
 7367            editor,
 7368            cx,
 7369            indoc! {"
 7370                a.f(«one», two, «three») b
 7371                a.f(«one», two, «three») b
 7372                a.f(«one», two, «three») b
 7373            "},
 7374        );
 7375
 7376        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7377        assert(
 7378            editor,
 7379            cx,
 7380            indoc! {"
 7381                a.f(one, «two», three) b
 7382                a.f(one, «two», three) b
 7383                a.f(one, «two», three) b
 7384            "},
 7385        );
 7386        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7387        assert(
 7388            editor,
 7389            cx,
 7390            indoc! {"
 7391                a.f(one, two, three)ˇ b
 7392                a.f(one, two, three)ˇ b
 7393                a.f(one, two, three)ˇ b
 7394            "},
 7395        );
 7396
 7397        // As soon as the last tab stop is reached, snippet state is gone
 7398        editor.move_to_prev_snippet_tabstop(window, cx);
 7399        assert(
 7400            editor,
 7401            cx,
 7402            indoc! {"
 7403                a.f(one, two, three)ˇ b
 7404                a.f(one, two, three)ˇ b
 7405                a.f(one, two, three)ˇ b
 7406            "},
 7407        );
 7408    });
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7413    init_test(cx, |_| {});
 7414
 7415    let fs = FakeFs::new(cx.executor());
 7416    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7417
 7418    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7419
 7420    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7421    language_registry.add(rust_lang());
 7422    let mut fake_servers = language_registry.register_fake_lsp(
 7423        "Rust",
 7424        FakeLspAdapter {
 7425            capabilities: lsp::ServerCapabilities {
 7426                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7427                ..Default::default()
 7428            },
 7429            ..Default::default()
 7430        },
 7431    );
 7432
 7433    let buffer = project
 7434        .update(cx, |project, cx| {
 7435            project.open_local_buffer(path!("/file.rs"), cx)
 7436        })
 7437        .await
 7438        .unwrap();
 7439
 7440    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7441    let (editor, cx) = cx.add_window_view(|window, cx| {
 7442        build_editor_with_project(project.clone(), buffer, window, cx)
 7443    });
 7444    editor.update_in(cx, |editor, window, cx| {
 7445        editor.set_text("one\ntwo\nthree\n", window, cx)
 7446    });
 7447    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7448
 7449    cx.executor().start_waiting();
 7450    let fake_server = fake_servers.next().await.unwrap();
 7451
 7452    let save = editor
 7453        .update_in(cx, |editor, window, cx| {
 7454            editor.save(true, project.clone(), window, cx)
 7455        })
 7456        .unwrap();
 7457    fake_server
 7458        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7459            assert_eq!(
 7460                params.text_document.uri,
 7461                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7462            );
 7463            assert_eq!(params.options.tab_size, 4);
 7464            Ok(Some(vec![lsp::TextEdit::new(
 7465                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7466                ", ".to_string(),
 7467            )]))
 7468        })
 7469        .next()
 7470        .await;
 7471    cx.executor().start_waiting();
 7472    save.await;
 7473
 7474    assert_eq!(
 7475        editor.update(cx, |editor, cx| editor.text(cx)),
 7476        "one, two\nthree\n"
 7477    );
 7478    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7479
 7480    editor.update_in(cx, |editor, window, cx| {
 7481        editor.set_text("one\ntwo\nthree\n", window, cx)
 7482    });
 7483    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7484
 7485    // Ensure we can still save even if formatting hangs.
 7486    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7487        assert_eq!(
 7488            params.text_document.uri,
 7489            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7490        );
 7491        futures::future::pending::<()>().await;
 7492        unreachable!()
 7493    });
 7494    let save = editor
 7495        .update_in(cx, |editor, window, cx| {
 7496            editor.save(true, project.clone(), window, cx)
 7497        })
 7498        .unwrap();
 7499    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7500    cx.executor().start_waiting();
 7501    save.await;
 7502    assert_eq!(
 7503        editor.update(cx, |editor, cx| editor.text(cx)),
 7504        "one\ntwo\nthree\n"
 7505    );
 7506    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7507
 7508    // For non-dirty buffer, no formatting request should be sent
 7509    let save = editor
 7510        .update_in(cx, |editor, window, cx| {
 7511            editor.save(true, project.clone(), window, cx)
 7512        })
 7513        .unwrap();
 7514    let _pending_format_request = fake_server
 7515        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7516            panic!("Should not be invoked on non-dirty buffer");
 7517        })
 7518        .next();
 7519    cx.executor().start_waiting();
 7520    save.await;
 7521
 7522    // Set rust language override and assert overridden tabsize is sent to language server
 7523    update_test_language_settings(cx, |settings| {
 7524        settings.languages.insert(
 7525            "Rust".into(),
 7526            LanguageSettingsContent {
 7527                tab_size: NonZeroU32::new(8),
 7528                ..Default::default()
 7529            },
 7530        );
 7531    });
 7532
 7533    editor.update_in(cx, |editor, window, cx| {
 7534        editor.set_text("somehting_new\n", window, cx)
 7535    });
 7536    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7537    let save = editor
 7538        .update_in(cx, |editor, window, cx| {
 7539            editor.save(true, project.clone(), window, cx)
 7540        })
 7541        .unwrap();
 7542    fake_server
 7543        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7544            assert_eq!(
 7545                params.text_document.uri,
 7546                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7547            );
 7548            assert_eq!(params.options.tab_size, 8);
 7549            Ok(Some(vec![]))
 7550        })
 7551        .next()
 7552        .await;
 7553    cx.executor().start_waiting();
 7554    save.await;
 7555}
 7556
 7557#[gpui::test]
 7558async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7559    init_test(cx, |_| {});
 7560
 7561    let cols = 4;
 7562    let rows = 10;
 7563    let sample_text_1 = sample_text(rows, cols, 'a');
 7564    assert_eq!(
 7565        sample_text_1,
 7566        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7567    );
 7568    let sample_text_2 = sample_text(rows, cols, 'l');
 7569    assert_eq!(
 7570        sample_text_2,
 7571        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7572    );
 7573    let sample_text_3 = sample_text(rows, cols, 'v');
 7574    assert_eq!(
 7575        sample_text_3,
 7576        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7577    );
 7578
 7579    let fs = FakeFs::new(cx.executor());
 7580    fs.insert_tree(
 7581        path!("/a"),
 7582        json!({
 7583            "main.rs": sample_text_1,
 7584            "other.rs": sample_text_2,
 7585            "lib.rs": sample_text_3,
 7586        }),
 7587    )
 7588    .await;
 7589
 7590    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7591    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7592    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7593
 7594    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7595    language_registry.add(rust_lang());
 7596    let mut fake_servers = language_registry.register_fake_lsp(
 7597        "Rust",
 7598        FakeLspAdapter {
 7599            capabilities: lsp::ServerCapabilities {
 7600                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7601                ..Default::default()
 7602            },
 7603            ..Default::default()
 7604        },
 7605    );
 7606
 7607    let worktree = project.update(cx, |project, cx| {
 7608        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7609        assert_eq!(worktrees.len(), 1);
 7610        worktrees.pop().unwrap()
 7611    });
 7612    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7613
 7614    let buffer_1 = project
 7615        .update(cx, |project, cx| {
 7616            project.open_buffer((worktree_id, "main.rs"), cx)
 7617        })
 7618        .await
 7619        .unwrap();
 7620    let buffer_2 = project
 7621        .update(cx, |project, cx| {
 7622            project.open_buffer((worktree_id, "other.rs"), cx)
 7623        })
 7624        .await
 7625        .unwrap();
 7626    let buffer_3 = project
 7627        .update(cx, |project, cx| {
 7628            project.open_buffer((worktree_id, "lib.rs"), cx)
 7629        })
 7630        .await
 7631        .unwrap();
 7632
 7633    let multi_buffer = cx.new(|cx| {
 7634        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7635        multi_buffer.push_excerpts(
 7636            buffer_1.clone(),
 7637            [
 7638                ExcerptRange {
 7639                    context: Point::new(0, 0)..Point::new(3, 0),
 7640                    primary: None,
 7641                },
 7642                ExcerptRange {
 7643                    context: Point::new(5, 0)..Point::new(7, 0),
 7644                    primary: None,
 7645                },
 7646                ExcerptRange {
 7647                    context: Point::new(9, 0)..Point::new(10, 4),
 7648                    primary: None,
 7649                },
 7650            ],
 7651            cx,
 7652        );
 7653        multi_buffer.push_excerpts(
 7654            buffer_2.clone(),
 7655            [
 7656                ExcerptRange {
 7657                    context: Point::new(0, 0)..Point::new(3, 0),
 7658                    primary: None,
 7659                },
 7660                ExcerptRange {
 7661                    context: Point::new(5, 0)..Point::new(7, 0),
 7662                    primary: None,
 7663                },
 7664                ExcerptRange {
 7665                    context: Point::new(9, 0)..Point::new(10, 4),
 7666                    primary: None,
 7667                },
 7668            ],
 7669            cx,
 7670        );
 7671        multi_buffer.push_excerpts(
 7672            buffer_3.clone(),
 7673            [
 7674                ExcerptRange {
 7675                    context: Point::new(0, 0)..Point::new(3, 0),
 7676                    primary: None,
 7677                },
 7678                ExcerptRange {
 7679                    context: Point::new(5, 0)..Point::new(7, 0),
 7680                    primary: None,
 7681                },
 7682                ExcerptRange {
 7683                    context: Point::new(9, 0)..Point::new(10, 4),
 7684                    primary: None,
 7685                },
 7686            ],
 7687            cx,
 7688        );
 7689        multi_buffer
 7690    });
 7691    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7692        Editor::new(
 7693            EditorMode::Full,
 7694            multi_buffer,
 7695            Some(project.clone()),
 7696            window,
 7697            cx,
 7698        )
 7699    });
 7700
 7701    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7702        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7703            s.select_ranges(Some(1..2))
 7704        });
 7705        editor.insert("|one|two|three|", window, cx);
 7706    });
 7707    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7708    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7709        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7710            s.select_ranges(Some(60..70))
 7711        });
 7712        editor.insert("|four|five|six|", window, cx);
 7713    });
 7714    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7715
 7716    // First two buffers should be edited, but not the third one.
 7717    assert_eq!(
 7718        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7719        "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}",
 7720    );
 7721    buffer_1.update(cx, |buffer, _| {
 7722        assert!(buffer.is_dirty());
 7723        assert_eq!(
 7724            buffer.text(),
 7725            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7726        )
 7727    });
 7728    buffer_2.update(cx, |buffer, _| {
 7729        assert!(buffer.is_dirty());
 7730        assert_eq!(
 7731            buffer.text(),
 7732            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7733        )
 7734    });
 7735    buffer_3.update(cx, |buffer, _| {
 7736        assert!(!buffer.is_dirty());
 7737        assert_eq!(buffer.text(), sample_text_3,)
 7738    });
 7739    cx.executor().run_until_parked();
 7740
 7741    cx.executor().start_waiting();
 7742    let save = multi_buffer_editor
 7743        .update_in(cx, |editor, window, cx| {
 7744            editor.save(true, project.clone(), window, cx)
 7745        })
 7746        .unwrap();
 7747
 7748    let fake_server = fake_servers.next().await.unwrap();
 7749    fake_server
 7750        .server
 7751        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7752            Ok(Some(vec![lsp::TextEdit::new(
 7753                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7754                format!("[{} formatted]", params.text_document.uri),
 7755            )]))
 7756        })
 7757        .detach();
 7758    save.await;
 7759
 7760    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7761    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7762    assert_eq!(
 7763        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7764        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}"),
 7765    );
 7766    buffer_1.update(cx, |buffer, _| {
 7767        assert!(!buffer.is_dirty());
 7768        assert_eq!(
 7769            buffer.text(),
 7770            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7771        )
 7772    });
 7773    buffer_2.update(cx, |buffer, _| {
 7774        assert!(!buffer.is_dirty());
 7775        assert_eq!(
 7776            buffer.text(),
 7777            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7778        )
 7779    });
 7780    buffer_3.update(cx, |buffer, _| {
 7781        assert!(!buffer.is_dirty());
 7782        assert_eq!(buffer.text(), sample_text_3,)
 7783    });
 7784}
 7785
 7786#[gpui::test]
 7787async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7788    init_test(cx, |_| {});
 7789
 7790    let fs = FakeFs::new(cx.executor());
 7791    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7792
 7793    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7794
 7795    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7796    language_registry.add(rust_lang());
 7797    let mut fake_servers = language_registry.register_fake_lsp(
 7798        "Rust",
 7799        FakeLspAdapter {
 7800            capabilities: lsp::ServerCapabilities {
 7801                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7802                ..Default::default()
 7803            },
 7804            ..Default::default()
 7805        },
 7806    );
 7807
 7808    let buffer = project
 7809        .update(cx, |project, cx| {
 7810            project.open_local_buffer(path!("/file.rs"), cx)
 7811        })
 7812        .await
 7813        .unwrap();
 7814
 7815    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7816    let (editor, cx) = cx.add_window_view(|window, cx| {
 7817        build_editor_with_project(project.clone(), buffer, window, cx)
 7818    });
 7819    editor.update_in(cx, |editor, window, cx| {
 7820        editor.set_text("one\ntwo\nthree\n", window, cx)
 7821    });
 7822    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7823
 7824    cx.executor().start_waiting();
 7825    let fake_server = fake_servers.next().await.unwrap();
 7826
 7827    let save = editor
 7828        .update_in(cx, |editor, window, cx| {
 7829            editor.save(true, project.clone(), window, cx)
 7830        })
 7831        .unwrap();
 7832    fake_server
 7833        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7834            assert_eq!(
 7835                params.text_document.uri,
 7836                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7837            );
 7838            assert_eq!(params.options.tab_size, 4);
 7839            Ok(Some(vec![lsp::TextEdit::new(
 7840                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7841                ", ".to_string(),
 7842            )]))
 7843        })
 7844        .next()
 7845        .await;
 7846    cx.executor().start_waiting();
 7847    save.await;
 7848    assert_eq!(
 7849        editor.update(cx, |editor, cx| editor.text(cx)),
 7850        "one, two\nthree\n"
 7851    );
 7852    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7853
 7854    editor.update_in(cx, |editor, window, cx| {
 7855        editor.set_text("one\ntwo\nthree\n", window, cx)
 7856    });
 7857    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7858
 7859    // Ensure we can still save even if formatting hangs.
 7860    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7861        move |params, _| async move {
 7862            assert_eq!(
 7863                params.text_document.uri,
 7864                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7865            );
 7866            futures::future::pending::<()>().await;
 7867            unreachable!()
 7868        },
 7869    );
 7870    let save = editor
 7871        .update_in(cx, |editor, window, cx| {
 7872            editor.save(true, project.clone(), window, cx)
 7873        })
 7874        .unwrap();
 7875    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7876    cx.executor().start_waiting();
 7877    save.await;
 7878    assert_eq!(
 7879        editor.update(cx, |editor, cx| editor.text(cx)),
 7880        "one\ntwo\nthree\n"
 7881    );
 7882    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7883
 7884    // For non-dirty buffer, no formatting request should be sent
 7885    let save = editor
 7886        .update_in(cx, |editor, window, cx| {
 7887            editor.save(true, project.clone(), window, cx)
 7888        })
 7889        .unwrap();
 7890    let _pending_format_request = fake_server
 7891        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7892            panic!("Should not be invoked on non-dirty buffer");
 7893        })
 7894        .next();
 7895    cx.executor().start_waiting();
 7896    save.await;
 7897
 7898    // Set Rust language override and assert overridden tabsize is sent to language server
 7899    update_test_language_settings(cx, |settings| {
 7900        settings.languages.insert(
 7901            "Rust".into(),
 7902            LanguageSettingsContent {
 7903                tab_size: NonZeroU32::new(8),
 7904                ..Default::default()
 7905            },
 7906        );
 7907    });
 7908
 7909    editor.update_in(cx, |editor, window, cx| {
 7910        editor.set_text("somehting_new\n", window, cx)
 7911    });
 7912    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7913    let save = editor
 7914        .update_in(cx, |editor, window, cx| {
 7915            editor.save(true, project.clone(), window, cx)
 7916        })
 7917        .unwrap();
 7918    fake_server
 7919        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7920            assert_eq!(
 7921                params.text_document.uri,
 7922                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7923            );
 7924            assert_eq!(params.options.tab_size, 8);
 7925            Ok(Some(vec![]))
 7926        })
 7927        .next()
 7928        .await;
 7929    cx.executor().start_waiting();
 7930    save.await;
 7931}
 7932
 7933#[gpui::test]
 7934async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7935    init_test(cx, |settings| {
 7936        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7937            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7938        ))
 7939    });
 7940
 7941    let fs = FakeFs::new(cx.executor());
 7942    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7943
 7944    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7945
 7946    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7947    language_registry.add(Arc::new(Language::new(
 7948        LanguageConfig {
 7949            name: "Rust".into(),
 7950            matcher: LanguageMatcher {
 7951                path_suffixes: vec!["rs".to_string()],
 7952                ..Default::default()
 7953            },
 7954            ..LanguageConfig::default()
 7955        },
 7956        Some(tree_sitter_rust::LANGUAGE.into()),
 7957    )));
 7958    update_test_language_settings(cx, |settings| {
 7959        // Enable Prettier formatting for the same buffer, and ensure
 7960        // LSP is called instead of Prettier.
 7961        settings.defaults.prettier = Some(PrettierSettings {
 7962            allowed: true,
 7963            ..PrettierSettings::default()
 7964        });
 7965    });
 7966    let mut fake_servers = language_registry.register_fake_lsp(
 7967        "Rust",
 7968        FakeLspAdapter {
 7969            capabilities: lsp::ServerCapabilities {
 7970                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7971                ..Default::default()
 7972            },
 7973            ..Default::default()
 7974        },
 7975    );
 7976
 7977    let buffer = project
 7978        .update(cx, |project, cx| {
 7979            project.open_local_buffer(path!("/file.rs"), cx)
 7980        })
 7981        .await
 7982        .unwrap();
 7983
 7984    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7985    let (editor, cx) = cx.add_window_view(|window, cx| {
 7986        build_editor_with_project(project.clone(), buffer, window, cx)
 7987    });
 7988    editor.update_in(cx, |editor, window, cx| {
 7989        editor.set_text("one\ntwo\nthree\n", window, cx)
 7990    });
 7991
 7992    cx.executor().start_waiting();
 7993    let fake_server = fake_servers.next().await.unwrap();
 7994
 7995    let format = editor
 7996        .update_in(cx, |editor, window, cx| {
 7997            editor.perform_format(
 7998                project.clone(),
 7999                FormatTrigger::Manual,
 8000                FormatTarget::Buffers,
 8001                window,
 8002                cx,
 8003            )
 8004        })
 8005        .unwrap();
 8006    fake_server
 8007        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8008            assert_eq!(
 8009                params.text_document.uri,
 8010                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8011            );
 8012            assert_eq!(params.options.tab_size, 4);
 8013            Ok(Some(vec![lsp::TextEdit::new(
 8014                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8015                ", ".to_string(),
 8016            )]))
 8017        })
 8018        .next()
 8019        .await;
 8020    cx.executor().start_waiting();
 8021    format.await;
 8022    assert_eq!(
 8023        editor.update(cx, |editor, cx| editor.text(cx)),
 8024        "one, two\nthree\n"
 8025    );
 8026
 8027    editor.update_in(cx, |editor, window, cx| {
 8028        editor.set_text("one\ntwo\nthree\n", window, cx)
 8029    });
 8030    // Ensure we don't lock if formatting hangs.
 8031    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8032        assert_eq!(
 8033            params.text_document.uri,
 8034            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8035        );
 8036        futures::future::pending::<()>().await;
 8037        unreachable!()
 8038    });
 8039    let format = editor
 8040        .update_in(cx, |editor, window, cx| {
 8041            editor.perform_format(
 8042                project,
 8043                FormatTrigger::Manual,
 8044                FormatTarget::Buffers,
 8045                window,
 8046                cx,
 8047            )
 8048        })
 8049        .unwrap();
 8050    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8051    cx.executor().start_waiting();
 8052    format.await;
 8053    assert_eq!(
 8054        editor.update(cx, |editor, cx| editor.text(cx)),
 8055        "one\ntwo\nthree\n"
 8056    );
 8057}
 8058
 8059#[gpui::test]
 8060async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8061    init_test(cx, |settings| {
 8062        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8063            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8064        ))
 8065    });
 8066
 8067    let fs = FakeFs::new(cx.executor());
 8068    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8069
 8070    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8071
 8072    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8073    language_registry.add(Arc::new(Language::new(
 8074        LanguageConfig {
 8075            name: "TypeScript".into(),
 8076            matcher: LanguageMatcher {
 8077                path_suffixes: vec!["ts".to_string()],
 8078                ..Default::default()
 8079            },
 8080            ..LanguageConfig::default()
 8081        },
 8082        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8083    )));
 8084    update_test_language_settings(cx, |settings| {
 8085        settings.defaults.prettier = Some(PrettierSettings {
 8086            allowed: true,
 8087            ..PrettierSettings::default()
 8088        });
 8089    });
 8090    let mut fake_servers = language_registry.register_fake_lsp(
 8091        "TypeScript",
 8092        FakeLspAdapter {
 8093            capabilities: lsp::ServerCapabilities {
 8094                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8095                ..Default::default()
 8096            },
 8097            ..Default::default()
 8098        },
 8099    );
 8100
 8101    let buffer = project
 8102        .update(cx, |project, cx| {
 8103            project.open_local_buffer(path!("/file.ts"), cx)
 8104        })
 8105        .await
 8106        .unwrap();
 8107
 8108    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8109    let (editor, cx) = cx.add_window_view(|window, cx| {
 8110        build_editor_with_project(project.clone(), buffer, window, cx)
 8111    });
 8112    editor.update_in(cx, |editor, window, cx| {
 8113        editor.set_text(
 8114            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8115            window,
 8116            cx,
 8117        )
 8118    });
 8119
 8120    cx.executor().start_waiting();
 8121    let fake_server = fake_servers.next().await.unwrap();
 8122
 8123    let format = editor
 8124        .update_in(cx, |editor, window, cx| {
 8125            editor.perform_code_action_kind(
 8126                project.clone(),
 8127                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8128                window,
 8129                cx,
 8130            )
 8131        })
 8132        .unwrap();
 8133    fake_server
 8134        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8135            assert_eq!(
 8136                params.text_document.uri,
 8137                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8138            );
 8139            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8140                lsp::CodeAction {
 8141                    title: "Organize Imports".to_string(),
 8142                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8143                    edit: Some(lsp::WorkspaceEdit {
 8144                        changes: Some(
 8145                            [(
 8146                                params.text_document.uri.clone(),
 8147                                vec![lsp::TextEdit::new(
 8148                                    lsp::Range::new(
 8149                                        lsp::Position::new(1, 0),
 8150                                        lsp::Position::new(2, 0),
 8151                                    ),
 8152                                    "".to_string(),
 8153                                )],
 8154                            )]
 8155                            .into_iter()
 8156                            .collect(),
 8157                        ),
 8158                        ..Default::default()
 8159                    }),
 8160                    ..Default::default()
 8161                },
 8162            )]))
 8163        })
 8164        .next()
 8165        .await;
 8166    cx.executor().start_waiting();
 8167    format.await;
 8168    assert_eq!(
 8169        editor.update(cx, |editor, cx| editor.text(cx)),
 8170        "import { a } from 'module';\n\nconst x = a;\n"
 8171    );
 8172
 8173    editor.update_in(cx, |editor, window, cx| {
 8174        editor.set_text(
 8175            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8176            window,
 8177            cx,
 8178        )
 8179    });
 8180    // Ensure we don't lock if code action hangs.
 8181    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8182        move |params, _| async move {
 8183            assert_eq!(
 8184                params.text_document.uri,
 8185                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8186            );
 8187            futures::future::pending::<()>().await;
 8188            unreachable!()
 8189        },
 8190    );
 8191    let format = editor
 8192        .update_in(cx, |editor, window, cx| {
 8193            editor.perform_code_action_kind(
 8194                project,
 8195                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8196                window,
 8197                cx,
 8198            )
 8199        })
 8200        .unwrap();
 8201    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8202    cx.executor().start_waiting();
 8203    format.await;
 8204    assert_eq!(
 8205        editor.update(cx, |editor, cx| editor.text(cx)),
 8206        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8207    );
 8208}
 8209
 8210#[gpui::test]
 8211async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8212    init_test(cx, |_| {});
 8213
 8214    let mut cx = EditorLspTestContext::new_rust(
 8215        lsp::ServerCapabilities {
 8216            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8217            ..Default::default()
 8218        },
 8219        cx,
 8220    )
 8221    .await;
 8222
 8223    cx.set_state(indoc! {"
 8224        one.twoˇ
 8225    "});
 8226
 8227    // The format request takes a long time. When it completes, it inserts
 8228    // a newline and an indent before the `.`
 8229    cx.lsp
 8230        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8231            let executor = cx.background_executor().clone();
 8232            async move {
 8233                executor.timer(Duration::from_millis(100)).await;
 8234                Ok(Some(vec![lsp::TextEdit {
 8235                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8236                    new_text: "\n    ".into(),
 8237                }]))
 8238            }
 8239        });
 8240
 8241    // Submit a format request.
 8242    let format_1 = cx
 8243        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8244        .unwrap();
 8245    cx.executor().run_until_parked();
 8246
 8247    // Submit a second format request.
 8248    let format_2 = cx
 8249        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8250        .unwrap();
 8251    cx.executor().run_until_parked();
 8252
 8253    // Wait for both format requests to complete
 8254    cx.executor().advance_clock(Duration::from_millis(200));
 8255    cx.executor().start_waiting();
 8256    format_1.await.unwrap();
 8257    cx.executor().start_waiting();
 8258    format_2.await.unwrap();
 8259
 8260    // The formatting edits only happens once.
 8261    cx.assert_editor_state(indoc! {"
 8262        one
 8263            .twoˇ
 8264    "});
 8265}
 8266
 8267#[gpui::test]
 8268async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8269    init_test(cx, |settings| {
 8270        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8271    });
 8272
 8273    let mut cx = EditorLspTestContext::new_rust(
 8274        lsp::ServerCapabilities {
 8275            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8276            ..Default::default()
 8277        },
 8278        cx,
 8279    )
 8280    .await;
 8281
 8282    // Set up a buffer white some trailing whitespace and no trailing newline.
 8283    cx.set_state(
 8284        &[
 8285            "one ",   //
 8286            "twoˇ",   //
 8287            "three ", //
 8288            "four",   //
 8289        ]
 8290        .join("\n"),
 8291    );
 8292
 8293    // Submit a format request.
 8294    let format = cx
 8295        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8296        .unwrap();
 8297
 8298    // Record which buffer changes have been sent to the language server
 8299    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8300    cx.lsp
 8301        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8302            let buffer_changes = buffer_changes.clone();
 8303            move |params, _| {
 8304                buffer_changes.lock().extend(
 8305                    params
 8306                        .content_changes
 8307                        .into_iter()
 8308                        .map(|e| (e.range.unwrap(), e.text)),
 8309                );
 8310            }
 8311        });
 8312
 8313    // Handle formatting requests to the language server.
 8314    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8315        let buffer_changes = buffer_changes.clone();
 8316        move |_, _| {
 8317            // When formatting is requested, trailing whitespace has already been stripped,
 8318            // and the trailing newline has already been added.
 8319            assert_eq!(
 8320                &buffer_changes.lock()[1..],
 8321                &[
 8322                    (
 8323                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8324                        "".into()
 8325                    ),
 8326                    (
 8327                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8328                        "".into()
 8329                    ),
 8330                    (
 8331                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8332                        "\n".into()
 8333                    ),
 8334                ]
 8335            );
 8336
 8337            // Insert blank lines between each line of the buffer.
 8338            async move {
 8339                Ok(Some(vec![
 8340                    lsp::TextEdit {
 8341                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8342                        new_text: "\n".into(),
 8343                    },
 8344                    lsp::TextEdit {
 8345                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8346                        new_text: "\n".into(),
 8347                    },
 8348                ]))
 8349            }
 8350        }
 8351    });
 8352
 8353    // After formatting the buffer, the trailing whitespace is stripped,
 8354    // a newline is appended, and the edits provided by the language server
 8355    // have been applied.
 8356    format.await.unwrap();
 8357    cx.assert_editor_state(
 8358        &[
 8359            "one",   //
 8360            "",      //
 8361            "twoˇ",  //
 8362            "",      //
 8363            "three", //
 8364            "four",  //
 8365            "",      //
 8366        ]
 8367        .join("\n"),
 8368    );
 8369
 8370    // Undoing the formatting undoes the trailing whitespace removal, the
 8371    // trailing newline, and the LSP edits.
 8372    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8373    cx.assert_editor_state(
 8374        &[
 8375            "one ",   //
 8376            "twoˇ",   //
 8377            "three ", //
 8378            "four",   //
 8379        ]
 8380        .join("\n"),
 8381    );
 8382}
 8383
 8384#[gpui::test]
 8385async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8386    cx: &mut TestAppContext,
 8387) {
 8388    init_test(cx, |_| {});
 8389
 8390    cx.update(|cx| {
 8391        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8392            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8393                settings.auto_signature_help = Some(true);
 8394            });
 8395        });
 8396    });
 8397
 8398    let mut cx = EditorLspTestContext::new_rust(
 8399        lsp::ServerCapabilities {
 8400            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8401                ..Default::default()
 8402            }),
 8403            ..Default::default()
 8404        },
 8405        cx,
 8406    )
 8407    .await;
 8408
 8409    let language = Language::new(
 8410        LanguageConfig {
 8411            name: "Rust".into(),
 8412            brackets: BracketPairConfig {
 8413                pairs: vec![
 8414                    BracketPair {
 8415                        start: "{".to_string(),
 8416                        end: "}".to_string(),
 8417                        close: true,
 8418                        surround: true,
 8419                        newline: true,
 8420                    },
 8421                    BracketPair {
 8422                        start: "(".to_string(),
 8423                        end: ")".to_string(),
 8424                        close: true,
 8425                        surround: true,
 8426                        newline: true,
 8427                    },
 8428                    BracketPair {
 8429                        start: "/*".to_string(),
 8430                        end: " */".to_string(),
 8431                        close: true,
 8432                        surround: true,
 8433                        newline: true,
 8434                    },
 8435                    BracketPair {
 8436                        start: "[".to_string(),
 8437                        end: "]".to_string(),
 8438                        close: false,
 8439                        surround: false,
 8440                        newline: true,
 8441                    },
 8442                    BracketPair {
 8443                        start: "\"".to_string(),
 8444                        end: "\"".to_string(),
 8445                        close: true,
 8446                        surround: true,
 8447                        newline: false,
 8448                    },
 8449                    BracketPair {
 8450                        start: "<".to_string(),
 8451                        end: ">".to_string(),
 8452                        close: false,
 8453                        surround: true,
 8454                        newline: true,
 8455                    },
 8456                ],
 8457                ..Default::default()
 8458            },
 8459            autoclose_before: "})]".to_string(),
 8460            ..Default::default()
 8461        },
 8462        Some(tree_sitter_rust::LANGUAGE.into()),
 8463    );
 8464    let language = Arc::new(language);
 8465
 8466    cx.language_registry().add(language.clone());
 8467    cx.update_buffer(|buffer, cx| {
 8468        buffer.set_language(Some(language), cx);
 8469    });
 8470
 8471    cx.set_state(
 8472        &r#"
 8473            fn main() {
 8474                sampleˇ
 8475            }
 8476        "#
 8477        .unindent(),
 8478    );
 8479
 8480    cx.update_editor(|editor, window, cx| {
 8481        editor.handle_input("(", window, cx);
 8482    });
 8483    cx.assert_editor_state(
 8484        &"
 8485            fn main() {
 8486                sample(ˇ)
 8487            }
 8488        "
 8489        .unindent(),
 8490    );
 8491
 8492    let mocked_response = lsp::SignatureHelp {
 8493        signatures: vec![lsp::SignatureInformation {
 8494            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8495            documentation: None,
 8496            parameters: Some(vec![
 8497                lsp::ParameterInformation {
 8498                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8499                    documentation: None,
 8500                },
 8501                lsp::ParameterInformation {
 8502                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8503                    documentation: None,
 8504                },
 8505            ]),
 8506            active_parameter: None,
 8507        }],
 8508        active_signature: Some(0),
 8509        active_parameter: Some(0),
 8510    };
 8511    handle_signature_help_request(&mut cx, mocked_response).await;
 8512
 8513    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8514        .await;
 8515
 8516    cx.editor(|editor, _, _| {
 8517        let signature_help_state = editor.signature_help_state.popover().cloned();
 8518        assert_eq!(
 8519            signature_help_state.unwrap().label,
 8520            "param1: u8, param2: u8"
 8521        );
 8522    });
 8523}
 8524
 8525#[gpui::test]
 8526async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8527    init_test(cx, |_| {});
 8528
 8529    cx.update(|cx| {
 8530        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8531            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8532                settings.auto_signature_help = Some(false);
 8533                settings.show_signature_help_after_edits = Some(false);
 8534            });
 8535        });
 8536    });
 8537
 8538    let mut cx = EditorLspTestContext::new_rust(
 8539        lsp::ServerCapabilities {
 8540            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8541                ..Default::default()
 8542            }),
 8543            ..Default::default()
 8544        },
 8545        cx,
 8546    )
 8547    .await;
 8548
 8549    let language = Language::new(
 8550        LanguageConfig {
 8551            name: "Rust".into(),
 8552            brackets: BracketPairConfig {
 8553                pairs: vec![
 8554                    BracketPair {
 8555                        start: "{".to_string(),
 8556                        end: "}".to_string(),
 8557                        close: true,
 8558                        surround: true,
 8559                        newline: true,
 8560                    },
 8561                    BracketPair {
 8562                        start: "(".to_string(),
 8563                        end: ")".to_string(),
 8564                        close: true,
 8565                        surround: true,
 8566                        newline: true,
 8567                    },
 8568                    BracketPair {
 8569                        start: "/*".to_string(),
 8570                        end: " */".to_string(),
 8571                        close: true,
 8572                        surround: true,
 8573                        newline: true,
 8574                    },
 8575                    BracketPair {
 8576                        start: "[".to_string(),
 8577                        end: "]".to_string(),
 8578                        close: false,
 8579                        surround: false,
 8580                        newline: true,
 8581                    },
 8582                    BracketPair {
 8583                        start: "\"".to_string(),
 8584                        end: "\"".to_string(),
 8585                        close: true,
 8586                        surround: true,
 8587                        newline: false,
 8588                    },
 8589                    BracketPair {
 8590                        start: "<".to_string(),
 8591                        end: ">".to_string(),
 8592                        close: false,
 8593                        surround: true,
 8594                        newline: true,
 8595                    },
 8596                ],
 8597                ..Default::default()
 8598            },
 8599            autoclose_before: "})]".to_string(),
 8600            ..Default::default()
 8601        },
 8602        Some(tree_sitter_rust::LANGUAGE.into()),
 8603    );
 8604    let language = Arc::new(language);
 8605
 8606    cx.language_registry().add(language.clone());
 8607    cx.update_buffer(|buffer, cx| {
 8608        buffer.set_language(Some(language), cx);
 8609    });
 8610
 8611    // Ensure that signature_help is not called when no signature help is enabled.
 8612    cx.set_state(
 8613        &r#"
 8614            fn main() {
 8615                sampleˇ
 8616            }
 8617        "#
 8618        .unindent(),
 8619    );
 8620    cx.update_editor(|editor, window, cx| {
 8621        editor.handle_input("(", window, cx);
 8622    });
 8623    cx.assert_editor_state(
 8624        &"
 8625            fn main() {
 8626                sample(ˇ)
 8627            }
 8628        "
 8629        .unindent(),
 8630    );
 8631    cx.editor(|editor, _, _| {
 8632        assert!(editor.signature_help_state.task().is_none());
 8633    });
 8634
 8635    let mocked_response = lsp::SignatureHelp {
 8636        signatures: vec![lsp::SignatureInformation {
 8637            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8638            documentation: None,
 8639            parameters: Some(vec![
 8640                lsp::ParameterInformation {
 8641                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8642                    documentation: None,
 8643                },
 8644                lsp::ParameterInformation {
 8645                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8646                    documentation: None,
 8647                },
 8648            ]),
 8649            active_parameter: None,
 8650        }],
 8651        active_signature: Some(0),
 8652        active_parameter: Some(0),
 8653    };
 8654
 8655    // Ensure that signature_help is called when enabled afte edits
 8656    cx.update(|_, cx| {
 8657        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8658            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8659                settings.auto_signature_help = Some(false);
 8660                settings.show_signature_help_after_edits = Some(true);
 8661            });
 8662        });
 8663    });
 8664    cx.set_state(
 8665        &r#"
 8666            fn main() {
 8667                sampleˇ
 8668            }
 8669        "#
 8670        .unindent(),
 8671    );
 8672    cx.update_editor(|editor, window, cx| {
 8673        editor.handle_input("(", window, cx);
 8674    });
 8675    cx.assert_editor_state(
 8676        &"
 8677            fn main() {
 8678                sample(ˇ)
 8679            }
 8680        "
 8681        .unindent(),
 8682    );
 8683    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8684    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8685        .await;
 8686    cx.update_editor(|editor, _, _| {
 8687        let signature_help_state = editor.signature_help_state.popover().cloned();
 8688        assert!(signature_help_state.is_some());
 8689        assert_eq!(
 8690            signature_help_state.unwrap().label,
 8691            "param1: u8, param2: u8"
 8692        );
 8693        editor.signature_help_state = SignatureHelpState::default();
 8694    });
 8695
 8696    // Ensure that signature_help is called when auto signature help override is enabled
 8697    cx.update(|_, cx| {
 8698        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8699            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8700                settings.auto_signature_help = Some(true);
 8701                settings.show_signature_help_after_edits = Some(false);
 8702            });
 8703        });
 8704    });
 8705    cx.set_state(
 8706        &r#"
 8707            fn main() {
 8708                sampleˇ
 8709            }
 8710        "#
 8711        .unindent(),
 8712    );
 8713    cx.update_editor(|editor, window, cx| {
 8714        editor.handle_input("(", window, cx);
 8715    });
 8716    cx.assert_editor_state(
 8717        &"
 8718            fn main() {
 8719                sample(ˇ)
 8720            }
 8721        "
 8722        .unindent(),
 8723    );
 8724    handle_signature_help_request(&mut cx, mocked_response).await;
 8725    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8726        .await;
 8727    cx.editor(|editor, _, _| {
 8728        let signature_help_state = editor.signature_help_state.popover().cloned();
 8729        assert!(signature_help_state.is_some());
 8730        assert_eq!(
 8731            signature_help_state.unwrap().label,
 8732            "param1: u8, param2: u8"
 8733        );
 8734    });
 8735}
 8736
 8737#[gpui::test]
 8738async fn test_signature_help(cx: &mut TestAppContext) {
 8739    init_test(cx, |_| {});
 8740    cx.update(|cx| {
 8741        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8742            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8743                settings.auto_signature_help = Some(true);
 8744            });
 8745        });
 8746    });
 8747
 8748    let mut cx = EditorLspTestContext::new_rust(
 8749        lsp::ServerCapabilities {
 8750            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8751                ..Default::default()
 8752            }),
 8753            ..Default::default()
 8754        },
 8755        cx,
 8756    )
 8757    .await;
 8758
 8759    // A test that directly calls `show_signature_help`
 8760    cx.update_editor(|editor, window, cx| {
 8761        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8762    });
 8763
 8764    let mocked_response = lsp::SignatureHelp {
 8765        signatures: vec![lsp::SignatureInformation {
 8766            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8767            documentation: None,
 8768            parameters: Some(vec![
 8769                lsp::ParameterInformation {
 8770                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8771                    documentation: None,
 8772                },
 8773                lsp::ParameterInformation {
 8774                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8775                    documentation: None,
 8776                },
 8777            ]),
 8778            active_parameter: None,
 8779        }],
 8780        active_signature: Some(0),
 8781        active_parameter: Some(0),
 8782    };
 8783    handle_signature_help_request(&mut cx, mocked_response).await;
 8784
 8785    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8786        .await;
 8787
 8788    cx.editor(|editor, _, _| {
 8789        let signature_help_state = editor.signature_help_state.popover().cloned();
 8790        assert!(signature_help_state.is_some());
 8791        assert_eq!(
 8792            signature_help_state.unwrap().label,
 8793            "param1: u8, param2: u8"
 8794        );
 8795    });
 8796
 8797    // When exiting outside from inside the brackets, `signature_help` is closed.
 8798    cx.set_state(indoc! {"
 8799        fn main() {
 8800            sample(ˇ);
 8801        }
 8802
 8803        fn sample(param1: u8, param2: u8) {}
 8804    "});
 8805
 8806    cx.update_editor(|editor, window, cx| {
 8807        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8808    });
 8809
 8810    let mocked_response = lsp::SignatureHelp {
 8811        signatures: Vec::new(),
 8812        active_signature: None,
 8813        active_parameter: None,
 8814    };
 8815    handle_signature_help_request(&mut cx, mocked_response).await;
 8816
 8817    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8818        .await;
 8819
 8820    cx.editor(|editor, _, _| {
 8821        assert!(!editor.signature_help_state.is_shown());
 8822    });
 8823
 8824    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8825    cx.set_state(indoc! {"
 8826        fn main() {
 8827            sample(ˇ);
 8828        }
 8829
 8830        fn sample(param1: u8, param2: u8) {}
 8831    "});
 8832
 8833    let mocked_response = lsp::SignatureHelp {
 8834        signatures: vec![lsp::SignatureInformation {
 8835            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8836            documentation: None,
 8837            parameters: Some(vec![
 8838                lsp::ParameterInformation {
 8839                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8840                    documentation: None,
 8841                },
 8842                lsp::ParameterInformation {
 8843                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8844                    documentation: None,
 8845                },
 8846            ]),
 8847            active_parameter: None,
 8848        }],
 8849        active_signature: Some(0),
 8850        active_parameter: Some(0),
 8851    };
 8852    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8853    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8854        .await;
 8855    cx.editor(|editor, _, _| {
 8856        assert!(editor.signature_help_state.is_shown());
 8857    });
 8858
 8859    // Restore the popover with more parameter input
 8860    cx.set_state(indoc! {"
 8861        fn main() {
 8862            sample(param1, param2ˇ);
 8863        }
 8864
 8865        fn sample(param1: u8, param2: u8) {}
 8866    "});
 8867
 8868    let mocked_response = lsp::SignatureHelp {
 8869        signatures: vec![lsp::SignatureInformation {
 8870            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8871            documentation: None,
 8872            parameters: Some(vec![
 8873                lsp::ParameterInformation {
 8874                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8875                    documentation: None,
 8876                },
 8877                lsp::ParameterInformation {
 8878                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8879                    documentation: None,
 8880                },
 8881            ]),
 8882            active_parameter: None,
 8883        }],
 8884        active_signature: Some(0),
 8885        active_parameter: Some(1),
 8886    };
 8887    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8888    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8889        .await;
 8890
 8891    // When selecting a range, the popover is gone.
 8892    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8893    cx.update_editor(|editor, window, cx| {
 8894        editor.change_selections(None, window, cx, |s| {
 8895            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8896        })
 8897    });
 8898    cx.assert_editor_state(indoc! {"
 8899        fn main() {
 8900            sample(param1, «ˇparam2»);
 8901        }
 8902
 8903        fn sample(param1: u8, param2: u8) {}
 8904    "});
 8905    cx.editor(|editor, _, _| {
 8906        assert!(!editor.signature_help_state.is_shown());
 8907    });
 8908
 8909    // When unselecting again, the popover is back if within the brackets.
 8910    cx.update_editor(|editor, window, cx| {
 8911        editor.change_selections(None, window, cx, |s| {
 8912            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8913        })
 8914    });
 8915    cx.assert_editor_state(indoc! {"
 8916        fn main() {
 8917            sample(param1, ˇparam2);
 8918        }
 8919
 8920        fn sample(param1: u8, param2: u8) {}
 8921    "});
 8922    handle_signature_help_request(&mut cx, mocked_response).await;
 8923    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8924        .await;
 8925    cx.editor(|editor, _, _| {
 8926        assert!(editor.signature_help_state.is_shown());
 8927    });
 8928
 8929    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8930    cx.update_editor(|editor, window, cx| {
 8931        editor.change_selections(None, window, cx, |s| {
 8932            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8933            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8934        })
 8935    });
 8936    cx.assert_editor_state(indoc! {"
 8937        fn main() {
 8938            sample(param1, ˇparam2);
 8939        }
 8940
 8941        fn sample(param1: u8, param2: u8) {}
 8942    "});
 8943
 8944    let mocked_response = lsp::SignatureHelp {
 8945        signatures: vec![lsp::SignatureInformation {
 8946            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8947            documentation: None,
 8948            parameters: Some(vec![
 8949                lsp::ParameterInformation {
 8950                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8951                    documentation: None,
 8952                },
 8953                lsp::ParameterInformation {
 8954                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8955                    documentation: None,
 8956                },
 8957            ]),
 8958            active_parameter: None,
 8959        }],
 8960        active_signature: Some(0),
 8961        active_parameter: Some(1),
 8962    };
 8963    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8964    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8965        .await;
 8966    cx.update_editor(|editor, _, cx| {
 8967        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8968    });
 8969    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8970        .await;
 8971    cx.update_editor(|editor, window, cx| {
 8972        editor.change_selections(None, window, cx, |s| {
 8973            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8974        })
 8975    });
 8976    cx.assert_editor_state(indoc! {"
 8977        fn main() {
 8978            sample(param1, «ˇparam2»);
 8979        }
 8980
 8981        fn sample(param1: u8, param2: u8) {}
 8982    "});
 8983    cx.update_editor(|editor, window, cx| {
 8984        editor.change_selections(None, window, cx, |s| {
 8985            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8986        })
 8987    });
 8988    cx.assert_editor_state(indoc! {"
 8989        fn main() {
 8990            sample(param1, ˇparam2);
 8991        }
 8992
 8993        fn sample(param1: u8, param2: u8) {}
 8994    "});
 8995    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8996        .await;
 8997}
 8998
 8999#[gpui::test]
 9000async fn test_completion(cx: &mut TestAppContext) {
 9001    init_test(cx, |_| {});
 9002
 9003    let mut cx = EditorLspTestContext::new_rust(
 9004        lsp::ServerCapabilities {
 9005            completion_provider: Some(lsp::CompletionOptions {
 9006                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9007                resolve_provider: Some(true),
 9008                ..Default::default()
 9009            }),
 9010            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9011            ..Default::default()
 9012        },
 9013        cx,
 9014    )
 9015    .await;
 9016    let counter = Arc::new(AtomicUsize::new(0));
 9017
 9018    cx.set_state(indoc! {"
 9019        oneˇ
 9020        two
 9021        three
 9022    "});
 9023    cx.simulate_keystroke(".");
 9024    handle_completion_request(
 9025        &mut cx,
 9026        indoc! {"
 9027            one.|<>
 9028            two
 9029            three
 9030        "},
 9031        vec!["first_completion", "second_completion"],
 9032        counter.clone(),
 9033    )
 9034    .await;
 9035    cx.condition(|editor, _| editor.context_menu_visible())
 9036        .await;
 9037    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9038
 9039    let _handler = handle_signature_help_request(
 9040        &mut cx,
 9041        lsp::SignatureHelp {
 9042            signatures: vec![lsp::SignatureInformation {
 9043                label: "test signature".to_string(),
 9044                documentation: None,
 9045                parameters: Some(vec![lsp::ParameterInformation {
 9046                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9047                    documentation: None,
 9048                }]),
 9049                active_parameter: None,
 9050            }],
 9051            active_signature: None,
 9052            active_parameter: None,
 9053        },
 9054    );
 9055    cx.update_editor(|editor, window, cx| {
 9056        assert!(
 9057            !editor.signature_help_state.is_shown(),
 9058            "No signature help was called for"
 9059        );
 9060        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9061    });
 9062    cx.run_until_parked();
 9063    cx.update_editor(|editor, _, _| {
 9064        assert!(
 9065            !editor.signature_help_state.is_shown(),
 9066            "No signature help should be shown when completions menu is open"
 9067        );
 9068    });
 9069
 9070    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9071        editor.context_menu_next(&Default::default(), window, cx);
 9072        editor
 9073            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9074            .unwrap()
 9075    });
 9076    cx.assert_editor_state(indoc! {"
 9077        one.second_completionˇ
 9078        two
 9079        three
 9080    "});
 9081
 9082    handle_resolve_completion_request(
 9083        &mut cx,
 9084        Some(vec![
 9085            (
 9086                //This overlaps with the primary completion edit which is
 9087                //misbehavior from the LSP spec, test that we filter it out
 9088                indoc! {"
 9089                    one.second_ˇcompletion
 9090                    two
 9091                    threeˇ
 9092                "},
 9093                "overlapping additional edit",
 9094            ),
 9095            (
 9096                indoc! {"
 9097                    one.second_completion
 9098                    two
 9099                    threeˇ
 9100                "},
 9101                "\nadditional edit",
 9102            ),
 9103        ]),
 9104    )
 9105    .await;
 9106    apply_additional_edits.await.unwrap();
 9107    cx.assert_editor_state(indoc! {"
 9108        one.second_completionˇ
 9109        two
 9110        three
 9111        additional edit
 9112    "});
 9113
 9114    cx.set_state(indoc! {"
 9115        one.second_completion
 9116        twoˇ
 9117        threeˇ
 9118        additional edit
 9119    "});
 9120    cx.simulate_keystroke(" ");
 9121    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9122    cx.simulate_keystroke("s");
 9123    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9124
 9125    cx.assert_editor_state(indoc! {"
 9126        one.second_completion
 9127        two sˇ
 9128        three sˇ
 9129        additional edit
 9130    "});
 9131    handle_completion_request(
 9132        &mut cx,
 9133        indoc! {"
 9134            one.second_completion
 9135            two s
 9136            three <s|>
 9137            additional edit
 9138        "},
 9139        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9140        counter.clone(),
 9141    )
 9142    .await;
 9143    cx.condition(|editor, _| editor.context_menu_visible())
 9144        .await;
 9145    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9146
 9147    cx.simulate_keystroke("i");
 9148
 9149    handle_completion_request(
 9150        &mut cx,
 9151        indoc! {"
 9152            one.second_completion
 9153            two si
 9154            three <si|>
 9155            additional edit
 9156        "},
 9157        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9158        counter.clone(),
 9159    )
 9160    .await;
 9161    cx.condition(|editor, _| editor.context_menu_visible())
 9162        .await;
 9163    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9164
 9165    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9166        editor
 9167            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9168            .unwrap()
 9169    });
 9170    cx.assert_editor_state(indoc! {"
 9171        one.second_completion
 9172        two sixth_completionˇ
 9173        three sixth_completionˇ
 9174        additional edit
 9175    "});
 9176
 9177    apply_additional_edits.await.unwrap();
 9178
 9179    update_test_language_settings(&mut cx, |settings| {
 9180        settings.defaults.show_completions_on_input = Some(false);
 9181    });
 9182    cx.set_state("editorˇ");
 9183    cx.simulate_keystroke(".");
 9184    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9185    cx.simulate_keystroke("c");
 9186    cx.simulate_keystroke("l");
 9187    cx.simulate_keystroke("o");
 9188    cx.assert_editor_state("editor.cloˇ");
 9189    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9190    cx.update_editor(|editor, window, cx| {
 9191        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9192    });
 9193    handle_completion_request(
 9194        &mut cx,
 9195        "editor.<clo|>",
 9196        vec!["close", "clobber"],
 9197        counter.clone(),
 9198    )
 9199    .await;
 9200    cx.condition(|editor, _| editor.context_menu_visible())
 9201        .await;
 9202    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9203
 9204    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9205        editor
 9206            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9207            .unwrap()
 9208    });
 9209    cx.assert_editor_state("editor.closeˇ");
 9210    handle_resolve_completion_request(&mut cx, None).await;
 9211    apply_additional_edits.await.unwrap();
 9212}
 9213
 9214#[gpui::test]
 9215async fn test_words_completion(cx: &mut TestAppContext) {
 9216    let lsp_fetch_timeout_ms = 10;
 9217    init_test(cx, |language_settings| {
 9218        language_settings.defaults.completions = Some(CompletionSettings {
 9219            words: WordsCompletionMode::Fallback,
 9220            lsp: true,
 9221            lsp_fetch_timeout_ms: 10,
 9222        });
 9223    });
 9224
 9225    let mut cx = EditorLspTestContext::new_rust(
 9226        lsp::ServerCapabilities {
 9227            completion_provider: Some(lsp::CompletionOptions {
 9228                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9229                ..lsp::CompletionOptions::default()
 9230            }),
 9231            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9232            ..lsp::ServerCapabilities::default()
 9233        },
 9234        cx,
 9235    )
 9236    .await;
 9237
 9238    let throttle_completions = Arc::new(AtomicBool::new(false));
 9239
 9240    let lsp_throttle_completions = throttle_completions.clone();
 9241    let _completion_requests_handler =
 9242        cx.lsp
 9243            .server
 9244            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9245                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9246                async move {
 9247                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9248                        cx.background_executor()
 9249                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9250                            .await;
 9251                    }
 9252                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9253                        lsp::CompletionItem {
 9254                            label: "first".into(),
 9255                            ..lsp::CompletionItem::default()
 9256                        },
 9257                        lsp::CompletionItem {
 9258                            label: "last".into(),
 9259                            ..lsp::CompletionItem::default()
 9260                        },
 9261                    ])))
 9262                }
 9263            });
 9264
 9265    cx.set_state(indoc! {"
 9266        oneˇ
 9267        two
 9268        three
 9269    "});
 9270    cx.simulate_keystroke(".");
 9271    cx.executor().run_until_parked();
 9272    cx.condition(|editor, _| editor.context_menu_visible())
 9273        .await;
 9274    cx.update_editor(|editor, window, cx| {
 9275        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9276        {
 9277            assert_eq!(
 9278                completion_menu_entries(&menu),
 9279                &["first", "last"],
 9280                "When LSP server is fast to reply, no fallback word completions are used"
 9281            );
 9282        } else {
 9283            panic!("expected completion menu to be open");
 9284        }
 9285        editor.cancel(&Cancel, window, cx);
 9286    });
 9287    cx.executor().run_until_parked();
 9288    cx.condition(|editor, _| !editor.context_menu_visible())
 9289        .await;
 9290
 9291    throttle_completions.store(true, atomic::Ordering::Release);
 9292    cx.simulate_keystroke(".");
 9293    cx.executor()
 9294        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9295    cx.executor().run_until_parked();
 9296    cx.condition(|editor, _| editor.context_menu_visible())
 9297        .await;
 9298    cx.update_editor(|editor, _, _| {
 9299        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9300        {
 9301            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9302                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9303        } else {
 9304            panic!("expected completion menu to be open");
 9305        }
 9306    });
 9307}
 9308
 9309#[gpui::test]
 9310async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9311    init_test(cx, |language_settings| {
 9312        language_settings.defaults.completions = Some(CompletionSettings {
 9313            words: WordsCompletionMode::Enabled,
 9314            lsp: true,
 9315            lsp_fetch_timeout_ms: 0,
 9316        });
 9317    });
 9318
 9319    let mut cx = EditorLspTestContext::new_rust(
 9320        lsp::ServerCapabilities {
 9321            completion_provider: Some(lsp::CompletionOptions {
 9322                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9323                ..lsp::CompletionOptions::default()
 9324            }),
 9325            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9326            ..lsp::ServerCapabilities::default()
 9327        },
 9328        cx,
 9329    )
 9330    .await;
 9331
 9332    let _completion_requests_handler =
 9333        cx.lsp
 9334            .server
 9335            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9336                Ok(Some(lsp::CompletionResponse::Array(vec![
 9337                    lsp::CompletionItem {
 9338                        label: "first".into(),
 9339                        ..lsp::CompletionItem::default()
 9340                    },
 9341                    lsp::CompletionItem {
 9342                        label: "last".into(),
 9343                        ..lsp::CompletionItem::default()
 9344                    },
 9345                ])))
 9346            });
 9347
 9348    cx.set_state(indoc! {"ˇ
 9349        first
 9350        last
 9351        second
 9352    "});
 9353    cx.simulate_keystroke(".");
 9354    cx.executor().run_until_parked();
 9355    cx.condition(|editor, _| editor.context_menu_visible())
 9356        .await;
 9357    cx.update_editor(|editor, window, cx| {
 9358        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9359        {
 9360            assert_eq!(
 9361                completion_menu_entries(&menu),
 9362                &["first", "last", "second"],
 9363                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9364            );
 9365        } else {
 9366            panic!("expected completion menu to be open");
 9367        }
 9368        editor.cancel(&Cancel, window, cx);
 9369    });
 9370}
 9371
 9372#[gpui::test]
 9373async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9374    init_test(cx, |language_settings| {
 9375        language_settings.defaults.completions = Some(CompletionSettings {
 9376            words: WordsCompletionMode::Fallback,
 9377            lsp: false,
 9378            lsp_fetch_timeout_ms: 0,
 9379        });
 9380    });
 9381
 9382    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9383
 9384    cx.set_state(indoc! {"ˇ
 9385        0_usize
 9386        let
 9387        33
 9388        4.5f32
 9389    "});
 9390    cx.update_editor(|editor, window, cx| {
 9391        editor.show_completions(&ShowCompletions::default(), window, cx);
 9392    });
 9393    cx.executor().run_until_parked();
 9394    cx.condition(|editor, _| editor.context_menu_visible())
 9395        .await;
 9396    cx.update_editor(|editor, window, cx| {
 9397        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9398        {
 9399            assert_eq!(
 9400                completion_menu_entries(&menu),
 9401                &["let"],
 9402                "With no digits in the completion query, no digits should be in the word completions"
 9403            );
 9404        } else {
 9405            panic!("expected completion menu to be open");
 9406        }
 9407        editor.cancel(&Cancel, window, cx);
 9408    });
 9409
 9410    cx.set_state(indoc! {" 9411        0_usize
 9412        let
 9413        3
 9414        33.35f32
 9415    "});
 9416    cx.update_editor(|editor, window, cx| {
 9417        editor.show_completions(&ShowCompletions::default(), window, cx);
 9418    });
 9419    cx.executor().run_until_parked();
 9420    cx.condition(|editor, _| editor.context_menu_visible())
 9421        .await;
 9422    cx.update_editor(|editor, _, _| {
 9423        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9424        {
 9425            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9426                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9427        } else {
 9428            panic!("expected completion menu to be open");
 9429        }
 9430    });
 9431}
 9432
 9433#[gpui::test]
 9434async fn test_multiline_completion(cx: &mut TestAppContext) {
 9435    init_test(cx, |_| {});
 9436
 9437    let fs = FakeFs::new(cx.executor());
 9438    fs.insert_tree(
 9439        path!("/a"),
 9440        json!({
 9441            "main.ts": "a",
 9442        }),
 9443    )
 9444    .await;
 9445
 9446    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9447    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9448    let typescript_language = Arc::new(Language::new(
 9449        LanguageConfig {
 9450            name: "TypeScript".into(),
 9451            matcher: LanguageMatcher {
 9452                path_suffixes: vec!["ts".to_string()],
 9453                ..LanguageMatcher::default()
 9454            },
 9455            line_comments: vec!["// ".into()],
 9456            ..LanguageConfig::default()
 9457        },
 9458        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9459    ));
 9460    language_registry.add(typescript_language.clone());
 9461    let mut fake_servers = language_registry.register_fake_lsp(
 9462        "TypeScript",
 9463        FakeLspAdapter {
 9464            capabilities: lsp::ServerCapabilities {
 9465                completion_provider: Some(lsp::CompletionOptions {
 9466                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9467                    ..lsp::CompletionOptions::default()
 9468                }),
 9469                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9470                ..lsp::ServerCapabilities::default()
 9471            },
 9472            // Emulate vtsls label generation
 9473            label_for_completion: Some(Box::new(|item, _| {
 9474                let text = if let Some(description) = item
 9475                    .label_details
 9476                    .as_ref()
 9477                    .and_then(|label_details| label_details.description.as_ref())
 9478                {
 9479                    format!("{} {}", item.label, description)
 9480                } else if let Some(detail) = &item.detail {
 9481                    format!("{} {}", item.label, detail)
 9482                } else {
 9483                    item.label.clone()
 9484                };
 9485                let len = text.len();
 9486                Some(language::CodeLabel {
 9487                    text,
 9488                    runs: Vec::new(),
 9489                    filter_range: 0..len,
 9490                })
 9491            })),
 9492            ..FakeLspAdapter::default()
 9493        },
 9494    );
 9495    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9496    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9497    let worktree_id = workspace
 9498        .update(cx, |workspace, _window, cx| {
 9499            workspace.project().update(cx, |project, cx| {
 9500                project.worktrees(cx).next().unwrap().read(cx).id()
 9501            })
 9502        })
 9503        .unwrap();
 9504    let _buffer = project
 9505        .update(cx, |project, cx| {
 9506            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9507        })
 9508        .await
 9509        .unwrap();
 9510    let editor = workspace
 9511        .update(cx, |workspace, window, cx| {
 9512            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9513        })
 9514        .unwrap()
 9515        .await
 9516        .unwrap()
 9517        .downcast::<Editor>()
 9518        .unwrap();
 9519    let fake_server = fake_servers.next().await.unwrap();
 9520
 9521    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9522    let multiline_label_2 = "a\nb\nc\n";
 9523    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9524    let multiline_description = "d\ne\nf\n";
 9525    let multiline_detail_2 = "g\nh\ni\n";
 9526
 9527    let mut completion_handle =
 9528        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9529            Ok(Some(lsp::CompletionResponse::Array(vec![
 9530                lsp::CompletionItem {
 9531                    label: multiline_label.to_string(),
 9532                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9533                        range: lsp::Range {
 9534                            start: lsp::Position {
 9535                                line: params.text_document_position.position.line,
 9536                                character: params.text_document_position.position.character,
 9537                            },
 9538                            end: lsp::Position {
 9539                                line: params.text_document_position.position.line,
 9540                                character: params.text_document_position.position.character,
 9541                            },
 9542                        },
 9543                        new_text: "new_text_1".to_string(),
 9544                    })),
 9545                    ..lsp::CompletionItem::default()
 9546                },
 9547                lsp::CompletionItem {
 9548                    label: "single line label 1".to_string(),
 9549                    detail: Some(multiline_detail.to_string()),
 9550                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9551                        range: lsp::Range {
 9552                            start: lsp::Position {
 9553                                line: params.text_document_position.position.line,
 9554                                character: params.text_document_position.position.character,
 9555                            },
 9556                            end: lsp::Position {
 9557                                line: params.text_document_position.position.line,
 9558                                character: params.text_document_position.position.character,
 9559                            },
 9560                        },
 9561                        new_text: "new_text_2".to_string(),
 9562                    })),
 9563                    ..lsp::CompletionItem::default()
 9564                },
 9565                lsp::CompletionItem {
 9566                    label: "single line label 2".to_string(),
 9567                    label_details: Some(lsp::CompletionItemLabelDetails {
 9568                        description: Some(multiline_description.to_string()),
 9569                        detail: None,
 9570                    }),
 9571                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9572                        range: lsp::Range {
 9573                            start: lsp::Position {
 9574                                line: params.text_document_position.position.line,
 9575                                character: params.text_document_position.position.character,
 9576                            },
 9577                            end: lsp::Position {
 9578                                line: params.text_document_position.position.line,
 9579                                character: params.text_document_position.position.character,
 9580                            },
 9581                        },
 9582                        new_text: "new_text_2".to_string(),
 9583                    })),
 9584                    ..lsp::CompletionItem::default()
 9585                },
 9586                lsp::CompletionItem {
 9587                    label: multiline_label_2.to_string(),
 9588                    detail: Some(multiline_detail_2.to_string()),
 9589                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9590                        range: lsp::Range {
 9591                            start: lsp::Position {
 9592                                line: params.text_document_position.position.line,
 9593                                character: params.text_document_position.position.character,
 9594                            },
 9595                            end: lsp::Position {
 9596                                line: params.text_document_position.position.line,
 9597                                character: params.text_document_position.position.character,
 9598                            },
 9599                        },
 9600                        new_text: "new_text_3".to_string(),
 9601                    })),
 9602                    ..lsp::CompletionItem::default()
 9603                },
 9604                lsp::CompletionItem {
 9605                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9606                    detail: Some(
 9607                        "Details with many     spaces and \t but without newlines".to_string(),
 9608                    ),
 9609                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9610                        range: lsp::Range {
 9611                            start: lsp::Position {
 9612                                line: params.text_document_position.position.line,
 9613                                character: params.text_document_position.position.character,
 9614                            },
 9615                            end: lsp::Position {
 9616                                line: params.text_document_position.position.line,
 9617                                character: params.text_document_position.position.character,
 9618                            },
 9619                        },
 9620                        new_text: "new_text_4".to_string(),
 9621                    })),
 9622                    ..lsp::CompletionItem::default()
 9623                },
 9624            ])))
 9625        });
 9626
 9627    editor.update_in(cx, |editor, window, cx| {
 9628        cx.focus_self(window);
 9629        editor.move_to_end(&MoveToEnd, window, cx);
 9630        editor.handle_input(".", window, cx);
 9631    });
 9632    cx.run_until_parked();
 9633    completion_handle.next().await.unwrap();
 9634
 9635    editor.update(cx, |editor, _| {
 9636        assert!(editor.context_menu_visible());
 9637        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9638        {
 9639            let completion_labels = menu
 9640                .completions
 9641                .borrow()
 9642                .iter()
 9643                .map(|c| c.label.text.clone())
 9644                .collect::<Vec<_>>();
 9645            assert_eq!(
 9646                completion_labels,
 9647                &[
 9648                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9649                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9650                    "single line label 2 d e f ",
 9651                    "a b c g h i ",
 9652                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9653                ],
 9654                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9655            );
 9656
 9657            for completion in menu
 9658                .completions
 9659                .borrow()
 9660                .iter() {
 9661                    assert_eq!(
 9662                        completion.label.filter_range,
 9663                        0..completion.label.text.len(),
 9664                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9665                    );
 9666                }
 9667
 9668        } else {
 9669            panic!("expected completion menu to be open");
 9670        }
 9671    });
 9672}
 9673
 9674#[gpui::test]
 9675async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9676    init_test(cx, |_| {});
 9677    let mut cx = EditorLspTestContext::new_rust(
 9678        lsp::ServerCapabilities {
 9679            completion_provider: Some(lsp::CompletionOptions {
 9680                trigger_characters: Some(vec![".".to_string()]),
 9681                ..Default::default()
 9682            }),
 9683            ..Default::default()
 9684        },
 9685        cx,
 9686    )
 9687    .await;
 9688    cx.lsp
 9689        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9690            Ok(Some(lsp::CompletionResponse::Array(vec![
 9691                lsp::CompletionItem {
 9692                    label: "first".into(),
 9693                    ..Default::default()
 9694                },
 9695                lsp::CompletionItem {
 9696                    label: "last".into(),
 9697                    ..Default::default()
 9698                },
 9699            ])))
 9700        });
 9701    cx.set_state("variableˇ");
 9702    cx.simulate_keystroke(".");
 9703    cx.executor().run_until_parked();
 9704
 9705    cx.update_editor(|editor, _, _| {
 9706        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9707        {
 9708            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9709        } else {
 9710            panic!("expected completion menu to be open");
 9711        }
 9712    });
 9713
 9714    cx.update_editor(|editor, window, cx| {
 9715        editor.move_page_down(&MovePageDown::default(), window, cx);
 9716        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9717        {
 9718            assert!(
 9719                menu.selected_item == 1,
 9720                "expected PageDown to select the last item from the context menu"
 9721            );
 9722        } else {
 9723            panic!("expected completion menu to stay open after PageDown");
 9724        }
 9725    });
 9726
 9727    cx.update_editor(|editor, window, cx| {
 9728        editor.move_page_up(&MovePageUp::default(), window, cx);
 9729        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9730        {
 9731            assert!(
 9732                menu.selected_item == 0,
 9733                "expected PageUp to select the first item from the context menu"
 9734            );
 9735        } else {
 9736            panic!("expected completion menu to stay open after PageUp");
 9737        }
 9738    });
 9739}
 9740
 9741#[gpui::test]
 9742async fn test_completion_sort(cx: &mut TestAppContext) {
 9743    init_test(cx, |_| {});
 9744    let mut cx = EditorLspTestContext::new_rust(
 9745        lsp::ServerCapabilities {
 9746            completion_provider: Some(lsp::CompletionOptions {
 9747                trigger_characters: Some(vec![".".to_string()]),
 9748                ..Default::default()
 9749            }),
 9750            ..Default::default()
 9751        },
 9752        cx,
 9753    )
 9754    .await;
 9755    cx.lsp
 9756        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9757            Ok(Some(lsp::CompletionResponse::Array(vec![
 9758                lsp::CompletionItem {
 9759                    label: "Range".into(),
 9760                    sort_text: Some("a".into()),
 9761                    ..Default::default()
 9762                },
 9763                lsp::CompletionItem {
 9764                    label: "r".into(),
 9765                    sort_text: Some("b".into()),
 9766                    ..Default::default()
 9767                },
 9768                lsp::CompletionItem {
 9769                    label: "ret".into(),
 9770                    sort_text: Some("c".into()),
 9771                    ..Default::default()
 9772                },
 9773                lsp::CompletionItem {
 9774                    label: "return".into(),
 9775                    sort_text: Some("d".into()),
 9776                    ..Default::default()
 9777                },
 9778                lsp::CompletionItem {
 9779                    label: "slice".into(),
 9780                    sort_text: Some("d".into()),
 9781                    ..Default::default()
 9782                },
 9783            ])))
 9784        });
 9785    cx.set_state("");
 9786    cx.executor().run_until_parked();
 9787    cx.update_editor(|editor, window, cx| {
 9788        editor.show_completions(
 9789            &ShowCompletions {
 9790                trigger: Some("r".into()),
 9791            },
 9792            window,
 9793            cx,
 9794        );
 9795    });
 9796    cx.executor().run_until_parked();
 9797
 9798    cx.update_editor(|editor, _, _| {
 9799        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9800        {
 9801            assert_eq!(
 9802                completion_menu_entries(&menu),
 9803                &["r", "ret", "Range", "return"]
 9804            );
 9805        } else {
 9806            panic!("expected completion menu to be open");
 9807        }
 9808    });
 9809}
 9810
 9811#[gpui::test]
 9812async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9813    init_test(cx, |_| {});
 9814
 9815    let mut cx = EditorLspTestContext::new_rust(
 9816        lsp::ServerCapabilities {
 9817            completion_provider: Some(lsp::CompletionOptions {
 9818                trigger_characters: Some(vec![".".to_string()]),
 9819                resolve_provider: Some(true),
 9820                ..Default::default()
 9821            }),
 9822            ..Default::default()
 9823        },
 9824        cx,
 9825    )
 9826    .await;
 9827
 9828    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9829    cx.simulate_keystroke(".");
 9830    let completion_item = lsp::CompletionItem {
 9831        label: "Some".into(),
 9832        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9833        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9834        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9835            kind: lsp::MarkupKind::Markdown,
 9836            value: "```rust\nSome(2)\n```".to_string(),
 9837        })),
 9838        deprecated: Some(false),
 9839        sort_text: Some("Some".to_string()),
 9840        filter_text: Some("Some".to_string()),
 9841        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9842        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9843            range: lsp::Range {
 9844                start: lsp::Position {
 9845                    line: 0,
 9846                    character: 22,
 9847                },
 9848                end: lsp::Position {
 9849                    line: 0,
 9850                    character: 22,
 9851                },
 9852            },
 9853            new_text: "Some(2)".to_string(),
 9854        })),
 9855        additional_text_edits: Some(vec![lsp::TextEdit {
 9856            range: lsp::Range {
 9857                start: lsp::Position {
 9858                    line: 0,
 9859                    character: 20,
 9860                },
 9861                end: lsp::Position {
 9862                    line: 0,
 9863                    character: 22,
 9864                },
 9865            },
 9866            new_text: "".to_string(),
 9867        }]),
 9868        ..Default::default()
 9869    };
 9870
 9871    let closure_completion_item = completion_item.clone();
 9872    let counter = Arc::new(AtomicUsize::new(0));
 9873    let counter_clone = counter.clone();
 9874    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9875        let task_completion_item = closure_completion_item.clone();
 9876        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9877        async move {
 9878            Ok(Some(lsp::CompletionResponse::Array(vec![
 9879                task_completion_item,
 9880            ])))
 9881        }
 9882    });
 9883
 9884    cx.condition(|editor, _| editor.context_menu_visible())
 9885        .await;
 9886    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9887    assert!(request.next().await.is_some());
 9888    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9889
 9890    cx.simulate_keystroke("S");
 9891    cx.simulate_keystroke("o");
 9892    cx.simulate_keystroke("m");
 9893    cx.condition(|editor, _| editor.context_menu_visible())
 9894        .await;
 9895    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9896    assert!(request.next().await.is_some());
 9897    assert!(request.next().await.is_some());
 9898    assert!(request.next().await.is_some());
 9899    request.close();
 9900    assert!(request.next().await.is_none());
 9901    assert_eq!(
 9902        counter.load(atomic::Ordering::Acquire),
 9903        4,
 9904        "With the completions menu open, only one LSP request should happen per input"
 9905    );
 9906}
 9907
 9908#[gpui::test]
 9909async fn test_toggle_comment(cx: &mut TestAppContext) {
 9910    init_test(cx, |_| {});
 9911    let mut cx = EditorTestContext::new(cx).await;
 9912    let language = Arc::new(Language::new(
 9913        LanguageConfig {
 9914            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9915            ..Default::default()
 9916        },
 9917        Some(tree_sitter_rust::LANGUAGE.into()),
 9918    ));
 9919    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9920
 9921    // If multiple selections intersect a line, the line is only toggled once.
 9922    cx.set_state(indoc! {"
 9923        fn a() {
 9924            «//b();
 9925            ˇ»// «c();
 9926            //ˇ»  d();
 9927        }
 9928    "});
 9929
 9930    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9931
 9932    cx.assert_editor_state(indoc! {"
 9933        fn a() {
 9934            «b();
 9935            c();
 9936            ˇ» d();
 9937        }
 9938    "});
 9939
 9940    // The comment prefix is inserted at the same column for every line in a
 9941    // selection.
 9942    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9943
 9944    cx.assert_editor_state(indoc! {"
 9945        fn a() {
 9946            // «b();
 9947            // c();
 9948            ˇ»//  d();
 9949        }
 9950    "});
 9951
 9952    // If a selection ends at the beginning of a line, that line is not toggled.
 9953    cx.set_selections_state(indoc! {"
 9954        fn a() {
 9955            // b();
 9956            «// c();
 9957        ˇ»    //  d();
 9958        }
 9959    "});
 9960
 9961    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9962
 9963    cx.assert_editor_state(indoc! {"
 9964        fn a() {
 9965            // b();
 9966            «c();
 9967        ˇ»    //  d();
 9968        }
 9969    "});
 9970
 9971    // If a selection span a single line and is empty, the line is toggled.
 9972    cx.set_state(indoc! {"
 9973        fn a() {
 9974            a();
 9975            b();
 9976        ˇ
 9977        }
 9978    "});
 9979
 9980    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9981
 9982    cx.assert_editor_state(indoc! {"
 9983        fn a() {
 9984            a();
 9985            b();
 9986        //•ˇ
 9987        }
 9988    "});
 9989
 9990    // If a selection span multiple lines, empty lines are not toggled.
 9991    cx.set_state(indoc! {"
 9992        fn a() {
 9993            «a();
 9994
 9995            c();ˇ»
 9996        }
 9997    "});
 9998
 9999    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10000
10001    cx.assert_editor_state(indoc! {"
10002        fn a() {
10003            // «a();
10004
10005            // c();ˇ»
10006        }
10007    "});
10008
10009    // If a selection includes multiple comment prefixes, all lines are uncommented.
10010    cx.set_state(indoc! {"
10011        fn a() {
10012            «// a();
10013            /// b();
10014            //! c();ˇ»
10015        }
10016    "});
10017
10018    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10019
10020    cx.assert_editor_state(indoc! {"
10021        fn a() {
10022            «a();
10023            b();
10024            c();ˇ»
10025        }
10026    "});
10027}
10028
10029#[gpui::test]
10030async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10031    init_test(cx, |_| {});
10032    let mut cx = EditorTestContext::new(cx).await;
10033    let language = Arc::new(Language::new(
10034        LanguageConfig {
10035            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10036            ..Default::default()
10037        },
10038        Some(tree_sitter_rust::LANGUAGE.into()),
10039    ));
10040    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10041
10042    let toggle_comments = &ToggleComments {
10043        advance_downwards: false,
10044        ignore_indent: true,
10045    };
10046
10047    // If multiple selections intersect a line, the line is only toggled once.
10048    cx.set_state(indoc! {"
10049        fn a() {
10050        //    «b();
10051        //    c();
10052        //    ˇ» d();
10053        }
10054    "});
10055
10056    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10057
10058    cx.assert_editor_state(indoc! {"
10059        fn a() {
10060            «b();
10061            c();
10062            ˇ» d();
10063        }
10064    "});
10065
10066    // The comment prefix is inserted at the beginning of each line
10067    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10068
10069    cx.assert_editor_state(indoc! {"
10070        fn a() {
10071        //    «b();
10072        //    c();
10073        //    ˇ» d();
10074        }
10075    "});
10076
10077    // If a selection ends at the beginning of a line, that line is not toggled.
10078    cx.set_selections_state(indoc! {"
10079        fn a() {
10080        //    b();
10081        //    «c();
10082        ˇ»//     d();
10083        }
10084    "});
10085
10086    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10087
10088    cx.assert_editor_state(indoc! {"
10089        fn a() {
10090        //    b();
10091            «c();
10092        ˇ»//     d();
10093        }
10094    "});
10095
10096    // If a selection span a single line and is empty, the line is toggled.
10097    cx.set_state(indoc! {"
10098        fn a() {
10099            a();
10100            b();
10101        ˇ
10102        }
10103    "});
10104
10105    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10106
10107    cx.assert_editor_state(indoc! {"
10108        fn a() {
10109            a();
10110            b();
10111        //ˇ
10112        }
10113    "});
10114
10115    // If a selection span multiple lines, empty lines are not toggled.
10116    cx.set_state(indoc! {"
10117        fn a() {
10118            «a();
10119
10120            c();ˇ»
10121        }
10122    "});
10123
10124    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10125
10126    cx.assert_editor_state(indoc! {"
10127        fn a() {
10128        //    «a();
10129
10130        //    c();ˇ»
10131        }
10132    "});
10133
10134    // If a selection includes multiple comment prefixes, all lines are uncommented.
10135    cx.set_state(indoc! {"
10136        fn a() {
10137        //    «a();
10138        ///    b();
10139        //!    c();ˇ»
10140        }
10141    "});
10142
10143    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10144
10145    cx.assert_editor_state(indoc! {"
10146        fn a() {
10147            «a();
10148            b();
10149            c();ˇ»
10150        }
10151    "});
10152}
10153
10154#[gpui::test]
10155async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10156    init_test(cx, |_| {});
10157
10158    let language = Arc::new(Language::new(
10159        LanguageConfig {
10160            line_comments: vec!["// ".into()],
10161            ..Default::default()
10162        },
10163        Some(tree_sitter_rust::LANGUAGE.into()),
10164    ));
10165
10166    let mut cx = EditorTestContext::new(cx).await;
10167
10168    cx.language_registry().add(language.clone());
10169    cx.update_buffer(|buffer, cx| {
10170        buffer.set_language(Some(language), cx);
10171    });
10172
10173    let toggle_comments = &ToggleComments {
10174        advance_downwards: true,
10175        ignore_indent: false,
10176    };
10177
10178    // Single cursor on one line -> advance
10179    // Cursor moves horizontally 3 characters as well on non-blank line
10180    cx.set_state(indoc!(
10181        "fn a() {
10182             ˇdog();
10183             cat();
10184        }"
10185    ));
10186    cx.update_editor(|editor, window, cx| {
10187        editor.toggle_comments(toggle_comments, window, cx);
10188    });
10189    cx.assert_editor_state(indoc!(
10190        "fn a() {
10191             // dog();
10192             catˇ();
10193        }"
10194    ));
10195
10196    // Single selection on one line -> don't advance
10197    cx.set_state(indoc!(
10198        "fn a() {
10199             «dog()ˇ»;
10200             cat();
10201        }"
10202    ));
10203    cx.update_editor(|editor, window, cx| {
10204        editor.toggle_comments(toggle_comments, window, cx);
10205    });
10206    cx.assert_editor_state(indoc!(
10207        "fn a() {
10208             // «dog()ˇ»;
10209             cat();
10210        }"
10211    ));
10212
10213    // Multiple cursors on one line -> advance
10214    cx.set_state(indoc!(
10215        "fn a() {
10216             ˇdˇog();
10217             cat();
10218        }"
10219    ));
10220    cx.update_editor(|editor, window, cx| {
10221        editor.toggle_comments(toggle_comments, window, cx);
10222    });
10223    cx.assert_editor_state(indoc!(
10224        "fn a() {
10225             // dog();
10226             catˇ(ˇ);
10227        }"
10228    ));
10229
10230    // Multiple cursors on one line, with selection -> don't advance
10231    cx.set_state(indoc!(
10232        "fn a() {
10233             ˇdˇog«()ˇ»;
10234             cat();
10235        }"
10236    ));
10237    cx.update_editor(|editor, window, cx| {
10238        editor.toggle_comments(toggle_comments, window, cx);
10239    });
10240    cx.assert_editor_state(indoc!(
10241        "fn a() {
10242             // ˇdˇog«()ˇ»;
10243             cat();
10244        }"
10245    ));
10246
10247    // Single cursor on one line -> advance
10248    // Cursor moves to column 0 on blank line
10249    cx.set_state(indoc!(
10250        "fn a() {
10251             ˇdog();
10252
10253             cat();
10254        }"
10255    ));
10256    cx.update_editor(|editor, window, cx| {
10257        editor.toggle_comments(toggle_comments, window, cx);
10258    });
10259    cx.assert_editor_state(indoc!(
10260        "fn a() {
10261             // dog();
10262        ˇ
10263             cat();
10264        }"
10265    ));
10266
10267    // Single cursor on one line -> advance
10268    // Cursor starts and ends at column 0
10269    cx.set_state(indoc!(
10270        "fn a() {
10271         ˇ    dog();
10272             cat();
10273        }"
10274    ));
10275    cx.update_editor(|editor, window, cx| {
10276        editor.toggle_comments(toggle_comments, window, cx);
10277    });
10278    cx.assert_editor_state(indoc!(
10279        "fn a() {
10280             // dog();
10281         ˇ    cat();
10282        }"
10283    ));
10284}
10285
10286#[gpui::test]
10287async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10288    init_test(cx, |_| {});
10289
10290    let mut cx = EditorTestContext::new(cx).await;
10291
10292    let html_language = Arc::new(
10293        Language::new(
10294            LanguageConfig {
10295                name: "HTML".into(),
10296                block_comment: Some(("<!-- ".into(), " -->".into())),
10297                ..Default::default()
10298            },
10299            Some(tree_sitter_html::LANGUAGE.into()),
10300        )
10301        .with_injection_query(
10302            r#"
10303            (script_element
10304                (raw_text) @injection.content
10305                (#set! injection.language "javascript"))
10306            "#,
10307        )
10308        .unwrap(),
10309    );
10310
10311    let javascript_language = Arc::new(Language::new(
10312        LanguageConfig {
10313            name: "JavaScript".into(),
10314            line_comments: vec!["// ".into()],
10315            ..Default::default()
10316        },
10317        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10318    ));
10319
10320    cx.language_registry().add(html_language.clone());
10321    cx.language_registry().add(javascript_language.clone());
10322    cx.update_buffer(|buffer, cx| {
10323        buffer.set_language(Some(html_language), cx);
10324    });
10325
10326    // Toggle comments for empty selections
10327    cx.set_state(
10328        &r#"
10329            <p>A</p>ˇ
10330            <p>B</p>ˇ
10331            <p>C</p>ˇ
10332        "#
10333        .unindent(),
10334    );
10335    cx.update_editor(|editor, window, cx| {
10336        editor.toggle_comments(&ToggleComments::default(), window, cx)
10337    });
10338    cx.assert_editor_state(
10339        &r#"
10340            <!-- <p>A</p>ˇ -->
10341            <!-- <p>B</p>ˇ -->
10342            <!-- <p>C</p>ˇ -->
10343        "#
10344        .unindent(),
10345    );
10346    cx.update_editor(|editor, window, cx| {
10347        editor.toggle_comments(&ToggleComments::default(), window, cx)
10348    });
10349    cx.assert_editor_state(
10350        &r#"
10351            <p>A</p>ˇ
10352            <p>B</p>ˇ
10353            <p>C</p>ˇ
10354        "#
10355        .unindent(),
10356    );
10357
10358    // Toggle comments for mixture of empty and non-empty selections, where
10359    // multiple selections occupy a given line.
10360    cx.set_state(
10361        &r#"
10362            <p>A«</p>
10363            <p>ˇ»B</p>ˇ
10364            <p>C«</p>
10365            <p>ˇ»D</p>ˇ
10366        "#
10367        .unindent(),
10368    );
10369
10370    cx.update_editor(|editor, window, cx| {
10371        editor.toggle_comments(&ToggleComments::default(), window, cx)
10372    });
10373    cx.assert_editor_state(
10374        &r#"
10375            <!-- <p>A«</p>
10376            <p>ˇ»B</p>ˇ -->
10377            <!-- <p>C«</p>
10378            <p>ˇ»D</p>ˇ -->
10379        "#
10380        .unindent(),
10381    );
10382    cx.update_editor(|editor, window, cx| {
10383        editor.toggle_comments(&ToggleComments::default(), window, cx)
10384    });
10385    cx.assert_editor_state(
10386        &r#"
10387            <p>A«</p>
10388            <p>ˇ»B</p>ˇ
10389            <p>C«</p>
10390            <p>ˇ»D</p>ˇ
10391        "#
10392        .unindent(),
10393    );
10394
10395    // Toggle comments when different languages are active for different
10396    // selections.
10397    cx.set_state(
10398        &r#"
10399            ˇ<script>
10400                ˇvar x = new Y();
10401            ˇ</script>
10402        "#
10403        .unindent(),
10404    );
10405    cx.executor().run_until_parked();
10406    cx.update_editor(|editor, window, cx| {
10407        editor.toggle_comments(&ToggleComments::default(), window, cx)
10408    });
10409    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10410    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10411    cx.assert_editor_state(
10412        &r#"
10413            <!-- ˇ<script> -->
10414                // ˇvar x = new Y();
10415            <!-- ˇ</script> -->
10416        "#
10417        .unindent(),
10418    );
10419}
10420
10421#[gpui::test]
10422fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10423    init_test(cx, |_| {});
10424
10425    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10426    let multibuffer = cx.new(|cx| {
10427        let mut multibuffer = MultiBuffer::new(ReadWrite);
10428        multibuffer.push_excerpts(
10429            buffer.clone(),
10430            [
10431                ExcerptRange {
10432                    context: Point::new(0, 0)..Point::new(0, 4),
10433                    primary: None,
10434                },
10435                ExcerptRange {
10436                    context: Point::new(1, 0)..Point::new(1, 4),
10437                    primary: None,
10438                },
10439            ],
10440            cx,
10441        );
10442        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10443        multibuffer
10444    });
10445
10446    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10447    editor.update_in(cx, |editor, window, cx| {
10448        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10449        editor.change_selections(None, window, cx, |s| {
10450            s.select_ranges([
10451                Point::new(0, 0)..Point::new(0, 0),
10452                Point::new(1, 0)..Point::new(1, 0),
10453            ])
10454        });
10455
10456        editor.handle_input("X", window, cx);
10457        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10458        assert_eq!(
10459            editor.selections.ranges(cx),
10460            [
10461                Point::new(0, 1)..Point::new(0, 1),
10462                Point::new(1, 1)..Point::new(1, 1),
10463            ]
10464        );
10465
10466        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10467        editor.change_selections(None, window, cx, |s| {
10468            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10469        });
10470        editor.backspace(&Default::default(), window, cx);
10471        assert_eq!(editor.text(cx), "Xa\nbbb");
10472        assert_eq!(
10473            editor.selections.ranges(cx),
10474            [Point::new(1, 0)..Point::new(1, 0)]
10475        );
10476
10477        editor.change_selections(None, window, cx, |s| {
10478            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10479        });
10480        editor.backspace(&Default::default(), window, cx);
10481        assert_eq!(editor.text(cx), "X\nbb");
10482        assert_eq!(
10483            editor.selections.ranges(cx),
10484            [Point::new(0, 1)..Point::new(0, 1)]
10485        );
10486    });
10487}
10488
10489#[gpui::test]
10490fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10491    init_test(cx, |_| {});
10492
10493    let markers = vec![('[', ']').into(), ('(', ')').into()];
10494    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10495        indoc! {"
10496            [aaaa
10497            (bbbb]
10498            cccc)",
10499        },
10500        markers.clone(),
10501    );
10502    let excerpt_ranges = markers.into_iter().map(|marker| {
10503        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10504        ExcerptRange {
10505            context,
10506            primary: None,
10507        }
10508    });
10509    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10510    let multibuffer = cx.new(|cx| {
10511        let mut multibuffer = MultiBuffer::new(ReadWrite);
10512        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10513        multibuffer
10514    });
10515
10516    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10517    editor.update_in(cx, |editor, window, cx| {
10518        let (expected_text, selection_ranges) = marked_text_ranges(
10519            indoc! {"
10520                aaaa
10521                bˇbbb
10522                bˇbbˇb
10523                cccc"
10524            },
10525            true,
10526        );
10527        assert_eq!(editor.text(cx), expected_text);
10528        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10529
10530        editor.handle_input("X", window, cx);
10531
10532        let (expected_text, expected_selections) = marked_text_ranges(
10533            indoc! {"
10534                aaaa
10535                bXˇbbXb
10536                bXˇbbXˇb
10537                cccc"
10538            },
10539            false,
10540        );
10541        assert_eq!(editor.text(cx), expected_text);
10542        assert_eq!(editor.selections.ranges(cx), expected_selections);
10543
10544        editor.newline(&Newline, window, cx);
10545        let (expected_text, expected_selections) = marked_text_ranges(
10546            indoc! {"
10547                aaaa
10548                bX
10549                ˇbbX
10550                b
10551                bX
10552                ˇbbX
10553                ˇb
10554                cccc"
10555            },
10556            false,
10557        );
10558        assert_eq!(editor.text(cx), expected_text);
10559        assert_eq!(editor.selections.ranges(cx), expected_selections);
10560    });
10561}
10562
10563#[gpui::test]
10564fn test_refresh_selections(cx: &mut TestAppContext) {
10565    init_test(cx, |_| {});
10566
10567    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10568    let mut excerpt1_id = None;
10569    let multibuffer = cx.new(|cx| {
10570        let mut multibuffer = MultiBuffer::new(ReadWrite);
10571        excerpt1_id = multibuffer
10572            .push_excerpts(
10573                buffer.clone(),
10574                [
10575                    ExcerptRange {
10576                        context: Point::new(0, 0)..Point::new(1, 4),
10577                        primary: None,
10578                    },
10579                    ExcerptRange {
10580                        context: Point::new(1, 0)..Point::new(2, 4),
10581                        primary: None,
10582                    },
10583                ],
10584                cx,
10585            )
10586            .into_iter()
10587            .next();
10588        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10589        multibuffer
10590    });
10591
10592    let editor = cx.add_window(|window, cx| {
10593        let mut editor = build_editor(multibuffer.clone(), window, cx);
10594        let snapshot = editor.snapshot(window, cx);
10595        editor.change_selections(None, window, cx, |s| {
10596            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10597        });
10598        editor.begin_selection(
10599            Point::new(2, 1).to_display_point(&snapshot),
10600            true,
10601            1,
10602            window,
10603            cx,
10604        );
10605        assert_eq!(
10606            editor.selections.ranges(cx),
10607            [
10608                Point::new(1, 3)..Point::new(1, 3),
10609                Point::new(2, 1)..Point::new(2, 1),
10610            ]
10611        );
10612        editor
10613    });
10614
10615    // Refreshing selections is a no-op when excerpts haven't changed.
10616    _ = editor.update(cx, |editor, window, cx| {
10617        editor.change_selections(None, window, cx, |s| s.refresh());
10618        assert_eq!(
10619            editor.selections.ranges(cx),
10620            [
10621                Point::new(1, 3)..Point::new(1, 3),
10622                Point::new(2, 1)..Point::new(2, 1),
10623            ]
10624        );
10625    });
10626
10627    multibuffer.update(cx, |multibuffer, cx| {
10628        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10629    });
10630    _ = editor.update(cx, |editor, window, cx| {
10631        // Removing an excerpt causes the first selection to become degenerate.
10632        assert_eq!(
10633            editor.selections.ranges(cx),
10634            [
10635                Point::new(0, 0)..Point::new(0, 0),
10636                Point::new(0, 1)..Point::new(0, 1)
10637            ]
10638        );
10639
10640        // Refreshing selections will relocate the first selection to the original buffer
10641        // location.
10642        editor.change_selections(None, window, cx, |s| s.refresh());
10643        assert_eq!(
10644            editor.selections.ranges(cx),
10645            [
10646                Point::new(0, 1)..Point::new(0, 1),
10647                Point::new(0, 3)..Point::new(0, 3)
10648            ]
10649        );
10650        assert!(editor.selections.pending_anchor().is_some());
10651    });
10652}
10653
10654#[gpui::test]
10655fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10656    init_test(cx, |_| {});
10657
10658    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10659    let mut excerpt1_id = None;
10660    let multibuffer = cx.new(|cx| {
10661        let mut multibuffer = MultiBuffer::new(ReadWrite);
10662        excerpt1_id = multibuffer
10663            .push_excerpts(
10664                buffer.clone(),
10665                [
10666                    ExcerptRange {
10667                        context: Point::new(0, 0)..Point::new(1, 4),
10668                        primary: None,
10669                    },
10670                    ExcerptRange {
10671                        context: Point::new(1, 0)..Point::new(2, 4),
10672                        primary: None,
10673                    },
10674                ],
10675                cx,
10676            )
10677            .into_iter()
10678            .next();
10679        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10680        multibuffer
10681    });
10682
10683    let editor = cx.add_window(|window, cx| {
10684        let mut editor = build_editor(multibuffer.clone(), window, cx);
10685        let snapshot = editor.snapshot(window, cx);
10686        editor.begin_selection(
10687            Point::new(1, 3).to_display_point(&snapshot),
10688            false,
10689            1,
10690            window,
10691            cx,
10692        );
10693        assert_eq!(
10694            editor.selections.ranges(cx),
10695            [Point::new(1, 3)..Point::new(1, 3)]
10696        );
10697        editor
10698    });
10699
10700    multibuffer.update(cx, |multibuffer, cx| {
10701        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10702    });
10703    _ = editor.update(cx, |editor, window, cx| {
10704        assert_eq!(
10705            editor.selections.ranges(cx),
10706            [Point::new(0, 0)..Point::new(0, 0)]
10707        );
10708
10709        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10710        editor.change_selections(None, window, cx, |s| s.refresh());
10711        assert_eq!(
10712            editor.selections.ranges(cx),
10713            [Point::new(0, 3)..Point::new(0, 3)]
10714        );
10715        assert!(editor.selections.pending_anchor().is_some());
10716    });
10717}
10718
10719#[gpui::test]
10720async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10721    init_test(cx, |_| {});
10722
10723    let language = Arc::new(
10724        Language::new(
10725            LanguageConfig {
10726                brackets: BracketPairConfig {
10727                    pairs: vec![
10728                        BracketPair {
10729                            start: "{".to_string(),
10730                            end: "}".to_string(),
10731                            close: true,
10732                            surround: true,
10733                            newline: true,
10734                        },
10735                        BracketPair {
10736                            start: "/* ".to_string(),
10737                            end: " */".to_string(),
10738                            close: true,
10739                            surround: true,
10740                            newline: true,
10741                        },
10742                    ],
10743                    ..Default::default()
10744                },
10745                ..Default::default()
10746            },
10747            Some(tree_sitter_rust::LANGUAGE.into()),
10748        )
10749        .with_indents_query("")
10750        .unwrap(),
10751    );
10752
10753    let text = concat!(
10754        "{   }\n",     //
10755        "  x\n",       //
10756        "  /*   */\n", //
10757        "x\n",         //
10758        "{{} }\n",     //
10759    );
10760
10761    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10762    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10763    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10764    editor
10765        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10766        .await;
10767
10768    editor.update_in(cx, |editor, window, cx| {
10769        editor.change_selections(None, window, cx, |s| {
10770            s.select_display_ranges([
10771                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10772                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10773                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10774            ])
10775        });
10776        editor.newline(&Newline, window, cx);
10777
10778        assert_eq!(
10779            editor.buffer().read(cx).read(cx).text(),
10780            concat!(
10781                "{ \n",    // Suppress rustfmt
10782                "\n",      //
10783                "}\n",     //
10784                "  x\n",   //
10785                "  /* \n", //
10786                "  \n",    //
10787                "  */\n",  //
10788                "x\n",     //
10789                "{{} \n",  //
10790                "}\n",     //
10791            )
10792        );
10793    });
10794}
10795
10796#[gpui::test]
10797fn test_highlighted_ranges(cx: &mut TestAppContext) {
10798    init_test(cx, |_| {});
10799
10800    let editor = cx.add_window(|window, cx| {
10801        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10802        build_editor(buffer.clone(), window, cx)
10803    });
10804
10805    _ = editor.update(cx, |editor, window, cx| {
10806        struct Type1;
10807        struct Type2;
10808
10809        let buffer = editor.buffer.read(cx).snapshot(cx);
10810
10811        let anchor_range =
10812            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10813
10814        editor.highlight_background::<Type1>(
10815            &[
10816                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10817                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10818                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10819                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10820            ],
10821            |_| Hsla::red(),
10822            cx,
10823        );
10824        editor.highlight_background::<Type2>(
10825            &[
10826                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10827                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10828                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10829                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10830            ],
10831            |_| Hsla::green(),
10832            cx,
10833        );
10834
10835        let snapshot = editor.snapshot(window, cx);
10836        let mut highlighted_ranges = editor.background_highlights_in_range(
10837            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10838            &snapshot,
10839            cx.theme().colors(),
10840        );
10841        // Enforce a consistent ordering based on color without relying on the ordering of the
10842        // highlight's `TypeId` which is non-executor.
10843        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10844        assert_eq!(
10845            highlighted_ranges,
10846            &[
10847                (
10848                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10849                    Hsla::red(),
10850                ),
10851                (
10852                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10853                    Hsla::red(),
10854                ),
10855                (
10856                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10857                    Hsla::green(),
10858                ),
10859                (
10860                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10861                    Hsla::green(),
10862                ),
10863            ]
10864        );
10865        assert_eq!(
10866            editor.background_highlights_in_range(
10867                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10868                &snapshot,
10869                cx.theme().colors(),
10870            ),
10871            &[(
10872                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10873                Hsla::red(),
10874            )]
10875        );
10876    });
10877}
10878
10879#[gpui::test]
10880async fn test_following(cx: &mut TestAppContext) {
10881    init_test(cx, |_| {});
10882
10883    let fs = FakeFs::new(cx.executor());
10884    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10885
10886    let buffer = project.update(cx, |project, cx| {
10887        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10888        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10889    });
10890    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10891    let follower = cx.update(|cx| {
10892        cx.open_window(
10893            WindowOptions {
10894                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10895                    gpui::Point::new(px(0.), px(0.)),
10896                    gpui::Point::new(px(10.), px(80.)),
10897                ))),
10898                ..Default::default()
10899            },
10900            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10901        )
10902        .unwrap()
10903    });
10904
10905    let is_still_following = Rc::new(RefCell::new(true));
10906    let follower_edit_event_count = Rc::new(RefCell::new(0));
10907    let pending_update = Rc::new(RefCell::new(None));
10908    let leader_entity = leader.root(cx).unwrap();
10909    let follower_entity = follower.root(cx).unwrap();
10910    _ = follower.update(cx, {
10911        let update = pending_update.clone();
10912        let is_still_following = is_still_following.clone();
10913        let follower_edit_event_count = follower_edit_event_count.clone();
10914        |_, window, cx| {
10915            cx.subscribe_in(
10916                &leader_entity,
10917                window,
10918                move |_, leader, event, window, cx| {
10919                    leader.read(cx).add_event_to_update_proto(
10920                        event,
10921                        &mut update.borrow_mut(),
10922                        window,
10923                        cx,
10924                    );
10925                },
10926            )
10927            .detach();
10928
10929            cx.subscribe_in(
10930                &follower_entity,
10931                window,
10932                move |_, _, event: &EditorEvent, _window, _cx| {
10933                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10934                        *is_still_following.borrow_mut() = false;
10935                    }
10936
10937                    if let EditorEvent::BufferEdited = event {
10938                        *follower_edit_event_count.borrow_mut() += 1;
10939                    }
10940                },
10941            )
10942            .detach();
10943        }
10944    });
10945
10946    // Update the selections only
10947    _ = leader.update(cx, |leader, window, cx| {
10948        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10949    });
10950    follower
10951        .update(cx, |follower, window, cx| {
10952            follower.apply_update_proto(
10953                &project,
10954                pending_update.borrow_mut().take().unwrap(),
10955                window,
10956                cx,
10957            )
10958        })
10959        .unwrap()
10960        .await
10961        .unwrap();
10962    _ = follower.update(cx, |follower, _, cx| {
10963        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10964    });
10965    assert!(*is_still_following.borrow());
10966    assert_eq!(*follower_edit_event_count.borrow(), 0);
10967
10968    // Update the scroll position only
10969    _ = leader.update(cx, |leader, window, cx| {
10970        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10971    });
10972    follower
10973        .update(cx, |follower, window, cx| {
10974            follower.apply_update_proto(
10975                &project,
10976                pending_update.borrow_mut().take().unwrap(),
10977                window,
10978                cx,
10979            )
10980        })
10981        .unwrap()
10982        .await
10983        .unwrap();
10984    assert_eq!(
10985        follower
10986            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10987            .unwrap(),
10988        gpui::Point::new(1.5, 3.5)
10989    );
10990    assert!(*is_still_following.borrow());
10991    assert_eq!(*follower_edit_event_count.borrow(), 0);
10992
10993    // Update the selections and scroll position. The follower's scroll position is updated
10994    // via autoscroll, not via the leader's exact scroll position.
10995    _ = leader.update(cx, |leader, window, cx| {
10996        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10997        leader.request_autoscroll(Autoscroll::newest(), cx);
10998        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10999    });
11000    follower
11001        .update(cx, |follower, window, cx| {
11002            follower.apply_update_proto(
11003                &project,
11004                pending_update.borrow_mut().take().unwrap(),
11005                window,
11006                cx,
11007            )
11008        })
11009        .unwrap()
11010        .await
11011        .unwrap();
11012    _ = follower.update(cx, |follower, _, cx| {
11013        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11014        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11015    });
11016    assert!(*is_still_following.borrow());
11017
11018    // Creating a pending selection that precedes another selection
11019    _ = leader.update(cx, |leader, window, cx| {
11020        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11021        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11022    });
11023    follower
11024        .update(cx, |follower, window, cx| {
11025            follower.apply_update_proto(
11026                &project,
11027                pending_update.borrow_mut().take().unwrap(),
11028                window,
11029                cx,
11030            )
11031        })
11032        .unwrap()
11033        .await
11034        .unwrap();
11035    _ = follower.update(cx, |follower, _, cx| {
11036        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11037    });
11038    assert!(*is_still_following.borrow());
11039
11040    // Extend the pending selection so that it surrounds another selection
11041    _ = leader.update(cx, |leader, window, cx| {
11042        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11043    });
11044    follower
11045        .update(cx, |follower, window, cx| {
11046            follower.apply_update_proto(
11047                &project,
11048                pending_update.borrow_mut().take().unwrap(),
11049                window,
11050                cx,
11051            )
11052        })
11053        .unwrap()
11054        .await
11055        .unwrap();
11056    _ = follower.update(cx, |follower, _, cx| {
11057        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11058    });
11059
11060    // Scrolling locally breaks the follow
11061    _ = follower.update(cx, |follower, window, cx| {
11062        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11063        follower.set_scroll_anchor(
11064            ScrollAnchor {
11065                anchor: top_anchor,
11066                offset: gpui::Point::new(0.0, 0.5),
11067            },
11068            window,
11069            cx,
11070        );
11071    });
11072    assert!(!(*is_still_following.borrow()));
11073}
11074
11075#[gpui::test]
11076async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11077    init_test(cx, |_| {});
11078
11079    let fs = FakeFs::new(cx.executor());
11080    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11081    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11082    let pane = workspace
11083        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11084        .unwrap();
11085
11086    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11087
11088    let leader = pane.update_in(cx, |_, window, cx| {
11089        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11090        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11091    });
11092
11093    // Start following the editor when it has no excerpts.
11094    let mut state_message =
11095        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11096    let workspace_entity = workspace.root(cx).unwrap();
11097    let follower_1 = cx
11098        .update_window(*workspace.deref(), |_, window, cx| {
11099            Editor::from_state_proto(
11100                workspace_entity,
11101                ViewId {
11102                    creator: Default::default(),
11103                    id: 0,
11104                },
11105                &mut state_message,
11106                window,
11107                cx,
11108            )
11109        })
11110        .unwrap()
11111        .unwrap()
11112        .await
11113        .unwrap();
11114
11115    let update_message = Rc::new(RefCell::new(None));
11116    follower_1.update_in(cx, {
11117        let update = update_message.clone();
11118        |_, window, cx| {
11119            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11120                leader.read(cx).add_event_to_update_proto(
11121                    event,
11122                    &mut update.borrow_mut(),
11123                    window,
11124                    cx,
11125                );
11126            })
11127            .detach();
11128        }
11129    });
11130
11131    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11132        (
11133            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11134            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11135        )
11136    });
11137
11138    // Insert some excerpts.
11139    leader.update(cx, |leader, cx| {
11140        leader.buffer.update(cx, |multibuffer, cx| {
11141            let excerpt_ids = multibuffer.push_excerpts(
11142                buffer_1.clone(),
11143                [
11144                    ExcerptRange {
11145                        context: 1..6,
11146                        primary: None,
11147                    },
11148                    ExcerptRange {
11149                        context: 12..15,
11150                        primary: None,
11151                    },
11152                    ExcerptRange {
11153                        context: 0..3,
11154                        primary: None,
11155                    },
11156                ],
11157                cx,
11158            );
11159            multibuffer.insert_excerpts_after(
11160                excerpt_ids[0],
11161                buffer_2.clone(),
11162                [
11163                    ExcerptRange {
11164                        context: 8..12,
11165                        primary: None,
11166                    },
11167                    ExcerptRange {
11168                        context: 0..6,
11169                        primary: None,
11170                    },
11171                ],
11172                cx,
11173            );
11174        });
11175    });
11176
11177    // Apply the update of adding the excerpts.
11178    follower_1
11179        .update_in(cx, |follower, window, cx| {
11180            follower.apply_update_proto(
11181                &project,
11182                update_message.borrow().clone().unwrap(),
11183                window,
11184                cx,
11185            )
11186        })
11187        .await
11188        .unwrap();
11189    assert_eq!(
11190        follower_1.update(cx, |editor, cx| editor.text(cx)),
11191        leader.update(cx, |editor, cx| editor.text(cx))
11192    );
11193    update_message.borrow_mut().take();
11194
11195    // Start following separately after it already has excerpts.
11196    let mut state_message =
11197        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11198    let workspace_entity = workspace.root(cx).unwrap();
11199    let follower_2 = cx
11200        .update_window(*workspace.deref(), |_, window, cx| {
11201            Editor::from_state_proto(
11202                workspace_entity,
11203                ViewId {
11204                    creator: Default::default(),
11205                    id: 0,
11206                },
11207                &mut state_message,
11208                window,
11209                cx,
11210            )
11211        })
11212        .unwrap()
11213        .unwrap()
11214        .await
11215        .unwrap();
11216    assert_eq!(
11217        follower_2.update(cx, |editor, cx| editor.text(cx)),
11218        leader.update(cx, |editor, cx| editor.text(cx))
11219    );
11220
11221    // Remove some excerpts.
11222    leader.update(cx, |leader, cx| {
11223        leader.buffer.update(cx, |multibuffer, cx| {
11224            let excerpt_ids = multibuffer.excerpt_ids();
11225            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11226            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11227        });
11228    });
11229
11230    // Apply the update of removing the excerpts.
11231    follower_1
11232        .update_in(cx, |follower, window, cx| {
11233            follower.apply_update_proto(
11234                &project,
11235                update_message.borrow().clone().unwrap(),
11236                window,
11237                cx,
11238            )
11239        })
11240        .await
11241        .unwrap();
11242    follower_2
11243        .update_in(cx, |follower, window, cx| {
11244            follower.apply_update_proto(
11245                &project,
11246                update_message.borrow().clone().unwrap(),
11247                window,
11248                cx,
11249            )
11250        })
11251        .await
11252        .unwrap();
11253    update_message.borrow_mut().take();
11254    assert_eq!(
11255        follower_1.update(cx, |editor, cx| editor.text(cx)),
11256        leader.update(cx, |editor, cx| editor.text(cx))
11257    );
11258}
11259
11260#[gpui::test]
11261async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11262    init_test(cx, |_| {});
11263
11264    let mut cx = EditorTestContext::new(cx).await;
11265    let lsp_store =
11266        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11267
11268    cx.set_state(indoc! {"
11269        ˇfn func(abc def: i32) -> u32 {
11270        }
11271    "});
11272
11273    cx.update(|_, cx| {
11274        lsp_store.update(cx, |lsp_store, cx| {
11275            lsp_store
11276                .update_diagnostics(
11277                    LanguageServerId(0),
11278                    lsp::PublishDiagnosticsParams {
11279                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11280                        version: None,
11281                        diagnostics: vec![
11282                            lsp::Diagnostic {
11283                                range: lsp::Range::new(
11284                                    lsp::Position::new(0, 11),
11285                                    lsp::Position::new(0, 12),
11286                                ),
11287                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11288                                ..Default::default()
11289                            },
11290                            lsp::Diagnostic {
11291                                range: lsp::Range::new(
11292                                    lsp::Position::new(0, 12),
11293                                    lsp::Position::new(0, 15),
11294                                ),
11295                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11296                                ..Default::default()
11297                            },
11298                            lsp::Diagnostic {
11299                                range: lsp::Range::new(
11300                                    lsp::Position::new(0, 25),
11301                                    lsp::Position::new(0, 28),
11302                                ),
11303                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11304                                ..Default::default()
11305                            },
11306                        ],
11307                    },
11308                    &[],
11309                    cx,
11310                )
11311                .unwrap()
11312        });
11313    });
11314
11315    executor.run_until_parked();
11316
11317    cx.update_editor(|editor, window, cx| {
11318        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11319    });
11320
11321    cx.assert_editor_state(indoc! {"
11322        fn func(abc def: i32) -> ˇu32 {
11323        }
11324    "});
11325
11326    cx.update_editor(|editor, window, cx| {
11327        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11328    });
11329
11330    cx.assert_editor_state(indoc! {"
11331        fn func(abc ˇdef: i32) -> u32 {
11332        }
11333    "});
11334
11335    cx.update_editor(|editor, window, cx| {
11336        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11337    });
11338
11339    cx.assert_editor_state(indoc! {"
11340        fn func(abcˇ def: i32) -> u32 {
11341        }
11342    "});
11343
11344    cx.update_editor(|editor, window, cx| {
11345        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11346    });
11347
11348    cx.assert_editor_state(indoc! {"
11349        fn func(abc def: i32) -> ˇu32 {
11350        }
11351    "});
11352}
11353
11354#[gpui::test]
11355async fn cycle_through_same_place_diagnostics(
11356    executor: BackgroundExecutor,
11357    cx: &mut TestAppContext,
11358) {
11359    init_test(cx, |_| {});
11360
11361    let mut cx = EditorTestContext::new(cx).await;
11362    let lsp_store =
11363        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11364
11365    cx.set_state(indoc! {"
11366        ˇfn func(abc def: i32) -> u32 {
11367        }
11368    "});
11369
11370    cx.update(|_, cx| {
11371        lsp_store.update(cx, |lsp_store, cx| {
11372            lsp_store
11373                .update_diagnostics(
11374                    LanguageServerId(0),
11375                    lsp::PublishDiagnosticsParams {
11376                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11377                        version: None,
11378                        diagnostics: vec![
11379                            lsp::Diagnostic {
11380                                range: lsp::Range::new(
11381                                    lsp::Position::new(0, 11),
11382                                    lsp::Position::new(0, 12),
11383                                ),
11384                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11385                                ..Default::default()
11386                            },
11387                            lsp::Diagnostic {
11388                                range: lsp::Range::new(
11389                                    lsp::Position::new(0, 12),
11390                                    lsp::Position::new(0, 15),
11391                                ),
11392                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11393                                ..Default::default()
11394                            },
11395                            lsp::Diagnostic {
11396                                range: lsp::Range::new(
11397                                    lsp::Position::new(0, 12),
11398                                    lsp::Position::new(0, 15),
11399                                ),
11400                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11401                                ..Default::default()
11402                            },
11403                            lsp::Diagnostic {
11404                                range: lsp::Range::new(
11405                                    lsp::Position::new(0, 25),
11406                                    lsp::Position::new(0, 28),
11407                                ),
11408                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11409                                ..Default::default()
11410                            },
11411                        ],
11412                    },
11413                    &[],
11414                    cx,
11415                )
11416                .unwrap()
11417        });
11418    });
11419    executor.run_until_parked();
11420
11421    //// Backward
11422
11423    // Fourth diagnostic
11424    cx.update_editor(|editor, window, cx| {
11425        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11426    });
11427    cx.assert_editor_state(indoc! {"
11428        fn func(abc def: i32) -> ˇu32 {
11429        }
11430    "});
11431
11432    // Third diagnostic
11433    cx.update_editor(|editor, window, cx| {
11434        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11435    });
11436    cx.assert_editor_state(indoc! {"
11437        fn func(abc ˇdef: i32) -> u32 {
11438        }
11439    "});
11440
11441    // Second diagnostic, same place
11442    cx.update_editor(|editor, window, cx| {
11443        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11444    });
11445    cx.assert_editor_state(indoc! {"
11446        fn func(abc ˇdef: i32) -> u32 {
11447        }
11448    "});
11449
11450    // First diagnostic
11451    cx.update_editor(|editor, window, cx| {
11452        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11453    });
11454    cx.assert_editor_state(indoc! {"
11455        fn func(abcˇ def: i32) -> u32 {
11456        }
11457    "});
11458
11459    // Wrapped over, fourth diagnostic
11460    cx.update_editor(|editor, window, cx| {
11461        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11462    });
11463    cx.assert_editor_state(indoc! {"
11464        fn func(abc def: i32) -> ˇu32 {
11465        }
11466    "});
11467
11468    cx.update_editor(|editor, window, cx| {
11469        editor.move_to_beginning(&MoveToBeginning, window, cx);
11470    });
11471    cx.assert_editor_state(indoc! {"
11472        ˇfn func(abc def: i32) -> u32 {
11473        }
11474    "});
11475
11476    //// Forward
11477
11478    // First diagnostic
11479    cx.update_editor(|editor, window, cx| {
11480        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11481    });
11482    cx.assert_editor_state(indoc! {"
11483        fn func(abcˇ def: i32) -> u32 {
11484        }
11485    "});
11486
11487    // Second diagnostic
11488    cx.update_editor(|editor, window, cx| {
11489        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11490    });
11491    cx.assert_editor_state(indoc! {"
11492        fn func(abc ˇdef: i32) -> u32 {
11493        }
11494    "});
11495
11496    // Third diagnostic, same place
11497    cx.update_editor(|editor, window, cx| {
11498        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11499    });
11500    cx.assert_editor_state(indoc! {"
11501        fn func(abc ˇdef: i32) -> u32 {
11502        }
11503    "});
11504
11505    // Fourth diagnostic
11506    cx.update_editor(|editor, window, cx| {
11507        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11508    });
11509    cx.assert_editor_state(indoc! {"
11510        fn func(abc def: i32) -> ˇu32 {
11511        }
11512    "});
11513
11514    // Wrapped around, first diagnostic
11515    cx.update_editor(|editor, window, cx| {
11516        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11517    });
11518    cx.assert_editor_state(indoc! {"
11519        fn func(abcˇ def: i32) -> u32 {
11520        }
11521    "});
11522}
11523
11524#[gpui::test]
11525async fn active_diagnostics_dismiss_after_invalidation(
11526    executor: BackgroundExecutor,
11527    cx: &mut TestAppContext,
11528) {
11529    init_test(cx, |_| {});
11530
11531    let mut cx = EditorTestContext::new(cx).await;
11532    let lsp_store =
11533        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11534
11535    cx.set_state(indoc! {"
11536        ˇfn func(abc def: i32) -> u32 {
11537        }
11538    "});
11539
11540    let message = "Something's wrong!";
11541    cx.update(|_, cx| {
11542        lsp_store.update(cx, |lsp_store, cx| {
11543            lsp_store
11544                .update_diagnostics(
11545                    LanguageServerId(0),
11546                    lsp::PublishDiagnosticsParams {
11547                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11548                        version: None,
11549                        diagnostics: vec![lsp::Diagnostic {
11550                            range: lsp::Range::new(
11551                                lsp::Position::new(0, 11),
11552                                lsp::Position::new(0, 12),
11553                            ),
11554                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11555                            message: message.to_string(),
11556                            ..Default::default()
11557                        }],
11558                    },
11559                    &[],
11560                    cx,
11561                )
11562                .unwrap()
11563        });
11564    });
11565    executor.run_until_parked();
11566
11567    cx.update_editor(|editor, window, cx| {
11568        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11569        assert_eq!(
11570            editor
11571                .active_diagnostics
11572                .as_ref()
11573                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11574            Some(message),
11575            "Should have a diagnostics group activated"
11576        );
11577    });
11578    cx.assert_editor_state(indoc! {"
11579        fn func(abcˇ def: i32) -> u32 {
11580        }
11581    "});
11582
11583    cx.update(|_, cx| {
11584        lsp_store.update(cx, |lsp_store, cx| {
11585            lsp_store
11586                .update_diagnostics(
11587                    LanguageServerId(0),
11588                    lsp::PublishDiagnosticsParams {
11589                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11590                        version: None,
11591                        diagnostics: Vec::new(),
11592                    },
11593                    &[],
11594                    cx,
11595                )
11596                .unwrap()
11597        });
11598    });
11599    executor.run_until_parked();
11600    cx.update_editor(|editor, _, _| {
11601        assert_eq!(
11602            editor.active_diagnostics, None,
11603            "After no diagnostics set to the editor, no diagnostics should be active"
11604        );
11605    });
11606    cx.assert_editor_state(indoc! {"
11607        fn func(abcˇ def: i32) -> u32 {
11608        }
11609    "});
11610
11611    cx.update_editor(|editor, window, cx| {
11612        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11613        assert_eq!(
11614            editor.active_diagnostics, None,
11615            "Should be no diagnostics to go to and activate"
11616        );
11617    });
11618    cx.assert_editor_state(indoc! {"
11619        fn func(abcˇ def: i32) -> u32 {
11620        }
11621    "});
11622}
11623
11624#[gpui::test]
11625async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11626    init_test(cx, |_| {});
11627
11628    let mut cx = EditorTestContext::new(cx).await;
11629
11630    cx.set_state(indoc! {"
11631        fn func(abˇc def: i32) -> u32 {
11632        }
11633    "});
11634    let lsp_store =
11635        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11636
11637    cx.update(|_, cx| {
11638        lsp_store.update(cx, |lsp_store, cx| {
11639            lsp_store.update_diagnostics(
11640                LanguageServerId(0),
11641                lsp::PublishDiagnosticsParams {
11642                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11643                    version: None,
11644                    diagnostics: vec![lsp::Diagnostic {
11645                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11646                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11647                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11648                        ..Default::default()
11649                    }],
11650                },
11651                &[],
11652                cx,
11653            )
11654        })
11655    }).unwrap();
11656    cx.run_until_parked();
11657    cx.update_editor(|editor, window, cx| {
11658        hover_popover::hover(editor, &Default::default(), window, cx)
11659    });
11660    cx.run_until_parked();
11661    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11662}
11663
11664#[gpui::test]
11665async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11666    init_test(cx, |_| {});
11667
11668    let mut cx = EditorTestContext::new(cx).await;
11669
11670    let diff_base = r#"
11671        use some::mod;
11672
11673        const A: u32 = 42;
11674
11675        fn main() {
11676            println!("hello");
11677
11678            println!("world");
11679        }
11680        "#
11681    .unindent();
11682
11683    // Edits are modified, removed, modified, added
11684    cx.set_state(
11685        &r#"
11686        use some::modified;
11687
11688        ˇ
11689        fn main() {
11690            println!("hello there");
11691
11692            println!("around the");
11693            println!("world");
11694        }
11695        "#
11696        .unindent(),
11697    );
11698
11699    cx.set_head_text(&diff_base);
11700    executor.run_until_parked();
11701
11702    cx.update_editor(|editor, window, cx| {
11703        //Wrap around the bottom of the buffer
11704        for _ in 0..3 {
11705            editor.go_to_next_hunk(&GoToHunk, window, cx);
11706        }
11707    });
11708
11709    cx.assert_editor_state(
11710        &r#"
11711        ˇuse some::modified;
11712
11713
11714        fn main() {
11715            println!("hello there");
11716
11717            println!("around the");
11718            println!("world");
11719        }
11720        "#
11721        .unindent(),
11722    );
11723
11724    cx.update_editor(|editor, window, cx| {
11725        //Wrap around the top of the buffer
11726        for _ in 0..2 {
11727            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11728        }
11729    });
11730
11731    cx.assert_editor_state(
11732        &r#"
11733        use some::modified;
11734
11735
11736        fn main() {
11737        ˇ    println!("hello there");
11738
11739            println!("around the");
11740            println!("world");
11741        }
11742        "#
11743        .unindent(),
11744    );
11745
11746    cx.update_editor(|editor, window, cx| {
11747        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11748    });
11749
11750    cx.assert_editor_state(
11751        &r#"
11752        use some::modified;
11753
11754        ˇ
11755        fn main() {
11756            println!("hello there");
11757
11758            println!("around the");
11759            println!("world");
11760        }
11761        "#
11762        .unindent(),
11763    );
11764
11765    cx.update_editor(|editor, window, cx| {
11766        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11767    });
11768
11769    cx.assert_editor_state(
11770        &r#"
11771        ˇuse some::modified;
11772
11773
11774        fn main() {
11775            println!("hello there");
11776
11777            println!("around the");
11778            println!("world");
11779        }
11780        "#
11781        .unindent(),
11782    );
11783
11784    cx.update_editor(|editor, window, cx| {
11785        for _ in 0..2 {
11786            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11787        }
11788    });
11789
11790    cx.assert_editor_state(
11791        &r#"
11792        use some::modified;
11793
11794
11795        fn main() {
11796        ˇ    println!("hello there");
11797
11798            println!("around the");
11799            println!("world");
11800        }
11801        "#
11802        .unindent(),
11803    );
11804
11805    cx.update_editor(|editor, window, cx| {
11806        editor.fold(&Fold, window, cx);
11807    });
11808
11809    cx.update_editor(|editor, window, cx| {
11810        editor.go_to_next_hunk(&GoToHunk, window, cx);
11811    });
11812
11813    cx.assert_editor_state(
11814        &r#"
11815        ˇuse some::modified;
11816
11817
11818        fn main() {
11819            println!("hello there");
11820
11821            println!("around the");
11822            println!("world");
11823        }
11824        "#
11825        .unindent(),
11826    );
11827}
11828
11829#[test]
11830fn test_split_words() {
11831    fn split(text: &str) -> Vec<&str> {
11832        split_words(text).collect()
11833    }
11834
11835    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11836    assert_eq!(split("hello_world"), &["hello_", "world"]);
11837    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11838    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11839    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11840    assert_eq!(split("helloworld"), &["helloworld"]);
11841
11842    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11843}
11844
11845#[gpui::test]
11846async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11847    init_test(cx, |_| {});
11848
11849    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11850    let mut assert = |before, after| {
11851        let _state_context = cx.set_state(before);
11852        cx.run_until_parked();
11853        cx.update_editor(|editor, window, cx| {
11854            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11855        });
11856        cx.assert_editor_state(after);
11857    };
11858
11859    // Outside bracket jumps to outside of matching bracket
11860    assert("console.logˇ(var);", "console.log(var)ˇ;");
11861    assert("console.log(var)ˇ;", "console.logˇ(var);");
11862
11863    // Inside bracket jumps to inside of matching bracket
11864    assert("console.log(ˇvar);", "console.log(varˇ);");
11865    assert("console.log(varˇ);", "console.log(ˇvar);");
11866
11867    // When outside a bracket and inside, favor jumping to the inside bracket
11868    assert(
11869        "console.log('foo', [1, 2, 3]ˇ);",
11870        "console.log(ˇ'foo', [1, 2, 3]);",
11871    );
11872    assert(
11873        "console.log(ˇ'foo', [1, 2, 3]);",
11874        "console.log('foo', [1, 2, 3]ˇ);",
11875    );
11876
11877    // Bias forward if two options are equally likely
11878    assert(
11879        "let result = curried_fun()ˇ();",
11880        "let result = curried_fun()()ˇ;",
11881    );
11882
11883    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11884    assert(
11885        indoc! {"
11886            function test() {
11887                console.log('test')ˇ
11888            }"},
11889        indoc! {"
11890            function test() {
11891                console.logˇ('test')
11892            }"},
11893    );
11894}
11895
11896#[gpui::test]
11897async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11898    init_test(cx, |_| {});
11899
11900    let fs = FakeFs::new(cx.executor());
11901    fs.insert_tree(
11902        path!("/a"),
11903        json!({
11904            "main.rs": "fn main() { let a = 5; }",
11905            "other.rs": "// Test file",
11906        }),
11907    )
11908    .await;
11909    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11910
11911    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11912    language_registry.add(Arc::new(Language::new(
11913        LanguageConfig {
11914            name: "Rust".into(),
11915            matcher: LanguageMatcher {
11916                path_suffixes: vec!["rs".to_string()],
11917                ..Default::default()
11918            },
11919            brackets: BracketPairConfig {
11920                pairs: vec![BracketPair {
11921                    start: "{".to_string(),
11922                    end: "}".to_string(),
11923                    close: true,
11924                    surround: true,
11925                    newline: true,
11926                }],
11927                disabled_scopes_by_bracket_ix: Vec::new(),
11928            },
11929            ..Default::default()
11930        },
11931        Some(tree_sitter_rust::LANGUAGE.into()),
11932    )));
11933    let mut fake_servers = language_registry.register_fake_lsp(
11934        "Rust",
11935        FakeLspAdapter {
11936            capabilities: lsp::ServerCapabilities {
11937                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11938                    first_trigger_character: "{".to_string(),
11939                    more_trigger_character: None,
11940                }),
11941                ..Default::default()
11942            },
11943            ..Default::default()
11944        },
11945    );
11946
11947    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11948
11949    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11950
11951    let worktree_id = workspace
11952        .update(cx, |workspace, _, cx| {
11953            workspace.project().update(cx, |project, cx| {
11954                project.worktrees(cx).next().unwrap().read(cx).id()
11955            })
11956        })
11957        .unwrap();
11958
11959    let buffer = project
11960        .update(cx, |project, cx| {
11961            project.open_local_buffer(path!("/a/main.rs"), cx)
11962        })
11963        .await
11964        .unwrap();
11965    let editor_handle = workspace
11966        .update(cx, |workspace, window, cx| {
11967            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11968        })
11969        .unwrap()
11970        .await
11971        .unwrap()
11972        .downcast::<Editor>()
11973        .unwrap();
11974
11975    cx.executor().start_waiting();
11976    let fake_server = fake_servers.next().await.unwrap();
11977
11978    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11979        assert_eq!(
11980            params.text_document_position.text_document.uri,
11981            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11982        );
11983        assert_eq!(
11984            params.text_document_position.position,
11985            lsp::Position::new(0, 21),
11986        );
11987
11988        Ok(Some(vec![lsp::TextEdit {
11989            new_text: "]".to_string(),
11990            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11991        }]))
11992    });
11993
11994    editor_handle.update_in(cx, |editor, window, cx| {
11995        window.focus(&editor.focus_handle(cx));
11996        editor.change_selections(None, window, cx, |s| {
11997            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11998        });
11999        editor.handle_input("{", window, cx);
12000    });
12001
12002    cx.executor().run_until_parked();
12003
12004    buffer.update(cx, |buffer, _| {
12005        assert_eq!(
12006            buffer.text(),
12007            "fn main() { let a = {5}; }",
12008            "No extra braces from on type formatting should appear in the buffer"
12009        )
12010    });
12011}
12012
12013#[gpui::test]
12014async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12015    init_test(cx, |_| {});
12016
12017    let fs = FakeFs::new(cx.executor());
12018    fs.insert_tree(
12019        path!("/a"),
12020        json!({
12021            "main.rs": "fn main() { let a = 5; }",
12022            "other.rs": "// Test file",
12023        }),
12024    )
12025    .await;
12026
12027    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12028
12029    let server_restarts = Arc::new(AtomicUsize::new(0));
12030    let closure_restarts = Arc::clone(&server_restarts);
12031    let language_server_name = "test language server";
12032    let language_name: LanguageName = "Rust".into();
12033
12034    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12035    language_registry.add(Arc::new(Language::new(
12036        LanguageConfig {
12037            name: language_name.clone(),
12038            matcher: LanguageMatcher {
12039                path_suffixes: vec!["rs".to_string()],
12040                ..Default::default()
12041            },
12042            ..Default::default()
12043        },
12044        Some(tree_sitter_rust::LANGUAGE.into()),
12045    )));
12046    let mut fake_servers = language_registry.register_fake_lsp(
12047        "Rust",
12048        FakeLspAdapter {
12049            name: language_server_name,
12050            initialization_options: Some(json!({
12051                "testOptionValue": true
12052            })),
12053            initializer: Some(Box::new(move |fake_server| {
12054                let task_restarts = Arc::clone(&closure_restarts);
12055                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12056                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12057                    futures::future::ready(Ok(()))
12058                });
12059            })),
12060            ..Default::default()
12061        },
12062    );
12063
12064    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12065    let _buffer = project
12066        .update(cx, |project, cx| {
12067            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12068        })
12069        .await
12070        .unwrap();
12071    let _fake_server = fake_servers.next().await.unwrap();
12072    update_test_language_settings(cx, |language_settings| {
12073        language_settings.languages.insert(
12074            language_name.clone(),
12075            LanguageSettingsContent {
12076                tab_size: NonZeroU32::new(8),
12077                ..Default::default()
12078            },
12079        );
12080    });
12081    cx.executor().run_until_parked();
12082    assert_eq!(
12083        server_restarts.load(atomic::Ordering::Acquire),
12084        0,
12085        "Should not restart LSP server on an unrelated change"
12086    );
12087
12088    update_test_project_settings(cx, |project_settings| {
12089        project_settings.lsp.insert(
12090            "Some other server name".into(),
12091            LspSettings {
12092                binary: None,
12093                settings: None,
12094                initialization_options: Some(json!({
12095                    "some other init value": false
12096                })),
12097            },
12098        );
12099    });
12100    cx.executor().run_until_parked();
12101    assert_eq!(
12102        server_restarts.load(atomic::Ordering::Acquire),
12103        0,
12104        "Should not restart LSP server on an unrelated LSP settings change"
12105    );
12106
12107    update_test_project_settings(cx, |project_settings| {
12108        project_settings.lsp.insert(
12109            language_server_name.into(),
12110            LspSettings {
12111                binary: None,
12112                settings: None,
12113                initialization_options: Some(json!({
12114                    "anotherInitValue": false
12115                })),
12116            },
12117        );
12118    });
12119    cx.executor().run_until_parked();
12120    assert_eq!(
12121        server_restarts.load(atomic::Ordering::Acquire),
12122        1,
12123        "Should restart LSP server on a related LSP settings change"
12124    );
12125
12126    update_test_project_settings(cx, |project_settings| {
12127        project_settings.lsp.insert(
12128            language_server_name.into(),
12129            LspSettings {
12130                binary: None,
12131                settings: None,
12132                initialization_options: Some(json!({
12133                    "anotherInitValue": false
12134                })),
12135            },
12136        );
12137    });
12138    cx.executor().run_until_parked();
12139    assert_eq!(
12140        server_restarts.load(atomic::Ordering::Acquire),
12141        1,
12142        "Should not restart LSP server on a related LSP settings change that is the same"
12143    );
12144
12145    update_test_project_settings(cx, |project_settings| {
12146        project_settings.lsp.insert(
12147            language_server_name.into(),
12148            LspSettings {
12149                binary: None,
12150                settings: None,
12151                initialization_options: None,
12152            },
12153        );
12154    });
12155    cx.executor().run_until_parked();
12156    assert_eq!(
12157        server_restarts.load(atomic::Ordering::Acquire),
12158        2,
12159        "Should restart LSP server on another related LSP settings change"
12160    );
12161}
12162
12163#[gpui::test]
12164async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12165    init_test(cx, |_| {});
12166
12167    let mut cx = EditorLspTestContext::new_rust(
12168        lsp::ServerCapabilities {
12169            completion_provider: Some(lsp::CompletionOptions {
12170                trigger_characters: Some(vec![".".to_string()]),
12171                resolve_provider: Some(true),
12172                ..Default::default()
12173            }),
12174            ..Default::default()
12175        },
12176        cx,
12177    )
12178    .await;
12179
12180    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12181    cx.simulate_keystroke(".");
12182    let completion_item = lsp::CompletionItem {
12183        label: "some".into(),
12184        kind: Some(lsp::CompletionItemKind::SNIPPET),
12185        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12186        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12187            kind: lsp::MarkupKind::Markdown,
12188            value: "```rust\nSome(2)\n```".to_string(),
12189        })),
12190        deprecated: Some(false),
12191        sort_text: Some("fffffff2".to_string()),
12192        filter_text: Some("some".to_string()),
12193        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12194        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12195            range: lsp::Range {
12196                start: lsp::Position {
12197                    line: 0,
12198                    character: 22,
12199                },
12200                end: lsp::Position {
12201                    line: 0,
12202                    character: 22,
12203                },
12204            },
12205            new_text: "Some(2)".to_string(),
12206        })),
12207        additional_text_edits: Some(vec![lsp::TextEdit {
12208            range: lsp::Range {
12209                start: lsp::Position {
12210                    line: 0,
12211                    character: 20,
12212                },
12213                end: lsp::Position {
12214                    line: 0,
12215                    character: 22,
12216                },
12217            },
12218            new_text: "".to_string(),
12219        }]),
12220        ..Default::default()
12221    };
12222
12223    let closure_completion_item = completion_item.clone();
12224    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12225        let task_completion_item = closure_completion_item.clone();
12226        async move {
12227            Ok(Some(lsp::CompletionResponse::Array(vec![
12228                task_completion_item,
12229            ])))
12230        }
12231    });
12232
12233    request.next().await;
12234
12235    cx.condition(|editor, _| editor.context_menu_visible())
12236        .await;
12237    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12238        editor
12239            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12240            .unwrap()
12241    });
12242    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12243
12244    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12245        let task_completion_item = completion_item.clone();
12246        async move { Ok(task_completion_item) }
12247    })
12248    .next()
12249    .await
12250    .unwrap();
12251    apply_additional_edits.await.unwrap();
12252    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12253}
12254
12255#[gpui::test]
12256async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12257    init_test(cx, |_| {});
12258
12259    let mut cx = EditorLspTestContext::new_rust(
12260        lsp::ServerCapabilities {
12261            completion_provider: Some(lsp::CompletionOptions {
12262                trigger_characters: Some(vec![".".to_string()]),
12263                resolve_provider: Some(true),
12264                ..Default::default()
12265            }),
12266            ..Default::default()
12267        },
12268        cx,
12269    )
12270    .await;
12271
12272    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12273    cx.simulate_keystroke(".");
12274
12275    let item1 = lsp::CompletionItem {
12276        label: "method id()".to_string(),
12277        filter_text: Some("id".to_string()),
12278        detail: None,
12279        documentation: None,
12280        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12281            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12282            new_text: ".id".to_string(),
12283        })),
12284        ..lsp::CompletionItem::default()
12285    };
12286
12287    let item2 = lsp::CompletionItem {
12288        label: "other".to_string(),
12289        filter_text: Some("other".to_string()),
12290        detail: None,
12291        documentation: None,
12292        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12293            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12294            new_text: ".other".to_string(),
12295        })),
12296        ..lsp::CompletionItem::default()
12297    };
12298
12299    let item1 = item1.clone();
12300    cx.handle_request::<lsp::request::Completion, _, _>({
12301        let item1 = item1.clone();
12302        move |_, _, _| {
12303            let item1 = item1.clone();
12304            let item2 = item2.clone();
12305            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12306        }
12307    })
12308    .next()
12309    .await;
12310
12311    cx.condition(|editor, _| editor.context_menu_visible())
12312        .await;
12313    cx.update_editor(|editor, _, _| {
12314        let context_menu = editor.context_menu.borrow_mut();
12315        let context_menu = context_menu
12316            .as_ref()
12317            .expect("Should have the context menu deployed");
12318        match context_menu {
12319            CodeContextMenu::Completions(completions_menu) => {
12320                let completions = completions_menu.completions.borrow_mut();
12321                assert_eq!(
12322                    completions
12323                        .iter()
12324                        .map(|completion| &completion.label.text)
12325                        .collect::<Vec<_>>(),
12326                    vec!["method id()", "other"]
12327                )
12328            }
12329            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12330        }
12331    });
12332
12333    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12334        let item1 = item1.clone();
12335        move |_, item_to_resolve, _| {
12336            let item1 = item1.clone();
12337            async move {
12338                if item1 == item_to_resolve {
12339                    Ok(lsp::CompletionItem {
12340                        label: "method id()".to_string(),
12341                        filter_text: Some("id".to_string()),
12342                        detail: Some("Now resolved!".to_string()),
12343                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12344                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12345                            range: lsp::Range::new(
12346                                lsp::Position::new(0, 22),
12347                                lsp::Position::new(0, 22),
12348                            ),
12349                            new_text: ".id".to_string(),
12350                        })),
12351                        ..lsp::CompletionItem::default()
12352                    })
12353                } else {
12354                    Ok(item_to_resolve)
12355                }
12356            }
12357        }
12358    })
12359    .next()
12360    .await
12361    .unwrap();
12362    cx.run_until_parked();
12363
12364    cx.update_editor(|editor, window, cx| {
12365        editor.context_menu_next(&Default::default(), window, cx);
12366    });
12367
12368    cx.update_editor(|editor, _, _| {
12369        let context_menu = editor.context_menu.borrow_mut();
12370        let context_menu = context_menu
12371            .as_ref()
12372            .expect("Should have the context menu deployed");
12373        match context_menu {
12374            CodeContextMenu::Completions(completions_menu) => {
12375                let completions = completions_menu.completions.borrow_mut();
12376                assert_eq!(
12377                    completions
12378                        .iter()
12379                        .map(|completion| &completion.label.text)
12380                        .collect::<Vec<_>>(),
12381                    vec!["method id() Now resolved!", "other"],
12382                    "Should update first completion label, but not second as the filter text did not match."
12383                );
12384            }
12385            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12386        }
12387    });
12388}
12389
12390#[gpui::test]
12391async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12392    init_test(cx, |_| {});
12393
12394    let mut cx = EditorLspTestContext::new_rust(
12395        lsp::ServerCapabilities {
12396            completion_provider: Some(lsp::CompletionOptions {
12397                trigger_characters: Some(vec![".".to_string()]),
12398                resolve_provider: Some(true),
12399                ..Default::default()
12400            }),
12401            ..Default::default()
12402        },
12403        cx,
12404    )
12405    .await;
12406
12407    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12408    cx.simulate_keystroke(".");
12409
12410    let unresolved_item_1 = lsp::CompletionItem {
12411        label: "id".to_string(),
12412        filter_text: Some("id".to_string()),
12413        detail: None,
12414        documentation: None,
12415        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12416            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12417            new_text: ".id".to_string(),
12418        })),
12419        ..lsp::CompletionItem::default()
12420    };
12421    let resolved_item_1 = lsp::CompletionItem {
12422        additional_text_edits: Some(vec![lsp::TextEdit {
12423            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12424            new_text: "!!".to_string(),
12425        }]),
12426        ..unresolved_item_1.clone()
12427    };
12428    let unresolved_item_2 = lsp::CompletionItem {
12429        label: "other".to_string(),
12430        filter_text: Some("other".to_string()),
12431        detail: None,
12432        documentation: None,
12433        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12434            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12435            new_text: ".other".to_string(),
12436        })),
12437        ..lsp::CompletionItem::default()
12438    };
12439    let resolved_item_2 = lsp::CompletionItem {
12440        additional_text_edits: Some(vec![lsp::TextEdit {
12441            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12442            new_text: "??".to_string(),
12443        }]),
12444        ..unresolved_item_2.clone()
12445    };
12446
12447    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12448    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12449    cx.lsp
12450        .server
12451        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12452            let unresolved_item_1 = unresolved_item_1.clone();
12453            let resolved_item_1 = resolved_item_1.clone();
12454            let unresolved_item_2 = unresolved_item_2.clone();
12455            let resolved_item_2 = resolved_item_2.clone();
12456            let resolve_requests_1 = resolve_requests_1.clone();
12457            let resolve_requests_2 = resolve_requests_2.clone();
12458            move |unresolved_request, _| {
12459                let unresolved_item_1 = unresolved_item_1.clone();
12460                let resolved_item_1 = resolved_item_1.clone();
12461                let unresolved_item_2 = unresolved_item_2.clone();
12462                let resolved_item_2 = resolved_item_2.clone();
12463                let resolve_requests_1 = resolve_requests_1.clone();
12464                let resolve_requests_2 = resolve_requests_2.clone();
12465                async move {
12466                    if unresolved_request == unresolved_item_1 {
12467                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12468                        Ok(resolved_item_1.clone())
12469                    } else if unresolved_request == unresolved_item_2 {
12470                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12471                        Ok(resolved_item_2.clone())
12472                    } else {
12473                        panic!("Unexpected completion item {unresolved_request:?}")
12474                    }
12475                }
12476            }
12477        })
12478        .detach();
12479
12480    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12481        let unresolved_item_1 = unresolved_item_1.clone();
12482        let unresolved_item_2 = unresolved_item_2.clone();
12483        async move {
12484            Ok(Some(lsp::CompletionResponse::Array(vec![
12485                unresolved_item_1,
12486                unresolved_item_2,
12487            ])))
12488        }
12489    })
12490    .next()
12491    .await;
12492
12493    cx.condition(|editor, _| editor.context_menu_visible())
12494        .await;
12495    cx.update_editor(|editor, _, _| {
12496        let context_menu = editor.context_menu.borrow_mut();
12497        let context_menu = context_menu
12498            .as_ref()
12499            .expect("Should have the context menu deployed");
12500        match context_menu {
12501            CodeContextMenu::Completions(completions_menu) => {
12502                let completions = completions_menu.completions.borrow_mut();
12503                assert_eq!(
12504                    completions
12505                        .iter()
12506                        .map(|completion| &completion.label.text)
12507                        .collect::<Vec<_>>(),
12508                    vec!["id", "other"]
12509                )
12510            }
12511            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12512        }
12513    });
12514    cx.run_until_parked();
12515
12516    cx.update_editor(|editor, window, cx| {
12517        editor.context_menu_next(&ContextMenuNext, window, cx);
12518    });
12519    cx.run_until_parked();
12520    cx.update_editor(|editor, window, cx| {
12521        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12522    });
12523    cx.run_until_parked();
12524    cx.update_editor(|editor, window, cx| {
12525        editor.context_menu_next(&ContextMenuNext, window, cx);
12526    });
12527    cx.run_until_parked();
12528    cx.update_editor(|editor, window, cx| {
12529        editor
12530            .compose_completion(&ComposeCompletion::default(), window, cx)
12531            .expect("No task returned")
12532    })
12533    .await
12534    .expect("Completion failed");
12535    cx.run_until_parked();
12536
12537    cx.update_editor(|editor, _, cx| {
12538        assert_eq!(
12539            resolve_requests_1.load(atomic::Ordering::Acquire),
12540            1,
12541            "Should always resolve once despite multiple selections"
12542        );
12543        assert_eq!(
12544            resolve_requests_2.load(atomic::Ordering::Acquire),
12545            1,
12546            "Should always resolve once after multiple selections and applying the completion"
12547        );
12548        assert_eq!(
12549            editor.text(cx),
12550            "fn main() { let a = ??.other; }",
12551            "Should use resolved data when applying the completion"
12552        );
12553    });
12554}
12555
12556#[gpui::test]
12557async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12558    init_test(cx, |_| {});
12559
12560    let item_0 = lsp::CompletionItem {
12561        label: "abs".into(),
12562        insert_text: Some("abs".into()),
12563        data: Some(json!({ "very": "special"})),
12564        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12565        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12566            lsp::InsertReplaceEdit {
12567                new_text: "abs".to_string(),
12568                insert: lsp::Range::default(),
12569                replace: lsp::Range::default(),
12570            },
12571        )),
12572        ..lsp::CompletionItem::default()
12573    };
12574    let items = iter::once(item_0.clone())
12575        .chain((11..51).map(|i| lsp::CompletionItem {
12576            label: format!("item_{}", i),
12577            insert_text: Some(format!("item_{}", i)),
12578            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12579            ..lsp::CompletionItem::default()
12580        }))
12581        .collect::<Vec<_>>();
12582
12583    let default_commit_characters = vec!["?".to_string()];
12584    let default_data = json!({ "default": "data"});
12585    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12586    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12587    let default_edit_range = lsp::Range {
12588        start: lsp::Position {
12589            line: 0,
12590            character: 5,
12591        },
12592        end: lsp::Position {
12593            line: 0,
12594            character: 5,
12595        },
12596    };
12597
12598    let mut cx = EditorLspTestContext::new_rust(
12599        lsp::ServerCapabilities {
12600            completion_provider: Some(lsp::CompletionOptions {
12601                trigger_characters: Some(vec![".".to_string()]),
12602                resolve_provider: Some(true),
12603                ..Default::default()
12604            }),
12605            ..Default::default()
12606        },
12607        cx,
12608    )
12609    .await;
12610
12611    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12612    cx.simulate_keystroke(".");
12613
12614    let completion_data = default_data.clone();
12615    let completion_characters = default_commit_characters.clone();
12616    let completion_items = items.clone();
12617    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12618        let default_data = completion_data.clone();
12619        let default_commit_characters = completion_characters.clone();
12620        let items = completion_items.clone();
12621        async move {
12622            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12623                items,
12624                item_defaults: Some(lsp::CompletionListItemDefaults {
12625                    data: Some(default_data.clone()),
12626                    commit_characters: Some(default_commit_characters.clone()),
12627                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12628                        default_edit_range,
12629                    )),
12630                    insert_text_format: Some(default_insert_text_format),
12631                    insert_text_mode: Some(default_insert_text_mode),
12632                }),
12633                ..lsp::CompletionList::default()
12634            })))
12635        }
12636    })
12637    .next()
12638    .await;
12639
12640    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12641    cx.lsp
12642        .server
12643        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12644            let closure_resolved_items = resolved_items.clone();
12645            move |item_to_resolve, _| {
12646                let closure_resolved_items = closure_resolved_items.clone();
12647                async move {
12648                    closure_resolved_items.lock().push(item_to_resolve.clone());
12649                    Ok(item_to_resolve)
12650                }
12651            }
12652        })
12653        .detach();
12654
12655    cx.condition(|editor, _| editor.context_menu_visible())
12656        .await;
12657    cx.run_until_parked();
12658    cx.update_editor(|editor, _, _| {
12659        let menu = editor.context_menu.borrow_mut();
12660        match menu.as_ref().expect("should have the completions menu") {
12661            CodeContextMenu::Completions(completions_menu) => {
12662                assert_eq!(
12663                    completions_menu
12664                        .entries
12665                        .borrow()
12666                        .iter()
12667                        .map(|mat| mat.string.clone())
12668                        .collect::<Vec<String>>(),
12669                    items
12670                        .iter()
12671                        .map(|completion| completion.label.clone())
12672                        .collect::<Vec<String>>()
12673                );
12674            }
12675            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12676        }
12677    });
12678    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12679    // with 4 from the end.
12680    assert_eq!(
12681        *resolved_items.lock(),
12682        [&items[0..16], &items[items.len() - 4..items.len()]]
12683            .concat()
12684            .iter()
12685            .cloned()
12686            .map(|mut item| {
12687                if item.data.is_none() {
12688                    item.data = Some(default_data.clone());
12689                }
12690                item
12691            })
12692            .collect::<Vec<lsp::CompletionItem>>(),
12693        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12694    );
12695    resolved_items.lock().clear();
12696
12697    cx.update_editor(|editor, window, cx| {
12698        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12699    });
12700    cx.run_until_parked();
12701    // Completions that have already been resolved are skipped.
12702    assert_eq!(
12703        *resolved_items.lock(),
12704        items[items.len() - 16..items.len() - 4]
12705            .iter()
12706            .cloned()
12707            .map(|mut item| {
12708                if item.data.is_none() {
12709                    item.data = Some(default_data.clone());
12710                }
12711                item
12712            })
12713            .collect::<Vec<lsp::CompletionItem>>()
12714    );
12715    resolved_items.lock().clear();
12716}
12717
12718#[gpui::test]
12719async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12720    init_test(cx, |_| {});
12721
12722    let mut cx = EditorLspTestContext::new(
12723        Language::new(
12724            LanguageConfig {
12725                matcher: LanguageMatcher {
12726                    path_suffixes: vec!["jsx".into()],
12727                    ..Default::default()
12728                },
12729                overrides: [(
12730                    "element".into(),
12731                    LanguageConfigOverride {
12732                        word_characters: Override::Set(['-'].into_iter().collect()),
12733                        ..Default::default()
12734                    },
12735                )]
12736                .into_iter()
12737                .collect(),
12738                ..Default::default()
12739            },
12740            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12741        )
12742        .with_override_query("(jsx_self_closing_element) @element")
12743        .unwrap(),
12744        lsp::ServerCapabilities {
12745            completion_provider: Some(lsp::CompletionOptions {
12746                trigger_characters: Some(vec![":".to_string()]),
12747                ..Default::default()
12748            }),
12749            ..Default::default()
12750        },
12751        cx,
12752    )
12753    .await;
12754
12755    cx.lsp
12756        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12757            Ok(Some(lsp::CompletionResponse::Array(vec![
12758                lsp::CompletionItem {
12759                    label: "bg-blue".into(),
12760                    ..Default::default()
12761                },
12762                lsp::CompletionItem {
12763                    label: "bg-red".into(),
12764                    ..Default::default()
12765                },
12766                lsp::CompletionItem {
12767                    label: "bg-yellow".into(),
12768                    ..Default::default()
12769                },
12770            ])))
12771        });
12772
12773    cx.set_state(r#"<p class="bgˇ" />"#);
12774
12775    // Trigger completion when typing a dash, because the dash is an extra
12776    // word character in the 'element' scope, which contains the cursor.
12777    cx.simulate_keystroke("-");
12778    cx.executor().run_until_parked();
12779    cx.update_editor(|editor, _, _| {
12780        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12781        {
12782            assert_eq!(
12783                completion_menu_entries(&menu),
12784                &["bg-red", "bg-blue", "bg-yellow"]
12785            );
12786        } else {
12787            panic!("expected completion menu to be open");
12788        }
12789    });
12790
12791    cx.simulate_keystroke("l");
12792    cx.executor().run_until_parked();
12793    cx.update_editor(|editor, _, _| {
12794        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12795        {
12796            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12797        } else {
12798            panic!("expected completion menu to be open");
12799        }
12800    });
12801
12802    // When filtering completions, consider the character after the '-' to
12803    // be the start of a subword.
12804    cx.set_state(r#"<p class="yelˇ" />"#);
12805    cx.simulate_keystroke("l");
12806    cx.executor().run_until_parked();
12807    cx.update_editor(|editor, _, _| {
12808        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12809        {
12810            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12811        } else {
12812            panic!("expected completion menu to be open");
12813        }
12814    });
12815}
12816
12817fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12818    let entries = menu.entries.borrow();
12819    entries.iter().map(|mat| mat.string.clone()).collect()
12820}
12821
12822#[gpui::test]
12823async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12824    init_test(cx, |settings| {
12825        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12826            FormatterList(vec![Formatter::Prettier].into()),
12827        ))
12828    });
12829
12830    let fs = FakeFs::new(cx.executor());
12831    fs.insert_file(path!("/file.ts"), Default::default()).await;
12832
12833    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12834    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12835
12836    language_registry.add(Arc::new(Language::new(
12837        LanguageConfig {
12838            name: "TypeScript".into(),
12839            matcher: LanguageMatcher {
12840                path_suffixes: vec!["ts".to_string()],
12841                ..Default::default()
12842            },
12843            ..Default::default()
12844        },
12845        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12846    )));
12847    update_test_language_settings(cx, |settings| {
12848        settings.defaults.prettier = Some(PrettierSettings {
12849            allowed: true,
12850            ..PrettierSettings::default()
12851        });
12852    });
12853
12854    let test_plugin = "test_plugin";
12855    let _ = language_registry.register_fake_lsp(
12856        "TypeScript",
12857        FakeLspAdapter {
12858            prettier_plugins: vec![test_plugin],
12859            ..Default::default()
12860        },
12861    );
12862
12863    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12864    let buffer = project
12865        .update(cx, |project, cx| {
12866            project.open_local_buffer(path!("/file.ts"), cx)
12867        })
12868        .await
12869        .unwrap();
12870
12871    let buffer_text = "one\ntwo\nthree\n";
12872    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12873    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12874    editor.update_in(cx, |editor, window, cx| {
12875        editor.set_text(buffer_text, window, cx)
12876    });
12877
12878    editor
12879        .update_in(cx, |editor, window, cx| {
12880            editor.perform_format(
12881                project.clone(),
12882                FormatTrigger::Manual,
12883                FormatTarget::Buffers,
12884                window,
12885                cx,
12886            )
12887        })
12888        .unwrap()
12889        .await;
12890    assert_eq!(
12891        editor.update(cx, |editor, cx| editor.text(cx)),
12892        buffer_text.to_string() + prettier_format_suffix,
12893        "Test prettier formatting was not applied to the original buffer text",
12894    );
12895
12896    update_test_language_settings(cx, |settings| {
12897        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12898    });
12899    let format = editor.update_in(cx, |editor, window, cx| {
12900        editor.perform_format(
12901            project.clone(),
12902            FormatTrigger::Manual,
12903            FormatTarget::Buffers,
12904            window,
12905            cx,
12906        )
12907    });
12908    format.await.unwrap();
12909    assert_eq!(
12910        editor.update(cx, |editor, cx| editor.text(cx)),
12911        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12912        "Autoformatting (via test prettier) was not applied to the original buffer text",
12913    );
12914}
12915
12916#[gpui::test]
12917async fn test_addition_reverts(cx: &mut TestAppContext) {
12918    init_test(cx, |_| {});
12919    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12920    let base_text = indoc! {r#"
12921        struct Row;
12922        struct Row1;
12923        struct Row2;
12924
12925        struct Row4;
12926        struct Row5;
12927        struct Row6;
12928
12929        struct Row8;
12930        struct Row9;
12931        struct Row10;"#};
12932
12933    // When addition hunks are not adjacent to carets, no hunk revert is performed
12934    assert_hunk_revert(
12935        indoc! {r#"struct Row;
12936                   struct Row1;
12937                   struct Row1.1;
12938                   struct Row1.2;
12939                   struct Row2;ˇ
12940
12941                   struct Row4;
12942                   struct Row5;
12943                   struct Row6;
12944
12945                   struct Row8;
12946                   ˇstruct Row9;
12947                   struct Row9.1;
12948                   struct Row9.2;
12949                   struct Row9.3;
12950                   struct Row10;"#},
12951        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12952        indoc! {r#"struct Row;
12953                   struct Row1;
12954                   struct Row1.1;
12955                   struct Row1.2;
12956                   struct Row2;ˇ
12957
12958                   struct Row4;
12959                   struct Row5;
12960                   struct Row6;
12961
12962                   struct Row8;
12963                   ˇstruct Row9;
12964                   struct Row9.1;
12965                   struct Row9.2;
12966                   struct Row9.3;
12967                   struct Row10;"#},
12968        base_text,
12969        &mut cx,
12970    );
12971    // Same for selections
12972    assert_hunk_revert(
12973        indoc! {r#"struct Row;
12974                   struct Row1;
12975                   struct Row2;
12976                   struct Row2.1;
12977                   struct Row2.2;
12978                   «ˇ
12979                   struct Row4;
12980                   struct» Row5;
12981                   «struct Row6;
12982                   ˇ»
12983                   struct Row9.1;
12984                   struct Row9.2;
12985                   struct Row9.3;
12986                   struct Row8;
12987                   struct Row9;
12988                   struct Row10;"#},
12989        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12990        indoc! {r#"struct Row;
12991                   struct Row1;
12992                   struct Row2;
12993                   struct Row2.1;
12994                   struct Row2.2;
12995                   «ˇ
12996                   struct Row4;
12997                   struct» Row5;
12998                   «struct Row6;
12999                   ˇ»
13000                   struct Row9.1;
13001                   struct Row9.2;
13002                   struct Row9.3;
13003                   struct Row8;
13004                   struct Row9;
13005                   struct Row10;"#},
13006        base_text,
13007        &mut cx,
13008    );
13009
13010    // When carets and selections intersect the addition hunks, those are reverted.
13011    // Adjacent carets got merged.
13012    assert_hunk_revert(
13013        indoc! {r#"struct Row;
13014                   ˇ// something on the top
13015                   struct Row1;
13016                   struct Row2;
13017                   struct Roˇw3.1;
13018                   struct Row2.2;
13019                   struct Row2.3;ˇ
13020
13021                   struct Row4;
13022                   struct ˇRow5.1;
13023                   struct Row5.2;
13024                   struct «Rowˇ»5.3;
13025                   struct Row5;
13026                   struct Row6;
13027                   ˇ
13028                   struct Row9.1;
13029                   struct «Rowˇ»9.2;
13030                   struct «ˇRow»9.3;
13031                   struct Row8;
13032                   struct Row9;
13033                   «ˇ// something on bottom»
13034                   struct Row10;"#},
13035        vec![
13036            DiffHunkStatusKind::Added,
13037            DiffHunkStatusKind::Added,
13038            DiffHunkStatusKind::Added,
13039            DiffHunkStatusKind::Added,
13040            DiffHunkStatusKind::Added,
13041        ],
13042        indoc! {r#"struct Row;
13043                   ˇstruct Row1;
13044                   struct Row2;
13045                   ˇ
13046                   struct Row4;
13047                   ˇstruct Row5;
13048                   struct Row6;
13049                   ˇ
13050                   ˇstruct Row8;
13051                   struct Row9;
13052                   ˇstruct Row10;"#},
13053        base_text,
13054        &mut cx,
13055    );
13056}
13057
13058#[gpui::test]
13059async fn test_modification_reverts(cx: &mut TestAppContext) {
13060    init_test(cx, |_| {});
13061    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13062    let base_text = indoc! {r#"
13063        struct Row;
13064        struct Row1;
13065        struct Row2;
13066
13067        struct Row4;
13068        struct Row5;
13069        struct Row6;
13070
13071        struct Row8;
13072        struct Row9;
13073        struct Row10;"#};
13074
13075    // Modification hunks behave the same as the addition ones.
13076    assert_hunk_revert(
13077        indoc! {r#"struct Row;
13078                   struct Row1;
13079                   struct Row33;
13080                   ˇ
13081                   struct Row4;
13082                   struct Row5;
13083                   struct Row6;
13084                   ˇ
13085                   struct Row99;
13086                   struct Row9;
13087                   struct Row10;"#},
13088        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13089        indoc! {r#"struct Row;
13090                   struct Row1;
13091                   struct Row33;
13092                   ˇ
13093                   struct Row4;
13094                   struct Row5;
13095                   struct Row6;
13096                   ˇ
13097                   struct Row99;
13098                   struct Row9;
13099                   struct Row10;"#},
13100        base_text,
13101        &mut cx,
13102    );
13103    assert_hunk_revert(
13104        indoc! {r#"struct Row;
13105                   struct Row1;
13106                   struct Row33;
13107                   «ˇ
13108                   struct Row4;
13109                   struct» Row5;
13110                   «struct Row6;
13111                   ˇ»
13112                   struct Row99;
13113                   struct Row9;
13114                   struct Row10;"#},
13115        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13116        indoc! {r#"struct Row;
13117                   struct Row1;
13118                   struct Row33;
13119                   «ˇ
13120                   struct Row4;
13121                   struct» Row5;
13122                   «struct Row6;
13123                   ˇ»
13124                   struct Row99;
13125                   struct Row9;
13126                   struct Row10;"#},
13127        base_text,
13128        &mut cx,
13129    );
13130
13131    assert_hunk_revert(
13132        indoc! {r#"ˇstruct Row1.1;
13133                   struct Row1;
13134                   «ˇstr»uct Row22;
13135
13136                   struct ˇRow44;
13137                   struct Row5;
13138                   struct «Rˇ»ow66;ˇ
13139
13140                   «struˇ»ct Row88;
13141                   struct Row9;
13142                   struct Row1011;ˇ"#},
13143        vec![
13144            DiffHunkStatusKind::Modified,
13145            DiffHunkStatusKind::Modified,
13146            DiffHunkStatusKind::Modified,
13147            DiffHunkStatusKind::Modified,
13148            DiffHunkStatusKind::Modified,
13149            DiffHunkStatusKind::Modified,
13150        ],
13151        indoc! {r#"struct Row;
13152                   ˇstruct Row1;
13153                   struct Row2;
13154                   ˇ
13155                   struct Row4;
13156                   ˇstruct Row5;
13157                   struct Row6;
13158                   ˇ
13159                   struct Row8;
13160                   ˇstruct Row9;
13161                   struct Row10;ˇ"#},
13162        base_text,
13163        &mut cx,
13164    );
13165}
13166
13167#[gpui::test]
13168async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13169    init_test(cx, |_| {});
13170    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13171    let base_text = indoc! {r#"
13172        one
13173
13174        two
13175        three
13176        "#};
13177
13178    cx.set_head_text(base_text);
13179    cx.set_state("\nˇ\n");
13180    cx.executor().run_until_parked();
13181    cx.update_editor(|editor, _window, cx| {
13182        editor.expand_selected_diff_hunks(cx);
13183    });
13184    cx.executor().run_until_parked();
13185    cx.update_editor(|editor, window, cx| {
13186        editor.backspace(&Default::default(), window, cx);
13187    });
13188    cx.run_until_parked();
13189    cx.assert_state_with_diff(
13190        indoc! {r#"
13191
13192        - two
13193        - threeˇ
13194        +
13195        "#}
13196        .to_string(),
13197    );
13198}
13199
13200#[gpui::test]
13201async fn test_deletion_reverts(cx: &mut TestAppContext) {
13202    init_test(cx, |_| {});
13203    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13204    let base_text = indoc! {r#"struct Row;
13205struct Row1;
13206struct Row2;
13207
13208struct Row4;
13209struct Row5;
13210struct Row6;
13211
13212struct Row8;
13213struct Row9;
13214struct Row10;"#};
13215
13216    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13217    assert_hunk_revert(
13218        indoc! {r#"struct Row;
13219                   struct Row2;
13220
13221                   ˇstruct Row4;
13222                   struct Row5;
13223                   struct Row6;
13224                   ˇ
13225                   struct Row8;
13226                   struct Row10;"#},
13227        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13228        indoc! {r#"struct Row;
13229                   struct Row2;
13230
13231                   ˇstruct Row4;
13232                   struct Row5;
13233                   struct Row6;
13234                   ˇ
13235                   struct Row8;
13236                   struct Row10;"#},
13237        base_text,
13238        &mut cx,
13239    );
13240    assert_hunk_revert(
13241        indoc! {r#"struct Row;
13242                   struct Row2;
13243
13244                   «ˇstruct Row4;
13245                   struct» Row5;
13246                   «struct Row6;
13247                   ˇ»
13248                   struct Row8;
13249                   struct Row10;"#},
13250        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13251        indoc! {r#"struct Row;
13252                   struct Row2;
13253
13254                   «ˇstruct Row4;
13255                   struct» Row5;
13256                   «struct Row6;
13257                   ˇ»
13258                   struct Row8;
13259                   struct Row10;"#},
13260        base_text,
13261        &mut cx,
13262    );
13263
13264    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13265    assert_hunk_revert(
13266        indoc! {r#"struct Row;
13267                   ˇstruct Row2;
13268
13269                   struct Row4;
13270                   struct Row5;
13271                   struct Row6;
13272
13273                   struct Row8;ˇ
13274                   struct Row10;"#},
13275        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13276        indoc! {r#"struct Row;
13277                   struct Row1;
13278                   ˇstruct Row2;
13279
13280                   struct Row4;
13281                   struct Row5;
13282                   struct Row6;
13283
13284                   struct Row8;ˇ
13285                   struct Row9;
13286                   struct Row10;"#},
13287        base_text,
13288        &mut cx,
13289    );
13290    assert_hunk_revert(
13291        indoc! {r#"struct Row;
13292                   struct Row2«ˇ;
13293                   struct Row4;
13294                   struct» Row5;
13295                   «struct Row6;
13296
13297                   struct Row8;ˇ»
13298                   struct Row10;"#},
13299        vec![
13300            DiffHunkStatusKind::Deleted,
13301            DiffHunkStatusKind::Deleted,
13302            DiffHunkStatusKind::Deleted,
13303        ],
13304        indoc! {r#"struct Row;
13305                   struct Row1;
13306                   struct Row2«ˇ;
13307
13308                   struct Row4;
13309                   struct» Row5;
13310                   «struct Row6;
13311
13312                   struct Row8;ˇ»
13313                   struct Row9;
13314                   struct Row10;"#},
13315        base_text,
13316        &mut cx,
13317    );
13318}
13319
13320#[gpui::test]
13321async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13322    init_test(cx, |_| {});
13323
13324    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13325    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13326    let base_text_3 =
13327        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13328
13329    let text_1 = edit_first_char_of_every_line(base_text_1);
13330    let text_2 = edit_first_char_of_every_line(base_text_2);
13331    let text_3 = edit_first_char_of_every_line(base_text_3);
13332
13333    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13334    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13335    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13336
13337    let multibuffer = cx.new(|cx| {
13338        let mut multibuffer = MultiBuffer::new(ReadWrite);
13339        multibuffer.push_excerpts(
13340            buffer_1.clone(),
13341            [
13342                ExcerptRange {
13343                    context: Point::new(0, 0)..Point::new(3, 0),
13344                    primary: None,
13345                },
13346                ExcerptRange {
13347                    context: Point::new(5, 0)..Point::new(7, 0),
13348                    primary: None,
13349                },
13350                ExcerptRange {
13351                    context: Point::new(9, 0)..Point::new(10, 4),
13352                    primary: None,
13353                },
13354            ],
13355            cx,
13356        );
13357        multibuffer.push_excerpts(
13358            buffer_2.clone(),
13359            [
13360                ExcerptRange {
13361                    context: Point::new(0, 0)..Point::new(3, 0),
13362                    primary: None,
13363                },
13364                ExcerptRange {
13365                    context: Point::new(5, 0)..Point::new(7, 0),
13366                    primary: None,
13367                },
13368                ExcerptRange {
13369                    context: Point::new(9, 0)..Point::new(10, 4),
13370                    primary: None,
13371                },
13372            ],
13373            cx,
13374        );
13375        multibuffer.push_excerpts(
13376            buffer_3.clone(),
13377            [
13378                ExcerptRange {
13379                    context: Point::new(0, 0)..Point::new(3, 0),
13380                    primary: None,
13381                },
13382                ExcerptRange {
13383                    context: Point::new(5, 0)..Point::new(7, 0),
13384                    primary: None,
13385                },
13386                ExcerptRange {
13387                    context: Point::new(9, 0)..Point::new(10, 4),
13388                    primary: None,
13389                },
13390            ],
13391            cx,
13392        );
13393        multibuffer
13394    });
13395
13396    let fs = FakeFs::new(cx.executor());
13397    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13398    let (editor, cx) = cx
13399        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13400    editor.update_in(cx, |editor, _window, cx| {
13401        for (buffer, diff_base) in [
13402            (buffer_1.clone(), base_text_1),
13403            (buffer_2.clone(), base_text_2),
13404            (buffer_3.clone(), base_text_3),
13405        ] {
13406            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13407            editor
13408                .buffer
13409                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13410        }
13411    });
13412    cx.executor().run_until_parked();
13413
13414    editor.update_in(cx, |editor, window, cx| {
13415        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}");
13416        editor.select_all(&SelectAll, window, cx);
13417        editor.git_restore(&Default::default(), window, cx);
13418    });
13419    cx.executor().run_until_parked();
13420
13421    // When all ranges are selected, all buffer hunks are reverted.
13422    editor.update(cx, |editor, cx| {
13423        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");
13424    });
13425    buffer_1.update(cx, |buffer, _| {
13426        assert_eq!(buffer.text(), base_text_1);
13427    });
13428    buffer_2.update(cx, |buffer, _| {
13429        assert_eq!(buffer.text(), base_text_2);
13430    });
13431    buffer_3.update(cx, |buffer, _| {
13432        assert_eq!(buffer.text(), base_text_3);
13433    });
13434
13435    editor.update_in(cx, |editor, window, cx| {
13436        editor.undo(&Default::default(), window, cx);
13437    });
13438
13439    editor.update_in(cx, |editor, window, cx| {
13440        editor.change_selections(None, window, cx, |s| {
13441            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13442        });
13443        editor.git_restore(&Default::default(), window, cx);
13444    });
13445
13446    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13447    // but not affect buffer_2 and its related excerpts.
13448    editor.update(cx, |editor, cx| {
13449        assert_eq!(
13450            editor.text(cx),
13451            "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}"
13452        );
13453    });
13454    buffer_1.update(cx, |buffer, _| {
13455        assert_eq!(buffer.text(), base_text_1);
13456    });
13457    buffer_2.update(cx, |buffer, _| {
13458        assert_eq!(
13459            buffer.text(),
13460            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13461        );
13462    });
13463    buffer_3.update(cx, |buffer, _| {
13464        assert_eq!(
13465            buffer.text(),
13466            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13467        );
13468    });
13469
13470    fn edit_first_char_of_every_line(text: &str) -> String {
13471        text.split('\n')
13472            .map(|line| format!("X{}", &line[1..]))
13473            .collect::<Vec<_>>()
13474            .join("\n")
13475    }
13476}
13477
13478#[gpui::test]
13479async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13480    init_test(cx, |_| {});
13481
13482    let cols = 4;
13483    let rows = 10;
13484    let sample_text_1 = sample_text(rows, cols, 'a');
13485    assert_eq!(
13486        sample_text_1,
13487        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13488    );
13489    let sample_text_2 = sample_text(rows, cols, 'l');
13490    assert_eq!(
13491        sample_text_2,
13492        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13493    );
13494    let sample_text_3 = sample_text(rows, cols, 'v');
13495    assert_eq!(
13496        sample_text_3,
13497        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13498    );
13499
13500    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13501    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13502    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13503
13504    let multi_buffer = cx.new(|cx| {
13505        let mut multibuffer = MultiBuffer::new(ReadWrite);
13506        multibuffer.push_excerpts(
13507            buffer_1.clone(),
13508            [
13509                ExcerptRange {
13510                    context: Point::new(0, 0)..Point::new(3, 0),
13511                    primary: None,
13512                },
13513                ExcerptRange {
13514                    context: Point::new(5, 0)..Point::new(7, 0),
13515                    primary: None,
13516                },
13517                ExcerptRange {
13518                    context: Point::new(9, 0)..Point::new(10, 4),
13519                    primary: None,
13520                },
13521            ],
13522            cx,
13523        );
13524        multibuffer.push_excerpts(
13525            buffer_2.clone(),
13526            [
13527                ExcerptRange {
13528                    context: Point::new(0, 0)..Point::new(3, 0),
13529                    primary: None,
13530                },
13531                ExcerptRange {
13532                    context: Point::new(5, 0)..Point::new(7, 0),
13533                    primary: None,
13534                },
13535                ExcerptRange {
13536                    context: Point::new(9, 0)..Point::new(10, 4),
13537                    primary: None,
13538                },
13539            ],
13540            cx,
13541        );
13542        multibuffer.push_excerpts(
13543            buffer_3.clone(),
13544            [
13545                ExcerptRange {
13546                    context: Point::new(0, 0)..Point::new(3, 0),
13547                    primary: None,
13548                },
13549                ExcerptRange {
13550                    context: Point::new(5, 0)..Point::new(7, 0),
13551                    primary: None,
13552                },
13553                ExcerptRange {
13554                    context: Point::new(9, 0)..Point::new(10, 4),
13555                    primary: None,
13556                },
13557            ],
13558            cx,
13559        );
13560        multibuffer
13561    });
13562
13563    let fs = FakeFs::new(cx.executor());
13564    fs.insert_tree(
13565        "/a",
13566        json!({
13567            "main.rs": sample_text_1,
13568            "other.rs": sample_text_2,
13569            "lib.rs": sample_text_3,
13570        }),
13571    )
13572    .await;
13573    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13574    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13575    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13576    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13577        Editor::new(
13578            EditorMode::Full,
13579            multi_buffer,
13580            Some(project.clone()),
13581            window,
13582            cx,
13583        )
13584    });
13585    let multibuffer_item_id = workspace
13586        .update(cx, |workspace, window, cx| {
13587            assert!(
13588                workspace.active_item(cx).is_none(),
13589                "active item should be None before the first item is added"
13590            );
13591            workspace.add_item_to_active_pane(
13592                Box::new(multi_buffer_editor.clone()),
13593                None,
13594                true,
13595                window,
13596                cx,
13597            );
13598            let active_item = workspace
13599                .active_item(cx)
13600                .expect("should have an active item after adding the multi buffer");
13601            assert!(
13602                !active_item.is_singleton(cx),
13603                "A multi buffer was expected to active after adding"
13604            );
13605            active_item.item_id()
13606        })
13607        .unwrap();
13608    cx.executor().run_until_parked();
13609
13610    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13611        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13612            s.select_ranges(Some(1..2))
13613        });
13614        editor.open_excerpts(&OpenExcerpts, window, cx);
13615    });
13616    cx.executor().run_until_parked();
13617    let first_item_id = workspace
13618        .update(cx, |workspace, window, cx| {
13619            let active_item = workspace
13620                .active_item(cx)
13621                .expect("should have an active item after navigating into the 1st buffer");
13622            let first_item_id = active_item.item_id();
13623            assert_ne!(
13624                first_item_id, multibuffer_item_id,
13625                "Should navigate into the 1st buffer and activate it"
13626            );
13627            assert!(
13628                active_item.is_singleton(cx),
13629                "New active item should be a singleton buffer"
13630            );
13631            assert_eq!(
13632                active_item
13633                    .act_as::<Editor>(cx)
13634                    .expect("should have navigated into an editor for the 1st buffer")
13635                    .read(cx)
13636                    .text(cx),
13637                sample_text_1
13638            );
13639
13640            workspace
13641                .go_back(workspace.active_pane().downgrade(), window, cx)
13642                .detach_and_log_err(cx);
13643
13644            first_item_id
13645        })
13646        .unwrap();
13647    cx.executor().run_until_parked();
13648    workspace
13649        .update(cx, |workspace, _, cx| {
13650            let active_item = workspace
13651                .active_item(cx)
13652                .expect("should have an active item after navigating back");
13653            assert_eq!(
13654                active_item.item_id(),
13655                multibuffer_item_id,
13656                "Should navigate back to the multi buffer"
13657            );
13658            assert!(!active_item.is_singleton(cx));
13659        })
13660        .unwrap();
13661
13662    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13663        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13664            s.select_ranges(Some(39..40))
13665        });
13666        editor.open_excerpts(&OpenExcerpts, window, cx);
13667    });
13668    cx.executor().run_until_parked();
13669    let second_item_id = workspace
13670        .update(cx, |workspace, window, cx| {
13671            let active_item = workspace
13672                .active_item(cx)
13673                .expect("should have an active item after navigating into the 2nd buffer");
13674            let second_item_id = active_item.item_id();
13675            assert_ne!(
13676                second_item_id, multibuffer_item_id,
13677                "Should navigate away from the multibuffer"
13678            );
13679            assert_ne!(
13680                second_item_id, first_item_id,
13681                "Should navigate into the 2nd buffer and activate it"
13682            );
13683            assert!(
13684                active_item.is_singleton(cx),
13685                "New active item should be a singleton buffer"
13686            );
13687            assert_eq!(
13688                active_item
13689                    .act_as::<Editor>(cx)
13690                    .expect("should have navigated into an editor")
13691                    .read(cx)
13692                    .text(cx),
13693                sample_text_2
13694            );
13695
13696            workspace
13697                .go_back(workspace.active_pane().downgrade(), window, cx)
13698                .detach_and_log_err(cx);
13699
13700            second_item_id
13701        })
13702        .unwrap();
13703    cx.executor().run_until_parked();
13704    workspace
13705        .update(cx, |workspace, _, cx| {
13706            let active_item = workspace
13707                .active_item(cx)
13708                .expect("should have an active item after navigating back from the 2nd buffer");
13709            assert_eq!(
13710                active_item.item_id(),
13711                multibuffer_item_id,
13712                "Should navigate back from the 2nd buffer to the multi buffer"
13713            );
13714            assert!(!active_item.is_singleton(cx));
13715        })
13716        .unwrap();
13717
13718    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13719        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13720            s.select_ranges(Some(70..70))
13721        });
13722        editor.open_excerpts(&OpenExcerpts, window, cx);
13723    });
13724    cx.executor().run_until_parked();
13725    workspace
13726        .update(cx, |workspace, window, cx| {
13727            let active_item = workspace
13728                .active_item(cx)
13729                .expect("should have an active item after navigating into the 3rd buffer");
13730            let third_item_id = active_item.item_id();
13731            assert_ne!(
13732                third_item_id, multibuffer_item_id,
13733                "Should navigate into the 3rd buffer and activate it"
13734            );
13735            assert_ne!(third_item_id, first_item_id);
13736            assert_ne!(third_item_id, second_item_id);
13737            assert!(
13738                active_item.is_singleton(cx),
13739                "New active item should be a singleton buffer"
13740            );
13741            assert_eq!(
13742                active_item
13743                    .act_as::<Editor>(cx)
13744                    .expect("should have navigated into an editor")
13745                    .read(cx)
13746                    .text(cx),
13747                sample_text_3
13748            );
13749
13750            workspace
13751                .go_back(workspace.active_pane().downgrade(), window, cx)
13752                .detach_and_log_err(cx);
13753        })
13754        .unwrap();
13755    cx.executor().run_until_parked();
13756    workspace
13757        .update(cx, |workspace, _, cx| {
13758            let active_item = workspace
13759                .active_item(cx)
13760                .expect("should have an active item after navigating back from the 3rd buffer");
13761            assert_eq!(
13762                active_item.item_id(),
13763                multibuffer_item_id,
13764                "Should navigate back from the 3rd buffer to the multi buffer"
13765            );
13766            assert!(!active_item.is_singleton(cx));
13767        })
13768        .unwrap();
13769}
13770
13771#[gpui::test]
13772async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13773    init_test(cx, |_| {});
13774
13775    let mut cx = EditorTestContext::new(cx).await;
13776
13777    let diff_base = r#"
13778        use some::mod;
13779
13780        const A: u32 = 42;
13781
13782        fn main() {
13783            println!("hello");
13784
13785            println!("world");
13786        }
13787        "#
13788    .unindent();
13789
13790    cx.set_state(
13791        &r#"
13792        use some::modified;
13793
13794        ˇ
13795        fn main() {
13796            println!("hello there");
13797
13798            println!("around the");
13799            println!("world");
13800        }
13801        "#
13802        .unindent(),
13803    );
13804
13805    cx.set_head_text(&diff_base);
13806    executor.run_until_parked();
13807
13808    cx.update_editor(|editor, window, cx| {
13809        editor.go_to_next_hunk(&GoToHunk, window, cx);
13810        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13811    });
13812    executor.run_until_parked();
13813    cx.assert_state_with_diff(
13814        r#"
13815          use some::modified;
13816
13817
13818          fn main() {
13819        -     println!("hello");
13820        + ˇ    println!("hello there");
13821
13822              println!("around the");
13823              println!("world");
13824          }
13825        "#
13826        .unindent(),
13827    );
13828
13829    cx.update_editor(|editor, window, cx| {
13830        for _ in 0..2 {
13831            editor.go_to_next_hunk(&GoToHunk, window, cx);
13832            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13833        }
13834    });
13835    executor.run_until_parked();
13836    cx.assert_state_with_diff(
13837        r#"
13838        - use some::mod;
13839        + ˇuse some::modified;
13840
13841
13842          fn main() {
13843        -     println!("hello");
13844        +     println!("hello there");
13845
13846        +     println!("around the");
13847              println!("world");
13848          }
13849        "#
13850        .unindent(),
13851    );
13852
13853    cx.update_editor(|editor, window, cx| {
13854        editor.go_to_next_hunk(&GoToHunk, window, cx);
13855        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13856    });
13857    executor.run_until_parked();
13858    cx.assert_state_with_diff(
13859        r#"
13860        - use some::mod;
13861        + use some::modified;
13862
13863        - const A: u32 = 42;
13864          ˇ
13865          fn main() {
13866        -     println!("hello");
13867        +     println!("hello there");
13868
13869        +     println!("around the");
13870              println!("world");
13871          }
13872        "#
13873        .unindent(),
13874    );
13875
13876    cx.update_editor(|editor, window, cx| {
13877        editor.cancel(&Cancel, window, cx);
13878    });
13879
13880    cx.assert_state_with_diff(
13881        r#"
13882          use some::modified;
13883
13884          ˇ
13885          fn main() {
13886              println!("hello there");
13887
13888              println!("around the");
13889              println!("world");
13890          }
13891        "#
13892        .unindent(),
13893    );
13894}
13895
13896#[gpui::test]
13897async fn test_diff_base_change_with_expanded_diff_hunks(
13898    executor: BackgroundExecutor,
13899    cx: &mut TestAppContext,
13900) {
13901    init_test(cx, |_| {});
13902
13903    let mut cx = EditorTestContext::new(cx).await;
13904
13905    let diff_base = r#"
13906        use some::mod1;
13907        use some::mod2;
13908
13909        const A: u32 = 42;
13910        const B: u32 = 42;
13911        const C: u32 = 42;
13912
13913        fn main() {
13914            println!("hello");
13915
13916            println!("world");
13917        }
13918        "#
13919    .unindent();
13920
13921    cx.set_state(
13922        &r#"
13923        use some::mod2;
13924
13925        const A: u32 = 42;
13926        const C: u32 = 42;
13927
13928        fn main(ˇ) {
13929            //println!("hello");
13930
13931            println!("world");
13932            //
13933            //
13934        }
13935        "#
13936        .unindent(),
13937    );
13938
13939    cx.set_head_text(&diff_base);
13940    executor.run_until_parked();
13941
13942    cx.update_editor(|editor, window, cx| {
13943        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13944    });
13945    executor.run_until_parked();
13946    cx.assert_state_with_diff(
13947        r#"
13948        - use some::mod1;
13949          use some::mod2;
13950
13951          const A: u32 = 42;
13952        - const B: u32 = 42;
13953          const C: u32 = 42;
13954
13955          fn main(ˇ) {
13956        -     println!("hello");
13957        +     //println!("hello");
13958
13959              println!("world");
13960        +     //
13961        +     //
13962          }
13963        "#
13964        .unindent(),
13965    );
13966
13967    cx.set_head_text("new diff base!");
13968    executor.run_until_parked();
13969    cx.assert_state_with_diff(
13970        r#"
13971        - new diff base!
13972        + use some::mod2;
13973        +
13974        + const A: u32 = 42;
13975        + const C: u32 = 42;
13976        +
13977        + fn main(ˇ) {
13978        +     //println!("hello");
13979        +
13980        +     println!("world");
13981        +     //
13982        +     //
13983        + }
13984        "#
13985        .unindent(),
13986    );
13987}
13988
13989#[gpui::test]
13990async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13991    init_test(cx, |_| {});
13992
13993    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13994    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13995    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13996    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13997    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13998    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13999
14000    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14001    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14002    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14003
14004    let multi_buffer = cx.new(|cx| {
14005        let mut multibuffer = MultiBuffer::new(ReadWrite);
14006        multibuffer.push_excerpts(
14007            buffer_1.clone(),
14008            [
14009                ExcerptRange {
14010                    context: Point::new(0, 0)..Point::new(3, 0),
14011                    primary: None,
14012                },
14013                ExcerptRange {
14014                    context: Point::new(5, 0)..Point::new(7, 0),
14015                    primary: None,
14016                },
14017                ExcerptRange {
14018                    context: Point::new(9, 0)..Point::new(10, 3),
14019                    primary: None,
14020                },
14021            ],
14022            cx,
14023        );
14024        multibuffer.push_excerpts(
14025            buffer_2.clone(),
14026            [
14027                ExcerptRange {
14028                    context: Point::new(0, 0)..Point::new(3, 0),
14029                    primary: None,
14030                },
14031                ExcerptRange {
14032                    context: Point::new(5, 0)..Point::new(7, 0),
14033                    primary: None,
14034                },
14035                ExcerptRange {
14036                    context: Point::new(9, 0)..Point::new(10, 3),
14037                    primary: None,
14038                },
14039            ],
14040            cx,
14041        );
14042        multibuffer.push_excerpts(
14043            buffer_3.clone(),
14044            [
14045                ExcerptRange {
14046                    context: Point::new(0, 0)..Point::new(3, 0),
14047                    primary: None,
14048                },
14049                ExcerptRange {
14050                    context: Point::new(5, 0)..Point::new(7, 0),
14051                    primary: None,
14052                },
14053                ExcerptRange {
14054                    context: Point::new(9, 0)..Point::new(10, 3),
14055                    primary: None,
14056                },
14057            ],
14058            cx,
14059        );
14060        multibuffer
14061    });
14062
14063    let editor =
14064        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14065    editor
14066        .update(cx, |editor, _window, cx| {
14067            for (buffer, diff_base) in [
14068                (buffer_1.clone(), file_1_old),
14069                (buffer_2.clone(), file_2_old),
14070                (buffer_3.clone(), file_3_old),
14071            ] {
14072                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14073                editor
14074                    .buffer
14075                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14076            }
14077        })
14078        .unwrap();
14079
14080    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14081    cx.run_until_parked();
14082
14083    cx.assert_editor_state(
14084        &"
14085            ˇaaa
14086            ccc
14087            ddd
14088
14089            ggg
14090            hhh
14091
14092
14093            lll
14094            mmm
14095            NNN
14096
14097            qqq
14098            rrr
14099
14100            uuu
14101            111
14102            222
14103            333
14104
14105            666
14106            777
14107
14108            000
14109            !!!"
14110        .unindent(),
14111    );
14112
14113    cx.update_editor(|editor, window, cx| {
14114        editor.select_all(&SelectAll, window, cx);
14115        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14116    });
14117    cx.executor().run_until_parked();
14118
14119    cx.assert_state_with_diff(
14120        "
14121            «aaa
14122          - bbb
14123            ccc
14124            ddd
14125
14126            ggg
14127            hhh
14128
14129
14130            lll
14131            mmm
14132          - nnn
14133          + NNN
14134
14135            qqq
14136            rrr
14137
14138            uuu
14139            111
14140            222
14141            333
14142
14143          + 666
14144            777
14145
14146            000
14147            !!!ˇ»"
14148            .unindent(),
14149    );
14150}
14151
14152#[gpui::test]
14153async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14154    init_test(cx, |_| {});
14155
14156    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14157    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14158
14159    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14160    let multi_buffer = cx.new(|cx| {
14161        let mut multibuffer = MultiBuffer::new(ReadWrite);
14162        multibuffer.push_excerpts(
14163            buffer.clone(),
14164            [
14165                ExcerptRange {
14166                    context: Point::new(0, 0)..Point::new(2, 0),
14167                    primary: None,
14168                },
14169                ExcerptRange {
14170                    context: Point::new(4, 0)..Point::new(7, 0),
14171                    primary: None,
14172                },
14173                ExcerptRange {
14174                    context: Point::new(9, 0)..Point::new(10, 0),
14175                    primary: None,
14176                },
14177            ],
14178            cx,
14179        );
14180        multibuffer
14181    });
14182
14183    let editor =
14184        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14185    editor
14186        .update(cx, |editor, _window, cx| {
14187            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14188            editor
14189                .buffer
14190                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14191        })
14192        .unwrap();
14193
14194    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14195    cx.run_until_parked();
14196
14197    cx.update_editor(|editor, window, cx| {
14198        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14199    });
14200    cx.executor().run_until_parked();
14201
14202    // When the start of a hunk coincides with the start of its excerpt,
14203    // the hunk is expanded. When the start of a a hunk is earlier than
14204    // the start of its excerpt, the hunk is not expanded.
14205    cx.assert_state_with_diff(
14206        "
14207            ˇaaa
14208          - bbb
14209          + BBB
14210
14211          - ddd
14212          - eee
14213          + DDD
14214          + EEE
14215            fff
14216
14217            iii
14218        "
14219        .unindent(),
14220    );
14221}
14222
14223#[gpui::test]
14224async fn test_edits_around_expanded_insertion_hunks(
14225    executor: BackgroundExecutor,
14226    cx: &mut TestAppContext,
14227) {
14228    init_test(cx, |_| {});
14229
14230    let mut cx = EditorTestContext::new(cx).await;
14231
14232    let diff_base = r#"
14233        use some::mod1;
14234        use some::mod2;
14235
14236        const A: u32 = 42;
14237
14238        fn main() {
14239            println!("hello");
14240
14241            println!("world");
14242        }
14243        "#
14244    .unindent();
14245    executor.run_until_parked();
14246    cx.set_state(
14247        &r#"
14248        use some::mod1;
14249        use some::mod2;
14250
14251        const A: u32 = 42;
14252        const B: u32 = 42;
14253        const C: u32 = 42;
14254        ˇ
14255
14256        fn main() {
14257            println!("hello");
14258
14259            println!("world");
14260        }
14261        "#
14262        .unindent(),
14263    );
14264
14265    cx.set_head_text(&diff_base);
14266    executor.run_until_parked();
14267
14268    cx.update_editor(|editor, window, cx| {
14269        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14270    });
14271    executor.run_until_parked();
14272
14273    cx.assert_state_with_diff(
14274        r#"
14275        use some::mod1;
14276        use some::mod2;
14277
14278        const A: u32 = 42;
14279      + const B: u32 = 42;
14280      + const C: u32 = 42;
14281      + ˇ
14282
14283        fn main() {
14284            println!("hello");
14285
14286            println!("world");
14287        }
14288      "#
14289        .unindent(),
14290    );
14291
14292    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14293    executor.run_until_parked();
14294
14295    cx.assert_state_with_diff(
14296        r#"
14297        use some::mod1;
14298        use some::mod2;
14299
14300        const A: u32 = 42;
14301      + const B: u32 = 42;
14302      + const C: u32 = 42;
14303      + const D: u32 = 42;
14304      + ˇ
14305
14306        fn main() {
14307            println!("hello");
14308
14309            println!("world");
14310        }
14311      "#
14312        .unindent(),
14313    );
14314
14315    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14316    executor.run_until_parked();
14317
14318    cx.assert_state_with_diff(
14319        r#"
14320        use some::mod1;
14321        use some::mod2;
14322
14323        const A: u32 = 42;
14324      + const B: u32 = 42;
14325      + const C: u32 = 42;
14326      + const D: u32 = 42;
14327      + const E: u32 = 42;
14328      + ˇ
14329
14330        fn main() {
14331            println!("hello");
14332
14333            println!("world");
14334        }
14335      "#
14336        .unindent(),
14337    );
14338
14339    cx.update_editor(|editor, window, cx| {
14340        editor.delete_line(&DeleteLine, window, cx);
14341    });
14342    executor.run_until_parked();
14343
14344    cx.assert_state_with_diff(
14345        r#"
14346        use some::mod1;
14347        use some::mod2;
14348
14349        const A: u32 = 42;
14350      + const B: u32 = 42;
14351      + const C: u32 = 42;
14352      + const D: u32 = 42;
14353      + const E: u32 = 42;
14354        ˇ
14355        fn main() {
14356            println!("hello");
14357
14358            println!("world");
14359        }
14360      "#
14361        .unindent(),
14362    );
14363
14364    cx.update_editor(|editor, window, cx| {
14365        editor.move_up(&MoveUp, window, cx);
14366        editor.delete_line(&DeleteLine, window, cx);
14367        editor.move_up(&MoveUp, window, cx);
14368        editor.delete_line(&DeleteLine, window, cx);
14369        editor.move_up(&MoveUp, window, cx);
14370        editor.delete_line(&DeleteLine, window, cx);
14371    });
14372    executor.run_until_parked();
14373    cx.assert_state_with_diff(
14374        r#"
14375        use some::mod1;
14376        use some::mod2;
14377
14378        const A: u32 = 42;
14379      + const B: u32 = 42;
14380        ˇ
14381        fn main() {
14382            println!("hello");
14383
14384            println!("world");
14385        }
14386      "#
14387        .unindent(),
14388    );
14389
14390    cx.update_editor(|editor, window, cx| {
14391        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14392        editor.delete_line(&DeleteLine, window, cx);
14393    });
14394    executor.run_until_parked();
14395    cx.assert_state_with_diff(
14396        r#"
14397        ˇ
14398        fn main() {
14399            println!("hello");
14400
14401            println!("world");
14402        }
14403      "#
14404        .unindent(),
14405    );
14406}
14407
14408#[gpui::test]
14409async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14410    init_test(cx, |_| {});
14411
14412    let mut cx = EditorTestContext::new(cx).await;
14413    cx.set_head_text(indoc! { "
14414        one
14415        two
14416        three
14417        four
14418        five
14419        "
14420    });
14421    cx.set_state(indoc! { "
14422        one
14423        ˇthree
14424        five
14425    "});
14426    cx.run_until_parked();
14427    cx.update_editor(|editor, window, cx| {
14428        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14429    });
14430    cx.assert_state_with_diff(
14431        indoc! { "
14432        one
14433      - two
14434        ˇthree
14435      - four
14436        five
14437    "}
14438        .to_string(),
14439    );
14440    cx.update_editor(|editor, window, cx| {
14441        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14442    });
14443
14444    cx.assert_state_with_diff(
14445        indoc! { "
14446        one
14447        ˇthree
14448        five
14449    "}
14450        .to_string(),
14451    );
14452
14453    cx.set_state(indoc! { "
14454        one
14455        ˇTWO
14456        three
14457        four
14458        five
14459    "});
14460    cx.run_until_parked();
14461    cx.update_editor(|editor, window, cx| {
14462        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14463    });
14464
14465    cx.assert_state_with_diff(
14466        indoc! { "
14467            one
14468          - two
14469          + ˇTWO
14470            three
14471            four
14472            five
14473        "}
14474        .to_string(),
14475    );
14476    cx.update_editor(|editor, window, cx| {
14477        editor.move_up(&Default::default(), window, cx);
14478        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14479    });
14480    cx.assert_state_with_diff(
14481        indoc! { "
14482            one
14483            ˇTWO
14484            three
14485            four
14486            five
14487        "}
14488        .to_string(),
14489    );
14490}
14491
14492#[gpui::test]
14493async fn test_edits_around_expanded_deletion_hunks(
14494    executor: BackgroundExecutor,
14495    cx: &mut TestAppContext,
14496) {
14497    init_test(cx, |_| {});
14498
14499    let mut cx = EditorTestContext::new(cx).await;
14500
14501    let diff_base = r#"
14502        use some::mod1;
14503        use some::mod2;
14504
14505        const A: u32 = 42;
14506        const B: u32 = 42;
14507        const C: u32 = 42;
14508
14509
14510        fn main() {
14511            println!("hello");
14512
14513            println!("world");
14514        }
14515    "#
14516    .unindent();
14517    executor.run_until_parked();
14518    cx.set_state(
14519        &r#"
14520        use some::mod1;
14521        use some::mod2;
14522
14523        ˇconst B: u32 = 42;
14524        const C: u32 = 42;
14525
14526
14527        fn main() {
14528            println!("hello");
14529
14530            println!("world");
14531        }
14532        "#
14533        .unindent(),
14534    );
14535
14536    cx.set_head_text(&diff_base);
14537    executor.run_until_parked();
14538
14539    cx.update_editor(|editor, window, cx| {
14540        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14541    });
14542    executor.run_until_parked();
14543
14544    cx.assert_state_with_diff(
14545        r#"
14546        use some::mod1;
14547        use some::mod2;
14548
14549      - const A: u32 = 42;
14550        ˇconst B: u32 = 42;
14551        const C: u32 = 42;
14552
14553
14554        fn main() {
14555            println!("hello");
14556
14557            println!("world");
14558        }
14559      "#
14560        .unindent(),
14561    );
14562
14563    cx.update_editor(|editor, window, cx| {
14564        editor.delete_line(&DeleteLine, window, cx);
14565    });
14566    executor.run_until_parked();
14567    cx.assert_state_with_diff(
14568        r#"
14569        use some::mod1;
14570        use some::mod2;
14571
14572      - const A: u32 = 42;
14573      - const B: u32 = 42;
14574        ˇconst C: u32 = 42;
14575
14576
14577        fn main() {
14578            println!("hello");
14579
14580            println!("world");
14581        }
14582      "#
14583        .unindent(),
14584    );
14585
14586    cx.update_editor(|editor, window, cx| {
14587        editor.delete_line(&DeleteLine, window, cx);
14588    });
14589    executor.run_until_parked();
14590    cx.assert_state_with_diff(
14591        r#"
14592        use some::mod1;
14593        use some::mod2;
14594
14595      - const A: u32 = 42;
14596      - const B: u32 = 42;
14597      - const C: u32 = 42;
14598        ˇ
14599
14600        fn main() {
14601            println!("hello");
14602
14603            println!("world");
14604        }
14605      "#
14606        .unindent(),
14607    );
14608
14609    cx.update_editor(|editor, window, cx| {
14610        editor.handle_input("replacement", window, cx);
14611    });
14612    executor.run_until_parked();
14613    cx.assert_state_with_diff(
14614        r#"
14615        use some::mod1;
14616        use some::mod2;
14617
14618      - const A: u32 = 42;
14619      - const B: u32 = 42;
14620      - const C: u32 = 42;
14621      -
14622      + replacementˇ
14623
14624        fn main() {
14625            println!("hello");
14626
14627            println!("world");
14628        }
14629      "#
14630        .unindent(),
14631    );
14632}
14633
14634#[gpui::test]
14635async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14636    init_test(cx, |_| {});
14637
14638    let mut cx = EditorTestContext::new(cx).await;
14639
14640    let base_text = r#"
14641        one
14642        two
14643        three
14644        four
14645        five
14646    "#
14647    .unindent();
14648    executor.run_until_parked();
14649    cx.set_state(
14650        &r#"
14651        one
14652        two
14653        fˇour
14654        five
14655        "#
14656        .unindent(),
14657    );
14658
14659    cx.set_head_text(&base_text);
14660    executor.run_until_parked();
14661
14662    cx.update_editor(|editor, window, cx| {
14663        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14664    });
14665    executor.run_until_parked();
14666
14667    cx.assert_state_with_diff(
14668        r#"
14669          one
14670          two
14671        - three
14672          fˇour
14673          five
14674        "#
14675        .unindent(),
14676    );
14677
14678    cx.update_editor(|editor, window, cx| {
14679        editor.backspace(&Backspace, window, cx);
14680        editor.backspace(&Backspace, window, cx);
14681    });
14682    executor.run_until_parked();
14683    cx.assert_state_with_diff(
14684        r#"
14685          one
14686          two
14687        - threeˇ
14688        - four
14689        + our
14690          five
14691        "#
14692        .unindent(),
14693    );
14694}
14695
14696#[gpui::test]
14697async fn test_edit_after_expanded_modification_hunk(
14698    executor: BackgroundExecutor,
14699    cx: &mut TestAppContext,
14700) {
14701    init_test(cx, |_| {});
14702
14703    let mut cx = EditorTestContext::new(cx).await;
14704
14705    let diff_base = r#"
14706        use some::mod1;
14707        use some::mod2;
14708
14709        const A: u32 = 42;
14710        const B: u32 = 42;
14711        const C: u32 = 42;
14712        const D: u32 = 42;
14713
14714
14715        fn main() {
14716            println!("hello");
14717
14718            println!("world");
14719        }"#
14720    .unindent();
14721
14722    cx.set_state(
14723        &r#"
14724        use some::mod1;
14725        use some::mod2;
14726
14727        const A: u32 = 42;
14728        const B: u32 = 42;
14729        const C: u32 = 43ˇ
14730        const D: u32 = 42;
14731
14732
14733        fn main() {
14734            println!("hello");
14735
14736            println!("world");
14737        }"#
14738        .unindent(),
14739    );
14740
14741    cx.set_head_text(&diff_base);
14742    executor.run_until_parked();
14743    cx.update_editor(|editor, window, cx| {
14744        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14745    });
14746    executor.run_until_parked();
14747
14748    cx.assert_state_with_diff(
14749        r#"
14750        use some::mod1;
14751        use some::mod2;
14752
14753        const A: u32 = 42;
14754        const B: u32 = 42;
14755      - const C: u32 = 42;
14756      + const C: u32 = 43ˇ
14757        const D: u32 = 42;
14758
14759
14760        fn main() {
14761            println!("hello");
14762
14763            println!("world");
14764        }"#
14765        .unindent(),
14766    );
14767
14768    cx.update_editor(|editor, window, cx| {
14769        editor.handle_input("\nnew_line\n", window, cx);
14770    });
14771    executor.run_until_parked();
14772
14773    cx.assert_state_with_diff(
14774        r#"
14775        use some::mod1;
14776        use some::mod2;
14777
14778        const A: u32 = 42;
14779        const B: u32 = 42;
14780      - const C: u32 = 42;
14781      + const C: u32 = 43
14782      + new_line
14783      + ˇ
14784        const D: u32 = 42;
14785
14786
14787        fn main() {
14788            println!("hello");
14789
14790            println!("world");
14791        }"#
14792        .unindent(),
14793    );
14794}
14795
14796#[gpui::test]
14797async fn test_stage_and_unstage_added_file_hunk(
14798    executor: BackgroundExecutor,
14799    cx: &mut TestAppContext,
14800) {
14801    init_test(cx, |_| {});
14802
14803    let mut cx = EditorTestContext::new(cx).await;
14804    cx.update_editor(|editor, _, cx| {
14805        editor.set_expand_all_diff_hunks(cx);
14806    });
14807
14808    let working_copy = r#"
14809            ˇfn main() {
14810                println!("hello, world!");
14811            }
14812        "#
14813    .unindent();
14814
14815    cx.set_state(&working_copy);
14816    executor.run_until_parked();
14817
14818    cx.assert_state_with_diff(
14819        r#"
14820            + ˇfn main() {
14821            +     println!("hello, world!");
14822            + }
14823        "#
14824        .unindent(),
14825    );
14826    cx.assert_index_text(None);
14827
14828    cx.update_editor(|editor, window, cx| {
14829        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14830    });
14831    executor.run_until_parked();
14832    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14833    cx.assert_state_with_diff(
14834        r#"
14835            + ˇfn main() {
14836            +     println!("hello, world!");
14837            + }
14838        "#
14839        .unindent(),
14840    );
14841
14842    cx.update_editor(|editor, window, cx| {
14843        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14844    });
14845    executor.run_until_parked();
14846    cx.assert_index_text(None);
14847}
14848
14849async fn setup_indent_guides_editor(
14850    text: &str,
14851    cx: &mut TestAppContext,
14852) -> (BufferId, EditorTestContext) {
14853    init_test(cx, |_| {});
14854
14855    let mut cx = EditorTestContext::new(cx).await;
14856
14857    let buffer_id = cx.update_editor(|editor, window, cx| {
14858        editor.set_text(text, window, cx);
14859        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14860
14861        buffer_ids[0]
14862    });
14863
14864    (buffer_id, cx)
14865}
14866
14867fn assert_indent_guides(
14868    range: Range<u32>,
14869    expected: Vec<IndentGuide>,
14870    active_indices: Option<Vec<usize>>,
14871    cx: &mut EditorTestContext,
14872) {
14873    let indent_guides = cx.update_editor(|editor, window, cx| {
14874        let snapshot = editor.snapshot(window, cx).display_snapshot;
14875        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14876            editor,
14877            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14878            true,
14879            &snapshot,
14880            cx,
14881        );
14882
14883        indent_guides.sort_by(|a, b| {
14884            a.depth.cmp(&b.depth).then(
14885                a.start_row
14886                    .cmp(&b.start_row)
14887                    .then(a.end_row.cmp(&b.end_row)),
14888            )
14889        });
14890        indent_guides
14891    });
14892
14893    if let Some(expected) = active_indices {
14894        let active_indices = cx.update_editor(|editor, window, cx| {
14895            let snapshot = editor.snapshot(window, cx).display_snapshot;
14896            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14897        });
14898
14899        assert_eq!(
14900            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14901            expected,
14902            "Active indent guide indices do not match"
14903        );
14904    }
14905
14906    assert_eq!(indent_guides, expected, "Indent guides do not match");
14907}
14908
14909fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14910    IndentGuide {
14911        buffer_id,
14912        start_row: MultiBufferRow(start_row),
14913        end_row: MultiBufferRow(end_row),
14914        depth,
14915        tab_size: 4,
14916        settings: IndentGuideSettings {
14917            enabled: true,
14918            line_width: 1,
14919            active_line_width: 1,
14920            ..Default::default()
14921        },
14922    }
14923}
14924
14925#[gpui::test]
14926async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14927    let (buffer_id, mut cx) = setup_indent_guides_editor(
14928        &"
14929    fn main() {
14930        let a = 1;
14931    }"
14932        .unindent(),
14933        cx,
14934    )
14935    .await;
14936
14937    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14938}
14939
14940#[gpui::test]
14941async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14942    let (buffer_id, mut cx) = setup_indent_guides_editor(
14943        &"
14944    fn main() {
14945        let a = 1;
14946        let b = 2;
14947    }"
14948        .unindent(),
14949        cx,
14950    )
14951    .await;
14952
14953    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14954}
14955
14956#[gpui::test]
14957async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14958    let (buffer_id, mut cx) = setup_indent_guides_editor(
14959        &"
14960    fn main() {
14961        let a = 1;
14962        if a == 3 {
14963            let b = 2;
14964        } else {
14965            let c = 3;
14966        }
14967    }"
14968        .unindent(),
14969        cx,
14970    )
14971    .await;
14972
14973    assert_indent_guides(
14974        0..8,
14975        vec![
14976            indent_guide(buffer_id, 1, 6, 0),
14977            indent_guide(buffer_id, 3, 3, 1),
14978            indent_guide(buffer_id, 5, 5, 1),
14979        ],
14980        None,
14981        &mut cx,
14982    );
14983}
14984
14985#[gpui::test]
14986async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14987    let (buffer_id, mut cx) = setup_indent_guides_editor(
14988        &"
14989    fn main() {
14990        let a = 1;
14991            let b = 2;
14992        let c = 3;
14993    }"
14994        .unindent(),
14995        cx,
14996    )
14997    .await;
14998
14999    assert_indent_guides(
15000        0..5,
15001        vec![
15002            indent_guide(buffer_id, 1, 3, 0),
15003            indent_guide(buffer_id, 2, 2, 1),
15004        ],
15005        None,
15006        &mut cx,
15007    );
15008}
15009
15010#[gpui::test]
15011async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15012    let (buffer_id, mut cx) = setup_indent_guides_editor(
15013        &"
15014        fn main() {
15015            let a = 1;
15016
15017            let c = 3;
15018        }"
15019        .unindent(),
15020        cx,
15021    )
15022    .await;
15023
15024    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15025}
15026
15027#[gpui::test]
15028async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15029    let (buffer_id, mut cx) = setup_indent_guides_editor(
15030        &"
15031        fn main() {
15032            let a = 1;
15033
15034            let c = 3;
15035
15036            if a == 3 {
15037                let b = 2;
15038            } else {
15039                let c = 3;
15040            }
15041        }"
15042        .unindent(),
15043        cx,
15044    )
15045    .await;
15046
15047    assert_indent_guides(
15048        0..11,
15049        vec![
15050            indent_guide(buffer_id, 1, 9, 0),
15051            indent_guide(buffer_id, 6, 6, 1),
15052            indent_guide(buffer_id, 8, 8, 1),
15053        ],
15054        None,
15055        &mut cx,
15056    );
15057}
15058
15059#[gpui::test]
15060async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15061    let (buffer_id, mut cx) = setup_indent_guides_editor(
15062        &"
15063        fn main() {
15064            let a = 1;
15065
15066            let c = 3;
15067
15068            if a == 3 {
15069                let b = 2;
15070            } else {
15071                let c = 3;
15072            }
15073        }"
15074        .unindent(),
15075        cx,
15076    )
15077    .await;
15078
15079    assert_indent_guides(
15080        1..11,
15081        vec![
15082            indent_guide(buffer_id, 1, 9, 0),
15083            indent_guide(buffer_id, 6, 6, 1),
15084            indent_guide(buffer_id, 8, 8, 1),
15085        ],
15086        None,
15087        &mut cx,
15088    );
15089}
15090
15091#[gpui::test]
15092async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15093    let (buffer_id, mut cx) = setup_indent_guides_editor(
15094        &"
15095        fn main() {
15096            let a = 1;
15097
15098            let c = 3;
15099
15100            if a == 3 {
15101                let b = 2;
15102            } else {
15103                let c = 3;
15104            }
15105        }"
15106        .unindent(),
15107        cx,
15108    )
15109    .await;
15110
15111    assert_indent_guides(
15112        1..10,
15113        vec![
15114            indent_guide(buffer_id, 1, 9, 0),
15115            indent_guide(buffer_id, 6, 6, 1),
15116            indent_guide(buffer_id, 8, 8, 1),
15117        ],
15118        None,
15119        &mut cx,
15120    );
15121}
15122
15123#[gpui::test]
15124async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15125    let (buffer_id, mut cx) = setup_indent_guides_editor(
15126        &"
15127        block1
15128            block2
15129                block3
15130                    block4
15131            block2
15132        block1
15133        block1"
15134            .unindent(),
15135        cx,
15136    )
15137    .await;
15138
15139    assert_indent_guides(
15140        1..10,
15141        vec![
15142            indent_guide(buffer_id, 1, 4, 0),
15143            indent_guide(buffer_id, 2, 3, 1),
15144            indent_guide(buffer_id, 3, 3, 2),
15145        ],
15146        None,
15147        &mut cx,
15148    );
15149}
15150
15151#[gpui::test]
15152async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15153    let (buffer_id, mut cx) = setup_indent_guides_editor(
15154        &"
15155        block1
15156            block2
15157                block3
15158
15159        block1
15160        block1"
15161            .unindent(),
15162        cx,
15163    )
15164    .await;
15165
15166    assert_indent_guides(
15167        0..6,
15168        vec![
15169            indent_guide(buffer_id, 1, 2, 0),
15170            indent_guide(buffer_id, 2, 2, 1),
15171        ],
15172        None,
15173        &mut cx,
15174    );
15175}
15176
15177#[gpui::test]
15178async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15179    let (buffer_id, mut cx) = setup_indent_guides_editor(
15180        &"
15181        block1
15182
15183
15184
15185            block2
15186        "
15187        .unindent(),
15188        cx,
15189    )
15190    .await;
15191
15192    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15193}
15194
15195#[gpui::test]
15196async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15197    let (buffer_id, mut cx) = setup_indent_guides_editor(
15198        &"
15199        def a:
15200        \tb = 3
15201        \tif True:
15202        \t\tc = 4
15203        \t\td = 5
15204        \tprint(b)
15205        "
15206        .unindent(),
15207        cx,
15208    )
15209    .await;
15210
15211    assert_indent_guides(
15212        0..6,
15213        vec![
15214            indent_guide(buffer_id, 1, 6, 0),
15215            indent_guide(buffer_id, 3, 4, 1),
15216        ],
15217        None,
15218        &mut cx,
15219    );
15220}
15221
15222#[gpui::test]
15223async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15224    let (buffer_id, mut cx) = setup_indent_guides_editor(
15225        &"
15226    fn main() {
15227        let a = 1;
15228    }"
15229        .unindent(),
15230        cx,
15231    )
15232    .await;
15233
15234    cx.update_editor(|editor, window, cx| {
15235        editor.change_selections(None, window, cx, |s| {
15236            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15237        });
15238    });
15239
15240    assert_indent_guides(
15241        0..3,
15242        vec![indent_guide(buffer_id, 1, 1, 0)],
15243        Some(vec![0]),
15244        &mut cx,
15245    );
15246}
15247
15248#[gpui::test]
15249async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15250    let (buffer_id, mut cx) = setup_indent_guides_editor(
15251        &"
15252    fn main() {
15253        if 1 == 2 {
15254            let a = 1;
15255        }
15256    }"
15257        .unindent(),
15258        cx,
15259    )
15260    .await;
15261
15262    cx.update_editor(|editor, window, cx| {
15263        editor.change_selections(None, window, cx, |s| {
15264            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15265        });
15266    });
15267
15268    assert_indent_guides(
15269        0..4,
15270        vec![
15271            indent_guide(buffer_id, 1, 3, 0),
15272            indent_guide(buffer_id, 2, 2, 1),
15273        ],
15274        Some(vec![1]),
15275        &mut cx,
15276    );
15277
15278    cx.update_editor(|editor, window, cx| {
15279        editor.change_selections(None, window, cx, |s| {
15280            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15281        });
15282    });
15283
15284    assert_indent_guides(
15285        0..4,
15286        vec![
15287            indent_guide(buffer_id, 1, 3, 0),
15288            indent_guide(buffer_id, 2, 2, 1),
15289        ],
15290        Some(vec![1]),
15291        &mut cx,
15292    );
15293
15294    cx.update_editor(|editor, window, cx| {
15295        editor.change_selections(None, window, cx, |s| {
15296            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15297        });
15298    });
15299
15300    assert_indent_guides(
15301        0..4,
15302        vec![
15303            indent_guide(buffer_id, 1, 3, 0),
15304            indent_guide(buffer_id, 2, 2, 1),
15305        ],
15306        Some(vec![0]),
15307        &mut cx,
15308    );
15309}
15310
15311#[gpui::test]
15312async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15313    let (buffer_id, mut cx) = setup_indent_guides_editor(
15314        &"
15315    fn main() {
15316        let a = 1;
15317
15318        let b = 2;
15319    }"
15320        .unindent(),
15321        cx,
15322    )
15323    .await;
15324
15325    cx.update_editor(|editor, window, cx| {
15326        editor.change_selections(None, window, cx, |s| {
15327            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15328        });
15329    });
15330
15331    assert_indent_guides(
15332        0..5,
15333        vec![indent_guide(buffer_id, 1, 3, 0)],
15334        Some(vec![0]),
15335        &mut cx,
15336    );
15337}
15338
15339#[gpui::test]
15340async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15341    let (buffer_id, mut cx) = setup_indent_guides_editor(
15342        &"
15343    def m:
15344        a = 1
15345        pass"
15346            .unindent(),
15347        cx,
15348    )
15349    .await;
15350
15351    cx.update_editor(|editor, window, cx| {
15352        editor.change_selections(None, window, cx, |s| {
15353            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15354        });
15355    });
15356
15357    assert_indent_guides(
15358        0..3,
15359        vec![indent_guide(buffer_id, 1, 2, 0)],
15360        Some(vec![0]),
15361        &mut cx,
15362    );
15363}
15364
15365#[gpui::test]
15366async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15367    init_test(cx, |_| {});
15368    let mut cx = EditorTestContext::new(cx).await;
15369    let text = indoc! {
15370        "
15371        impl A {
15372            fn b() {
15373                0;
15374                3;
15375                5;
15376                6;
15377                7;
15378            }
15379        }
15380        "
15381    };
15382    let base_text = indoc! {
15383        "
15384        impl A {
15385            fn b() {
15386                0;
15387                1;
15388                2;
15389                3;
15390                4;
15391            }
15392            fn c() {
15393                5;
15394                6;
15395                7;
15396            }
15397        }
15398        "
15399    };
15400
15401    cx.update_editor(|editor, window, cx| {
15402        editor.set_text(text, window, cx);
15403
15404        editor.buffer().update(cx, |multibuffer, cx| {
15405            let buffer = multibuffer.as_singleton().unwrap();
15406            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15407
15408            multibuffer.set_all_diff_hunks_expanded(cx);
15409            multibuffer.add_diff(diff, cx);
15410
15411            buffer.read(cx).remote_id()
15412        })
15413    });
15414    cx.run_until_parked();
15415
15416    cx.assert_state_with_diff(
15417        indoc! { "
15418          impl A {
15419              fn b() {
15420                  0;
15421        -         1;
15422        -         2;
15423                  3;
15424        -         4;
15425        -     }
15426        -     fn c() {
15427                  5;
15428                  6;
15429                  7;
15430              }
15431          }
15432          ˇ"
15433        }
15434        .to_string(),
15435    );
15436
15437    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15438        editor
15439            .snapshot(window, cx)
15440            .buffer_snapshot
15441            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15442            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15443            .collect::<Vec<_>>()
15444    });
15445    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15446    assert_eq!(
15447        actual_guides,
15448        vec![
15449            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15450            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15451            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15452        ]
15453    );
15454}
15455
15456#[gpui::test]
15457async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15458    init_test(cx, |_| {});
15459    let mut cx = EditorTestContext::new(cx).await;
15460
15461    let diff_base = r#"
15462        a
15463        b
15464        c
15465        "#
15466    .unindent();
15467
15468    cx.set_state(
15469        &r#"
15470        ˇA
15471        b
15472        C
15473        "#
15474        .unindent(),
15475    );
15476    cx.set_head_text(&diff_base);
15477    cx.update_editor(|editor, window, cx| {
15478        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15479    });
15480    executor.run_until_parked();
15481
15482    let both_hunks_expanded = r#"
15483        - a
15484        + ˇA
15485          b
15486        - c
15487        + C
15488        "#
15489    .unindent();
15490
15491    cx.assert_state_with_diff(both_hunks_expanded.clone());
15492
15493    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15494        let snapshot = editor.snapshot(window, cx);
15495        let hunks = editor
15496            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15497            .collect::<Vec<_>>();
15498        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15499        let buffer_id = hunks[0].buffer_id;
15500        hunks
15501            .into_iter()
15502            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15503            .collect::<Vec<_>>()
15504    });
15505    assert_eq!(hunk_ranges.len(), 2);
15506
15507    cx.update_editor(|editor, _, cx| {
15508        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15509    });
15510    executor.run_until_parked();
15511
15512    let second_hunk_expanded = r#"
15513          ˇA
15514          b
15515        - c
15516        + C
15517        "#
15518    .unindent();
15519
15520    cx.assert_state_with_diff(second_hunk_expanded);
15521
15522    cx.update_editor(|editor, _, cx| {
15523        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15524    });
15525    executor.run_until_parked();
15526
15527    cx.assert_state_with_diff(both_hunks_expanded.clone());
15528
15529    cx.update_editor(|editor, _, cx| {
15530        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15531    });
15532    executor.run_until_parked();
15533
15534    let first_hunk_expanded = r#"
15535        - a
15536        + ˇA
15537          b
15538          C
15539        "#
15540    .unindent();
15541
15542    cx.assert_state_with_diff(first_hunk_expanded);
15543
15544    cx.update_editor(|editor, _, cx| {
15545        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15546    });
15547    executor.run_until_parked();
15548
15549    cx.assert_state_with_diff(both_hunks_expanded);
15550
15551    cx.set_state(
15552        &r#"
15553        ˇA
15554        b
15555        "#
15556        .unindent(),
15557    );
15558    cx.run_until_parked();
15559
15560    // TODO this cursor position seems bad
15561    cx.assert_state_with_diff(
15562        r#"
15563        - ˇa
15564        + A
15565          b
15566        "#
15567        .unindent(),
15568    );
15569
15570    cx.update_editor(|editor, window, cx| {
15571        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15572    });
15573
15574    cx.assert_state_with_diff(
15575        r#"
15576            - ˇa
15577            + A
15578              b
15579            - c
15580            "#
15581        .unindent(),
15582    );
15583
15584    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15585        let snapshot = editor.snapshot(window, cx);
15586        let hunks = editor
15587            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15588            .collect::<Vec<_>>();
15589        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15590        let buffer_id = hunks[0].buffer_id;
15591        hunks
15592            .into_iter()
15593            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15594            .collect::<Vec<_>>()
15595    });
15596    assert_eq!(hunk_ranges.len(), 2);
15597
15598    cx.update_editor(|editor, _, cx| {
15599        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15600    });
15601    executor.run_until_parked();
15602
15603    cx.assert_state_with_diff(
15604        r#"
15605        - ˇa
15606        + A
15607          b
15608        "#
15609        .unindent(),
15610    );
15611}
15612
15613#[gpui::test]
15614async fn test_toggle_deletion_hunk_at_start_of_file(
15615    executor: BackgroundExecutor,
15616    cx: &mut TestAppContext,
15617) {
15618    init_test(cx, |_| {});
15619    let mut cx = EditorTestContext::new(cx).await;
15620
15621    let diff_base = r#"
15622        a
15623        b
15624        c
15625        "#
15626    .unindent();
15627
15628    cx.set_state(
15629        &r#"
15630        ˇb
15631        c
15632        "#
15633        .unindent(),
15634    );
15635    cx.set_head_text(&diff_base);
15636    cx.update_editor(|editor, window, cx| {
15637        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15638    });
15639    executor.run_until_parked();
15640
15641    let hunk_expanded = r#"
15642        - a
15643          ˇb
15644          c
15645        "#
15646    .unindent();
15647
15648    cx.assert_state_with_diff(hunk_expanded.clone());
15649
15650    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15651        let snapshot = editor.snapshot(window, cx);
15652        let hunks = editor
15653            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15654            .collect::<Vec<_>>();
15655        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15656        let buffer_id = hunks[0].buffer_id;
15657        hunks
15658            .into_iter()
15659            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15660            .collect::<Vec<_>>()
15661    });
15662    assert_eq!(hunk_ranges.len(), 1);
15663
15664    cx.update_editor(|editor, _, cx| {
15665        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15666    });
15667    executor.run_until_parked();
15668
15669    let hunk_collapsed = r#"
15670          ˇb
15671          c
15672        "#
15673    .unindent();
15674
15675    cx.assert_state_with_diff(hunk_collapsed);
15676
15677    cx.update_editor(|editor, _, cx| {
15678        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15679    });
15680    executor.run_until_parked();
15681
15682    cx.assert_state_with_diff(hunk_expanded.clone());
15683}
15684
15685#[gpui::test]
15686async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15687    init_test(cx, |_| {});
15688
15689    let fs = FakeFs::new(cx.executor());
15690    fs.insert_tree(
15691        path!("/test"),
15692        json!({
15693            ".git": {},
15694            "file-1": "ONE\n",
15695            "file-2": "TWO\n",
15696            "file-3": "THREE\n",
15697        }),
15698    )
15699    .await;
15700
15701    fs.set_head_for_repo(
15702        path!("/test/.git").as_ref(),
15703        &[
15704            ("file-1".into(), "one\n".into()),
15705            ("file-2".into(), "two\n".into()),
15706            ("file-3".into(), "three\n".into()),
15707        ],
15708    );
15709
15710    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15711    let mut buffers = vec![];
15712    for i in 1..=3 {
15713        let buffer = project
15714            .update(cx, |project, cx| {
15715                let path = format!(path!("/test/file-{}"), i);
15716                project.open_local_buffer(path, cx)
15717            })
15718            .await
15719            .unwrap();
15720        buffers.push(buffer);
15721    }
15722
15723    let multibuffer = cx.new(|cx| {
15724        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15725        multibuffer.set_all_diff_hunks_expanded(cx);
15726        for buffer in &buffers {
15727            let snapshot = buffer.read(cx).snapshot();
15728            multibuffer.set_excerpts_for_path(
15729                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15730                buffer.clone(),
15731                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15732                DEFAULT_MULTIBUFFER_CONTEXT,
15733                cx,
15734            );
15735        }
15736        multibuffer
15737    });
15738
15739    let editor = cx.add_window(|window, cx| {
15740        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15741    });
15742    cx.run_until_parked();
15743
15744    let snapshot = editor
15745        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15746        .unwrap();
15747    let hunks = snapshot
15748        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15749        .map(|hunk| match hunk {
15750            DisplayDiffHunk::Unfolded {
15751                display_row_range, ..
15752            } => display_row_range,
15753            DisplayDiffHunk::Folded { .. } => unreachable!(),
15754        })
15755        .collect::<Vec<_>>();
15756    assert_eq!(
15757        hunks,
15758        [
15759            DisplayRow(2)..DisplayRow(4),
15760            DisplayRow(7)..DisplayRow(9),
15761            DisplayRow(12)..DisplayRow(14),
15762        ]
15763    );
15764}
15765
15766#[gpui::test]
15767async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15768    init_test(cx, |_| {});
15769
15770    let mut cx = EditorTestContext::new(cx).await;
15771    cx.set_head_text(indoc! { "
15772        one
15773        two
15774        three
15775        four
15776        five
15777        "
15778    });
15779    cx.set_index_text(indoc! { "
15780        one
15781        two
15782        three
15783        four
15784        five
15785        "
15786    });
15787    cx.set_state(indoc! {"
15788        one
15789        TWO
15790        ˇTHREE
15791        FOUR
15792        five
15793    "});
15794    cx.run_until_parked();
15795    cx.update_editor(|editor, window, cx| {
15796        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15797    });
15798    cx.run_until_parked();
15799    cx.assert_index_text(Some(indoc! {"
15800        one
15801        TWO
15802        THREE
15803        FOUR
15804        five
15805    "}));
15806    cx.set_state(indoc! { "
15807        one
15808        TWO
15809        ˇTHREE-HUNDRED
15810        FOUR
15811        five
15812    "});
15813    cx.run_until_parked();
15814    cx.update_editor(|editor, window, cx| {
15815        let snapshot = editor.snapshot(window, cx);
15816        let hunks = editor
15817            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15818            .collect::<Vec<_>>();
15819        assert_eq!(hunks.len(), 1);
15820        assert_eq!(
15821            hunks[0].status(),
15822            DiffHunkStatus {
15823                kind: DiffHunkStatusKind::Modified,
15824                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15825            }
15826        );
15827
15828        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15829    });
15830    cx.run_until_parked();
15831    cx.assert_index_text(Some(indoc! {"
15832        one
15833        TWO
15834        THREE-HUNDRED
15835        FOUR
15836        five
15837    "}));
15838}
15839
15840#[gpui::test]
15841fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15842    init_test(cx, |_| {});
15843
15844    let editor = cx.add_window(|window, cx| {
15845        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15846        build_editor(buffer, window, cx)
15847    });
15848
15849    let render_args = Arc::new(Mutex::new(None));
15850    let snapshot = editor
15851        .update(cx, |editor, window, cx| {
15852            let snapshot = editor.buffer().read(cx).snapshot(cx);
15853            let range =
15854                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15855
15856            struct RenderArgs {
15857                row: MultiBufferRow,
15858                folded: bool,
15859                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15860            }
15861
15862            let crease = Crease::inline(
15863                range,
15864                FoldPlaceholder::test(),
15865                {
15866                    let toggle_callback = render_args.clone();
15867                    move |row, folded, callback, _window, _cx| {
15868                        *toggle_callback.lock() = Some(RenderArgs {
15869                            row,
15870                            folded,
15871                            callback,
15872                        });
15873                        div()
15874                    }
15875                },
15876                |_row, _folded, _window, _cx| div(),
15877            );
15878
15879            editor.insert_creases(Some(crease), cx);
15880            let snapshot = editor.snapshot(window, cx);
15881            let _div = snapshot.render_crease_toggle(
15882                MultiBufferRow(1),
15883                false,
15884                cx.entity().clone(),
15885                window,
15886                cx,
15887            );
15888            snapshot
15889        })
15890        .unwrap();
15891
15892    let render_args = render_args.lock().take().unwrap();
15893    assert_eq!(render_args.row, MultiBufferRow(1));
15894    assert!(!render_args.folded);
15895    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15896
15897    cx.update_window(*editor, |_, window, cx| {
15898        (render_args.callback)(true, window, cx)
15899    })
15900    .unwrap();
15901    let snapshot = editor
15902        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15903        .unwrap();
15904    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15905
15906    cx.update_window(*editor, |_, window, cx| {
15907        (render_args.callback)(false, window, cx)
15908    })
15909    .unwrap();
15910    let snapshot = editor
15911        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15912        .unwrap();
15913    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15914}
15915
15916#[gpui::test]
15917async fn test_input_text(cx: &mut TestAppContext) {
15918    init_test(cx, |_| {});
15919    let mut cx = EditorTestContext::new(cx).await;
15920
15921    cx.set_state(
15922        &r#"ˇone
15923        two
15924
15925        three
15926        fourˇ
15927        five
15928
15929        siˇx"#
15930            .unindent(),
15931    );
15932
15933    cx.dispatch_action(HandleInput(String::new()));
15934    cx.assert_editor_state(
15935        &r#"ˇone
15936        two
15937
15938        three
15939        fourˇ
15940        five
15941
15942        siˇx"#
15943            .unindent(),
15944    );
15945
15946    cx.dispatch_action(HandleInput("AAAA".to_string()));
15947    cx.assert_editor_state(
15948        &r#"AAAAˇone
15949        two
15950
15951        three
15952        fourAAAAˇ
15953        five
15954
15955        siAAAAˇx"#
15956            .unindent(),
15957    );
15958}
15959
15960#[gpui::test]
15961async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15962    init_test(cx, |_| {});
15963
15964    let mut cx = EditorTestContext::new(cx).await;
15965    cx.set_state(
15966        r#"let foo = 1;
15967let foo = 2;
15968let foo = 3;
15969let fooˇ = 4;
15970let foo = 5;
15971let foo = 6;
15972let foo = 7;
15973let foo = 8;
15974let foo = 9;
15975let foo = 10;
15976let foo = 11;
15977let foo = 12;
15978let foo = 13;
15979let foo = 14;
15980let foo = 15;"#,
15981    );
15982
15983    cx.update_editor(|e, window, cx| {
15984        assert_eq!(
15985            e.next_scroll_position,
15986            NextScrollCursorCenterTopBottom::Center,
15987            "Default next scroll direction is center",
15988        );
15989
15990        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15991        assert_eq!(
15992            e.next_scroll_position,
15993            NextScrollCursorCenterTopBottom::Top,
15994            "After center, next scroll direction should be top",
15995        );
15996
15997        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15998        assert_eq!(
15999            e.next_scroll_position,
16000            NextScrollCursorCenterTopBottom::Bottom,
16001            "After top, next scroll direction should be bottom",
16002        );
16003
16004        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16005        assert_eq!(
16006            e.next_scroll_position,
16007            NextScrollCursorCenterTopBottom::Center,
16008            "After bottom, scrolling should start over",
16009        );
16010
16011        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16012        assert_eq!(
16013            e.next_scroll_position,
16014            NextScrollCursorCenterTopBottom::Top,
16015            "Scrolling continues if retriggered fast enough"
16016        );
16017    });
16018
16019    cx.executor()
16020        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16021    cx.executor().run_until_parked();
16022    cx.update_editor(|e, _, _| {
16023        assert_eq!(
16024            e.next_scroll_position,
16025            NextScrollCursorCenterTopBottom::Center,
16026            "If scrolling is not triggered fast enough, it should reset"
16027        );
16028    });
16029}
16030
16031#[gpui::test]
16032async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16033    init_test(cx, |_| {});
16034    let mut cx = EditorLspTestContext::new_rust(
16035        lsp::ServerCapabilities {
16036            definition_provider: Some(lsp::OneOf::Left(true)),
16037            references_provider: Some(lsp::OneOf::Left(true)),
16038            ..lsp::ServerCapabilities::default()
16039        },
16040        cx,
16041    )
16042    .await;
16043
16044    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16045        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16046            move |params, _| async move {
16047                if empty_go_to_definition {
16048                    Ok(None)
16049                } else {
16050                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16051                        uri: params.text_document_position_params.text_document.uri,
16052                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16053                    })))
16054                }
16055            },
16056        );
16057        let references =
16058            cx.lsp
16059                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16060                    Ok(Some(vec![lsp::Location {
16061                        uri: params.text_document_position.text_document.uri,
16062                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16063                    }]))
16064                });
16065        (go_to_definition, references)
16066    };
16067
16068    cx.set_state(
16069        &r#"fn one() {
16070            let mut a = ˇtwo();
16071        }
16072
16073        fn two() {}"#
16074            .unindent(),
16075    );
16076    set_up_lsp_handlers(false, &mut cx);
16077    let navigated = cx
16078        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16079        .await
16080        .expect("Failed to navigate to definition");
16081    assert_eq!(
16082        navigated,
16083        Navigated::Yes,
16084        "Should have navigated to definition from the GetDefinition response"
16085    );
16086    cx.assert_editor_state(
16087        &r#"fn one() {
16088            let mut a = two();
16089        }
16090
16091        fn «twoˇ»() {}"#
16092            .unindent(),
16093    );
16094
16095    let editors = cx.update_workspace(|workspace, _, cx| {
16096        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16097    });
16098    cx.update_editor(|_, _, test_editor_cx| {
16099        assert_eq!(
16100            editors.len(),
16101            1,
16102            "Initially, only one, test, editor should be open in the workspace"
16103        );
16104        assert_eq!(
16105            test_editor_cx.entity(),
16106            editors.last().expect("Asserted len is 1").clone()
16107        );
16108    });
16109
16110    set_up_lsp_handlers(true, &mut cx);
16111    let navigated = cx
16112        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16113        .await
16114        .expect("Failed to navigate to lookup references");
16115    assert_eq!(
16116        navigated,
16117        Navigated::Yes,
16118        "Should have navigated to references as a fallback after empty GoToDefinition response"
16119    );
16120    // We should not change the selections in the existing file,
16121    // if opening another milti buffer with the references
16122    cx.assert_editor_state(
16123        &r#"fn one() {
16124            let mut a = two();
16125        }
16126
16127        fn «twoˇ»() {}"#
16128            .unindent(),
16129    );
16130    let editors = cx.update_workspace(|workspace, _, cx| {
16131        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16132    });
16133    cx.update_editor(|_, _, test_editor_cx| {
16134        assert_eq!(
16135            editors.len(),
16136            2,
16137            "After falling back to references search, we open a new editor with the results"
16138        );
16139        let references_fallback_text = editors
16140            .into_iter()
16141            .find(|new_editor| *new_editor != test_editor_cx.entity())
16142            .expect("Should have one non-test editor now")
16143            .read(test_editor_cx)
16144            .text(test_editor_cx);
16145        assert_eq!(
16146            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16147            "Should use the range from the references response and not the GoToDefinition one"
16148        );
16149    });
16150}
16151
16152#[gpui::test]
16153async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16154    init_test(cx, |_| {});
16155
16156    let language = Arc::new(Language::new(
16157        LanguageConfig::default(),
16158        Some(tree_sitter_rust::LANGUAGE.into()),
16159    ));
16160
16161    let text = r#"
16162        #[cfg(test)]
16163        mod tests() {
16164            #[test]
16165            fn runnable_1() {
16166                let a = 1;
16167            }
16168
16169            #[test]
16170            fn runnable_2() {
16171                let a = 1;
16172                let b = 2;
16173            }
16174        }
16175    "#
16176    .unindent();
16177
16178    let fs = FakeFs::new(cx.executor());
16179    fs.insert_file("/file.rs", Default::default()).await;
16180
16181    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16182    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16183    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16184    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16185    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16186
16187    let editor = cx.new_window_entity(|window, cx| {
16188        Editor::new(
16189            EditorMode::Full,
16190            multi_buffer,
16191            Some(project.clone()),
16192            window,
16193            cx,
16194        )
16195    });
16196
16197    editor.update_in(cx, |editor, window, cx| {
16198        let snapshot = editor.buffer().read(cx).snapshot(cx);
16199        editor.tasks.insert(
16200            (buffer.read(cx).remote_id(), 3),
16201            RunnableTasks {
16202                templates: vec![],
16203                offset: snapshot.anchor_before(43),
16204                column: 0,
16205                extra_variables: HashMap::default(),
16206                context_range: BufferOffset(43)..BufferOffset(85),
16207            },
16208        );
16209        editor.tasks.insert(
16210            (buffer.read(cx).remote_id(), 8),
16211            RunnableTasks {
16212                templates: vec![],
16213                offset: snapshot.anchor_before(86),
16214                column: 0,
16215                extra_variables: HashMap::default(),
16216                context_range: BufferOffset(86)..BufferOffset(191),
16217            },
16218        );
16219
16220        // Test finding task when cursor is inside function body
16221        editor.change_selections(None, window, cx, |s| {
16222            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16223        });
16224        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16225        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16226
16227        // Test finding task when cursor is on function name
16228        editor.change_selections(None, window, cx, |s| {
16229            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16230        });
16231        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16232        assert_eq!(row, 8, "Should find task when cursor is on function name");
16233    });
16234}
16235
16236#[gpui::test]
16237async fn test_folding_buffers(cx: &mut TestAppContext) {
16238    init_test(cx, |_| {});
16239
16240    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16241    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16242    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16243
16244    let fs = FakeFs::new(cx.executor());
16245    fs.insert_tree(
16246        path!("/a"),
16247        json!({
16248            "first.rs": sample_text_1,
16249            "second.rs": sample_text_2,
16250            "third.rs": sample_text_3,
16251        }),
16252    )
16253    .await;
16254    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16255    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16256    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16257    let worktree = project.update(cx, |project, cx| {
16258        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16259        assert_eq!(worktrees.len(), 1);
16260        worktrees.pop().unwrap()
16261    });
16262    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16263
16264    let buffer_1 = project
16265        .update(cx, |project, cx| {
16266            project.open_buffer((worktree_id, "first.rs"), cx)
16267        })
16268        .await
16269        .unwrap();
16270    let buffer_2 = project
16271        .update(cx, |project, cx| {
16272            project.open_buffer((worktree_id, "second.rs"), cx)
16273        })
16274        .await
16275        .unwrap();
16276    let buffer_3 = project
16277        .update(cx, |project, cx| {
16278            project.open_buffer((worktree_id, "third.rs"), cx)
16279        })
16280        .await
16281        .unwrap();
16282
16283    let multi_buffer = cx.new(|cx| {
16284        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16285        multi_buffer.push_excerpts(
16286            buffer_1.clone(),
16287            [
16288                ExcerptRange {
16289                    context: Point::new(0, 0)..Point::new(3, 0),
16290                    primary: None,
16291                },
16292                ExcerptRange {
16293                    context: Point::new(5, 0)..Point::new(7, 0),
16294                    primary: None,
16295                },
16296                ExcerptRange {
16297                    context: Point::new(9, 0)..Point::new(10, 4),
16298                    primary: None,
16299                },
16300            ],
16301            cx,
16302        );
16303        multi_buffer.push_excerpts(
16304            buffer_2.clone(),
16305            [
16306                ExcerptRange {
16307                    context: Point::new(0, 0)..Point::new(3, 0),
16308                    primary: None,
16309                },
16310                ExcerptRange {
16311                    context: Point::new(5, 0)..Point::new(7, 0),
16312                    primary: None,
16313                },
16314                ExcerptRange {
16315                    context: Point::new(9, 0)..Point::new(10, 4),
16316                    primary: None,
16317                },
16318            ],
16319            cx,
16320        );
16321        multi_buffer.push_excerpts(
16322            buffer_3.clone(),
16323            [
16324                ExcerptRange {
16325                    context: Point::new(0, 0)..Point::new(3, 0),
16326                    primary: None,
16327                },
16328                ExcerptRange {
16329                    context: Point::new(5, 0)..Point::new(7, 0),
16330                    primary: None,
16331                },
16332                ExcerptRange {
16333                    context: Point::new(9, 0)..Point::new(10, 4),
16334                    primary: None,
16335                },
16336            ],
16337            cx,
16338        );
16339        multi_buffer
16340    });
16341    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16342        Editor::new(
16343            EditorMode::Full,
16344            multi_buffer.clone(),
16345            Some(project.clone()),
16346            window,
16347            cx,
16348        )
16349    });
16350
16351    assert_eq!(
16352        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16353        "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16354    );
16355
16356    multi_buffer_editor.update(cx, |editor, cx| {
16357        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16358    });
16359    assert_eq!(
16360        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16361        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16362        "After folding the first buffer, its text should not be displayed"
16363    );
16364
16365    multi_buffer_editor.update(cx, |editor, cx| {
16366        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16367    });
16368    assert_eq!(
16369        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16370        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16371        "After folding the second buffer, its text should not be displayed"
16372    );
16373
16374    multi_buffer_editor.update(cx, |editor, cx| {
16375        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16376    });
16377    assert_eq!(
16378        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16379        "\n\n\n\n\n",
16380        "After folding the third buffer, its text should not be displayed"
16381    );
16382
16383    // Emulate selection inside the fold logic, that should work
16384    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16385        editor
16386            .snapshot(window, cx)
16387            .next_line_boundary(Point::new(0, 4));
16388    });
16389
16390    multi_buffer_editor.update(cx, |editor, cx| {
16391        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16392    });
16393    assert_eq!(
16394        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16395        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16396        "After unfolding the second buffer, its text should be displayed"
16397    );
16398
16399    // Typing inside of buffer 1 causes that buffer to be unfolded.
16400    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16401        assert_eq!(
16402            multi_buffer
16403                .read(cx)
16404                .snapshot(cx)
16405                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16406                .collect::<String>(),
16407            "bbbb"
16408        );
16409        editor.change_selections(None, window, cx, |selections| {
16410            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16411        });
16412        editor.handle_input("B", window, cx);
16413    });
16414
16415    assert_eq!(
16416        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16417        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16418        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16419    );
16420
16421    multi_buffer_editor.update(cx, |editor, cx| {
16422        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16423    });
16424    assert_eq!(
16425        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16426        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16427        "After unfolding the all buffers, all original text should be displayed"
16428    );
16429}
16430
16431#[gpui::test]
16432async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16433    init_test(cx, |_| {});
16434
16435    let sample_text_1 = "1111\n2222\n3333".to_string();
16436    let sample_text_2 = "4444\n5555\n6666".to_string();
16437    let sample_text_3 = "7777\n8888\n9999".to_string();
16438
16439    let fs = FakeFs::new(cx.executor());
16440    fs.insert_tree(
16441        path!("/a"),
16442        json!({
16443            "first.rs": sample_text_1,
16444            "second.rs": sample_text_2,
16445            "third.rs": sample_text_3,
16446        }),
16447    )
16448    .await;
16449    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16450    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16451    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16452    let worktree = project.update(cx, |project, cx| {
16453        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16454        assert_eq!(worktrees.len(), 1);
16455        worktrees.pop().unwrap()
16456    });
16457    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16458
16459    let buffer_1 = project
16460        .update(cx, |project, cx| {
16461            project.open_buffer((worktree_id, "first.rs"), cx)
16462        })
16463        .await
16464        .unwrap();
16465    let buffer_2 = project
16466        .update(cx, |project, cx| {
16467            project.open_buffer((worktree_id, "second.rs"), cx)
16468        })
16469        .await
16470        .unwrap();
16471    let buffer_3 = project
16472        .update(cx, |project, cx| {
16473            project.open_buffer((worktree_id, "third.rs"), cx)
16474        })
16475        .await
16476        .unwrap();
16477
16478    let multi_buffer = cx.new(|cx| {
16479        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16480        multi_buffer.push_excerpts(
16481            buffer_1.clone(),
16482            [ExcerptRange {
16483                context: Point::new(0, 0)..Point::new(3, 0),
16484                primary: None,
16485            }],
16486            cx,
16487        );
16488        multi_buffer.push_excerpts(
16489            buffer_2.clone(),
16490            [ExcerptRange {
16491                context: Point::new(0, 0)..Point::new(3, 0),
16492                primary: None,
16493            }],
16494            cx,
16495        );
16496        multi_buffer.push_excerpts(
16497            buffer_3.clone(),
16498            [ExcerptRange {
16499                context: Point::new(0, 0)..Point::new(3, 0),
16500                primary: None,
16501            }],
16502            cx,
16503        );
16504        multi_buffer
16505    });
16506
16507    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16508        Editor::new(
16509            EditorMode::Full,
16510            multi_buffer,
16511            Some(project.clone()),
16512            window,
16513            cx,
16514        )
16515    });
16516
16517    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16518    assert_eq!(
16519        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16520        full_text,
16521    );
16522
16523    multi_buffer_editor.update(cx, |editor, cx| {
16524        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16525    });
16526    assert_eq!(
16527        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16528        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16529        "After folding the first buffer, its text should not be displayed"
16530    );
16531
16532    multi_buffer_editor.update(cx, |editor, cx| {
16533        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16534    });
16535
16536    assert_eq!(
16537        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16538        "\n\n\n\n\n\n7777\n8888\n9999",
16539        "After folding the second buffer, its text should not be displayed"
16540    );
16541
16542    multi_buffer_editor.update(cx, |editor, cx| {
16543        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16544    });
16545    assert_eq!(
16546        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16547        "\n\n\n\n\n",
16548        "After folding the third buffer, its text should not be displayed"
16549    );
16550
16551    multi_buffer_editor.update(cx, |editor, cx| {
16552        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16553    });
16554    assert_eq!(
16555        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16556        "\n\n\n\n4444\n5555\n6666\n\n",
16557        "After unfolding the second buffer, its text should be displayed"
16558    );
16559
16560    multi_buffer_editor.update(cx, |editor, cx| {
16561        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16562    });
16563    assert_eq!(
16564        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16565        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16566        "After unfolding the first buffer, its text should be displayed"
16567    );
16568
16569    multi_buffer_editor.update(cx, |editor, cx| {
16570        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16571    });
16572    assert_eq!(
16573        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16574        full_text,
16575        "After unfolding all buffers, all original text should be displayed"
16576    );
16577}
16578
16579#[gpui::test]
16580async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16581    init_test(cx, |_| {});
16582
16583    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16584
16585    let fs = FakeFs::new(cx.executor());
16586    fs.insert_tree(
16587        path!("/a"),
16588        json!({
16589            "main.rs": sample_text,
16590        }),
16591    )
16592    .await;
16593    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16594    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16595    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16596    let worktree = project.update(cx, |project, cx| {
16597        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16598        assert_eq!(worktrees.len(), 1);
16599        worktrees.pop().unwrap()
16600    });
16601    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16602
16603    let buffer_1 = project
16604        .update(cx, |project, cx| {
16605            project.open_buffer((worktree_id, "main.rs"), cx)
16606        })
16607        .await
16608        .unwrap();
16609
16610    let multi_buffer = cx.new(|cx| {
16611        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16612        multi_buffer.push_excerpts(
16613            buffer_1.clone(),
16614            [ExcerptRange {
16615                context: Point::new(0, 0)
16616                    ..Point::new(
16617                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16618                        0,
16619                    ),
16620                primary: None,
16621            }],
16622            cx,
16623        );
16624        multi_buffer
16625    });
16626    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16627        Editor::new(
16628            EditorMode::Full,
16629            multi_buffer,
16630            Some(project.clone()),
16631            window,
16632            cx,
16633        )
16634    });
16635
16636    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16637    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16638        enum TestHighlight {}
16639        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16640        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16641        editor.highlight_text::<TestHighlight>(
16642            vec![highlight_range.clone()],
16643            HighlightStyle::color(Hsla::green()),
16644            cx,
16645        );
16646        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16647    });
16648
16649    let full_text = format!("\n\n{sample_text}");
16650    assert_eq!(
16651        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16652        full_text,
16653    );
16654}
16655
16656#[gpui::test]
16657async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16658    init_test(cx, |_| {});
16659    cx.update(|cx| {
16660        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16661            "keymaps/default-linux.json",
16662            cx,
16663        )
16664        .unwrap();
16665        cx.bind_keys(default_key_bindings);
16666    });
16667
16668    let (editor, cx) = cx.add_window_view(|window, cx| {
16669        let multi_buffer = MultiBuffer::build_multi(
16670            [
16671                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16672                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16673                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16674                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16675            ],
16676            cx,
16677        );
16678        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16679
16680        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16681        // fold all but the second buffer, so that we test navigating between two
16682        // adjacent folded buffers, as well as folded buffers at the start and
16683        // end the multibuffer
16684        editor.fold_buffer(buffer_ids[0], cx);
16685        editor.fold_buffer(buffer_ids[2], cx);
16686        editor.fold_buffer(buffer_ids[3], cx);
16687
16688        editor
16689    });
16690    cx.simulate_resize(size(px(1000.), px(1000.)));
16691
16692    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16693    cx.assert_excerpts_with_selections(indoc! {"
16694        [EXCERPT]
16695        ˇ[FOLDED]
16696        [EXCERPT]
16697        a1
16698        b1
16699        [EXCERPT]
16700        [FOLDED]
16701        [EXCERPT]
16702        [FOLDED]
16703        "
16704    });
16705    cx.simulate_keystroke("down");
16706    cx.assert_excerpts_with_selections(indoc! {"
16707        [EXCERPT]
16708        [FOLDED]
16709        [EXCERPT]
16710        ˇa1
16711        b1
16712        [EXCERPT]
16713        [FOLDED]
16714        [EXCERPT]
16715        [FOLDED]
16716        "
16717    });
16718    cx.simulate_keystroke("down");
16719    cx.assert_excerpts_with_selections(indoc! {"
16720        [EXCERPT]
16721        [FOLDED]
16722        [EXCERPT]
16723        a1
16724        ˇb1
16725        [EXCERPT]
16726        [FOLDED]
16727        [EXCERPT]
16728        [FOLDED]
16729        "
16730    });
16731    cx.simulate_keystroke("down");
16732    cx.assert_excerpts_with_selections(indoc! {"
16733        [EXCERPT]
16734        [FOLDED]
16735        [EXCERPT]
16736        a1
16737        b1
16738        ˇ[EXCERPT]
16739        [FOLDED]
16740        [EXCERPT]
16741        [FOLDED]
16742        "
16743    });
16744    cx.simulate_keystroke("down");
16745    cx.assert_excerpts_with_selections(indoc! {"
16746        [EXCERPT]
16747        [FOLDED]
16748        [EXCERPT]
16749        a1
16750        b1
16751        [EXCERPT]
16752        ˇ[FOLDED]
16753        [EXCERPT]
16754        [FOLDED]
16755        "
16756    });
16757    for _ in 0..5 {
16758        cx.simulate_keystroke("down");
16759        cx.assert_excerpts_with_selections(indoc! {"
16760            [EXCERPT]
16761            [FOLDED]
16762            [EXCERPT]
16763            a1
16764            b1
16765            [EXCERPT]
16766            [FOLDED]
16767            [EXCERPT]
16768            ˇ[FOLDED]
16769            "
16770        });
16771    }
16772
16773    cx.simulate_keystroke("up");
16774    cx.assert_excerpts_with_selections(indoc! {"
16775        [EXCERPT]
16776        [FOLDED]
16777        [EXCERPT]
16778        a1
16779        b1
16780        [EXCERPT]
16781        ˇ[FOLDED]
16782        [EXCERPT]
16783        [FOLDED]
16784        "
16785    });
16786    cx.simulate_keystroke("up");
16787    cx.assert_excerpts_with_selections(indoc! {"
16788        [EXCERPT]
16789        [FOLDED]
16790        [EXCERPT]
16791        a1
16792        b1
16793        ˇ[EXCERPT]
16794        [FOLDED]
16795        [EXCERPT]
16796        [FOLDED]
16797        "
16798    });
16799    cx.simulate_keystroke("up");
16800    cx.assert_excerpts_with_selections(indoc! {"
16801        [EXCERPT]
16802        [FOLDED]
16803        [EXCERPT]
16804        a1
16805        ˇb1
16806        [EXCERPT]
16807        [FOLDED]
16808        [EXCERPT]
16809        [FOLDED]
16810        "
16811    });
16812    cx.simulate_keystroke("up");
16813    cx.assert_excerpts_with_selections(indoc! {"
16814        [EXCERPT]
16815        [FOLDED]
16816        [EXCERPT]
16817        ˇa1
16818        b1
16819        [EXCERPT]
16820        [FOLDED]
16821        [EXCERPT]
16822        [FOLDED]
16823        "
16824    });
16825    for _ in 0..5 {
16826        cx.simulate_keystroke("up");
16827        cx.assert_excerpts_with_selections(indoc! {"
16828            [EXCERPT]
16829            ˇ[FOLDED]
16830            [EXCERPT]
16831            a1
16832            b1
16833            [EXCERPT]
16834            [FOLDED]
16835            [EXCERPT]
16836            [FOLDED]
16837            "
16838        });
16839    }
16840}
16841
16842#[gpui::test]
16843async fn test_inline_completion_text(cx: &mut TestAppContext) {
16844    init_test(cx, |_| {});
16845
16846    // Simple insertion
16847    assert_highlighted_edits(
16848        "Hello, world!",
16849        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16850        true,
16851        cx,
16852        |highlighted_edits, cx| {
16853            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16854            assert_eq!(highlighted_edits.highlights.len(), 1);
16855            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16856            assert_eq!(
16857                highlighted_edits.highlights[0].1.background_color,
16858                Some(cx.theme().status().created_background)
16859            );
16860        },
16861    )
16862    .await;
16863
16864    // Replacement
16865    assert_highlighted_edits(
16866        "This is a test.",
16867        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16868        false,
16869        cx,
16870        |highlighted_edits, cx| {
16871            assert_eq!(highlighted_edits.text, "That is a test.");
16872            assert_eq!(highlighted_edits.highlights.len(), 1);
16873            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16874            assert_eq!(
16875                highlighted_edits.highlights[0].1.background_color,
16876                Some(cx.theme().status().created_background)
16877            );
16878        },
16879    )
16880    .await;
16881
16882    // Multiple edits
16883    assert_highlighted_edits(
16884        "Hello, world!",
16885        vec![
16886            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16887            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16888        ],
16889        false,
16890        cx,
16891        |highlighted_edits, cx| {
16892            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16893            assert_eq!(highlighted_edits.highlights.len(), 2);
16894            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16895            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16896            assert_eq!(
16897                highlighted_edits.highlights[0].1.background_color,
16898                Some(cx.theme().status().created_background)
16899            );
16900            assert_eq!(
16901                highlighted_edits.highlights[1].1.background_color,
16902                Some(cx.theme().status().created_background)
16903            );
16904        },
16905    )
16906    .await;
16907
16908    // Multiple lines with edits
16909    assert_highlighted_edits(
16910        "First line\nSecond line\nThird line\nFourth line",
16911        vec![
16912            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16913            (
16914                Point::new(2, 0)..Point::new(2, 10),
16915                "New third line".to_string(),
16916            ),
16917            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16918        ],
16919        false,
16920        cx,
16921        |highlighted_edits, cx| {
16922            assert_eq!(
16923                highlighted_edits.text,
16924                "Second modified\nNew third line\nFourth updated line"
16925            );
16926            assert_eq!(highlighted_edits.highlights.len(), 3);
16927            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16928            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16929            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16930            for highlight in &highlighted_edits.highlights {
16931                assert_eq!(
16932                    highlight.1.background_color,
16933                    Some(cx.theme().status().created_background)
16934                );
16935            }
16936        },
16937    )
16938    .await;
16939}
16940
16941#[gpui::test]
16942async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16943    init_test(cx, |_| {});
16944
16945    // Deletion
16946    assert_highlighted_edits(
16947        "Hello, world!",
16948        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16949        true,
16950        cx,
16951        |highlighted_edits, cx| {
16952            assert_eq!(highlighted_edits.text, "Hello, world!");
16953            assert_eq!(highlighted_edits.highlights.len(), 1);
16954            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16955            assert_eq!(
16956                highlighted_edits.highlights[0].1.background_color,
16957                Some(cx.theme().status().deleted_background)
16958            );
16959        },
16960    )
16961    .await;
16962
16963    // Insertion
16964    assert_highlighted_edits(
16965        "Hello, world!",
16966        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16967        true,
16968        cx,
16969        |highlighted_edits, cx| {
16970            assert_eq!(highlighted_edits.highlights.len(), 1);
16971            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16972            assert_eq!(
16973                highlighted_edits.highlights[0].1.background_color,
16974                Some(cx.theme().status().created_background)
16975            );
16976        },
16977    )
16978    .await;
16979}
16980
16981async fn assert_highlighted_edits(
16982    text: &str,
16983    edits: Vec<(Range<Point>, String)>,
16984    include_deletions: bool,
16985    cx: &mut TestAppContext,
16986    assertion_fn: impl Fn(HighlightedText, &App),
16987) {
16988    let window = cx.add_window(|window, cx| {
16989        let buffer = MultiBuffer::build_simple(text, cx);
16990        Editor::new(EditorMode::Full, buffer, None, window, cx)
16991    });
16992    let cx = &mut VisualTestContext::from_window(*window, cx);
16993
16994    let (buffer, snapshot) = window
16995        .update(cx, |editor, _window, cx| {
16996            (
16997                editor.buffer().clone(),
16998                editor.buffer().read(cx).snapshot(cx),
16999            )
17000        })
17001        .unwrap();
17002
17003    let edits = edits
17004        .into_iter()
17005        .map(|(range, edit)| {
17006            (
17007                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17008                edit,
17009            )
17010        })
17011        .collect::<Vec<_>>();
17012
17013    let text_anchor_edits = edits
17014        .clone()
17015        .into_iter()
17016        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17017        .collect::<Vec<_>>();
17018
17019    let edit_preview = window
17020        .update(cx, |_, _window, cx| {
17021            buffer
17022                .read(cx)
17023                .as_singleton()
17024                .unwrap()
17025                .read(cx)
17026                .preview_edits(text_anchor_edits.into(), cx)
17027        })
17028        .unwrap()
17029        .await;
17030
17031    cx.update(|_window, cx| {
17032        let highlighted_edits = inline_completion_edit_text(
17033            &snapshot.as_singleton().unwrap().2,
17034            &edits,
17035            &edit_preview,
17036            include_deletions,
17037            cx,
17038        );
17039        assertion_fn(highlighted_edits, cx)
17040    });
17041}
17042
17043#[gpui::test]
17044async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17045    init_test(cx, |_| {});
17046    let capabilities = lsp::ServerCapabilities {
17047        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17048            prepare_provider: Some(true),
17049            work_done_progress_options: Default::default(),
17050        })),
17051        ..Default::default()
17052    };
17053    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17054
17055    cx.set_state(indoc! {"
17056        struct Fˇoo {}
17057    "});
17058
17059    cx.update_editor(|editor, _, cx| {
17060        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17061        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17062        editor.highlight_background::<DocumentHighlightRead>(
17063            &[highlight_range],
17064            |c| c.editor_document_highlight_read_background,
17065            cx,
17066        );
17067    });
17068
17069    let mut prepare_rename_handler =
17070        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17071            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17072                start: lsp::Position {
17073                    line: 0,
17074                    character: 7,
17075                },
17076                end: lsp::Position {
17077                    line: 0,
17078                    character: 10,
17079                },
17080            })))
17081        });
17082    let prepare_rename_task = cx
17083        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17084        .expect("Prepare rename was not started");
17085    prepare_rename_handler.next().await.unwrap();
17086    prepare_rename_task.await.expect("Prepare rename failed");
17087
17088    let mut rename_handler =
17089        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17090            let edit = lsp::TextEdit {
17091                range: lsp::Range {
17092                    start: lsp::Position {
17093                        line: 0,
17094                        character: 7,
17095                    },
17096                    end: lsp::Position {
17097                        line: 0,
17098                        character: 10,
17099                    },
17100                },
17101                new_text: "FooRenamed".to_string(),
17102            };
17103            Ok(Some(lsp::WorkspaceEdit::new(
17104                // Specify the same edit twice
17105                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17106            )))
17107        });
17108    let rename_task = cx
17109        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17110        .expect("Confirm rename was not started");
17111    rename_handler.next().await.unwrap();
17112    rename_task.await.expect("Confirm rename failed");
17113    cx.run_until_parked();
17114
17115    // Despite two edits, only one is actually applied as those are identical
17116    cx.assert_editor_state(indoc! {"
17117        struct FooRenamedˇ {}
17118    "});
17119}
17120
17121#[gpui::test]
17122async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17123    init_test(cx, |_| {});
17124    // These capabilities indicate that the server does not support prepare rename.
17125    let capabilities = lsp::ServerCapabilities {
17126        rename_provider: Some(lsp::OneOf::Left(true)),
17127        ..Default::default()
17128    };
17129    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17130
17131    cx.set_state(indoc! {"
17132        struct Fˇoo {}
17133    "});
17134
17135    cx.update_editor(|editor, _window, cx| {
17136        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17137        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17138        editor.highlight_background::<DocumentHighlightRead>(
17139            &[highlight_range],
17140            |c| c.editor_document_highlight_read_background,
17141            cx,
17142        );
17143    });
17144
17145    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17146        .expect("Prepare rename was not started")
17147        .await
17148        .expect("Prepare rename failed");
17149
17150    let mut rename_handler =
17151        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17152            let edit = lsp::TextEdit {
17153                range: lsp::Range {
17154                    start: lsp::Position {
17155                        line: 0,
17156                        character: 7,
17157                    },
17158                    end: lsp::Position {
17159                        line: 0,
17160                        character: 10,
17161                    },
17162                },
17163                new_text: "FooRenamed".to_string(),
17164            };
17165            Ok(Some(lsp::WorkspaceEdit::new(
17166                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17167            )))
17168        });
17169    let rename_task = cx
17170        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17171        .expect("Confirm rename was not started");
17172    rename_handler.next().await.unwrap();
17173    rename_task.await.expect("Confirm rename failed");
17174    cx.run_until_parked();
17175
17176    // Correct range is renamed, as `surrounding_word` is used to find it.
17177    cx.assert_editor_state(indoc! {"
17178        struct FooRenamedˇ {}
17179    "});
17180}
17181
17182#[gpui::test]
17183async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17184    init_test(cx, |_| {});
17185    let mut cx = EditorTestContext::new(cx).await;
17186
17187    let language = Arc::new(
17188        Language::new(
17189            LanguageConfig::default(),
17190            Some(tree_sitter_html::LANGUAGE.into()),
17191        )
17192        .with_brackets_query(
17193            r#"
17194            ("<" @open "/>" @close)
17195            ("</" @open ">" @close)
17196            ("<" @open ">" @close)
17197            ("\"" @open "\"" @close)
17198            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17199        "#,
17200        )
17201        .unwrap(),
17202    );
17203    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17204
17205    cx.set_state(indoc! {"
17206        <span>ˇ</span>
17207    "});
17208    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17209    cx.assert_editor_state(indoc! {"
17210        <span>
17211        ˇ
17212        </span>
17213    "});
17214
17215    cx.set_state(indoc! {"
17216        <span><span></span>ˇ</span>
17217    "});
17218    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17219    cx.assert_editor_state(indoc! {"
17220        <span><span></span>
17221        ˇ</span>
17222    "});
17223
17224    cx.set_state(indoc! {"
17225        <span>ˇ
17226        </span>
17227    "});
17228    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17229    cx.assert_editor_state(indoc! {"
17230        <span>
17231        ˇ
17232        </span>
17233    "});
17234}
17235
17236#[gpui::test(iterations = 10)]
17237async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17238    init_test(cx, |_| {});
17239
17240    let fs = FakeFs::new(cx.executor());
17241    fs.insert_tree(
17242        path!("/dir"),
17243        json!({
17244            "a.ts": "a",
17245        }),
17246    )
17247    .await;
17248
17249    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17250    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17251    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17252
17253    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17254    language_registry.add(Arc::new(Language::new(
17255        LanguageConfig {
17256            name: "TypeScript".into(),
17257            matcher: LanguageMatcher {
17258                path_suffixes: vec!["ts".to_string()],
17259                ..Default::default()
17260            },
17261            ..Default::default()
17262        },
17263        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17264    )));
17265    let mut fake_language_servers = language_registry.register_fake_lsp(
17266        "TypeScript",
17267        FakeLspAdapter {
17268            capabilities: lsp::ServerCapabilities {
17269                code_lens_provider: Some(lsp::CodeLensOptions {
17270                    resolve_provider: Some(true),
17271                }),
17272                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17273                    commands: vec!["_the/command".to_string()],
17274                    ..lsp::ExecuteCommandOptions::default()
17275                }),
17276                ..lsp::ServerCapabilities::default()
17277            },
17278            ..FakeLspAdapter::default()
17279        },
17280    );
17281
17282    let (buffer, _handle) = project
17283        .update(cx, |p, cx| {
17284            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17285        })
17286        .await
17287        .unwrap();
17288    cx.executor().run_until_parked();
17289
17290    let fake_server = fake_language_servers.next().await.unwrap();
17291
17292    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17293    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17294    drop(buffer_snapshot);
17295    let actions = cx
17296        .update_window(*workspace, |_, window, cx| {
17297            project.code_actions(&buffer, anchor..anchor, window, cx)
17298        })
17299        .unwrap();
17300
17301    fake_server
17302        .handle_request::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17303            Ok(Some(vec![
17304                lsp::CodeLens {
17305                    range: lsp::Range::default(),
17306                    command: Some(lsp::Command {
17307                        title: "Code lens command".to_owned(),
17308                        command: "_the/command".to_owned(),
17309                        arguments: None,
17310                    }),
17311                    data: None,
17312                },
17313                lsp::CodeLens {
17314                    range: lsp::Range::default(),
17315                    command: Some(lsp::Command {
17316                        title: "Command not in capabilities".to_owned(),
17317                        command: "not in capabilities".to_owned(),
17318                        arguments: None,
17319                    }),
17320                    data: None,
17321                },
17322                lsp::CodeLens {
17323                    range: lsp::Range {
17324                        start: lsp::Position {
17325                            line: 1,
17326                            character: 1,
17327                        },
17328                        end: lsp::Position {
17329                            line: 1,
17330                            character: 1,
17331                        },
17332                    },
17333                    command: Some(lsp::Command {
17334                        title: "Command not in range".to_owned(),
17335                        command: "_the/command".to_owned(),
17336                        arguments: None,
17337                    }),
17338                    data: None,
17339                },
17340            ]))
17341        })
17342        .next()
17343        .await;
17344
17345    let actions = actions.await.unwrap();
17346    assert_eq!(
17347        actions.len(),
17348        1,
17349        "Should have only one valid action for the 0..0 range"
17350    );
17351    let action = actions[0].clone();
17352    let apply = project.update(cx, |project, cx| {
17353        project.apply_code_action(buffer.clone(), action, true, cx)
17354    });
17355
17356    // Resolving the code action does not populate its edits. In absence of
17357    // edits, we must execute the given command.
17358    fake_server.handle_request::<lsp::request::CodeLensResolve, _, _>(|mut lens, _| async move {
17359        let lens_command = lens.command.as_mut().expect("should have a command");
17360        assert_eq!(lens_command.title, "Code lens command");
17361        lens_command.arguments = Some(vec![json!("the-argument")]);
17362        Ok(lens)
17363    });
17364
17365    // While executing the command, the language server sends the editor
17366    // a `workspaceEdit` request.
17367    fake_server
17368        .handle_request::<lsp::request::ExecuteCommand, _, _>({
17369            let fake = fake_server.clone();
17370            move |params, _| {
17371                assert_eq!(params.command, "_the/command");
17372                let fake = fake.clone();
17373                async move {
17374                    fake.server
17375                        .request::<lsp::request::ApplyWorkspaceEdit>(
17376                            lsp::ApplyWorkspaceEditParams {
17377                                label: None,
17378                                edit: lsp::WorkspaceEdit {
17379                                    changes: Some(
17380                                        [(
17381                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
17382                                            vec![lsp::TextEdit {
17383                                                range: lsp::Range::new(
17384                                                    lsp::Position::new(0, 0),
17385                                                    lsp::Position::new(0, 0),
17386                                                ),
17387                                                new_text: "X".into(),
17388                                            }],
17389                                        )]
17390                                        .into_iter()
17391                                        .collect(),
17392                                    ),
17393                                    ..Default::default()
17394                                },
17395                            },
17396                        )
17397                        .await
17398                        .unwrap();
17399                    Ok(Some(json!(null)))
17400                }
17401            }
17402        })
17403        .next()
17404        .await;
17405
17406    // Applying the code lens command returns a project transaction containing the edits
17407    // sent by the language server in its `workspaceEdit` request.
17408    let transaction = apply.await.unwrap();
17409    assert!(transaction.0.contains_key(&buffer));
17410    buffer.update(cx, |buffer, cx| {
17411        assert_eq!(buffer.text(), "Xa");
17412        buffer.undo(cx);
17413        assert_eq!(buffer.text(), "a");
17414    });
17415}
17416
17417mod autoclose_tags {
17418    use super::*;
17419    use language::language_settings::JsxTagAutoCloseSettings;
17420    use languages::language;
17421
17422    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17423        init_test(cx, |settings| {
17424            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17425        });
17426
17427        let mut cx = EditorTestContext::new(cx).await;
17428        cx.update_buffer(|buffer, cx| {
17429            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17430
17431            buffer.set_language(Some(language), cx)
17432        });
17433
17434        cx
17435    }
17436
17437    macro_rules! check {
17438        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17439            #[gpui::test]
17440            async fn $name(cx: &mut TestAppContext) {
17441                let mut cx = test_setup(cx).await;
17442                cx.set_state($initial);
17443                cx.run_until_parked();
17444
17445                cx.update_editor(|editor, window, cx| {
17446                    editor.handle_input($input, window, cx);
17447                });
17448                cx.run_until_parked();
17449                cx.assert_editor_state($expected);
17450            }
17451        };
17452    }
17453
17454    check!(
17455        test_basic,
17456        "<divˇ" + ">" => "<div>ˇ</div>"
17457    );
17458
17459    check!(
17460        test_basic_nested,
17461        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17462    );
17463
17464    check!(
17465        test_basic_ignore_already_closed,
17466        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17467    );
17468
17469    check!(
17470        test_doesnt_autoclose_closing_tag,
17471        "</divˇ" + ">" => "</div>ˇ"
17472    );
17473
17474    check!(
17475        test_jsx_attr,
17476        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17477    );
17478
17479    check!(
17480        test_ignores_closing_tags_in_expr_block,
17481        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17482    );
17483
17484    check!(
17485        test_doesnt_autoclose_on_gt_in_expr,
17486        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17487    );
17488
17489    check!(
17490        test_ignores_closing_tags_with_different_tag_names,
17491        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17492    );
17493
17494    check!(
17495        test_autocloses_in_jsx_expression,
17496        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17497    );
17498
17499    check!(
17500        test_doesnt_autoclose_already_closed_in_jsx_expression,
17501        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17502    );
17503
17504    check!(
17505        test_autocloses_fragment,
17506        "" + ">" => "<>ˇ</>"
17507    );
17508
17509    check!(
17510        test_does_not_include_type_argument_in_autoclose_tag_name,
17511        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17512    );
17513
17514    check!(
17515        test_does_not_autoclose_doctype,
17516        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17517    );
17518
17519    check!(
17520        test_does_not_autoclose_comment,
17521        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17522    );
17523
17524    check!(
17525        test_multi_cursor_autoclose_same_tag,
17526        r#"
17527        <divˇ
17528        <divˇ
17529        "#
17530        + ">" =>
17531        r#"
17532        <div>ˇ</div>
17533        <div>ˇ</div>
17534        "#
17535    );
17536
17537    check!(
17538        test_multi_cursor_autoclose_different_tags,
17539        r#"
17540        <divˇ
17541        <spanˇ
17542        "#
17543        + ">" =>
17544        r#"
17545        <div>ˇ</div>
17546        <span>ˇ</span>
17547        "#
17548    );
17549
17550    check!(
17551        test_multi_cursor_autoclose_some_dont_autoclose_others,
17552        r#"
17553        <divˇ
17554        <div /ˇ
17555        <spanˇ</span>
17556        <!DOCTYPE htmlˇ
17557        </headˇ
17558        <Component<T>ˇ
17559        ˇ
17560        "#
17561        + ">" =>
17562        r#"
17563        <div>ˇ</div>
17564        <div />ˇ
17565        <span>ˇ</span>
17566        <!DOCTYPE html>ˇ
17567        </head>ˇ
17568        <Component<T>>ˇ</Component>
1756917570        "#
17571    );
17572
17573    check!(
17574        test_doesnt_mess_up_trailing_text,
17575        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17576    );
17577
17578    #[gpui::test]
17579    async fn test_multibuffer(cx: &mut TestAppContext) {
17580        init_test(cx, |settings| {
17581            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17582        });
17583
17584        let buffer_a = cx.new(|cx| {
17585            let mut buf = language::Buffer::local("<div", cx);
17586            buf.set_language(
17587                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17588                cx,
17589            );
17590            buf
17591        });
17592        let buffer_b = cx.new(|cx| {
17593            let mut buf = language::Buffer::local("<pre", cx);
17594            buf.set_language(
17595                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17596                cx,
17597            );
17598            buf
17599        });
17600        let buffer_c = cx.new(|cx| {
17601            let buf = language::Buffer::local("<span", cx);
17602            buf
17603        });
17604        let buffer = cx.new(|cx| {
17605            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17606            buf.push_excerpts(
17607                buffer_a,
17608                [ExcerptRange {
17609                    context: text::Anchor::MIN..text::Anchor::MAX,
17610                    primary: None,
17611                }],
17612                cx,
17613            );
17614            buf.push_excerpts(
17615                buffer_b,
17616                [ExcerptRange {
17617                    context: text::Anchor::MIN..text::Anchor::MAX,
17618                    primary: None,
17619                }],
17620                cx,
17621            );
17622            buf.push_excerpts(
17623                buffer_c,
17624                [ExcerptRange {
17625                    context: text::Anchor::MIN..text::Anchor::MAX,
17626                    primary: None,
17627                }],
17628                cx,
17629            );
17630            buf
17631        });
17632        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17633
17634        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17635
17636        cx.update_editor(|editor, window, cx| {
17637            editor.change_selections(None, window, cx, |selections| {
17638                selections.select(vec![
17639                    Selection::from_offset(4),
17640                    Selection::from_offset(9),
17641                    Selection::from_offset(15),
17642                ])
17643            })
17644        });
17645        cx.run_until_parked();
17646
17647        cx.update_editor(|editor, window, cx| {
17648            editor.handle_input(">", window, cx);
17649        });
17650        cx.run_until_parked();
17651
17652        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17653    }
17654}
17655
17656fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17657    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17658    point..point
17659}
17660
17661fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17662    let (text, ranges) = marked_text_ranges(marked_text, true);
17663    assert_eq!(editor.text(cx), text);
17664    assert_eq!(
17665        editor.selections.ranges(cx),
17666        ranges,
17667        "Assert selections are {}",
17668        marked_text
17669    );
17670}
17671
17672pub fn handle_signature_help_request(
17673    cx: &mut EditorLspTestContext,
17674    mocked_response: lsp::SignatureHelp,
17675) -> impl Future<Output = ()> {
17676    let mut request =
17677        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17678            let mocked_response = mocked_response.clone();
17679            async move { Ok(Some(mocked_response)) }
17680        });
17681
17682    async move {
17683        request.next().await;
17684    }
17685}
17686
17687/// Handle completion request passing a marked string specifying where the completion
17688/// should be triggered from using '|' character, what range should be replaced, and what completions
17689/// should be returned using '<' and '>' to delimit the range
17690pub fn handle_completion_request(
17691    cx: &mut EditorLspTestContext,
17692    marked_string: &str,
17693    completions: Vec<&'static str>,
17694    counter: Arc<AtomicUsize>,
17695) -> impl Future<Output = ()> {
17696    let complete_from_marker: TextRangeMarker = '|'.into();
17697    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17698    let (_, mut marked_ranges) = marked_text_ranges_by(
17699        marked_string,
17700        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17701    );
17702
17703    let complete_from_position =
17704        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17705    let replace_range =
17706        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17707
17708    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17709        let completions = completions.clone();
17710        counter.fetch_add(1, atomic::Ordering::Release);
17711        async move {
17712            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17713            assert_eq!(
17714                params.text_document_position.position,
17715                complete_from_position
17716            );
17717            Ok(Some(lsp::CompletionResponse::Array(
17718                completions
17719                    .iter()
17720                    .map(|completion_text| lsp::CompletionItem {
17721                        label: completion_text.to_string(),
17722                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17723                            range: replace_range,
17724                            new_text: completion_text.to_string(),
17725                        })),
17726                        ..Default::default()
17727                    })
17728                    .collect(),
17729            )))
17730        }
17731    });
17732
17733    async move {
17734        request.next().await;
17735    }
17736}
17737
17738fn handle_resolve_completion_request(
17739    cx: &mut EditorLspTestContext,
17740    edits: Option<Vec<(&'static str, &'static str)>>,
17741) -> impl Future<Output = ()> {
17742    let edits = edits.map(|edits| {
17743        edits
17744            .iter()
17745            .map(|(marked_string, new_text)| {
17746                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17747                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17748                lsp::TextEdit::new(replace_range, new_text.to_string())
17749            })
17750            .collect::<Vec<_>>()
17751    });
17752
17753    let mut request =
17754        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17755            let edits = edits.clone();
17756            async move {
17757                Ok(lsp::CompletionItem {
17758                    additional_text_edits: edits,
17759                    ..Default::default()
17760                })
17761            }
17762        });
17763
17764    async move {
17765        request.next().await;
17766    }
17767}
17768
17769pub(crate) fn update_test_language_settings(
17770    cx: &mut TestAppContext,
17771    f: impl Fn(&mut AllLanguageSettingsContent),
17772) {
17773    cx.update(|cx| {
17774        SettingsStore::update_global(cx, |store, cx| {
17775            store.update_user_settings::<AllLanguageSettings>(cx, f);
17776        });
17777    });
17778}
17779
17780pub(crate) fn update_test_project_settings(
17781    cx: &mut TestAppContext,
17782    f: impl Fn(&mut ProjectSettings),
17783) {
17784    cx.update(|cx| {
17785        SettingsStore::update_global(cx, |store, cx| {
17786            store.update_user_settings::<ProjectSettings>(cx, f);
17787        });
17788    });
17789}
17790
17791pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17792    cx.update(|cx| {
17793        assets::Assets.load_test_fonts(cx);
17794        let store = SettingsStore::test(cx);
17795        cx.set_global(store);
17796        theme::init(theme::LoadThemes::JustBase, cx);
17797        release_channel::init(SemanticVersion::default(), cx);
17798        client::init_settings(cx);
17799        language::init(cx);
17800        Project::init_settings(cx);
17801        workspace::init_settings(cx);
17802        crate::init(cx);
17803    });
17804
17805    update_test_language_settings(cx, f);
17806}
17807
17808#[track_caller]
17809fn assert_hunk_revert(
17810    not_reverted_text_with_selections: &str,
17811    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17812    expected_reverted_text_with_selections: &str,
17813    base_text: &str,
17814    cx: &mut EditorLspTestContext,
17815) {
17816    cx.set_state(not_reverted_text_with_selections);
17817    cx.set_head_text(base_text);
17818    cx.executor().run_until_parked();
17819
17820    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17821        let snapshot = editor.snapshot(window, cx);
17822        let reverted_hunk_statuses = snapshot
17823            .buffer_snapshot
17824            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17825            .map(|hunk| hunk.status().kind)
17826            .collect::<Vec<_>>();
17827
17828        editor.git_restore(&Default::default(), window, cx);
17829        reverted_hunk_statuses
17830    });
17831    cx.executor().run_until_parked();
17832    cx.assert_editor_state(expected_reverted_text_with_selections);
17833    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17834}