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    // Autclose pair where the start and end characters are the same
 6361    cx.set_state("");
 6362    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6363    cx.assert_editor_state("a\"ˇ\"");
 6364    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6365    cx.assert_editor_state("a\"\"ˇ");
 6366
 6367    // Don't autoclose pair if autoclose is disabled
 6368    cx.set_state("ˇ");
 6369    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6370    cx.assert_editor_state("");
 6371
 6372    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6373    cx.set_state("«aˇ» b");
 6374    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6375    cx.assert_editor_state("<«aˇ»> b");
 6376}
 6377
 6378#[gpui::test]
 6379async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6380    init_test(cx, |settings| {
 6381        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6382    });
 6383
 6384    let mut cx = EditorTestContext::new(cx).await;
 6385
 6386    let language = Arc::new(Language::new(
 6387        LanguageConfig {
 6388            brackets: BracketPairConfig {
 6389                pairs: vec![
 6390                    BracketPair {
 6391                        start: "{".to_string(),
 6392                        end: "}".to_string(),
 6393                        close: true,
 6394                        surround: true,
 6395                        newline: true,
 6396                    },
 6397                    BracketPair {
 6398                        start: "(".to_string(),
 6399                        end: ")".to_string(),
 6400                        close: true,
 6401                        surround: true,
 6402                        newline: true,
 6403                    },
 6404                    BracketPair {
 6405                        start: "[".to_string(),
 6406                        end: "]".to_string(),
 6407                        close: false,
 6408                        surround: false,
 6409                        newline: true,
 6410                    },
 6411                ],
 6412                ..Default::default()
 6413            },
 6414            autoclose_before: "})]".to_string(),
 6415            ..Default::default()
 6416        },
 6417        Some(tree_sitter_rust::LANGUAGE.into()),
 6418    ));
 6419
 6420    cx.language_registry().add(language.clone());
 6421    cx.update_buffer(|buffer, cx| {
 6422        buffer.set_language(Some(language), cx);
 6423    });
 6424
 6425    cx.set_state(
 6426        &"
 6427            ˇ
 6428            ˇ
 6429            ˇ
 6430        "
 6431        .unindent(),
 6432    );
 6433
 6434    // ensure only matching closing brackets are skipped over
 6435    cx.update_editor(|editor, window, cx| {
 6436        editor.handle_input("}", window, cx);
 6437        editor.move_left(&MoveLeft, window, cx);
 6438        editor.handle_input(")", window, cx);
 6439        editor.move_left(&MoveLeft, window, cx);
 6440    });
 6441    cx.assert_editor_state(
 6442        &"
 6443            ˇ)}
 6444            ˇ)}
 6445            ˇ)}
 6446        "
 6447        .unindent(),
 6448    );
 6449
 6450    // skip-over closing brackets at multiple cursors
 6451    cx.update_editor(|editor, window, cx| {
 6452        editor.handle_input(")", window, cx);
 6453        editor.handle_input("}", window, cx);
 6454    });
 6455    cx.assert_editor_state(
 6456        &"
 6457            )}ˇ
 6458            )}ˇ
 6459            )}ˇ
 6460        "
 6461        .unindent(),
 6462    );
 6463
 6464    // ignore non-close brackets
 6465    cx.update_editor(|editor, window, cx| {
 6466        editor.handle_input("]", window, cx);
 6467        editor.move_left(&MoveLeft, window, cx);
 6468        editor.handle_input("]", window, cx);
 6469    });
 6470    cx.assert_editor_state(
 6471        &"
 6472            )}]ˇ]
 6473            )}]ˇ]
 6474            )}]ˇ]
 6475        "
 6476        .unindent(),
 6477    );
 6478}
 6479
 6480#[gpui::test]
 6481async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6482    init_test(cx, |_| {});
 6483
 6484    let mut cx = EditorTestContext::new(cx).await;
 6485
 6486    let html_language = Arc::new(
 6487        Language::new(
 6488            LanguageConfig {
 6489                name: "HTML".into(),
 6490                brackets: BracketPairConfig {
 6491                    pairs: vec![
 6492                        BracketPair {
 6493                            start: "<".into(),
 6494                            end: ">".into(),
 6495                            close: true,
 6496                            ..Default::default()
 6497                        },
 6498                        BracketPair {
 6499                            start: "{".into(),
 6500                            end: "}".into(),
 6501                            close: true,
 6502                            ..Default::default()
 6503                        },
 6504                        BracketPair {
 6505                            start: "(".into(),
 6506                            end: ")".into(),
 6507                            close: true,
 6508                            ..Default::default()
 6509                        },
 6510                    ],
 6511                    ..Default::default()
 6512                },
 6513                autoclose_before: "})]>".into(),
 6514                ..Default::default()
 6515            },
 6516            Some(tree_sitter_html::LANGUAGE.into()),
 6517        )
 6518        .with_injection_query(
 6519            r#"
 6520            (script_element
 6521                (raw_text) @injection.content
 6522                (#set! injection.language "javascript"))
 6523            "#,
 6524        )
 6525        .unwrap(),
 6526    );
 6527
 6528    let javascript_language = Arc::new(Language::new(
 6529        LanguageConfig {
 6530            name: "JavaScript".into(),
 6531            brackets: BracketPairConfig {
 6532                pairs: vec![
 6533                    BracketPair {
 6534                        start: "/*".into(),
 6535                        end: " */".into(),
 6536                        close: true,
 6537                        ..Default::default()
 6538                    },
 6539                    BracketPair {
 6540                        start: "{".into(),
 6541                        end: "}".into(),
 6542                        close: true,
 6543                        ..Default::default()
 6544                    },
 6545                    BracketPair {
 6546                        start: "(".into(),
 6547                        end: ")".into(),
 6548                        close: true,
 6549                        ..Default::default()
 6550                    },
 6551                ],
 6552                ..Default::default()
 6553            },
 6554            autoclose_before: "})]>".into(),
 6555            ..Default::default()
 6556        },
 6557        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6558    ));
 6559
 6560    cx.language_registry().add(html_language.clone());
 6561    cx.language_registry().add(javascript_language.clone());
 6562
 6563    cx.update_buffer(|buffer, cx| {
 6564        buffer.set_language(Some(html_language), cx);
 6565    });
 6566
 6567    cx.set_state(
 6568        &r#"
 6569            <body>ˇ
 6570                <script>
 6571                    var x = 1;ˇ
 6572                </script>
 6573            </body>ˇ
 6574        "#
 6575        .unindent(),
 6576    );
 6577
 6578    // Precondition: different languages are active at different locations.
 6579    cx.update_editor(|editor, window, cx| {
 6580        let snapshot = editor.snapshot(window, cx);
 6581        let cursors = editor.selections.ranges::<usize>(cx);
 6582        let languages = cursors
 6583            .iter()
 6584            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6585            .collect::<Vec<_>>();
 6586        assert_eq!(
 6587            languages,
 6588            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6589        );
 6590    });
 6591
 6592    // Angle brackets autoclose in HTML, but not JavaScript.
 6593    cx.update_editor(|editor, window, cx| {
 6594        editor.handle_input("<", window, cx);
 6595        editor.handle_input("a", window, cx);
 6596    });
 6597    cx.assert_editor_state(
 6598        &r#"
 6599            <body><aˇ>
 6600                <script>
 6601                    var x = 1;<aˇ
 6602                </script>
 6603            </body><aˇ>
 6604        "#
 6605        .unindent(),
 6606    );
 6607
 6608    // Curly braces and parens autoclose in both HTML and JavaScript.
 6609    cx.update_editor(|editor, window, cx| {
 6610        editor.handle_input(" b=", window, cx);
 6611        editor.handle_input("{", window, cx);
 6612        editor.handle_input("c", window, cx);
 6613        editor.handle_input("(", window, cx);
 6614    });
 6615    cx.assert_editor_state(
 6616        &r#"
 6617            <body><a b={c(ˇ)}>
 6618                <script>
 6619                    var x = 1;<a b={c(ˇ)}
 6620                </script>
 6621            </body><a b={c(ˇ)}>
 6622        "#
 6623        .unindent(),
 6624    );
 6625
 6626    // Brackets that were already autoclosed are skipped.
 6627    cx.update_editor(|editor, window, cx| {
 6628        editor.handle_input(")", window, cx);
 6629        editor.handle_input("d", window, cx);
 6630        editor.handle_input("}", window, cx);
 6631    });
 6632    cx.assert_editor_state(
 6633        &r#"
 6634            <body><a b={c()d}ˇ>
 6635                <script>
 6636                    var x = 1;<a b={c()d}ˇ
 6637                </script>
 6638            </body><a b={c()d}ˇ>
 6639        "#
 6640        .unindent(),
 6641    );
 6642    cx.update_editor(|editor, window, cx| {
 6643        editor.handle_input(">", window, cx);
 6644    });
 6645    cx.assert_editor_state(
 6646        &r#"
 6647            <body><a b={c()d}>ˇ
 6648                <script>
 6649                    var x = 1;<a b={c()d}>ˇ
 6650                </script>
 6651            </body><a b={c()d}>ˇ
 6652        "#
 6653        .unindent(),
 6654    );
 6655
 6656    // Reset
 6657    cx.set_state(
 6658        &r#"
 6659            <body>ˇ
 6660                <script>
 6661                    var x = 1;ˇ
 6662                </script>
 6663            </body>ˇ
 6664        "#
 6665        .unindent(),
 6666    );
 6667
 6668    cx.update_editor(|editor, window, cx| {
 6669        editor.handle_input("<", window, cx);
 6670    });
 6671    cx.assert_editor_state(
 6672        &r#"
 6673            <body><ˇ>
 6674                <script>
 6675                    var x = 1;<ˇ
 6676                </script>
 6677            </body><ˇ>
 6678        "#
 6679        .unindent(),
 6680    );
 6681
 6682    // When backspacing, the closing angle brackets are removed.
 6683    cx.update_editor(|editor, window, cx| {
 6684        editor.backspace(&Backspace, window, cx);
 6685    });
 6686    cx.assert_editor_state(
 6687        &r#"
 6688            <body>ˇ
 6689                <script>
 6690                    var x = 1;ˇ
 6691                </script>
 6692            </body>ˇ
 6693        "#
 6694        .unindent(),
 6695    );
 6696
 6697    // Block comments autoclose in JavaScript, but not HTML.
 6698    cx.update_editor(|editor, window, cx| {
 6699        editor.handle_input("/", window, cx);
 6700        editor.handle_input("*", window, cx);
 6701    });
 6702    cx.assert_editor_state(
 6703        &r#"
 6704            <body>/*ˇ
 6705                <script>
 6706                    var x = 1;/*ˇ */
 6707                </script>
 6708            </body>/*ˇ
 6709        "#
 6710        .unindent(),
 6711    );
 6712}
 6713
 6714#[gpui::test]
 6715async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6716    init_test(cx, |_| {});
 6717
 6718    let mut cx = EditorTestContext::new(cx).await;
 6719
 6720    let rust_language = Arc::new(
 6721        Language::new(
 6722            LanguageConfig {
 6723                name: "Rust".into(),
 6724                brackets: serde_json::from_value(json!([
 6725                    { "start": "{", "end": "}", "close": true, "newline": true },
 6726                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6727                ]))
 6728                .unwrap(),
 6729                autoclose_before: "})]>".into(),
 6730                ..Default::default()
 6731            },
 6732            Some(tree_sitter_rust::LANGUAGE.into()),
 6733        )
 6734        .with_override_query("(string_literal) @string")
 6735        .unwrap(),
 6736    );
 6737
 6738    cx.language_registry().add(rust_language.clone());
 6739    cx.update_buffer(|buffer, cx| {
 6740        buffer.set_language(Some(rust_language), cx);
 6741    });
 6742
 6743    cx.set_state(
 6744        &r#"
 6745            let x = ˇ
 6746        "#
 6747        .unindent(),
 6748    );
 6749
 6750    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6751    cx.update_editor(|editor, window, cx| {
 6752        editor.handle_input("\"", window, cx);
 6753    });
 6754    cx.assert_editor_state(
 6755        &r#"
 6756            let x = "ˇ"
 6757        "#
 6758        .unindent(),
 6759    );
 6760
 6761    // Inserting another quotation mark. The cursor moves across the existing
 6762    // automatically-inserted quotation mark.
 6763    cx.update_editor(|editor, window, cx| {
 6764        editor.handle_input("\"", window, cx);
 6765    });
 6766    cx.assert_editor_state(
 6767        &r#"
 6768            let x = ""ˇ
 6769        "#
 6770        .unindent(),
 6771    );
 6772
 6773    // Reset
 6774    cx.set_state(
 6775        &r#"
 6776            let x = ˇ
 6777        "#
 6778        .unindent(),
 6779    );
 6780
 6781    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6782    cx.update_editor(|editor, window, cx| {
 6783        editor.handle_input("\"", window, cx);
 6784        editor.handle_input(" ", window, cx);
 6785        editor.move_left(&Default::default(), window, cx);
 6786        editor.handle_input("\\", window, cx);
 6787        editor.handle_input("\"", window, cx);
 6788    });
 6789    cx.assert_editor_state(
 6790        &r#"
 6791            let x = "\"ˇ "
 6792        "#
 6793        .unindent(),
 6794    );
 6795
 6796    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6797    // mark. Nothing is inserted.
 6798    cx.update_editor(|editor, window, cx| {
 6799        editor.move_right(&Default::default(), window, cx);
 6800        editor.handle_input("\"", window, cx);
 6801    });
 6802    cx.assert_editor_state(
 6803        &r#"
 6804            let x = "\" "ˇ
 6805        "#
 6806        .unindent(),
 6807    );
 6808}
 6809
 6810#[gpui::test]
 6811async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6812    init_test(cx, |_| {});
 6813
 6814    let language = Arc::new(Language::new(
 6815        LanguageConfig {
 6816            brackets: BracketPairConfig {
 6817                pairs: vec![
 6818                    BracketPair {
 6819                        start: "{".to_string(),
 6820                        end: "}".to_string(),
 6821                        close: true,
 6822                        surround: true,
 6823                        newline: true,
 6824                    },
 6825                    BracketPair {
 6826                        start: "/* ".to_string(),
 6827                        end: "*/".to_string(),
 6828                        close: true,
 6829                        surround: true,
 6830                        ..Default::default()
 6831                    },
 6832                ],
 6833                ..Default::default()
 6834            },
 6835            ..Default::default()
 6836        },
 6837        Some(tree_sitter_rust::LANGUAGE.into()),
 6838    ));
 6839
 6840    let text = r#"
 6841        a
 6842        b
 6843        c
 6844    "#
 6845    .unindent();
 6846
 6847    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6848    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6849    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6850    editor
 6851        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6852        .await;
 6853
 6854    editor.update_in(cx, |editor, window, cx| {
 6855        editor.change_selections(None, window, cx, |s| {
 6856            s.select_display_ranges([
 6857                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6858                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6859                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6860            ])
 6861        });
 6862
 6863        editor.handle_input("{", window, cx);
 6864        editor.handle_input("{", window, cx);
 6865        editor.handle_input("{", window, cx);
 6866        assert_eq!(
 6867            editor.text(cx),
 6868            "
 6869                {{{a}}}
 6870                {{{b}}}
 6871                {{{c}}}
 6872            "
 6873            .unindent()
 6874        );
 6875        assert_eq!(
 6876            editor.selections.display_ranges(cx),
 6877            [
 6878                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6879                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6880                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6881            ]
 6882        );
 6883
 6884        editor.undo(&Undo, window, cx);
 6885        editor.undo(&Undo, window, cx);
 6886        editor.undo(&Undo, window, cx);
 6887        assert_eq!(
 6888            editor.text(cx),
 6889            "
 6890                a
 6891                b
 6892                c
 6893            "
 6894            .unindent()
 6895        );
 6896        assert_eq!(
 6897            editor.selections.display_ranges(cx),
 6898            [
 6899                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6900                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6901                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6902            ]
 6903        );
 6904
 6905        // Ensure inserting the first character of a multi-byte bracket pair
 6906        // doesn't surround the selections with the bracket.
 6907        editor.handle_input("/", window, cx);
 6908        assert_eq!(
 6909            editor.text(cx),
 6910            "
 6911                /
 6912                /
 6913                /
 6914            "
 6915            .unindent()
 6916        );
 6917        assert_eq!(
 6918            editor.selections.display_ranges(cx),
 6919            [
 6920                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6921                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6922                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6923            ]
 6924        );
 6925
 6926        editor.undo(&Undo, window, cx);
 6927        assert_eq!(
 6928            editor.text(cx),
 6929            "
 6930                a
 6931                b
 6932                c
 6933            "
 6934            .unindent()
 6935        );
 6936        assert_eq!(
 6937            editor.selections.display_ranges(cx),
 6938            [
 6939                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6940                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6941                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6942            ]
 6943        );
 6944
 6945        // Ensure inserting the last character of a multi-byte bracket pair
 6946        // doesn't surround the selections with the bracket.
 6947        editor.handle_input("*", window, cx);
 6948        assert_eq!(
 6949            editor.text(cx),
 6950            "
 6951                *
 6952                *
 6953                *
 6954            "
 6955            .unindent()
 6956        );
 6957        assert_eq!(
 6958            editor.selections.display_ranges(cx),
 6959            [
 6960                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6961                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6962                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6963            ]
 6964        );
 6965    });
 6966}
 6967
 6968#[gpui::test]
 6969async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6970    init_test(cx, |_| {});
 6971
 6972    let language = Arc::new(Language::new(
 6973        LanguageConfig {
 6974            brackets: BracketPairConfig {
 6975                pairs: vec![BracketPair {
 6976                    start: "{".to_string(),
 6977                    end: "}".to_string(),
 6978                    close: true,
 6979                    surround: true,
 6980                    newline: true,
 6981                }],
 6982                ..Default::default()
 6983            },
 6984            autoclose_before: "}".to_string(),
 6985            ..Default::default()
 6986        },
 6987        Some(tree_sitter_rust::LANGUAGE.into()),
 6988    ));
 6989
 6990    let text = r#"
 6991        a
 6992        b
 6993        c
 6994    "#
 6995    .unindent();
 6996
 6997    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6998    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6999    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7000    editor
 7001        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7002        .await;
 7003
 7004    editor.update_in(cx, |editor, window, cx| {
 7005        editor.change_selections(None, window, cx, |s| {
 7006            s.select_ranges([
 7007                Point::new(0, 1)..Point::new(0, 1),
 7008                Point::new(1, 1)..Point::new(1, 1),
 7009                Point::new(2, 1)..Point::new(2, 1),
 7010            ])
 7011        });
 7012
 7013        editor.handle_input("{", window, cx);
 7014        editor.handle_input("{", window, cx);
 7015        editor.handle_input("_", window, cx);
 7016        assert_eq!(
 7017            editor.text(cx),
 7018            "
 7019                a{{_}}
 7020                b{{_}}
 7021                c{{_}}
 7022            "
 7023            .unindent()
 7024        );
 7025        assert_eq!(
 7026            editor.selections.ranges::<Point>(cx),
 7027            [
 7028                Point::new(0, 4)..Point::new(0, 4),
 7029                Point::new(1, 4)..Point::new(1, 4),
 7030                Point::new(2, 4)..Point::new(2, 4)
 7031            ]
 7032        );
 7033
 7034        editor.backspace(&Default::default(), window, cx);
 7035        editor.backspace(&Default::default(), window, cx);
 7036        assert_eq!(
 7037            editor.text(cx),
 7038            "
 7039                a{}
 7040                b{}
 7041                c{}
 7042            "
 7043            .unindent()
 7044        );
 7045        assert_eq!(
 7046            editor.selections.ranges::<Point>(cx),
 7047            [
 7048                Point::new(0, 2)..Point::new(0, 2),
 7049                Point::new(1, 2)..Point::new(1, 2),
 7050                Point::new(2, 2)..Point::new(2, 2)
 7051            ]
 7052        );
 7053
 7054        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7055        assert_eq!(
 7056            editor.text(cx),
 7057            "
 7058                a
 7059                b
 7060                c
 7061            "
 7062            .unindent()
 7063        );
 7064        assert_eq!(
 7065            editor.selections.ranges::<Point>(cx),
 7066            [
 7067                Point::new(0, 1)..Point::new(0, 1),
 7068                Point::new(1, 1)..Point::new(1, 1),
 7069                Point::new(2, 1)..Point::new(2, 1)
 7070            ]
 7071        );
 7072    });
 7073}
 7074
 7075#[gpui::test]
 7076async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7077    init_test(cx, |settings| {
 7078        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7079    });
 7080
 7081    let mut cx = EditorTestContext::new(cx).await;
 7082
 7083    let language = Arc::new(Language::new(
 7084        LanguageConfig {
 7085            brackets: BracketPairConfig {
 7086                pairs: vec![
 7087                    BracketPair {
 7088                        start: "{".to_string(),
 7089                        end: "}".to_string(),
 7090                        close: true,
 7091                        surround: true,
 7092                        newline: true,
 7093                    },
 7094                    BracketPair {
 7095                        start: "(".to_string(),
 7096                        end: ")".to_string(),
 7097                        close: true,
 7098                        surround: true,
 7099                        newline: true,
 7100                    },
 7101                    BracketPair {
 7102                        start: "[".to_string(),
 7103                        end: "]".to_string(),
 7104                        close: false,
 7105                        surround: true,
 7106                        newline: true,
 7107                    },
 7108                ],
 7109                ..Default::default()
 7110            },
 7111            autoclose_before: "})]".to_string(),
 7112            ..Default::default()
 7113        },
 7114        Some(tree_sitter_rust::LANGUAGE.into()),
 7115    ));
 7116
 7117    cx.language_registry().add(language.clone());
 7118    cx.update_buffer(|buffer, cx| {
 7119        buffer.set_language(Some(language), cx);
 7120    });
 7121
 7122    cx.set_state(
 7123        &"
 7124            {(ˇ)}
 7125            [[ˇ]]
 7126            {(ˇ)}
 7127        "
 7128        .unindent(),
 7129    );
 7130
 7131    cx.update_editor(|editor, window, cx| {
 7132        editor.backspace(&Default::default(), window, cx);
 7133        editor.backspace(&Default::default(), window, cx);
 7134    });
 7135
 7136    cx.assert_editor_state(
 7137        &"
 7138            ˇ
 7139            ˇ]]
 7140            ˇ
 7141        "
 7142        .unindent(),
 7143    );
 7144
 7145    cx.update_editor(|editor, window, cx| {
 7146        editor.handle_input("{", window, cx);
 7147        editor.handle_input("{", window, cx);
 7148        editor.move_right(&MoveRight, window, cx);
 7149        editor.move_right(&MoveRight, window, cx);
 7150        editor.move_left(&MoveLeft, window, cx);
 7151        editor.move_left(&MoveLeft, window, cx);
 7152        editor.backspace(&Default::default(), window, cx);
 7153    });
 7154
 7155    cx.assert_editor_state(
 7156        &"
 7157            {ˇ}
 7158            {ˇ}]]
 7159            {ˇ}
 7160        "
 7161        .unindent(),
 7162    );
 7163
 7164    cx.update_editor(|editor, window, cx| {
 7165        editor.backspace(&Default::default(), window, cx);
 7166    });
 7167
 7168    cx.assert_editor_state(
 7169        &"
 7170            ˇ
 7171            ˇ]]
 7172            ˇ
 7173        "
 7174        .unindent(),
 7175    );
 7176}
 7177
 7178#[gpui::test]
 7179async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7180    init_test(cx, |_| {});
 7181
 7182    let language = Arc::new(Language::new(
 7183        LanguageConfig::default(),
 7184        Some(tree_sitter_rust::LANGUAGE.into()),
 7185    ));
 7186
 7187    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7188    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7189    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7190    editor
 7191        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7192        .await;
 7193
 7194    editor.update_in(cx, |editor, window, cx| {
 7195        editor.set_auto_replace_emoji_shortcode(true);
 7196
 7197        editor.handle_input("Hello ", window, cx);
 7198        editor.handle_input(":wave", window, cx);
 7199        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7200
 7201        editor.handle_input(":", window, cx);
 7202        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7203
 7204        editor.handle_input(" :smile", window, cx);
 7205        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7206
 7207        editor.handle_input(":", window, cx);
 7208        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7209
 7210        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7211        editor.handle_input(":wave", window, cx);
 7212        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7213
 7214        editor.handle_input(":", window, cx);
 7215        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7216
 7217        editor.handle_input(":1", window, cx);
 7218        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7219
 7220        editor.handle_input(":", window, cx);
 7221        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7222
 7223        // Ensure shortcode does not get replaced when it is part of a word
 7224        editor.handle_input(" Test:wave", window, cx);
 7225        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7226
 7227        editor.handle_input(":", window, cx);
 7228        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7229
 7230        editor.set_auto_replace_emoji_shortcode(false);
 7231
 7232        // Ensure shortcode does not get replaced when auto replace is off
 7233        editor.handle_input(" :wave", window, cx);
 7234        assert_eq!(
 7235            editor.text(cx),
 7236            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7237        );
 7238
 7239        editor.handle_input(":", window, cx);
 7240        assert_eq!(
 7241            editor.text(cx),
 7242            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7243        );
 7244    });
 7245}
 7246
 7247#[gpui::test]
 7248async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7249    init_test(cx, |_| {});
 7250
 7251    let (text, insertion_ranges) = marked_text_ranges(
 7252        indoc! {"
 7253            ˇ
 7254        "},
 7255        false,
 7256    );
 7257
 7258    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7259    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7260
 7261    _ = editor.update_in(cx, |editor, window, cx| {
 7262        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7263
 7264        editor
 7265            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7266            .unwrap();
 7267
 7268        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7269            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7270            assert_eq!(editor.text(cx), expected_text);
 7271            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7272        }
 7273
 7274        assert(
 7275            editor,
 7276            cx,
 7277            indoc! {"
 7278            type «» =•
 7279            "},
 7280        );
 7281
 7282        assert!(editor.context_menu_visible(), "There should be a matches");
 7283    });
 7284}
 7285
 7286#[gpui::test]
 7287async fn test_snippets(cx: &mut TestAppContext) {
 7288    init_test(cx, |_| {});
 7289
 7290    let (text, insertion_ranges) = marked_text_ranges(
 7291        indoc! {"
 7292            a.ˇ b
 7293            a.ˇ b
 7294            a.ˇ b
 7295        "},
 7296        false,
 7297    );
 7298
 7299    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7300    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7301
 7302    editor.update_in(cx, |editor, window, cx| {
 7303        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7304
 7305        editor
 7306            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7307            .unwrap();
 7308
 7309        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7310            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7311            assert_eq!(editor.text(cx), expected_text);
 7312            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7313        }
 7314
 7315        assert(
 7316            editor,
 7317            cx,
 7318            indoc! {"
 7319                a.f(«one», two, «three») b
 7320                a.f(«one», two, «three») b
 7321                a.f(«one», two, «three») b
 7322            "},
 7323        );
 7324
 7325        // Can't move earlier than the first tab stop
 7326        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7327        assert(
 7328            editor,
 7329            cx,
 7330            indoc! {"
 7331                a.f(«one», two, «three») b
 7332                a.f(«one», two, «three») b
 7333                a.f(«one», two, «three») b
 7334            "},
 7335        );
 7336
 7337        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7338        assert(
 7339            editor,
 7340            cx,
 7341            indoc! {"
 7342                a.f(one, «two», three) b
 7343                a.f(one, «two», three) b
 7344                a.f(one, «two», three) b
 7345            "},
 7346        );
 7347
 7348        editor.move_to_prev_snippet_tabstop(window, cx);
 7349        assert(
 7350            editor,
 7351            cx,
 7352            indoc! {"
 7353                a.f(«one», two, «three») b
 7354                a.f(«one», two, «three») b
 7355                a.f(«one», two, «three») b
 7356            "},
 7357        );
 7358
 7359        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7360        assert(
 7361            editor,
 7362            cx,
 7363            indoc! {"
 7364                a.f(one, «two», three) b
 7365                a.f(one, «two», three) b
 7366                a.f(one, «two», three) b
 7367            "},
 7368        );
 7369        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7370        assert(
 7371            editor,
 7372            cx,
 7373            indoc! {"
 7374                a.f(one, two, three)ˇ b
 7375                a.f(one, two, three)ˇ b
 7376                a.f(one, two, three)ˇ b
 7377            "},
 7378        );
 7379
 7380        // As soon as the last tab stop is reached, snippet state is gone
 7381        editor.move_to_prev_snippet_tabstop(window, cx);
 7382        assert(
 7383            editor,
 7384            cx,
 7385            indoc! {"
 7386                a.f(one, two, three)ˇ b
 7387                a.f(one, two, three)ˇ b
 7388                a.f(one, two, three)ˇ b
 7389            "},
 7390        );
 7391    });
 7392}
 7393
 7394#[gpui::test]
 7395async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7396    init_test(cx, |_| {});
 7397
 7398    let fs = FakeFs::new(cx.executor());
 7399    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7400
 7401    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7402
 7403    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7404    language_registry.add(rust_lang());
 7405    let mut fake_servers = language_registry.register_fake_lsp(
 7406        "Rust",
 7407        FakeLspAdapter {
 7408            capabilities: lsp::ServerCapabilities {
 7409                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7410                ..Default::default()
 7411            },
 7412            ..Default::default()
 7413        },
 7414    );
 7415
 7416    let buffer = project
 7417        .update(cx, |project, cx| {
 7418            project.open_local_buffer(path!("/file.rs"), cx)
 7419        })
 7420        .await
 7421        .unwrap();
 7422
 7423    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7424    let (editor, cx) = cx.add_window_view(|window, cx| {
 7425        build_editor_with_project(project.clone(), buffer, window, cx)
 7426    });
 7427    editor.update_in(cx, |editor, window, cx| {
 7428        editor.set_text("one\ntwo\nthree\n", window, cx)
 7429    });
 7430    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7431
 7432    cx.executor().start_waiting();
 7433    let fake_server = fake_servers.next().await.unwrap();
 7434
 7435    let save = editor
 7436        .update_in(cx, |editor, window, cx| {
 7437            editor.save(true, project.clone(), window, cx)
 7438        })
 7439        .unwrap();
 7440    fake_server
 7441        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7442            assert_eq!(
 7443                params.text_document.uri,
 7444                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7445            );
 7446            assert_eq!(params.options.tab_size, 4);
 7447            Ok(Some(vec![lsp::TextEdit::new(
 7448                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7449                ", ".to_string(),
 7450            )]))
 7451        })
 7452        .next()
 7453        .await;
 7454    cx.executor().start_waiting();
 7455    save.await;
 7456
 7457    assert_eq!(
 7458        editor.update(cx, |editor, cx| editor.text(cx)),
 7459        "one, two\nthree\n"
 7460    );
 7461    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7462
 7463    editor.update_in(cx, |editor, window, cx| {
 7464        editor.set_text("one\ntwo\nthree\n", window, cx)
 7465    });
 7466    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7467
 7468    // Ensure we can still save even if formatting hangs.
 7469    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7470        assert_eq!(
 7471            params.text_document.uri,
 7472            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7473        );
 7474        futures::future::pending::<()>().await;
 7475        unreachable!()
 7476    });
 7477    let save = editor
 7478        .update_in(cx, |editor, window, cx| {
 7479            editor.save(true, project.clone(), window, cx)
 7480        })
 7481        .unwrap();
 7482    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7483    cx.executor().start_waiting();
 7484    save.await;
 7485    assert_eq!(
 7486        editor.update(cx, |editor, cx| editor.text(cx)),
 7487        "one\ntwo\nthree\n"
 7488    );
 7489    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7490
 7491    // For non-dirty buffer, no formatting request should be sent
 7492    let save = editor
 7493        .update_in(cx, |editor, window, cx| {
 7494            editor.save(true, project.clone(), window, cx)
 7495        })
 7496        .unwrap();
 7497    let _pending_format_request = fake_server
 7498        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7499            panic!("Should not be invoked on non-dirty buffer");
 7500        })
 7501        .next();
 7502    cx.executor().start_waiting();
 7503    save.await;
 7504
 7505    // Set rust language override and assert overridden tabsize is sent to language server
 7506    update_test_language_settings(cx, |settings| {
 7507        settings.languages.insert(
 7508            "Rust".into(),
 7509            LanguageSettingsContent {
 7510                tab_size: NonZeroU32::new(8),
 7511                ..Default::default()
 7512            },
 7513        );
 7514    });
 7515
 7516    editor.update_in(cx, |editor, window, cx| {
 7517        editor.set_text("somehting_new\n", window, cx)
 7518    });
 7519    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7520    let save = editor
 7521        .update_in(cx, |editor, window, cx| {
 7522            editor.save(true, project.clone(), window, cx)
 7523        })
 7524        .unwrap();
 7525    fake_server
 7526        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7527            assert_eq!(
 7528                params.text_document.uri,
 7529                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7530            );
 7531            assert_eq!(params.options.tab_size, 8);
 7532            Ok(Some(vec![]))
 7533        })
 7534        .next()
 7535        .await;
 7536    cx.executor().start_waiting();
 7537    save.await;
 7538}
 7539
 7540#[gpui::test]
 7541async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7542    init_test(cx, |_| {});
 7543
 7544    let cols = 4;
 7545    let rows = 10;
 7546    let sample_text_1 = sample_text(rows, cols, 'a');
 7547    assert_eq!(
 7548        sample_text_1,
 7549        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7550    );
 7551    let sample_text_2 = sample_text(rows, cols, 'l');
 7552    assert_eq!(
 7553        sample_text_2,
 7554        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7555    );
 7556    let sample_text_3 = sample_text(rows, cols, 'v');
 7557    assert_eq!(
 7558        sample_text_3,
 7559        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7560    );
 7561
 7562    let fs = FakeFs::new(cx.executor());
 7563    fs.insert_tree(
 7564        path!("/a"),
 7565        json!({
 7566            "main.rs": sample_text_1,
 7567            "other.rs": sample_text_2,
 7568            "lib.rs": sample_text_3,
 7569        }),
 7570    )
 7571    .await;
 7572
 7573    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7574    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7575    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7576
 7577    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7578    language_registry.add(rust_lang());
 7579    let mut fake_servers = language_registry.register_fake_lsp(
 7580        "Rust",
 7581        FakeLspAdapter {
 7582            capabilities: lsp::ServerCapabilities {
 7583                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7584                ..Default::default()
 7585            },
 7586            ..Default::default()
 7587        },
 7588    );
 7589
 7590    let worktree = project.update(cx, |project, cx| {
 7591        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7592        assert_eq!(worktrees.len(), 1);
 7593        worktrees.pop().unwrap()
 7594    });
 7595    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7596
 7597    let buffer_1 = project
 7598        .update(cx, |project, cx| {
 7599            project.open_buffer((worktree_id, "main.rs"), cx)
 7600        })
 7601        .await
 7602        .unwrap();
 7603    let buffer_2 = project
 7604        .update(cx, |project, cx| {
 7605            project.open_buffer((worktree_id, "other.rs"), cx)
 7606        })
 7607        .await
 7608        .unwrap();
 7609    let buffer_3 = project
 7610        .update(cx, |project, cx| {
 7611            project.open_buffer((worktree_id, "lib.rs"), cx)
 7612        })
 7613        .await
 7614        .unwrap();
 7615
 7616    let multi_buffer = cx.new(|cx| {
 7617        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7618        multi_buffer.push_excerpts(
 7619            buffer_1.clone(),
 7620            [
 7621                ExcerptRange {
 7622                    context: Point::new(0, 0)..Point::new(3, 0),
 7623                    primary: None,
 7624                },
 7625                ExcerptRange {
 7626                    context: Point::new(5, 0)..Point::new(7, 0),
 7627                    primary: None,
 7628                },
 7629                ExcerptRange {
 7630                    context: Point::new(9, 0)..Point::new(10, 4),
 7631                    primary: None,
 7632                },
 7633            ],
 7634            cx,
 7635        );
 7636        multi_buffer.push_excerpts(
 7637            buffer_2.clone(),
 7638            [
 7639                ExcerptRange {
 7640                    context: Point::new(0, 0)..Point::new(3, 0),
 7641                    primary: None,
 7642                },
 7643                ExcerptRange {
 7644                    context: Point::new(5, 0)..Point::new(7, 0),
 7645                    primary: None,
 7646                },
 7647                ExcerptRange {
 7648                    context: Point::new(9, 0)..Point::new(10, 4),
 7649                    primary: None,
 7650                },
 7651            ],
 7652            cx,
 7653        );
 7654        multi_buffer.push_excerpts(
 7655            buffer_3.clone(),
 7656            [
 7657                ExcerptRange {
 7658                    context: Point::new(0, 0)..Point::new(3, 0),
 7659                    primary: None,
 7660                },
 7661                ExcerptRange {
 7662                    context: Point::new(5, 0)..Point::new(7, 0),
 7663                    primary: None,
 7664                },
 7665                ExcerptRange {
 7666                    context: Point::new(9, 0)..Point::new(10, 4),
 7667                    primary: None,
 7668                },
 7669            ],
 7670            cx,
 7671        );
 7672        multi_buffer
 7673    });
 7674    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7675        Editor::new(
 7676            EditorMode::Full,
 7677            multi_buffer,
 7678            Some(project.clone()),
 7679            window,
 7680            cx,
 7681        )
 7682    });
 7683
 7684    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7685        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7686            s.select_ranges(Some(1..2))
 7687        });
 7688        editor.insert("|one|two|three|", window, cx);
 7689    });
 7690    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7691    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7692        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7693            s.select_ranges(Some(60..70))
 7694        });
 7695        editor.insert("|four|five|six|", window, cx);
 7696    });
 7697    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7698
 7699    // First two buffers should be edited, but not the third one.
 7700    assert_eq!(
 7701        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7702        "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}",
 7703    );
 7704    buffer_1.update(cx, |buffer, _| {
 7705        assert!(buffer.is_dirty());
 7706        assert_eq!(
 7707            buffer.text(),
 7708            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7709        )
 7710    });
 7711    buffer_2.update(cx, |buffer, _| {
 7712        assert!(buffer.is_dirty());
 7713        assert_eq!(
 7714            buffer.text(),
 7715            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7716        )
 7717    });
 7718    buffer_3.update(cx, |buffer, _| {
 7719        assert!(!buffer.is_dirty());
 7720        assert_eq!(buffer.text(), sample_text_3,)
 7721    });
 7722    cx.executor().run_until_parked();
 7723
 7724    cx.executor().start_waiting();
 7725    let save = multi_buffer_editor
 7726        .update_in(cx, |editor, window, cx| {
 7727            editor.save(true, project.clone(), window, cx)
 7728        })
 7729        .unwrap();
 7730
 7731    let fake_server = fake_servers.next().await.unwrap();
 7732    fake_server
 7733        .server
 7734        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7735            Ok(Some(vec![lsp::TextEdit::new(
 7736                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7737                format!("[{} formatted]", params.text_document.uri),
 7738            )]))
 7739        })
 7740        .detach();
 7741    save.await;
 7742
 7743    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7744    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7745    assert_eq!(
 7746        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7747        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}"),
 7748    );
 7749    buffer_1.update(cx, |buffer, _| {
 7750        assert!(!buffer.is_dirty());
 7751        assert_eq!(
 7752            buffer.text(),
 7753            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7754        )
 7755    });
 7756    buffer_2.update(cx, |buffer, _| {
 7757        assert!(!buffer.is_dirty());
 7758        assert_eq!(
 7759            buffer.text(),
 7760            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7761        )
 7762    });
 7763    buffer_3.update(cx, |buffer, _| {
 7764        assert!(!buffer.is_dirty());
 7765        assert_eq!(buffer.text(), sample_text_3,)
 7766    });
 7767}
 7768
 7769#[gpui::test]
 7770async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7771    init_test(cx, |_| {});
 7772
 7773    let fs = FakeFs::new(cx.executor());
 7774    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7775
 7776    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7777
 7778    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7779    language_registry.add(rust_lang());
 7780    let mut fake_servers = language_registry.register_fake_lsp(
 7781        "Rust",
 7782        FakeLspAdapter {
 7783            capabilities: lsp::ServerCapabilities {
 7784                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7785                ..Default::default()
 7786            },
 7787            ..Default::default()
 7788        },
 7789    );
 7790
 7791    let buffer = project
 7792        .update(cx, |project, cx| {
 7793            project.open_local_buffer(path!("/file.rs"), cx)
 7794        })
 7795        .await
 7796        .unwrap();
 7797
 7798    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7799    let (editor, cx) = cx.add_window_view(|window, cx| {
 7800        build_editor_with_project(project.clone(), buffer, window, cx)
 7801    });
 7802    editor.update_in(cx, |editor, window, cx| {
 7803        editor.set_text("one\ntwo\nthree\n", window, cx)
 7804    });
 7805    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7806
 7807    cx.executor().start_waiting();
 7808    let fake_server = fake_servers.next().await.unwrap();
 7809
 7810    let save = editor
 7811        .update_in(cx, |editor, window, cx| {
 7812            editor.save(true, project.clone(), window, cx)
 7813        })
 7814        .unwrap();
 7815    fake_server
 7816        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7817            assert_eq!(
 7818                params.text_document.uri,
 7819                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7820            );
 7821            assert_eq!(params.options.tab_size, 4);
 7822            Ok(Some(vec![lsp::TextEdit::new(
 7823                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7824                ", ".to_string(),
 7825            )]))
 7826        })
 7827        .next()
 7828        .await;
 7829    cx.executor().start_waiting();
 7830    save.await;
 7831    assert_eq!(
 7832        editor.update(cx, |editor, cx| editor.text(cx)),
 7833        "one, two\nthree\n"
 7834    );
 7835    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7836
 7837    editor.update_in(cx, |editor, window, cx| {
 7838        editor.set_text("one\ntwo\nthree\n", window, cx)
 7839    });
 7840    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7841
 7842    // Ensure we can still save even if formatting hangs.
 7843    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7844        move |params, _| async move {
 7845            assert_eq!(
 7846                params.text_document.uri,
 7847                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7848            );
 7849            futures::future::pending::<()>().await;
 7850            unreachable!()
 7851        },
 7852    );
 7853    let save = editor
 7854        .update_in(cx, |editor, window, cx| {
 7855            editor.save(true, project.clone(), window, cx)
 7856        })
 7857        .unwrap();
 7858    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7859    cx.executor().start_waiting();
 7860    save.await;
 7861    assert_eq!(
 7862        editor.update(cx, |editor, cx| editor.text(cx)),
 7863        "one\ntwo\nthree\n"
 7864    );
 7865    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7866
 7867    // For non-dirty buffer, no formatting request should be sent
 7868    let save = editor
 7869        .update_in(cx, |editor, window, cx| {
 7870            editor.save(true, project.clone(), window, cx)
 7871        })
 7872        .unwrap();
 7873    let _pending_format_request = fake_server
 7874        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7875            panic!("Should not be invoked on non-dirty buffer");
 7876        })
 7877        .next();
 7878    cx.executor().start_waiting();
 7879    save.await;
 7880
 7881    // Set Rust language override and assert overridden tabsize is sent to language server
 7882    update_test_language_settings(cx, |settings| {
 7883        settings.languages.insert(
 7884            "Rust".into(),
 7885            LanguageSettingsContent {
 7886                tab_size: NonZeroU32::new(8),
 7887                ..Default::default()
 7888            },
 7889        );
 7890    });
 7891
 7892    editor.update_in(cx, |editor, window, cx| {
 7893        editor.set_text("somehting_new\n", window, cx)
 7894    });
 7895    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7896    let save = editor
 7897        .update_in(cx, |editor, window, cx| {
 7898            editor.save(true, project.clone(), window, cx)
 7899        })
 7900        .unwrap();
 7901    fake_server
 7902        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7903            assert_eq!(
 7904                params.text_document.uri,
 7905                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7906            );
 7907            assert_eq!(params.options.tab_size, 8);
 7908            Ok(Some(vec![]))
 7909        })
 7910        .next()
 7911        .await;
 7912    cx.executor().start_waiting();
 7913    save.await;
 7914}
 7915
 7916#[gpui::test]
 7917async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7918    init_test(cx, |settings| {
 7919        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7920            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7921        ))
 7922    });
 7923
 7924    let fs = FakeFs::new(cx.executor());
 7925    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7926
 7927    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7928
 7929    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7930    language_registry.add(Arc::new(Language::new(
 7931        LanguageConfig {
 7932            name: "Rust".into(),
 7933            matcher: LanguageMatcher {
 7934                path_suffixes: vec!["rs".to_string()],
 7935                ..Default::default()
 7936            },
 7937            ..LanguageConfig::default()
 7938        },
 7939        Some(tree_sitter_rust::LANGUAGE.into()),
 7940    )));
 7941    update_test_language_settings(cx, |settings| {
 7942        // Enable Prettier formatting for the same buffer, and ensure
 7943        // LSP is called instead of Prettier.
 7944        settings.defaults.prettier = Some(PrettierSettings {
 7945            allowed: true,
 7946            ..PrettierSettings::default()
 7947        });
 7948    });
 7949    let mut fake_servers = language_registry.register_fake_lsp(
 7950        "Rust",
 7951        FakeLspAdapter {
 7952            capabilities: lsp::ServerCapabilities {
 7953                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7954                ..Default::default()
 7955            },
 7956            ..Default::default()
 7957        },
 7958    );
 7959
 7960    let buffer = project
 7961        .update(cx, |project, cx| {
 7962            project.open_local_buffer(path!("/file.rs"), cx)
 7963        })
 7964        .await
 7965        .unwrap();
 7966
 7967    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7968    let (editor, cx) = cx.add_window_view(|window, cx| {
 7969        build_editor_with_project(project.clone(), buffer, window, cx)
 7970    });
 7971    editor.update_in(cx, |editor, window, cx| {
 7972        editor.set_text("one\ntwo\nthree\n", window, cx)
 7973    });
 7974
 7975    cx.executor().start_waiting();
 7976    let fake_server = fake_servers.next().await.unwrap();
 7977
 7978    let format = editor
 7979        .update_in(cx, |editor, window, cx| {
 7980            editor.perform_format(
 7981                project.clone(),
 7982                FormatTrigger::Manual,
 7983                FormatTarget::Buffers,
 7984                window,
 7985                cx,
 7986            )
 7987        })
 7988        .unwrap();
 7989    fake_server
 7990        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7991            assert_eq!(
 7992                params.text_document.uri,
 7993                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7994            );
 7995            assert_eq!(params.options.tab_size, 4);
 7996            Ok(Some(vec![lsp::TextEdit::new(
 7997                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7998                ", ".to_string(),
 7999            )]))
 8000        })
 8001        .next()
 8002        .await;
 8003    cx.executor().start_waiting();
 8004    format.await;
 8005    assert_eq!(
 8006        editor.update(cx, |editor, cx| editor.text(cx)),
 8007        "one, two\nthree\n"
 8008    );
 8009
 8010    editor.update_in(cx, |editor, window, cx| {
 8011        editor.set_text("one\ntwo\nthree\n", window, cx)
 8012    });
 8013    // Ensure we don't lock if formatting hangs.
 8014    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8015        assert_eq!(
 8016            params.text_document.uri,
 8017            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8018        );
 8019        futures::future::pending::<()>().await;
 8020        unreachable!()
 8021    });
 8022    let format = editor
 8023        .update_in(cx, |editor, window, cx| {
 8024            editor.perform_format(
 8025                project,
 8026                FormatTrigger::Manual,
 8027                FormatTarget::Buffers,
 8028                window,
 8029                cx,
 8030            )
 8031        })
 8032        .unwrap();
 8033    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8034    cx.executor().start_waiting();
 8035    format.await;
 8036    assert_eq!(
 8037        editor.update(cx, |editor, cx| editor.text(cx)),
 8038        "one\ntwo\nthree\n"
 8039    );
 8040}
 8041
 8042#[gpui::test]
 8043async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8044    init_test(cx, |settings| {
 8045        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8046            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8047        ))
 8048    });
 8049
 8050    let fs = FakeFs::new(cx.executor());
 8051    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8052
 8053    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8054
 8055    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8056    language_registry.add(Arc::new(Language::new(
 8057        LanguageConfig {
 8058            name: "TypeScript".into(),
 8059            matcher: LanguageMatcher {
 8060                path_suffixes: vec!["ts".to_string()],
 8061                ..Default::default()
 8062            },
 8063            ..LanguageConfig::default()
 8064        },
 8065        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8066    )));
 8067    update_test_language_settings(cx, |settings| {
 8068        settings.defaults.prettier = Some(PrettierSettings {
 8069            allowed: true,
 8070            ..PrettierSettings::default()
 8071        });
 8072    });
 8073    let mut fake_servers = language_registry.register_fake_lsp(
 8074        "TypeScript",
 8075        FakeLspAdapter {
 8076            capabilities: lsp::ServerCapabilities {
 8077                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8078                ..Default::default()
 8079            },
 8080            ..Default::default()
 8081        },
 8082    );
 8083
 8084    let buffer = project
 8085        .update(cx, |project, cx| {
 8086            project.open_local_buffer(path!("/file.ts"), cx)
 8087        })
 8088        .await
 8089        .unwrap();
 8090
 8091    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8092    let (editor, cx) = cx.add_window_view(|window, cx| {
 8093        build_editor_with_project(project.clone(), buffer, window, cx)
 8094    });
 8095    editor.update_in(cx, |editor, window, cx| {
 8096        editor.set_text(
 8097            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8098            window,
 8099            cx,
 8100        )
 8101    });
 8102
 8103    cx.executor().start_waiting();
 8104    let fake_server = fake_servers.next().await.unwrap();
 8105
 8106    let format = editor
 8107        .update_in(cx, |editor, window, cx| {
 8108            editor.perform_code_action_kind(
 8109                project.clone(),
 8110                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8111                window,
 8112                cx,
 8113            )
 8114        })
 8115        .unwrap();
 8116    fake_server
 8117        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8118            assert_eq!(
 8119                params.text_document.uri,
 8120                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8121            );
 8122            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8123                lsp::CodeAction {
 8124                    title: "Organize Imports".to_string(),
 8125                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8126                    edit: Some(lsp::WorkspaceEdit {
 8127                        changes: Some(
 8128                            [(
 8129                                params.text_document.uri.clone(),
 8130                                vec![lsp::TextEdit::new(
 8131                                    lsp::Range::new(
 8132                                        lsp::Position::new(1, 0),
 8133                                        lsp::Position::new(2, 0),
 8134                                    ),
 8135                                    "".to_string(),
 8136                                )],
 8137                            )]
 8138                            .into_iter()
 8139                            .collect(),
 8140                        ),
 8141                        ..Default::default()
 8142                    }),
 8143                    ..Default::default()
 8144                },
 8145            )]))
 8146        })
 8147        .next()
 8148        .await;
 8149    cx.executor().start_waiting();
 8150    format.await;
 8151    assert_eq!(
 8152        editor.update(cx, |editor, cx| editor.text(cx)),
 8153        "import { a } from 'module';\n\nconst x = a;\n"
 8154    );
 8155
 8156    editor.update_in(cx, |editor, window, cx| {
 8157        editor.set_text(
 8158            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8159            window,
 8160            cx,
 8161        )
 8162    });
 8163    // Ensure we don't lock if code action hangs.
 8164    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8165        move |params, _| async move {
 8166            assert_eq!(
 8167                params.text_document.uri,
 8168                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8169            );
 8170            futures::future::pending::<()>().await;
 8171            unreachable!()
 8172        },
 8173    );
 8174    let format = editor
 8175        .update_in(cx, |editor, window, cx| {
 8176            editor.perform_code_action_kind(
 8177                project,
 8178                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8179                window,
 8180                cx,
 8181            )
 8182        })
 8183        .unwrap();
 8184    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8185    cx.executor().start_waiting();
 8186    format.await;
 8187    assert_eq!(
 8188        editor.update(cx, |editor, cx| editor.text(cx)),
 8189        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8190    );
 8191}
 8192
 8193#[gpui::test]
 8194async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8195    init_test(cx, |_| {});
 8196
 8197    let mut cx = EditorLspTestContext::new_rust(
 8198        lsp::ServerCapabilities {
 8199            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8200            ..Default::default()
 8201        },
 8202        cx,
 8203    )
 8204    .await;
 8205
 8206    cx.set_state(indoc! {"
 8207        one.twoˇ
 8208    "});
 8209
 8210    // The format request takes a long time. When it completes, it inserts
 8211    // a newline and an indent before the `.`
 8212    cx.lsp
 8213        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8214            let executor = cx.background_executor().clone();
 8215            async move {
 8216                executor.timer(Duration::from_millis(100)).await;
 8217                Ok(Some(vec![lsp::TextEdit {
 8218                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8219                    new_text: "\n    ".into(),
 8220                }]))
 8221            }
 8222        });
 8223
 8224    // Submit a format request.
 8225    let format_1 = cx
 8226        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8227        .unwrap();
 8228    cx.executor().run_until_parked();
 8229
 8230    // Submit a second format request.
 8231    let format_2 = cx
 8232        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8233        .unwrap();
 8234    cx.executor().run_until_parked();
 8235
 8236    // Wait for both format requests to complete
 8237    cx.executor().advance_clock(Duration::from_millis(200));
 8238    cx.executor().start_waiting();
 8239    format_1.await.unwrap();
 8240    cx.executor().start_waiting();
 8241    format_2.await.unwrap();
 8242
 8243    // The formatting edits only happens once.
 8244    cx.assert_editor_state(indoc! {"
 8245        one
 8246            .twoˇ
 8247    "});
 8248}
 8249
 8250#[gpui::test]
 8251async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8252    init_test(cx, |settings| {
 8253        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8254    });
 8255
 8256    let mut cx = EditorLspTestContext::new_rust(
 8257        lsp::ServerCapabilities {
 8258            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8259            ..Default::default()
 8260        },
 8261        cx,
 8262    )
 8263    .await;
 8264
 8265    // Set up a buffer white some trailing whitespace and no trailing newline.
 8266    cx.set_state(
 8267        &[
 8268            "one ",   //
 8269            "twoˇ",   //
 8270            "three ", //
 8271            "four",   //
 8272        ]
 8273        .join("\n"),
 8274    );
 8275
 8276    // Submit a format request.
 8277    let format = cx
 8278        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8279        .unwrap();
 8280
 8281    // Record which buffer changes have been sent to the language server
 8282    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8283    cx.lsp
 8284        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8285            let buffer_changes = buffer_changes.clone();
 8286            move |params, _| {
 8287                buffer_changes.lock().extend(
 8288                    params
 8289                        .content_changes
 8290                        .into_iter()
 8291                        .map(|e| (e.range.unwrap(), e.text)),
 8292                );
 8293            }
 8294        });
 8295
 8296    // Handle formatting requests to the language server.
 8297    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8298        let buffer_changes = buffer_changes.clone();
 8299        move |_, _| {
 8300            // When formatting is requested, trailing whitespace has already been stripped,
 8301            // and the trailing newline has already been added.
 8302            assert_eq!(
 8303                &buffer_changes.lock()[1..],
 8304                &[
 8305                    (
 8306                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8307                        "".into()
 8308                    ),
 8309                    (
 8310                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8311                        "".into()
 8312                    ),
 8313                    (
 8314                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8315                        "\n".into()
 8316                    ),
 8317                ]
 8318            );
 8319
 8320            // Insert blank lines between each line of the buffer.
 8321            async move {
 8322                Ok(Some(vec![
 8323                    lsp::TextEdit {
 8324                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8325                        new_text: "\n".into(),
 8326                    },
 8327                    lsp::TextEdit {
 8328                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8329                        new_text: "\n".into(),
 8330                    },
 8331                ]))
 8332            }
 8333        }
 8334    });
 8335
 8336    // After formatting the buffer, the trailing whitespace is stripped,
 8337    // a newline is appended, and the edits provided by the language server
 8338    // have been applied.
 8339    format.await.unwrap();
 8340    cx.assert_editor_state(
 8341        &[
 8342            "one",   //
 8343            "",      //
 8344            "twoˇ",  //
 8345            "",      //
 8346            "three", //
 8347            "four",  //
 8348            "",      //
 8349        ]
 8350        .join("\n"),
 8351    );
 8352
 8353    // Undoing the formatting undoes the trailing whitespace removal, the
 8354    // trailing newline, and the LSP edits.
 8355    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8356    cx.assert_editor_state(
 8357        &[
 8358            "one ",   //
 8359            "twoˇ",   //
 8360            "three ", //
 8361            "four",   //
 8362        ]
 8363        .join("\n"),
 8364    );
 8365}
 8366
 8367#[gpui::test]
 8368async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8369    cx: &mut TestAppContext,
 8370) {
 8371    init_test(cx, |_| {});
 8372
 8373    cx.update(|cx| {
 8374        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8375            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8376                settings.auto_signature_help = Some(true);
 8377            });
 8378        });
 8379    });
 8380
 8381    let mut cx = EditorLspTestContext::new_rust(
 8382        lsp::ServerCapabilities {
 8383            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8384                ..Default::default()
 8385            }),
 8386            ..Default::default()
 8387        },
 8388        cx,
 8389    )
 8390    .await;
 8391
 8392    let language = Language::new(
 8393        LanguageConfig {
 8394            name: "Rust".into(),
 8395            brackets: BracketPairConfig {
 8396                pairs: vec![
 8397                    BracketPair {
 8398                        start: "{".to_string(),
 8399                        end: "}".to_string(),
 8400                        close: true,
 8401                        surround: true,
 8402                        newline: true,
 8403                    },
 8404                    BracketPair {
 8405                        start: "(".to_string(),
 8406                        end: ")".to_string(),
 8407                        close: true,
 8408                        surround: true,
 8409                        newline: true,
 8410                    },
 8411                    BracketPair {
 8412                        start: "/*".to_string(),
 8413                        end: " */".to_string(),
 8414                        close: true,
 8415                        surround: true,
 8416                        newline: true,
 8417                    },
 8418                    BracketPair {
 8419                        start: "[".to_string(),
 8420                        end: "]".to_string(),
 8421                        close: false,
 8422                        surround: false,
 8423                        newline: true,
 8424                    },
 8425                    BracketPair {
 8426                        start: "\"".to_string(),
 8427                        end: "\"".to_string(),
 8428                        close: true,
 8429                        surround: true,
 8430                        newline: false,
 8431                    },
 8432                    BracketPair {
 8433                        start: "<".to_string(),
 8434                        end: ">".to_string(),
 8435                        close: false,
 8436                        surround: true,
 8437                        newline: true,
 8438                    },
 8439                ],
 8440                ..Default::default()
 8441            },
 8442            autoclose_before: "})]".to_string(),
 8443            ..Default::default()
 8444        },
 8445        Some(tree_sitter_rust::LANGUAGE.into()),
 8446    );
 8447    let language = Arc::new(language);
 8448
 8449    cx.language_registry().add(language.clone());
 8450    cx.update_buffer(|buffer, cx| {
 8451        buffer.set_language(Some(language), cx);
 8452    });
 8453
 8454    cx.set_state(
 8455        &r#"
 8456            fn main() {
 8457                sampleˇ
 8458            }
 8459        "#
 8460        .unindent(),
 8461    );
 8462
 8463    cx.update_editor(|editor, window, cx| {
 8464        editor.handle_input("(", window, cx);
 8465    });
 8466    cx.assert_editor_state(
 8467        &"
 8468            fn main() {
 8469                sample(ˇ)
 8470            }
 8471        "
 8472        .unindent(),
 8473    );
 8474
 8475    let mocked_response = lsp::SignatureHelp {
 8476        signatures: vec![lsp::SignatureInformation {
 8477            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8478            documentation: None,
 8479            parameters: Some(vec![
 8480                lsp::ParameterInformation {
 8481                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8482                    documentation: None,
 8483                },
 8484                lsp::ParameterInformation {
 8485                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8486                    documentation: None,
 8487                },
 8488            ]),
 8489            active_parameter: None,
 8490        }],
 8491        active_signature: Some(0),
 8492        active_parameter: Some(0),
 8493    };
 8494    handle_signature_help_request(&mut cx, mocked_response).await;
 8495
 8496    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8497        .await;
 8498
 8499    cx.editor(|editor, _, _| {
 8500        let signature_help_state = editor.signature_help_state.popover().cloned();
 8501        assert_eq!(
 8502            signature_help_state.unwrap().label,
 8503            "param1: u8, param2: u8"
 8504        );
 8505    });
 8506}
 8507
 8508#[gpui::test]
 8509async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8510    init_test(cx, |_| {});
 8511
 8512    cx.update(|cx| {
 8513        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8514            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8515                settings.auto_signature_help = Some(false);
 8516                settings.show_signature_help_after_edits = Some(false);
 8517            });
 8518        });
 8519    });
 8520
 8521    let mut cx = EditorLspTestContext::new_rust(
 8522        lsp::ServerCapabilities {
 8523            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8524                ..Default::default()
 8525            }),
 8526            ..Default::default()
 8527        },
 8528        cx,
 8529    )
 8530    .await;
 8531
 8532    let language = Language::new(
 8533        LanguageConfig {
 8534            name: "Rust".into(),
 8535            brackets: BracketPairConfig {
 8536                pairs: vec![
 8537                    BracketPair {
 8538                        start: "{".to_string(),
 8539                        end: "}".to_string(),
 8540                        close: true,
 8541                        surround: true,
 8542                        newline: true,
 8543                    },
 8544                    BracketPair {
 8545                        start: "(".to_string(),
 8546                        end: ")".to_string(),
 8547                        close: true,
 8548                        surround: true,
 8549                        newline: true,
 8550                    },
 8551                    BracketPair {
 8552                        start: "/*".to_string(),
 8553                        end: " */".to_string(),
 8554                        close: true,
 8555                        surround: true,
 8556                        newline: true,
 8557                    },
 8558                    BracketPair {
 8559                        start: "[".to_string(),
 8560                        end: "]".to_string(),
 8561                        close: false,
 8562                        surround: false,
 8563                        newline: true,
 8564                    },
 8565                    BracketPair {
 8566                        start: "\"".to_string(),
 8567                        end: "\"".to_string(),
 8568                        close: true,
 8569                        surround: true,
 8570                        newline: false,
 8571                    },
 8572                    BracketPair {
 8573                        start: "<".to_string(),
 8574                        end: ">".to_string(),
 8575                        close: false,
 8576                        surround: true,
 8577                        newline: true,
 8578                    },
 8579                ],
 8580                ..Default::default()
 8581            },
 8582            autoclose_before: "})]".to_string(),
 8583            ..Default::default()
 8584        },
 8585        Some(tree_sitter_rust::LANGUAGE.into()),
 8586    );
 8587    let language = Arc::new(language);
 8588
 8589    cx.language_registry().add(language.clone());
 8590    cx.update_buffer(|buffer, cx| {
 8591        buffer.set_language(Some(language), cx);
 8592    });
 8593
 8594    // Ensure that signature_help is not called when no signature help is enabled.
 8595    cx.set_state(
 8596        &r#"
 8597            fn main() {
 8598                sampleˇ
 8599            }
 8600        "#
 8601        .unindent(),
 8602    );
 8603    cx.update_editor(|editor, window, cx| {
 8604        editor.handle_input("(", window, cx);
 8605    });
 8606    cx.assert_editor_state(
 8607        &"
 8608            fn main() {
 8609                sample(ˇ)
 8610            }
 8611        "
 8612        .unindent(),
 8613    );
 8614    cx.editor(|editor, _, _| {
 8615        assert!(editor.signature_help_state.task().is_none());
 8616    });
 8617
 8618    let mocked_response = lsp::SignatureHelp {
 8619        signatures: vec![lsp::SignatureInformation {
 8620            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8621            documentation: None,
 8622            parameters: Some(vec![
 8623                lsp::ParameterInformation {
 8624                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8625                    documentation: None,
 8626                },
 8627                lsp::ParameterInformation {
 8628                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8629                    documentation: None,
 8630                },
 8631            ]),
 8632            active_parameter: None,
 8633        }],
 8634        active_signature: Some(0),
 8635        active_parameter: Some(0),
 8636    };
 8637
 8638    // Ensure that signature_help is called when enabled afte edits
 8639    cx.update(|_, cx| {
 8640        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8641            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8642                settings.auto_signature_help = Some(false);
 8643                settings.show_signature_help_after_edits = Some(true);
 8644            });
 8645        });
 8646    });
 8647    cx.set_state(
 8648        &r#"
 8649            fn main() {
 8650                sampleˇ
 8651            }
 8652        "#
 8653        .unindent(),
 8654    );
 8655    cx.update_editor(|editor, window, cx| {
 8656        editor.handle_input("(", window, cx);
 8657    });
 8658    cx.assert_editor_state(
 8659        &"
 8660            fn main() {
 8661                sample(ˇ)
 8662            }
 8663        "
 8664        .unindent(),
 8665    );
 8666    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8667    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8668        .await;
 8669    cx.update_editor(|editor, _, _| {
 8670        let signature_help_state = editor.signature_help_state.popover().cloned();
 8671        assert!(signature_help_state.is_some());
 8672        assert_eq!(
 8673            signature_help_state.unwrap().label,
 8674            "param1: u8, param2: u8"
 8675        );
 8676        editor.signature_help_state = SignatureHelpState::default();
 8677    });
 8678
 8679    // Ensure that signature_help is called when auto signature help override is enabled
 8680    cx.update(|_, cx| {
 8681        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8682            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8683                settings.auto_signature_help = Some(true);
 8684                settings.show_signature_help_after_edits = Some(false);
 8685            });
 8686        });
 8687    });
 8688    cx.set_state(
 8689        &r#"
 8690            fn main() {
 8691                sampleˇ
 8692            }
 8693        "#
 8694        .unindent(),
 8695    );
 8696    cx.update_editor(|editor, window, cx| {
 8697        editor.handle_input("(", window, cx);
 8698    });
 8699    cx.assert_editor_state(
 8700        &"
 8701            fn main() {
 8702                sample(ˇ)
 8703            }
 8704        "
 8705        .unindent(),
 8706    );
 8707    handle_signature_help_request(&mut cx, mocked_response).await;
 8708    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8709        .await;
 8710    cx.editor(|editor, _, _| {
 8711        let signature_help_state = editor.signature_help_state.popover().cloned();
 8712        assert!(signature_help_state.is_some());
 8713        assert_eq!(
 8714            signature_help_state.unwrap().label,
 8715            "param1: u8, param2: u8"
 8716        );
 8717    });
 8718}
 8719
 8720#[gpui::test]
 8721async fn test_signature_help(cx: &mut TestAppContext) {
 8722    init_test(cx, |_| {});
 8723    cx.update(|cx| {
 8724        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8725            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8726                settings.auto_signature_help = Some(true);
 8727            });
 8728        });
 8729    });
 8730
 8731    let mut cx = EditorLspTestContext::new_rust(
 8732        lsp::ServerCapabilities {
 8733            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8734                ..Default::default()
 8735            }),
 8736            ..Default::default()
 8737        },
 8738        cx,
 8739    )
 8740    .await;
 8741
 8742    // A test that directly calls `show_signature_help`
 8743    cx.update_editor(|editor, window, cx| {
 8744        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8745    });
 8746
 8747    let mocked_response = lsp::SignatureHelp {
 8748        signatures: vec![lsp::SignatureInformation {
 8749            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8750            documentation: None,
 8751            parameters: Some(vec![
 8752                lsp::ParameterInformation {
 8753                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8754                    documentation: None,
 8755                },
 8756                lsp::ParameterInformation {
 8757                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8758                    documentation: None,
 8759                },
 8760            ]),
 8761            active_parameter: None,
 8762        }],
 8763        active_signature: Some(0),
 8764        active_parameter: Some(0),
 8765    };
 8766    handle_signature_help_request(&mut cx, mocked_response).await;
 8767
 8768    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8769        .await;
 8770
 8771    cx.editor(|editor, _, _| {
 8772        let signature_help_state = editor.signature_help_state.popover().cloned();
 8773        assert!(signature_help_state.is_some());
 8774        assert_eq!(
 8775            signature_help_state.unwrap().label,
 8776            "param1: u8, param2: u8"
 8777        );
 8778    });
 8779
 8780    // When exiting outside from inside the brackets, `signature_help` is closed.
 8781    cx.set_state(indoc! {"
 8782        fn main() {
 8783            sample(ˇ);
 8784        }
 8785
 8786        fn sample(param1: u8, param2: u8) {}
 8787    "});
 8788
 8789    cx.update_editor(|editor, window, cx| {
 8790        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8791    });
 8792
 8793    let mocked_response = lsp::SignatureHelp {
 8794        signatures: Vec::new(),
 8795        active_signature: None,
 8796        active_parameter: None,
 8797    };
 8798    handle_signature_help_request(&mut cx, mocked_response).await;
 8799
 8800    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8801        .await;
 8802
 8803    cx.editor(|editor, _, _| {
 8804        assert!(!editor.signature_help_state.is_shown());
 8805    });
 8806
 8807    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8808    cx.set_state(indoc! {"
 8809        fn main() {
 8810            sample(ˇ);
 8811        }
 8812
 8813        fn sample(param1: u8, param2: u8) {}
 8814    "});
 8815
 8816    let mocked_response = lsp::SignatureHelp {
 8817        signatures: vec![lsp::SignatureInformation {
 8818            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8819            documentation: None,
 8820            parameters: Some(vec![
 8821                lsp::ParameterInformation {
 8822                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8823                    documentation: None,
 8824                },
 8825                lsp::ParameterInformation {
 8826                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8827                    documentation: None,
 8828                },
 8829            ]),
 8830            active_parameter: None,
 8831        }],
 8832        active_signature: Some(0),
 8833        active_parameter: Some(0),
 8834    };
 8835    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8836    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8837        .await;
 8838    cx.editor(|editor, _, _| {
 8839        assert!(editor.signature_help_state.is_shown());
 8840    });
 8841
 8842    // Restore the popover with more parameter input
 8843    cx.set_state(indoc! {"
 8844        fn main() {
 8845            sample(param1, param2ˇ);
 8846        }
 8847
 8848        fn sample(param1: u8, param2: u8) {}
 8849    "});
 8850
 8851    let mocked_response = lsp::SignatureHelp {
 8852        signatures: vec![lsp::SignatureInformation {
 8853            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8854            documentation: None,
 8855            parameters: Some(vec![
 8856                lsp::ParameterInformation {
 8857                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8858                    documentation: None,
 8859                },
 8860                lsp::ParameterInformation {
 8861                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8862                    documentation: None,
 8863                },
 8864            ]),
 8865            active_parameter: None,
 8866        }],
 8867        active_signature: Some(0),
 8868        active_parameter: Some(1),
 8869    };
 8870    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8871    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8872        .await;
 8873
 8874    // When selecting a range, the popover is gone.
 8875    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8876    cx.update_editor(|editor, window, cx| {
 8877        editor.change_selections(None, window, cx, |s| {
 8878            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8879        })
 8880    });
 8881    cx.assert_editor_state(indoc! {"
 8882        fn main() {
 8883            sample(param1, «ˇparam2»);
 8884        }
 8885
 8886        fn sample(param1: u8, param2: u8) {}
 8887    "});
 8888    cx.editor(|editor, _, _| {
 8889        assert!(!editor.signature_help_state.is_shown());
 8890    });
 8891
 8892    // When unselecting again, the popover is back if within the brackets.
 8893    cx.update_editor(|editor, window, cx| {
 8894        editor.change_selections(None, window, cx, |s| {
 8895            s.select_ranges(Some(Point::new(1, 19)..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    handle_signature_help_request(&mut cx, mocked_response).await;
 8906    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8907        .await;
 8908    cx.editor(|editor, _, _| {
 8909        assert!(editor.signature_help_state.is_shown());
 8910    });
 8911
 8912    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8913    cx.update_editor(|editor, window, cx| {
 8914        editor.change_selections(None, window, cx, |s| {
 8915            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8916            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8917        })
 8918    });
 8919    cx.assert_editor_state(indoc! {"
 8920        fn main() {
 8921            sample(param1, ˇparam2);
 8922        }
 8923
 8924        fn sample(param1: u8, param2: u8) {}
 8925    "});
 8926
 8927    let mocked_response = lsp::SignatureHelp {
 8928        signatures: vec![lsp::SignatureInformation {
 8929            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8930            documentation: None,
 8931            parameters: Some(vec![
 8932                lsp::ParameterInformation {
 8933                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8934                    documentation: None,
 8935                },
 8936                lsp::ParameterInformation {
 8937                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8938                    documentation: None,
 8939                },
 8940            ]),
 8941            active_parameter: None,
 8942        }],
 8943        active_signature: Some(0),
 8944        active_parameter: Some(1),
 8945    };
 8946    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8947    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8948        .await;
 8949    cx.update_editor(|editor, _, cx| {
 8950        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8951    });
 8952    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8953        .await;
 8954    cx.update_editor(|editor, window, cx| {
 8955        editor.change_selections(None, window, cx, |s| {
 8956            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8957        })
 8958    });
 8959    cx.assert_editor_state(indoc! {"
 8960        fn main() {
 8961            sample(param1, «ˇparam2»);
 8962        }
 8963
 8964        fn sample(param1: u8, param2: u8) {}
 8965    "});
 8966    cx.update_editor(|editor, window, cx| {
 8967        editor.change_selections(None, window, cx, |s| {
 8968            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8969        })
 8970    });
 8971    cx.assert_editor_state(indoc! {"
 8972        fn main() {
 8973            sample(param1, ˇparam2);
 8974        }
 8975
 8976        fn sample(param1: u8, param2: u8) {}
 8977    "});
 8978    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8979        .await;
 8980}
 8981
 8982#[gpui::test]
 8983async fn test_completion(cx: &mut TestAppContext) {
 8984    init_test(cx, |_| {});
 8985
 8986    let mut cx = EditorLspTestContext::new_rust(
 8987        lsp::ServerCapabilities {
 8988            completion_provider: Some(lsp::CompletionOptions {
 8989                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 8990                resolve_provider: Some(true),
 8991                ..Default::default()
 8992            }),
 8993            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 8994            ..Default::default()
 8995        },
 8996        cx,
 8997    )
 8998    .await;
 8999    let counter = Arc::new(AtomicUsize::new(0));
 9000
 9001    cx.set_state(indoc! {"
 9002        oneˇ
 9003        two
 9004        three
 9005    "});
 9006    cx.simulate_keystroke(".");
 9007    handle_completion_request(
 9008        &mut cx,
 9009        indoc! {"
 9010            one.|<>
 9011            two
 9012            three
 9013        "},
 9014        vec!["first_completion", "second_completion"],
 9015        counter.clone(),
 9016    )
 9017    .await;
 9018    cx.condition(|editor, _| editor.context_menu_visible())
 9019        .await;
 9020    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9021
 9022    let _handler = handle_signature_help_request(
 9023        &mut cx,
 9024        lsp::SignatureHelp {
 9025            signatures: vec![lsp::SignatureInformation {
 9026                label: "test signature".to_string(),
 9027                documentation: None,
 9028                parameters: Some(vec![lsp::ParameterInformation {
 9029                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9030                    documentation: None,
 9031                }]),
 9032                active_parameter: None,
 9033            }],
 9034            active_signature: None,
 9035            active_parameter: None,
 9036        },
 9037    );
 9038    cx.update_editor(|editor, window, cx| {
 9039        assert!(
 9040            !editor.signature_help_state.is_shown(),
 9041            "No signature help was called for"
 9042        );
 9043        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9044    });
 9045    cx.run_until_parked();
 9046    cx.update_editor(|editor, _, _| {
 9047        assert!(
 9048            !editor.signature_help_state.is_shown(),
 9049            "No signature help should be shown when completions menu is open"
 9050        );
 9051    });
 9052
 9053    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9054        editor.context_menu_next(&Default::default(), window, cx);
 9055        editor
 9056            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9057            .unwrap()
 9058    });
 9059    cx.assert_editor_state(indoc! {"
 9060        one.second_completionˇ
 9061        two
 9062        three
 9063    "});
 9064
 9065    handle_resolve_completion_request(
 9066        &mut cx,
 9067        Some(vec![
 9068            (
 9069                //This overlaps with the primary completion edit which is
 9070                //misbehavior from the LSP spec, test that we filter it out
 9071                indoc! {"
 9072                    one.second_ˇcompletion
 9073                    two
 9074                    threeˇ
 9075                "},
 9076                "overlapping additional edit",
 9077            ),
 9078            (
 9079                indoc! {"
 9080                    one.second_completion
 9081                    two
 9082                    threeˇ
 9083                "},
 9084                "\nadditional edit",
 9085            ),
 9086        ]),
 9087    )
 9088    .await;
 9089    apply_additional_edits.await.unwrap();
 9090    cx.assert_editor_state(indoc! {"
 9091        one.second_completionˇ
 9092        two
 9093        three
 9094        additional edit
 9095    "});
 9096
 9097    cx.set_state(indoc! {"
 9098        one.second_completion
 9099        twoˇ
 9100        threeˇ
 9101        additional edit
 9102    "});
 9103    cx.simulate_keystroke(" ");
 9104    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9105    cx.simulate_keystroke("s");
 9106    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9107
 9108    cx.assert_editor_state(indoc! {"
 9109        one.second_completion
 9110        two sˇ
 9111        three sˇ
 9112        additional edit
 9113    "});
 9114    handle_completion_request(
 9115        &mut cx,
 9116        indoc! {"
 9117            one.second_completion
 9118            two s
 9119            three <s|>
 9120            additional edit
 9121        "},
 9122        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9123        counter.clone(),
 9124    )
 9125    .await;
 9126    cx.condition(|editor, _| editor.context_menu_visible())
 9127        .await;
 9128    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9129
 9130    cx.simulate_keystroke("i");
 9131
 9132    handle_completion_request(
 9133        &mut cx,
 9134        indoc! {"
 9135            one.second_completion
 9136            two si
 9137            three <si|>
 9138            additional edit
 9139        "},
 9140        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9141        counter.clone(),
 9142    )
 9143    .await;
 9144    cx.condition(|editor, _| editor.context_menu_visible())
 9145        .await;
 9146    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9147
 9148    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9149        editor
 9150            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9151            .unwrap()
 9152    });
 9153    cx.assert_editor_state(indoc! {"
 9154        one.second_completion
 9155        two sixth_completionˇ
 9156        three sixth_completionˇ
 9157        additional edit
 9158    "});
 9159
 9160    apply_additional_edits.await.unwrap();
 9161
 9162    update_test_language_settings(&mut cx, |settings| {
 9163        settings.defaults.show_completions_on_input = Some(false);
 9164    });
 9165    cx.set_state("editorˇ");
 9166    cx.simulate_keystroke(".");
 9167    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9168    cx.simulate_keystroke("c");
 9169    cx.simulate_keystroke("l");
 9170    cx.simulate_keystroke("o");
 9171    cx.assert_editor_state("editor.cloˇ");
 9172    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9173    cx.update_editor(|editor, window, cx| {
 9174        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9175    });
 9176    handle_completion_request(
 9177        &mut cx,
 9178        "editor.<clo|>",
 9179        vec!["close", "clobber"],
 9180        counter.clone(),
 9181    )
 9182    .await;
 9183    cx.condition(|editor, _| editor.context_menu_visible())
 9184        .await;
 9185    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9186
 9187    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9188        editor
 9189            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9190            .unwrap()
 9191    });
 9192    cx.assert_editor_state("editor.closeˇ");
 9193    handle_resolve_completion_request(&mut cx, None).await;
 9194    apply_additional_edits.await.unwrap();
 9195}
 9196
 9197#[gpui::test]
 9198async fn test_words_completion(cx: &mut TestAppContext) {
 9199    let lsp_fetch_timeout_ms = 10;
 9200    init_test(cx, |language_settings| {
 9201        language_settings.defaults.completions = Some(CompletionSettings {
 9202            words: WordsCompletionMode::Fallback,
 9203            lsp: true,
 9204            lsp_fetch_timeout_ms: 10,
 9205        });
 9206    });
 9207
 9208    let mut cx = EditorLspTestContext::new_rust(
 9209        lsp::ServerCapabilities {
 9210            completion_provider: Some(lsp::CompletionOptions {
 9211                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9212                ..lsp::CompletionOptions::default()
 9213            }),
 9214            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9215            ..lsp::ServerCapabilities::default()
 9216        },
 9217        cx,
 9218    )
 9219    .await;
 9220
 9221    let throttle_completions = Arc::new(AtomicBool::new(false));
 9222
 9223    let lsp_throttle_completions = throttle_completions.clone();
 9224    let _completion_requests_handler =
 9225        cx.lsp
 9226            .server
 9227            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9228                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9229                async move {
 9230                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9231                        cx.background_executor()
 9232                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9233                            .await;
 9234                    }
 9235                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9236                        lsp::CompletionItem {
 9237                            label: "first".into(),
 9238                            ..lsp::CompletionItem::default()
 9239                        },
 9240                        lsp::CompletionItem {
 9241                            label: "last".into(),
 9242                            ..lsp::CompletionItem::default()
 9243                        },
 9244                    ])))
 9245                }
 9246            });
 9247
 9248    cx.set_state(indoc! {"
 9249        oneˇ
 9250        two
 9251        three
 9252    "});
 9253    cx.simulate_keystroke(".");
 9254    cx.executor().run_until_parked();
 9255    cx.condition(|editor, _| editor.context_menu_visible())
 9256        .await;
 9257    cx.update_editor(|editor, window, cx| {
 9258        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9259        {
 9260            assert_eq!(
 9261                completion_menu_entries(&menu),
 9262                &["first", "last"],
 9263                "When LSP server is fast to reply, no fallback word completions are used"
 9264            );
 9265        } else {
 9266            panic!("expected completion menu to be open");
 9267        }
 9268        editor.cancel(&Cancel, window, cx);
 9269    });
 9270    cx.executor().run_until_parked();
 9271    cx.condition(|editor, _| !editor.context_menu_visible())
 9272        .await;
 9273
 9274    throttle_completions.store(true, atomic::Ordering::Release);
 9275    cx.simulate_keystroke(".");
 9276    cx.executor()
 9277        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9278    cx.executor().run_until_parked();
 9279    cx.condition(|editor, _| editor.context_menu_visible())
 9280        .await;
 9281    cx.update_editor(|editor, _, _| {
 9282        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9283        {
 9284            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9285                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9286        } else {
 9287            panic!("expected completion menu to be open");
 9288        }
 9289    });
 9290}
 9291
 9292#[gpui::test]
 9293async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9294    init_test(cx, |language_settings| {
 9295        language_settings.defaults.completions = Some(CompletionSettings {
 9296            words: WordsCompletionMode::Enabled,
 9297            lsp: true,
 9298            lsp_fetch_timeout_ms: 0,
 9299        });
 9300    });
 9301
 9302    let mut cx = EditorLspTestContext::new_rust(
 9303        lsp::ServerCapabilities {
 9304            completion_provider: Some(lsp::CompletionOptions {
 9305                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9306                ..lsp::CompletionOptions::default()
 9307            }),
 9308            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9309            ..lsp::ServerCapabilities::default()
 9310        },
 9311        cx,
 9312    )
 9313    .await;
 9314
 9315    let _completion_requests_handler =
 9316        cx.lsp
 9317            .server
 9318            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9319                Ok(Some(lsp::CompletionResponse::Array(vec![
 9320                    lsp::CompletionItem {
 9321                        label: "first".into(),
 9322                        ..lsp::CompletionItem::default()
 9323                    },
 9324                    lsp::CompletionItem {
 9325                        label: "last".into(),
 9326                        ..lsp::CompletionItem::default()
 9327                    },
 9328                ])))
 9329            });
 9330
 9331    cx.set_state(indoc! {"ˇ
 9332        first
 9333        last
 9334        second
 9335    "});
 9336    cx.simulate_keystroke(".");
 9337    cx.executor().run_until_parked();
 9338    cx.condition(|editor, _| editor.context_menu_visible())
 9339        .await;
 9340    cx.update_editor(|editor, window, cx| {
 9341        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9342        {
 9343            assert_eq!(
 9344                completion_menu_entries(&menu),
 9345                &["first", "last", "second"],
 9346                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9347            );
 9348        } else {
 9349            panic!("expected completion menu to be open");
 9350        }
 9351        editor.cancel(&Cancel, window, cx);
 9352    });
 9353}
 9354
 9355#[gpui::test]
 9356async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9357    init_test(cx, |language_settings| {
 9358        language_settings.defaults.completions = Some(CompletionSettings {
 9359            words: WordsCompletionMode::Fallback,
 9360            lsp: false,
 9361            lsp_fetch_timeout_ms: 0,
 9362        });
 9363    });
 9364
 9365    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9366
 9367    cx.set_state(indoc! {"ˇ
 9368        0_usize
 9369        let
 9370        33
 9371        4.5f32
 9372    "});
 9373    cx.update_editor(|editor, window, cx| {
 9374        editor.show_completions(&ShowCompletions::default(), window, cx);
 9375    });
 9376    cx.executor().run_until_parked();
 9377    cx.condition(|editor, _| editor.context_menu_visible())
 9378        .await;
 9379    cx.update_editor(|editor, window, cx| {
 9380        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9381        {
 9382            assert_eq!(
 9383                completion_menu_entries(&menu),
 9384                &["let"],
 9385                "With no digits in the completion query, no digits should be in the word completions"
 9386            );
 9387        } else {
 9388            panic!("expected completion menu to be open");
 9389        }
 9390        editor.cancel(&Cancel, window, cx);
 9391    });
 9392
 9393    cx.set_state(indoc! {" 9394        0_usize
 9395        let
 9396        3
 9397        33.35f32
 9398    "});
 9399    cx.update_editor(|editor, window, cx| {
 9400        editor.show_completions(&ShowCompletions::default(), window, cx);
 9401    });
 9402    cx.executor().run_until_parked();
 9403    cx.condition(|editor, _| editor.context_menu_visible())
 9404        .await;
 9405    cx.update_editor(|editor, _, _| {
 9406        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9407        {
 9408            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9409                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9410        } else {
 9411            panic!("expected completion menu to be open");
 9412        }
 9413    });
 9414}
 9415
 9416#[gpui::test]
 9417async fn test_multiline_completion(cx: &mut TestAppContext) {
 9418    init_test(cx, |_| {});
 9419
 9420    let fs = FakeFs::new(cx.executor());
 9421    fs.insert_tree(
 9422        path!("/a"),
 9423        json!({
 9424            "main.ts": "a",
 9425        }),
 9426    )
 9427    .await;
 9428
 9429    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9430    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9431    let typescript_language = Arc::new(Language::new(
 9432        LanguageConfig {
 9433            name: "TypeScript".into(),
 9434            matcher: LanguageMatcher {
 9435                path_suffixes: vec!["ts".to_string()],
 9436                ..LanguageMatcher::default()
 9437            },
 9438            line_comments: vec!["// ".into()],
 9439            ..LanguageConfig::default()
 9440        },
 9441        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9442    ));
 9443    language_registry.add(typescript_language.clone());
 9444    let mut fake_servers = language_registry.register_fake_lsp(
 9445        "TypeScript",
 9446        FakeLspAdapter {
 9447            capabilities: lsp::ServerCapabilities {
 9448                completion_provider: Some(lsp::CompletionOptions {
 9449                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9450                    ..lsp::CompletionOptions::default()
 9451                }),
 9452                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9453                ..lsp::ServerCapabilities::default()
 9454            },
 9455            // Emulate vtsls label generation
 9456            label_for_completion: Some(Box::new(|item, _| {
 9457                let text = if let Some(description) = item
 9458                    .label_details
 9459                    .as_ref()
 9460                    .and_then(|label_details| label_details.description.as_ref())
 9461                {
 9462                    format!("{} {}", item.label, description)
 9463                } else if let Some(detail) = &item.detail {
 9464                    format!("{} {}", item.label, detail)
 9465                } else {
 9466                    item.label.clone()
 9467                };
 9468                let len = text.len();
 9469                Some(language::CodeLabel {
 9470                    text,
 9471                    runs: Vec::new(),
 9472                    filter_range: 0..len,
 9473                })
 9474            })),
 9475            ..FakeLspAdapter::default()
 9476        },
 9477    );
 9478    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9479    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9480    let worktree_id = workspace
 9481        .update(cx, |workspace, _window, cx| {
 9482            workspace.project().update(cx, |project, cx| {
 9483                project.worktrees(cx).next().unwrap().read(cx).id()
 9484            })
 9485        })
 9486        .unwrap();
 9487    let _buffer = project
 9488        .update(cx, |project, cx| {
 9489            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9490        })
 9491        .await
 9492        .unwrap();
 9493    let editor = workspace
 9494        .update(cx, |workspace, window, cx| {
 9495            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9496        })
 9497        .unwrap()
 9498        .await
 9499        .unwrap()
 9500        .downcast::<Editor>()
 9501        .unwrap();
 9502    let fake_server = fake_servers.next().await.unwrap();
 9503
 9504    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9505    let multiline_label_2 = "a\nb\nc\n";
 9506    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9507    let multiline_description = "d\ne\nf\n";
 9508    let multiline_detail_2 = "g\nh\ni\n";
 9509
 9510    let mut completion_handle =
 9511        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9512            Ok(Some(lsp::CompletionResponse::Array(vec![
 9513                lsp::CompletionItem {
 9514                    label: multiline_label.to_string(),
 9515                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9516                        range: lsp::Range {
 9517                            start: lsp::Position {
 9518                                line: params.text_document_position.position.line,
 9519                                character: params.text_document_position.position.character,
 9520                            },
 9521                            end: lsp::Position {
 9522                                line: params.text_document_position.position.line,
 9523                                character: params.text_document_position.position.character,
 9524                            },
 9525                        },
 9526                        new_text: "new_text_1".to_string(),
 9527                    })),
 9528                    ..lsp::CompletionItem::default()
 9529                },
 9530                lsp::CompletionItem {
 9531                    label: "single line label 1".to_string(),
 9532                    detail: Some(multiline_detail.to_string()),
 9533                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9534                        range: lsp::Range {
 9535                            start: lsp::Position {
 9536                                line: params.text_document_position.position.line,
 9537                                character: params.text_document_position.position.character,
 9538                            },
 9539                            end: lsp::Position {
 9540                                line: params.text_document_position.position.line,
 9541                                character: params.text_document_position.position.character,
 9542                            },
 9543                        },
 9544                        new_text: "new_text_2".to_string(),
 9545                    })),
 9546                    ..lsp::CompletionItem::default()
 9547                },
 9548                lsp::CompletionItem {
 9549                    label: "single line label 2".to_string(),
 9550                    label_details: Some(lsp::CompletionItemLabelDetails {
 9551                        description: Some(multiline_description.to_string()),
 9552                        detail: None,
 9553                    }),
 9554                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9555                        range: lsp::Range {
 9556                            start: lsp::Position {
 9557                                line: params.text_document_position.position.line,
 9558                                character: params.text_document_position.position.character,
 9559                            },
 9560                            end: lsp::Position {
 9561                                line: params.text_document_position.position.line,
 9562                                character: params.text_document_position.position.character,
 9563                            },
 9564                        },
 9565                        new_text: "new_text_2".to_string(),
 9566                    })),
 9567                    ..lsp::CompletionItem::default()
 9568                },
 9569                lsp::CompletionItem {
 9570                    label: multiline_label_2.to_string(),
 9571                    detail: Some(multiline_detail_2.to_string()),
 9572                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9573                        range: lsp::Range {
 9574                            start: lsp::Position {
 9575                                line: params.text_document_position.position.line,
 9576                                character: params.text_document_position.position.character,
 9577                            },
 9578                            end: lsp::Position {
 9579                                line: params.text_document_position.position.line,
 9580                                character: params.text_document_position.position.character,
 9581                            },
 9582                        },
 9583                        new_text: "new_text_3".to_string(),
 9584                    })),
 9585                    ..lsp::CompletionItem::default()
 9586                },
 9587                lsp::CompletionItem {
 9588                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9589                    detail: Some(
 9590                        "Details with many     spaces and \t but without newlines".to_string(),
 9591                    ),
 9592                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9593                        range: lsp::Range {
 9594                            start: lsp::Position {
 9595                                line: params.text_document_position.position.line,
 9596                                character: params.text_document_position.position.character,
 9597                            },
 9598                            end: lsp::Position {
 9599                                line: params.text_document_position.position.line,
 9600                                character: params.text_document_position.position.character,
 9601                            },
 9602                        },
 9603                        new_text: "new_text_4".to_string(),
 9604                    })),
 9605                    ..lsp::CompletionItem::default()
 9606                },
 9607            ])))
 9608        });
 9609
 9610    editor.update_in(cx, |editor, window, cx| {
 9611        cx.focus_self(window);
 9612        editor.move_to_end(&MoveToEnd, window, cx);
 9613        editor.handle_input(".", window, cx);
 9614    });
 9615    cx.run_until_parked();
 9616    completion_handle.next().await.unwrap();
 9617
 9618    editor.update(cx, |editor, _| {
 9619        assert!(editor.context_menu_visible());
 9620        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9621        {
 9622            let completion_labels = menu
 9623                .completions
 9624                .borrow()
 9625                .iter()
 9626                .map(|c| c.label.text.clone())
 9627                .collect::<Vec<_>>();
 9628            assert_eq!(
 9629                completion_labels,
 9630                &[
 9631                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9632                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9633                    "single line label 2 d e f ",
 9634                    "a b c g h i ",
 9635                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9636                ],
 9637                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9638            );
 9639
 9640            for completion in menu
 9641                .completions
 9642                .borrow()
 9643                .iter() {
 9644                    assert_eq!(
 9645                        completion.label.filter_range,
 9646                        0..completion.label.text.len(),
 9647                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9648                    );
 9649                }
 9650
 9651        } else {
 9652            panic!("expected completion menu to be open");
 9653        }
 9654    });
 9655}
 9656
 9657#[gpui::test]
 9658async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9659    init_test(cx, |_| {});
 9660    let mut cx = EditorLspTestContext::new_rust(
 9661        lsp::ServerCapabilities {
 9662            completion_provider: Some(lsp::CompletionOptions {
 9663                trigger_characters: Some(vec![".".to_string()]),
 9664                ..Default::default()
 9665            }),
 9666            ..Default::default()
 9667        },
 9668        cx,
 9669    )
 9670    .await;
 9671    cx.lsp
 9672        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9673            Ok(Some(lsp::CompletionResponse::Array(vec![
 9674                lsp::CompletionItem {
 9675                    label: "first".into(),
 9676                    ..Default::default()
 9677                },
 9678                lsp::CompletionItem {
 9679                    label: "last".into(),
 9680                    ..Default::default()
 9681                },
 9682            ])))
 9683        });
 9684    cx.set_state("variableˇ");
 9685    cx.simulate_keystroke(".");
 9686    cx.executor().run_until_parked();
 9687
 9688    cx.update_editor(|editor, _, _| {
 9689        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9690        {
 9691            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9692        } else {
 9693            panic!("expected completion menu to be open");
 9694        }
 9695    });
 9696
 9697    cx.update_editor(|editor, window, cx| {
 9698        editor.move_page_down(&MovePageDown::default(), window, cx);
 9699        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9700        {
 9701            assert!(
 9702                menu.selected_item == 1,
 9703                "expected PageDown to select the last item from the context menu"
 9704            );
 9705        } else {
 9706            panic!("expected completion menu to stay open after PageDown");
 9707        }
 9708    });
 9709
 9710    cx.update_editor(|editor, window, cx| {
 9711        editor.move_page_up(&MovePageUp::default(), window, cx);
 9712        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9713        {
 9714            assert!(
 9715                menu.selected_item == 0,
 9716                "expected PageUp to select the first item from the context menu"
 9717            );
 9718        } else {
 9719            panic!("expected completion menu to stay open after PageUp");
 9720        }
 9721    });
 9722}
 9723
 9724#[gpui::test]
 9725async fn test_completion_sort(cx: &mut TestAppContext) {
 9726    init_test(cx, |_| {});
 9727    let mut cx = EditorLspTestContext::new_rust(
 9728        lsp::ServerCapabilities {
 9729            completion_provider: Some(lsp::CompletionOptions {
 9730                trigger_characters: Some(vec![".".to_string()]),
 9731                ..Default::default()
 9732            }),
 9733            ..Default::default()
 9734        },
 9735        cx,
 9736    )
 9737    .await;
 9738    cx.lsp
 9739        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9740            Ok(Some(lsp::CompletionResponse::Array(vec![
 9741                lsp::CompletionItem {
 9742                    label: "Range".into(),
 9743                    sort_text: Some("a".into()),
 9744                    ..Default::default()
 9745                },
 9746                lsp::CompletionItem {
 9747                    label: "r".into(),
 9748                    sort_text: Some("b".into()),
 9749                    ..Default::default()
 9750                },
 9751                lsp::CompletionItem {
 9752                    label: "ret".into(),
 9753                    sort_text: Some("c".into()),
 9754                    ..Default::default()
 9755                },
 9756                lsp::CompletionItem {
 9757                    label: "return".into(),
 9758                    sort_text: Some("d".into()),
 9759                    ..Default::default()
 9760                },
 9761                lsp::CompletionItem {
 9762                    label: "slice".into(),
 9763                    sort_text: Some("d".into()),
 9764                    ..Default::default()
 9765                },
 9766            ])))
 9767        });
 9768    cx.set_state("");
 9769    cx.executor().run_until_parked();
 9770    cx.update_editor(|editor, window, cx| {
 9771        editor.show_completions(
 9772            &ShowCompletions {
 9773                trigger: Some("r".into()),
 9774            },
 9775            window,
 9776            cx,
 9777        );
 9778    });
 9779    cx.executor().run_until_parked();
 9780
 9781    cx.update_editor(|editor, _, _| {
 9782        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9783        {
 9784            assert_eq!(
 9785                completion_menu_entries(&menu),
 9786                &["r", "ret", "Range", "return"]
 9787            );
 9788        } else {
 9789            panic!("expected completion menu to be open");
 9790        }
 9791    });
 9792}
 9793
 9794#[gpui::test]
 9795async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9796    init_test(cx, |_| {});
 9797
 9798    let mut cx = EditorLspTestContext::new_rust(
 9799        lsp::ServerCapabilities {
 9800            completion_provider: Some(lsp::CompletionOptions {
 9801                trigger_characters: Some(vec![".".to_string()]),
 9802                resolve_provider: Some(true),
 9803                ..Default::default()
 9804            }),
 9805            ..Default::default()
 9806        },
 9807        cx,
 9808    )
 9809    .await;
 9810
 9811    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9812    cx.simulate_keystroke(".");
 9813    let completion_item = lsp::CompletionItem {
 9814        label: "Some".into(),
 9815        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9816        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9817        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9818            kind: lsp::MarkupKind::Markdown,
 9819            value: "```rust\nSome(2)\n```".to_string(),
 9820        })),
 9821        deprecated: Some(false),
 9822        sort_text: Some("Some".to_string()),
 9823        filter_text: Some("Some".to_string()),
 9824        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9825        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9826            range: lsp::Range {
 9827                start: lsp::Position {
 9828                    line: 0,
 9829                    character: 22,
 9830                },
 9831                end: lsp::Position {
 9832                    line: 0,
 9833                    character: 22,
 9834                },
 9835            },
 9836            new_text: "Some(2)".to_string(),
 9837        })),
 9838        additional_text_edits: Some(vec![lsp::TextEdit {
 9839            range: lsp::Range {
 9840                start: lsp::Position {
 9841                    line: 0,
 9842                    character: 20,
 9843                },
 9844                end: lsp::Position {
 9845                    line: 0,
 9846                    character: 22,
 9847                },
 9848            },
 9849            new_text: "".to_string(),
 9850        }]),
 9851        ..Default::default()
 9852    };
 9853
 9854    let closure_completion_item = completion_item.clone();
 9855    let counter = Arc::new(AtomicUsize::new(0));
 9856    let counter_clone = counter.clone();
 9857    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9858        let task_completion_item = closure_completion_item.clone();
 9859        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9860        async move {
 9861            Ok(Some(lsp::CompletionResponse::Array(vec![
 9862                task_completion_item,
 9863            ])))
 9864        }
 9865    });
 9866
 9867    cx.condition(|editor, _| editor.context_menu_visible())
 9868        .await;
 9869    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9870    assert!(request.next().await.is_some());
 9871    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9872
 9873    cx.simulate_keystroke("S");
 9874    cx.simulate_keystroke("o");
 9875    cx.simulate_keystroke("m");
 9876    cx.condition(|editor, _| editor.context_menu_visible())
 9877        .await;
 9878    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9879    assert!(request.next().await.is_some());
 9880    assert!(request.next().await.is_some());
 9881    assert!(request.next().await.is_some());
 9882    request.close();
 9883    assert!(request.next().await.is_none());
 9884    assert_eq!(
 9885        counter.load(atomic::Ordering::Acquire),
 9886        4,
 9887        "With the completions menu open, only one LSP request should happen per input"
 9888    );
 9889}
 9890
 9891#[gpui::test]
 9892async fn test_toggle_comment(cx: &mut TestAppContext) {
 9893    init_test(cx, |_| {});
 9894    let mut cx = EditorTestContext::new(cx).await;
 9895    let language = Arc::new(Language::new(
 9896        LanguageConfig {
 9897            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9898            ..Default::default()
 9899        },
 9900        Some(tree_sitter_rust::LANGUAGE.into()),
 9901    ));
 9902    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9903
 9904    // If multiple selections intersect a line, the line is only toggled once.
 9905    cx.set_state(indoc! {"
 9906        fn a() {
 9907            «//b();
 9908            ˇ»// «c();
 9909            //ˇ»  d();
 9910        }
 9911    "});
 9912
 9913    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9914
 9915    cx.assert_editor_state(indoc! {"
 9916        fn a() {
 9917            «b();
 9918            c();
 9919            ˇ» d();
 9920        }
 9921    "});
 9922
 9923    // The comment prefix is inserted at the same column for every line in a
 9924    // selection.
 9925    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9926
 9927    cx.assert_editor_state(indoc! {"
 9928        fn a() {
 9929            // «b();
 9930            // c();
 9931            ˇ»//  d();
 9932        }
 9933    "});
 9934
 9935    // If a selection ends at the beginning of a line, that line is not toggled.
 9936    cx.set_selections_state(indoc! {"
 9937        fn a() {
 9938            // b();
 9939            «// c();
 9940        ˇ»    //  d();
 9941        }
 9942    "});
 9943
 9944    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9945
 9946    cx.assert_editor_state(indoc! {"
 9947        fn a() {
 9948            // b();
 9949            «c();
 9950        ˇ»    //  d();
 9951        }
 9952    "});
 9953
 9954    // If a selection span a single line and is empty, the line is toggled.
 9955    cx.set_state(indoc! {"
 9956        fn a() {
 9957            a();
 9958            b();
 9959        ˇ
 9960        }
 9961    "});
 9962
 9963    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9964
 9965    cx.assert_editor_state(indoc! {"
 9966        fn a() {
 9967            a();
 9968            b();
 9969        //•ˇ
 9970        }
 9971    "});
 9972
 9973    // If a selection span multiple lines, empty lines are not toggled.
 9974    cx.set_state(indoc! {"
 9975        fn a() {
 9976            «a();
 9977
 9978            c();ˇ»
 9979        }
 9980    "});
 9981
 9982    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9983
 9984    cx.assert_editor_state(indoc! {"
 9985        fn a() {
 9986            // «a();
 9987
 9988            // c();ˇ»
 9989        }
 9990    "});
 9991
 9992    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9993    cx.set_state(indoc! {"
 9994        fn a() {
 9995            «// a();
 9996            /// b();
 9997            //! c();ˇ»
 9998        }
 9999    "});
10000
10001    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10002
10003    cx.assert_editor_state(indoc! {"
10004        fn a() {
10005            «a();
10006            b();
10007            c();ˇ»
10008        }
10009    "});
10010}
10011
10012#[gpui::test]
10013async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10014    init_test(cx, |_| {});
10015    let mut cx = EditorTestContext::new(cx).await;
10016    let language = Arc::new(Language::new(
10017        LanguageConfig {
10018            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10019            ..Default::default()
10020        },
10021        Some(tree_sitter_rust::LANGUAGE.into()),
10022    ));
10023    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10024
10025    let toggle_comments = &ToggleComments {
10026        advance_downwards: false,
10027        ignore_indent: true,
10028    };
10029
10030    // If multiple selections intersect a line, the line is only toggled once.
10031    cx.set_state(indoc! {"
10032        fn a() {
10033        //    «b();
10034        //    c();
10035        //    ˇ» d();
10036        }
10037    "});
10038
10039    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10040
10041    cx.assert_editor_state(indoc! {"
10042        fn a() {
10043            «b();
10044            c();
10045            ˇ» d();
10046        }
10047    "});
10048
10049    // The comment prefix is inserted at the beginning of each line
10050    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10051
10052    cx.assert_editor_state(indoc! {"
10053        fn a() {
10054        //    «b();
10055        //    c();
10056        //    ˇ» d();
10057        }
10058    "});
10059
10060    // If a selection ends at the beginning of a line, that line is not toggled.
10061    cx.set_selections_state(indoc! {"
10062        fn a() {
10063        //    b();
10064        //    «c();
10065        ˇ»//     d();
10066        }
10067    "});
10068
10069    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10070
10071    cx.assert_editor_state(indoc! {"
10072        fn a() {
10073        //    b();
10074            «c();
10075        ˇ»//     d();
10076        }
10077    "});
10078
10079    // If a selection span a single line and is empty, the line is toggled.
10080    cx.set_state(indoc! {"
10081        fn a() {
10082            a();
10083            b();
10084        ˇ
10085        }
10086    "});
10087
10088    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10089
10090    cx.assert_editor_state(indoc! {"
10091        fn a() {
10092            a();
10093            b();
10094        //ˇ
10095        }
10096    "});
10097
10098    // If a selection span multiple lines, empty lines are not toggled.
10099    cx.set_state(indoc! {"
10100        fn a() {
10101            «a();
10102
10103            c();ˇ»
10104        }
10105    "});
10106
10107    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10108
10109    cx.assert_editor_state(indoc! {"
10110        fn a() {
10111        //    «a();
10112
10113        //    c();ˇ»
10114        }
10115    "});
10116
10117    // If a selection includes multiple comment prefixes, all lines are uncommented.
10118    cx.set_state(indoc! {"
10119        fn a() {
10120        //    «a();
10121        ///    b();
10122        //!    c();ˇ»
10123        }
10124    "});
10125
10126    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10127
10128    cx.assert_editor_state(indoc! {"
10129        fn a() {
10130            «a();
10131            b();
10132            c();ˇ»
10133        }
10134    "});
10135}
10136
10137#[gpui::test]
10138async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10139    init_test(cx, |_| {});
10140
10141    let language = Arc::new(Language::new(
10142        LanguageConfig {
10143            line_comments: vec!["// ".into()],
10144            ..Default::default()
10145        },
10146        Some(tree_sitter_rust::LANGUAGE.into()),
10147    ));
10148
10149    let mut cx = EditorTestContext::new(cx).await;
10150
10151    cx.language_registry().add(language.clone());
10152    cx.update_buffer(|buffer, cx| {
10153        buffer.set_language(Some(language), cx);
10154    });
10155
10156    let toggle_comments = &ToggleComments {
10157        advance_downwards: true,
10158        ignore_indent: false,
10159    };
10160
10161    // Single cursor on one line -> advance
10162    // Cursor moves horizontally 3 characters as well on non-blank line
10163    cx.set_state(indoc!(
10164        "fn a() {
10165             ˇdog();
10166             cat();
10167        }"
10168    ));
10169    cx.update_editor(|editor, window, cx| {
10170        editor.toggle_comments(toggle_comments, window, cx);
10171    });
10172    cx.assert_editor_state(indoc!(
10173        "fn a() {
10174             // dog();
10175             catˇ();
10176        }"
10177    ));
10178
10179    // Single selection on one line -> don't advance
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    // Multiple cursors on one line -> advance
10197    cx.set_state(indoc!(
10198        "fn a() {
10199             ˇdˇog();
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, with selection -> don't 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             // ˇdˇog«()ˇ»;
10226             cat();
10227        }"
10228    ));
10229
10230    // Single cursor on one line -> advance
10231    // Cursor moves to column 0 on blank line
10232    cx.set_state(indoc!(
10233        "fn a() {
10234             ˇdog();
10235
10236             cat();
10237        }"
10238    ));
10239    cx.update_editor(|editor, window, cx| {
10240        editor.toggle_comments(toggle_comments, window, cx);
10241    });
10242    cx.assert_editor_state(indoc!(
10243        "fn a() {
10244             // dog();
10245        ˇ
10246             cat();
10247        }"
10248    ));
10249
10250    // Single cursor on one line -> advance
10251    // Cursor starts and ends at column 0
10252    cx.set_state(indoc!(
10253        "fn a() {
10254         ˇ    dog();
10255             cat();
10256        }"
10257    ));
10258    cx.update_editor(|editor, window, cx| {
10259        editor.toggle_comments(toggle_comments, window, cx);
10260    });
10261    cx.assert_editor_state(indoc!(
10262        "fn a() {
10263             // dog();
10264         ˇ    cat();
10265        }"
10266    ));
10267}
10268
10269#[gpui::test]
10270async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10271    init_test(cx, |_| {});
10272
10273    let mut cx = EditorTestContext::new(cx).await;
10274
10275    let html_language = Arc::new(
10276        Language::new(
10277            LanguageConfig {
10278                name: "HTML".into(),
10279                block_comment: Some(("<!-- ".into(), " -->".into())),
10280                ..Default::default()
10281            },
10282            Some(tree_sitter_html::LANGUAGE.into()),
10283        )
10284        .with_injection_query(
10285            r#"
10286            (script_element
10287                (raw_text) @injection.content
10288                (#set! injection.language "javascript"))
10289            "#,
10290        )
10291        .unwrap(),
10292    );
10293
10294    let javascript_language = Arc::new(Language::new(
10295        LanguageConfig {
10296            name: "JavaScript".into(),
10297            line_comments: vec!["// ".into()],
10298            ..Default::default()
10299        },
10300        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10301    ));
10302
10303    cx.language_registry().add(html_language.clone());
10304    cx.language_registry().add(javascript_language.clone());
10305    cx.update_buffer(|buffer, cx| {
10306        buffer.set_language(Some(html_language), cx);
10307    });
10308
10309    // Toggle comments for empty selections
10310    cx.set_state(
10311        &r#"
10312            <p>A</p>ˇ
10313            <p>B</p>ˇ
10314            <p>C</p>ˇ
10315        "#
10316        .unindent(),
10317    );
10318    cx.update_editor(|editor, window, cx| {
10319        editor.toggle_comments(&ToggleComments::default(), window, cx)
10320    });
10321    cx.assert_editor_state(
10322        &r#"
10323            <!-- <p>A</p>ˇ -->
10324            <!-- <p>B</p>ˇ -->
10325            <!-- <p>C</p>ˇ -->
10326        "#
10327        .unindent(),
10328    );
10329    cx.update_editor(|editor, window, cx| {
10330        editor.toggle_comments(&ToggleComments::default(), window, cx)
10331    });
10332    cx.assert_editor_state(
10333        &r#"
10334            <p>A</p>ˇ
10335            <p>B</p>ˇ
10336            <p>C</p>ˇ
10337        "#
10338        .unindent(),
10339    );
10340
10341    // Toggle comments for mixture of empty and non-empty selections, where
10342    // multiple selections occupy a given line.
10343    cx.set_state(
10344        &r#"
10345            <p>A«</p>
10346            <p>ˇ»B</p>ˇ
10347            <p>C«</p>
10348            <p>ˇ»D</p>ˇ
10349        "#
10350        .unindent(),
10351    );
10352
10353    cx.update_editor(|editor, window, cx| {
10354        editor.toggle_comments(&ToggleComments::default(), window, cx)
10355    });
10356    cx.assert_editor_state(
10357        &r#"
10358            <!-- <p>A«</p>
10359            <p>ˇ»B</p>ˇ -->
10360            <!-- <p>C«</p>
10361            <p>ˇ»D</p>ˇ -->
10362        "#
10363        .unindent(),
10364    );
10365    cx.update_editor(|editor, window, cx| {
10366        editor.toggle_comments(&ToggleComments::default(), window, cx)
10367    });
10368    cx.assert_editor_state(
10369        &r#"
10370            <p>A«</p>
10371            <p>ˇ»B</p>ˇ
10372            <p>C«</p>
10373            <p>ˇ»D</p>ˇ
10374        "#
10375        .unindent(),
10376    );
10377
10378    // Toggle comments when different languages are active for different
10379    // selections.
10380    cx.set_state(
10381        &r#"
10382            ˇ<script>
10383                ˇvar x = new Y();
10384            ˇ</script>
10385        "#
10386        .unindent(),
10387    );
10388    cx.executor().run_until_parked();
10389    cx.update_editor(|editor, window, cx| {
10390        editor.toggle_comments(&ToggleComments::default(), window, cx)
10391    });
10392    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10393    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10394    cx.assert_editor_state(
10395        &r#"
10396            <!-- ˇ<script> -->
10397                // ˇvar x = new Y();
10398            <!-- ˇ</script> -->
10399        "#
10400        .unindent(),
10401    );
10402}
10403
10404#[gpui::test]
10405fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10406    init_test(cx, |_| {});
10407
10408    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10409    let multibuffer = cx.new(|cx| {
10410        let mut multibuffer = MultiBuffer::new(ReadWrite);
10411        multibuffer.push_excerpts(
10412            buffer.clone(),
10413            [
10414                ExcerptRange {
10415                    context: Point::new(0, 0)..Point::new(0, 4),
10416                    primary: None,
10417                },
10418                ExcerptRange {
10419                    context: Point::new(1, 0)..Point::new(1, 4),
10420                    primary: None,
10421                },
10422            ],
10423            cx,
10424        );
10425        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10426        multibuffer
10427    });
10428
10429    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10430    editor.update_in(cx, |editor, window, cx| {
10431        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10432        editor.change_selections(None, window, cx, |s| {
10433            s.select_ranges([
10434                Point::new(0, 0)..Point::new(0, 0),
10435                Point::new(1, 0)..Point::new(1, 0),
10436            ])
10437        });
10438
10439        editor.handle_input("X", window, cx);
10440        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10441        assert_eq!(
10442            editor.selections.ranges(cx),
10443            [
10444                Point::new(0, 1)..Point::new(0, 1),
10445                Point::new(1, 1)..Point::new(1, 1),
10446            ]
10447        );
10448
10449        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10450        editor.change_selections(None, window, cx, |s| {
10451            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10452        });
10453        editor.backspace(&Default::default(), window, cx);
10454        assert_eq!(editor.text(cx), "Xa\nbbb");
10455        assert_eq!(
10456            editor.selections.ranges(cx),
10457            [Point::new(1, 0)..Point::new(1, 0)]
10458        );
10459
10460        editor.change_selections(None, window, cx, |s| {
10461            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10462        });
10463        editor.backspace(&Default::default(), window, cx);
10464        assert_eq!(editor.text(cx), "X\nbb");
10465        assert_eq!(
10466            editor.selections.ranges(cx),
10467            [Point::new(0, 1)..Point::new(0, 1)]
10468        );
10469    });
10470}
10471
10472#[gpui::test]
10473fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10474    init_test(cx, |_| {});
10475
10476    let markers = vec![('[', ']').into(), ('(', ')').into()];
10477    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10478        indoc! {"
10479            [aaaa
10480            (bbbb]
10481            cccc)",
10482        },
10483        markers.clone(),
10484    );
10485    let excerpt_ranges = markers.into_iter().map(|marker| {
10486        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10487        ExcerptRange {
10488            context,
10489            primary: None,
10490        }
10491    });
10492    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10493    let multibuffer = cx.new(|cx| {
10494        let mut multibuffer = MultiBuffer::new(ReadWrite);
10495        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10496        multibuffer
10497    });
10498
10499    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10500    editor.update_in(cx, |editor, window, cx| {
10501        let (expected_text, selection_ranges) = marked_text_ranges(
10502            indoc! {"
10503                aaaa
10504                bˇbbb
10505                bˇbbˇb
10506                cccc"
10507            },
10508            true,
10509        );
10510        assert_eq!(editor.text(cx), expected_text);
10511        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10512
10513        editor.handle_input("X", window, cx);
10514
10515        let (expected_text, expected_selections) = marked_text_ranges(
10516            indoc! {"
10517                aaaa
10518                bXˇbbXb
10519                bXˇbbXˇb
10520                cccc"
10521            },
10522            false,
10523        );
10524        assert_eq!(editor.text(cx), expected_text);
10525        assert_eq!(editor.selections.ranges(cx), expected_selections);
10526
10527        editor.newline(&Newline, window, cx);
10528        let (expected_text, expected_selections) = marked_text_ranges(
10529            indoc! {"
10530                aaaa
10531                bX
10532                ˇbbX
10533                b
10534                bX
10535                ˇbbX
10536                ˇ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}
10545
10546#[gpui::test]
10547fn test_refresh_selections(cx: &mut TestAppContext) {
10548    init_test(cx, |_| {});
10549
10550    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10551    let mut excerpt1_id = None;
10552    let multibuffer = cx.new(|cx| {
10553        let mut multibuffer = MultiBuffer::new(ReadWrite);
10554        excerpt1_id = multibuffer
10555            .push_excerpts(
10556                buffer.clone(),
10557                [
10558                    ExcerptRange {
10559                        context: Point::new(0, 0)..Point::new(1, 4),
10560                        primary: None,
10561                    },
10562                    ExcerptRange {
10563                        context: Point::new(1, 0)..Point::new(2, 4),
10564                        primary: None,
10565                    },
10566                ],
10567                cx,
10568            )
10569            .into_iter()
10570            .next();
10571        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10572        multibuffer
10573    });
10574
10575    let editor = cx.add_window(|window, cx| {
10576        let mut editor = build_editor(multibuffer.clone(), window, cx);
10577        let snapshot = editor.snapshot(window, cx);
10578        editor.change_selections(None, window, cx, |s| {
10579            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10580        });
10581        editor.begin_selection(
10582            Point::new(2, 1).to_display_point(&snapshot),
10583            true,
10584            1,
10585            window,
10586            cx,
10587        );
10588        assert_eq!(
10589            editor.selections.ranges(cx),
10590            [
10591                Point::new(1, 3)..Point::new(1, 3),
10592                Point::new(2, 1)..Point::new(2, 1),
10593            ]
10594        );
10595        editor
10596    });
10597
10598    // Refreshing selections is a no-op when excerpts haven't changed.
10599    _ = editor.update(cx, |editor, window, cx| {
10600        editor.change_selections(None, window, cx, |s| s.refresh());
10601        assert_eq!(
10602            editor.selections.ranges(cx),
10603            [
10604                Point::new(1, 3)..Point::new(1, 3),
10605                Point::new(2, 1)..Point::new(2, 1),
10606            ]
10607        );
10608    });
10609
10610    multibuffer.update(cx, |multibuffer, cx| {
10611        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10612    });
10613    _ = editor.update(cx, |editor, window, cx| {
10614        // Removing an excerpt causes the first selection to become degenerate.
10615        assert_eq!(
10616            editor.selections.ranges(cx),
10617            [
10618                Point::new(0, 0)..Point::new(0, 0),
10619                Point::new(0, 1)..Point::new(0, 1)
10620            ]
10621        );
10622
10623        // Refreshing selections will relocate the first selection to the original buffer
10624        // location.
10625        editor.change_selections(None, window, cx, |s| s.refresh());
10626        assert_eq!(
10627            editor.selections.ranges(cx),
10628            [
10629                Point::new(0, 1)..Point::new(0, 1),
10630                Point::new(0, 3)..Point::new(0, 3)
10631            ]
10632        );
10633        assert!(editor.selections.pending_anchor().is_some());
10634    });
10635}
10636
10637#[gpui::test]
10638fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10639    init_test(cx, |_| {});
10640
10641    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10642    let mut excerpt1_id = None;
10643    let multibuffer = cx.new(|cx| {
10644        let mut multibuffer = MultiBuffer::new(ReadWrite);
10645        excerpt1_id = multibuffer
10646            .push_excerpts(
10647                buffer.clone(),
10648                [
10649                    ExcerptRange {
10650                        context: Point::new(0, 0)..Point::new(1, 4),
10651                        primary: None,
10652                    },
10653                    ExcerptRange {
10654                        context: Point::new(1, 0)..Point::new(2, 4),
10655                        primary: None,
10656                    },
10657                ],
10658                cx,
10659            )
10660            .into_iter()
10661            .next();
10662        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10663        multibuffer
10664    });
10665
10666    let editor = cx.add_window(|window, cx| {
10667        let mut editor = build_editor(multibuffer.clone(), window, cx);
10668        let snapshot = editor.snapshot(window, cx);
10669        editor.begin_selection(
10670            Point::new(1, 3).to_display_point(&snapshot),
10671            false,
10672            1,
10673            window,
10674            cx,
10675        );
10676        assert_eq!(
10677            editor.selections.ranges(cx),
10678            [Point::new(1, 3)..Point::new(1, 3)]
10679        );
10680        editor
10681    });
10682
10683    multibuffer.update(cx, |multibuffer, cx| {
10684        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10685    });
10686    _ = editor.update(cx, |editor, window, cx| {
10687        assert_eq!(
10688            editor.selections.ranges(cx),
10689            [Point::new(0, 0)..Point::new(0, 0)]
10690        );
10691
10692        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10693        editor.change_selections(None, window, cx, |s| s.refresh());
10694        assert_eq!(
10695            editor.selections.ranges(cx),
10696            [Point::new(0, 3)..Point::new(0, 3)]
10697        );
10698        assert!(editor.selections.pending_anchor().is_some());
10699    });
10700}
10701
10702#[gpui::test]
10703async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10704    init_test(cx, |_| {});
10705
10706    let language = Arc::new(
10707        Language::new(
10708            LanguageConfig {
10709                brackets: BracketPairConfig {
10710                    pairs: vec![
10711                        BracketPair {
10712                            start: "{".to_string(),
10713                            end: "}".to_string(),
10714                            close: true,
10715                            surround: true,
10716                            newline: true,
10717                        },
10718                        BracketPair {
10719                            start: "/* ".to_string(),
10720                            end: " */".to_string(),
10721                            close: true,
10722                            surround: true,
10723                            newline: true,
10724                        },
10725                    ],
10726                    ..Default::default()
10727                },
10728                ..Default::default()
10729            },
10730            Some(tree_sitter_rust::LANGUAGE.into()),
10731        )
10732        .with_indents_query("")
10733        .unwrap(),
10734    );
10735
10736    let text = concat!(
10737        "{   }\n",     //
10738        "  x\n",       //
10739        "  /*   */\n", //
10740        "x\n",         //
10741        "{{} }\n",     //
10742    );
10743
10744    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10745    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10746    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10747    editor
10748        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10749        .await;
10750
10751    editor.update_in(cx, |editor, window, cx| {
10752        editor.change_selections(None, window, cx, |s| {
10753            s.select_display_ranges([
10754                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10755                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10756                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10757            ])
10758        });
10759        editor.newline(&Newline, window, cx);
10760
10761        assert_eq!(
10762            editor.buffer().read(cx).read(cx).text(),
10763            concat!(
10764                "{ \n",    // Suppress rustfmt
10765                "\n",      //
10766                "}\n",     //
10767                "  x\n",   //
10768                "  /* \n", //
10769                "  \n",    //
10770                "  */\n",  //
10771                "x\n",     //
10772                "{{} \n",  //
10773                "}\n",     //
10774            )
10775        );
10776    });
10777}
10778
10779#[gpui::test]
10780fn test_highlighted_ranges(cx: &mut TestAppContext) {
10781    init_test(cx, |_| {});
10782
10783    let editor = cx.add_window(|window, cx| {
10784        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10785        build_editor(buffer.clone(), window, cx)
10786    });
10787
10788    _ = editor.update(cx, |editor, window, cx| {
10789        struct Type1;
10790        struct Type2;
10791
10792        let buffer = editor.buffer.read(cx).snapshot(cx);
10793
10794        let anchor_range =
10795            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10796
10797        editor.highlight_background::<Type1>(
10798            &[
10799                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10800                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10801                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10802                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10803            ],
10804            |_| Hsla::red(),
10805            cx,
10806        );
10807        editor.highlight_background::<Type2>(
10808            &[
10809                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10810                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10811                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10812                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10813            ],
10814            |_| Hsla::green(),
10815            cx,
10816        );
10817
10818        let snapshot = editor.snapshot(window, cx);
10819        let mut highlighted_ranges = editor.background_highlights_in_range(
10820            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10821            &snapshot,
10822            cx.theme().colors(),
10823        );
10824        // Enforce a consistent ordering based on color without relying on the ordering of the
10825        // highlight's `TypeId` which is non-executor.
10826        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10827        assert_eq!(
10828            highlighted_ranges,
10829            &[
10830                (
10831                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10832                    Hsla::red(),
10833                ),
10834                (
10835                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10836                    Hsla::red(),
10837                ),
10838                (
10839                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10840                    Hsla::green(),
10841                ),
10842                (
10843                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10844                    Hsla::green(),
10845                ),
10846            ]
10847        );
10848        assert_eq!(
10849            editor.background_highlights_in_range(
10850                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10851                &snapshot,
10852                cx.theme().colors(),
10853            ),
10854            &[(
10855                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10856                Hsla::red(),
10857            )]
10858        );
10859    });
10860}
10861
10862#[gpui::test]
10863async fn test_following(cx: &mut TestAppContext) {
10864    init_test(cx, |_| {});
10865
10866    let fs = FakeFs::new(cx.executor());
10867    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10868
10869    let buffer = project.update(cx, |project, cx| {
10870        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10871        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10872    });
10873    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10874    let follower = cx.update(|cx| {
10875        cx.open_window(
10876            WindowOptions {
10877                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10878                    gpui::Point::new(px(0.), px(0.)),
10879                    gpui::Point::new(px(10.), px(80.)),
10880                ))),
10881                ..Default::default()
10882            },
10883            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10884        )
10885        .unwrap()
10886    });
10887
10888    let is_still_following = Rc::new(RefCell::new(true));
10889    let follower_edit_event_count = Rc::new(RefCell::new(0));
10890    let pending_update = Rc::new(RefCell::new(None));
10891    let leader_entity = leader.root(cx).unwrap();
10892    let follower_entity = follower.root(cx).unwrap();
10893    _ = follower.update(cx, {
10894        let update = pending_update.clone();
10895        let is_still_following = is_still_following.clone();
10896        let follower_edit_event_count = follower_edit_event_count.clone();
10897        |_, window, cx| {
10898            cx.subscribe_in(
10899                &leader_entity,
10900                window,
10901                move |_, leader, event, window, cx| {
10902                    leader.read(cx).add_event_to_update_proto(
10903                        event,
10904                        &mut update.borrow_mut(),
10905                        window,
10906                        cx,
10907                    );
10908                },
10909            )
10910            .detach();
10911
10912            cx.subscribe_in(
10913                &follower_entity,
10914                window,
10915                move |_, _, event: &EditorEvent, _window, _cx| {
10916                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10917                        *is_still_following.borrow_mut() = false;
10918                    }
10919
10920                    if let EditorEvent::BufferEdited = event {
10921                        *follower_edit_event_count.borrow_mut() += 1;
10922                    }
10923                },
10924            )
10925            .detach();
10926        }
10927    });
10928
10929    // Update the selections only
10930    _ = leader.update(cx, |leader, window, cx| {
10931        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10932    });
10933    follower
10934        .update(cx, |follower, window, cx| {
10935            follower.apply_update_proto(
10936                &project,
10937                pending_update.borrow_mut().take().unwrap(),
10938                window,
10939                cx,
10940            )
10941        })
10942        .unwrap()
10943        .await
10944        .unwrap();
10945    _ = follower.update(cx, |follower, _, cx| {
10946        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10947    });
10948    assert!(*is_still_following.borrow());
10949    assert_eq!(*follower_edit_event_count.borrow(), 0);
10950
10951    // Update the scroll position only
10952    _ = leader.update(cx, |leader, window, cx| {
10953        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10954    });
10955    follower
10956        .update(cx, |follower, window, cx| {
10957            follower.apply_update_proto(
10958                &project,
10959                pending_update.borrow_mut().take().unwrap(),
10960                window,
10961                cx,
10962            )
10963        })
10964        .unwrap()
10965        .await
10966        .unwrap();
10967    assert_eq!(
10968        follower
10969            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10970            .unwrap(),
10971        gpui::Point::new(1.5, 3.5)
10972    );
10973    assert!(*is_still_following.borrow());
10974    assert_eq!(*follower_edit_event_count.borrow(), 0);
10975
10976    // Update the selections and scroll position. The follower's scroll position is updated
10977    // via autoscroll, not via the leader's exact scroll position.
10978    _ = leader.update(cx, |leader, window, cx| {
10979        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10980        leader.request_autoscroll(Autoscroll::newest(), cx);
10981        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10982    });
10983    follower
10984        .update(cx, |follower, window, cx| {
10985            follower.apply_update_proto(
10986                &project,
10987                pending_update.borrow_mut().take().unwrap(),
10988                window,
10989                cx,
10990            )
10991        })
10992        .unwrap()
10993        .await
10994        .unwrap();
10995    _ = follower.update(cx, |follower, _, cx| {
10996        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10997        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10998    });
10999    assert!(*is_still_following.borrow());
11000
11001    // Creating a pending selection that precedes another selection
11002    _ = leader.update(cx, |leader, window, cx| {
11003        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11004        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11005    });
11006    follower
11007        .update(cx, |follower, window, cx| {
11008            follower.apply_update_proto(
11009                &project,
11010                pending_update.borrow_mut().take().unwrap(),
11011                window,
11012                cx,
11013            )
11014        })
11015        .unwrap()
11016        .await
11017        .unwrap();
11018    _ = follower.update(cx, |follower, _, cx| {
11019        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11020    });
11021    assert!(*is_still_following.borrow());
11022
11023    // Extend the pending selection so that it surrounds another selection
11024    _ = leader.update(cx, |leader, window, cx| {
11025        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11026    });
11027    follower
11028        .update(cx, |follower, window, cx| {
11029            follower.apply_update_proto(
11030                &project,
11031                pending_update.borrow_mut().take().unwrap(),
11032                window,
11033                cx,
11034            )
11035        })
11036        .unwrap()
11037        .await
11038        .unwrap();
11039    _ = follower.update(cx, |follower, _, cx| {
11040        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11041    });
11042
11043    // Scrolling locally breaks the follow
11044    _ = follower.update(cx, |follower, window, cx| {
11045        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11046        follower.set_scroll_anchor(
11047            ScrollAnchor {
11048                anchor: top_anchor,
11049                offset: gpui::Point::new(0.0, 0.5),
11050            },
11051            window,
11052            cx,
11053        );
11054    });
11055    assert!(!(*is_still_following.borrow()));
11056}
11057
11058#[gpui::test]
11059async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11060    init_test(cx, |_| {});
11061
11062    let fs = FakeFs::new(cx.executor());
11063    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11064    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11065    let pane = workspace
11066        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11067        .unwrap();
11068
11069    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11070
11071    let leader = pane.update_in(cx, |_, window, cx| {
11072        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11073        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11074    });
11075
11076    // Start following the editor when it has no excerpts.
11077    let mut state_message =
11078        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11079    let workspace_entity = workspace.root(cx).unwrap();
11080    let follower_1 = cx
11081        .update_window(*workspace.deref(), |_, window, cx| {
11082            Editor::from_state_proto(
11083                workspace_entity,
11084                ViewId {
11085                    creator: Default::default(),
11086                    id: 0,
11087                },
11088                &mut state_message,
11089                window,
11090                cx,
11091            )
11092        })
11093        .unwrap()
11094        .unwrap()
11095        .await
11096        .unwrap();
11097
11098    let update_message = Rc::new(RefCell::new(None));
11099    follower_1.update_in(cx, {
11100        let update = update_message.clone();
11101        |_, window, cx| {
11102            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11103                leader.read(cx).add_event_to_update_proto(
11104                    event,
11105                    &mut update.borrow_mut(),
11106                    window,
11107                    cx,
11108                );
11109            })
11110            .detach();
11111        }
11112    });
11113
11114    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11115        (
11116            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11117            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11118        )
11119    });
11120
11121    // Insert some excerpts.
11122    leader.update(cx, |leader, cx| {
11123        leader.buffer.update(cx, |multibuffer, cx| {
11124            let excerpt_ids = multibuffer.push_excerpts(
11125                buffer_1.clone(),
11126                [
11127                    ExcerptRange {
11128                        context: 1..6,
11129                        primary: None,
11130                    },
11131                    ExcerptRange {
11132                        context: 12..15,
11133                        primary: None,
11134                    },
11135                    ExcerptRange {
11136                        context: 0..3,
11137                        primary: None,
11138                    },
11139                ],
11140                cx,
11141            );
11142            multibuffer.insert_excerpts_after(
11143                excerpt_ids[0],
11144                buffer_2.clone(),
11145                [
11146                    ExcerptRange {
11147                        context: 8..12,
11148                        primary: None,
11149                    },
11150                    ExcerptRange {
11151                        context: 0..6,
11152                        primary: None,
11153                    },
11154                ],
11155                cx,
11156            );
11157        });
11158    });
11159
11160    // Apply the update of adding the excerpts.
11161    follower_1
11162        .update_in(cx, |follower, window, cx| {
11163            follower.apply_update_proto(
11164                &project,
11165                update_message.borrow().clone().unwrap(),
11166                window,
11167                cx,
11168            )
11169        })
11170        .await
11171        .unwrap();
11172    assert_eq!(
11173        follower_1.update(cx, |editor, cx| editor.text(cx)),
11174        leader.update(cx, |editor, cx| editor.text(cx))
11175    );
11176    update_message.borrow_mut().take();
11177
11178    // Start following separately after it already has excerpts.
11179    let mut state_message =
11180        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11181    let workspace_entity = workspace.root(cx).unwrap();
11182    let follower_2 = cx
11183        .update_window(*workspace.deref(), |_, window, cx| {
11184            Editor::from_state_proto(
11185                workspace_entity,
11186                ViewId {
11187                    creator: Default::default(),
11188                    id: 0,
11189                },
11190                &mut state_message,
11191                window,
11192                cx,
11193            )
11194        })
11195        .unwrap()
11196        .unwrap()
11197        .await
11198        .unwrap();
11199    assert_eq!(
11200        follower_2.update(cx, |editor, cx| editor.text(cx)),
11201        leader.update(cx, |editor, cx| editor.text(cx))
11202    );
11203
11204    // Remove some excerpts.
11205    leader.update(cx, |leader, cx| {
11206        leader.buffer.update(cx, |multibuffer, cx| {
11207            let excerpt_ids = multibuffer.excerpt_ids();
11208            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11209            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11210        });
11211    });
11212
11213    // Apply the update of removing the excerpts.
11214    follower_1
11215        .update_in(cx, |follower, window, cx| {
11216            follower.apply_update_proto(
11217                &project,
11218                update_message.borrow().clone().unwrap(),
11219                window,
11220                cx,
11221            )
11222        })
11223        .await
11224        .unwrap();
11225    follower_2
11226        .update_in(cx, |follower, window, cx| {
11227            follower.apply_update_proto(
11228                &project,
11229                update_message.borrow().clone().unwrap(),
11230                window,
11231                cx,
11232            )
11233        })
11234        .await
11235        .unwrap();
11236    update_message.borrow_mut().take();
11237    assert_eq!(
11238        follower_1.update(cx, |editor, cx| editor.text(cx)),
11239        leader.update(cx, |editor, cx| editor.text(cx))
11240    );
11241}
11242
11243#[gpui::test]
11244async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11245    init_test(cx, |_| {});
11246
11247    let mut cx = EditorTestContext::new(cx).await;
11248    let lsp_store =
11249        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11250
11251    cx.set_state(indoc! {"
11252        ˇfn func(abc def: i32) -> u32 {
11253        }
11254    "});
11255
11256    cx.update(|_, cx| {
11257        lsp_store.update(cx, |lsp_store, cx| {
11258            lsp_store
11259                .update_diagnostics(
11260                    LanguageServerId(0),
11261                    lsp::PublishDiagnosticsParams {
11262                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11263                        version: None,
11264                        diagnostics: vec![
11265                            lsp::Diagnostic {
11266                                range: lsp::Range::new(
11267                                    lsp::Position::new(0, 11),
11268                                    lsp::Position::new(0, 12),
11269                                ),
11270                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11271                                ..Default::default()
11272                            },
11273                            lsp::Diagnostic {
11274                                range: lsp::Range::new(
11275                                    lsp::Position::new(0, 12),
11276                                    lsp::Position::new(0, 15),
11277                                ),
11278                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11279                                ..Default::default()
11280                            },
11281                            lsp::Diagnostic {
11282                                range: lsp::Range::new(
11283                                    lsp::Position::new(0, 25),
11284                                    lsp::Position::new(0, 28),
11285                                ),
11286                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11287                                ..Default::default()
11288                            },
11289                        ],
11290                    },
11291                    &[],
11292                    cx,
11293                )
11294                .unwrap()
11295        });
11296    });
11297
11298    executor.run_until_parked();
11299
11300    cx.update_editor(|editor, window, cx| {
11301        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11302    });
11303
11304    cx.assert_editor_state(indoc! {"
11305        fn func(abc def: i32) -> ˇu32 {
11306        }
11307    "});
11308
11309    cx.update_editor(|editor, window, cx| {
11310        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11311    });
11312
11313    cx.assert_editor_state(indoc! {"
11314        fn func(abc ˇdef: i32) -> u32 {
11315        }
11316    "});
11317
11318    cx.update_editor(|editor, window, cx| {
11319        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11320    });
11321
11322    cx.assert_editor_state(indoc! {"
11323        fn func(abcˇ def: i32) -> u32 {
11324        }
11325    "});
11326
11327    cx.update_editor(|editor, window, cx| {
11328        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11329    });
11330
11331    cx.assert_editor_state(indoc! {"
11332        fn func(abc def: i32) -> ˇu32 {
11333        }
11334    "});
11335}
11336
11337#[gpui::test]
11338async fn cycle_through_same_place_diagnostics(
11339    executor: BackgroundExecutor,
11340    cx: &mut TestAppContext,
11341) {
11342    init_test(cx, |_| {});
11343
11344    let mut cx = EditorTestContext::new(cx).await;
11345    let lsp_store =
11346        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11347
11348    cx.set_state(indoc! {"
11349        ˇfn func(abc def: i32) -> u32 {
11350        }
11351    "});
11352
11353    cx.update(|_, cx| {
11354        lsp_store.update(cx, |lsp_store, cx| {
11355            lsp_store
11356                .update_diagnostics(
11357                    LanguageServerId(0),
11358                    lsp::PublishDiagnosticsParams {
11359                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11360                        version: None,
11361                        diagnostics: vec![
11362                            lsp::Diagnostic {
11363                                range: lsp::Range::new(
11364                                    lsp::Position::new(0, 11),
11365                                    lsp::Position::new(0, 12),
11366                                ),
11367                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11368                                ..Default::default()
11369                            },
11370                            lsp::Diagnostic {
11371                                range: lsp::Range::new(
11372                                    lsp::Position::new(0, 12),
11373                                    lsp::Position::new(0, 15),
11374                                ),
11375                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11376                                ..Default::default()
11377                            },
11378                            lsp::Diagnostic {
11379                                range: lsp::Range::new(
11380                                    lsp::Position::new(0, 12),
11381                                    lsp::Position::new(0, 15),
11382                                ),
11383                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11384                                ..Default::default()
11385                            },
11386                            lsp::Diagnostic {
11387                                range: lsp::Range::new(
11388                                    lsp::Position::new(0, 25),
11389                                    lsp::Position::new(0, 28),
11390                                ),
11391                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11392                                ..Default::default()
11393                            },
11394                        ],
11395                    },
11396                    &[],
11397                    cx,
11398                )
11399                .unwrap()
11400        });
11401    });
11402    executor.run_until_parked();
11403
11404    //// Backward
11405
11406    // Fourth diagnostic
11407    cx.update_editor(|editor, window, cx| {
11408        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11409    });
11410    cx.assert_editor_state(indoc! {"
11411        fn func(abc def: i32) -> ˇu32 {
11412        }
11413    "});
11414
11415    // Third diagnostic
11416    cx.update_editor(|editor, window, cx| {
11417        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11418    });
11419    cx.assert_editor_state(indoc! {"
11420        fn func(abc ˇdef: i32) -> u32 {
11421        }
11422    "});
11423
11424    // Second diagnostic, same place
11425    cx.update_editor(|editor, window, cx| {
11426        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11427    });
11428    cx.assert_editor_state(indoc! {"
11429        fn func(abc ˇdef: i32) -> u32 {
11430        }
11431    "});
11432
11433    // First diagnostic
11434    cx.update_editor(|editor, window, cx| {
11435        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11436    });
11437    cx.assert_editor_state(indoc! {"
11438        fn func(abcˇ def: i32) -> u32 {
11439        }
11440    "});
11441
11442    // Wrapped over, fourth diagnostic
11443    cx.update_editor(|editor, window, cx| {
11444        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11445    });
11446    cx.assert_editor_state(indoc! {"
11447        fn func(abc def: i32) -> ˇu32 {
11448        }
11449    "});
11450
11451    cx.update_editor(|editor, window, cx| {
11452        editor.move_to_beginning(&MoveToBeginning, window, cx);
11453    });
11454    cx.assert_editor_state(indoc! {"
11455        ˇfn func(abc def: i32) -> u32 {
11456        }
11457    "});
11458
11459    //// Forward
11460
11461    // First diagnostic
11462    cx.update_editor(|editor, window, cx| {
11463        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11464    });
11465    cx.assert_editor_state(indoc! {"
11466        fn func(abcˇ def: i32) -> u32 {
11467        }
11468    "});
11469
11470    // Second diagnostic
11471    cx.update_editor(|editor, window, cx| {
11472        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11473    });
11474    cx.assert_editor_state(indoc! {"
11475        fn func(abc ˇdef: i32) -> u32 {
11476        }
11477    "});
11478
11479    // Third diagnostic, same place
11480    cx.update_editor(|editor, window, cx| {
11481        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11482    });
11483    cx.assert_editor_state(indoc! {"
11484        fn func(abc ˇdef: i32) -> u32 {
11485        }
11486    "});
11487
11488    // Fourth diagnostic
11489    cx.update_editor(|editor, window, cx| {
11490        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11491    });
11492    cx.assert_editor_state(indoc! {"
11493        fn func(abc def: i32) -> ˇu32 {
11494        }
11495    "});
11496
11497    // Wrapped around, first diagnostic
11498    cx.update_editor(|editor, window, cx| {
11499        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11500    });
11501    cx.assert_editor_state(indoc! {"
11502        fn func(abcˇ def: i32) -> u32 {
11503        }
11504    "});
11505}
11506
11507#[gpui::test]
11508async fn active_diagnostics_dismiss_after_invalidation(
11509    executor: BackgroundExecutor,
11510    cx: &mut TestAppContext,
11511) {
11512    init_test(cx, |_| {});
11513
11514    let mut cx = EditorTestContext::new(cx).await;
11515    let lsp_store =
11516        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11517
11518    cx.set_state(indoc! {"
11519        ˇfn func(abc def: i32) -> u32 {
11520        }
11521    "});
11522
11523    let message = "Something's wrong!";
11524    cx.update(|_, cx| {
11525        lsp_store.update(cx, |lsp_store, cx| {
11526            lsp_store
11527                .update_diagnostics(
11528                    LanguageServerId(0),
11529                    lsp::PublishDiagnosticsParams {
11530                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11531                        version: None,
11532                        diagnostics: vec![lsp::Diagnostic {
11533                            range: lsp::Range::new(
11534                                lsp::Position::new(0, 11),
11535                                lsp::Position::new(0, 12),
11536                            ),
11537                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11538                            message: message.to_string(),
11539                            ..Default::default()
11540                        }],
11541                    },
11542                    &[],
11543                    cx,
11544                )
11545                .unwrap()
11546        });
11547    });
11548    executor.run_until_parked();
11549
11550    cx.update_editor(|editor, window, cx| {
11551        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11552        assert_eq!(
11553            editor
11554                .active_diagnostics
11555                .as_ref()
11556                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11557            Some(message),
11558            "Should have a diagnostics group activated"
11559        );
11560    });
11561    cx.assert_editor_state(indoc! {"
11562        fn func(abcˇ def: i32) -> u32 {
11563        }
11564    "});
11565
11566    cx.update(|_, cx| {
11567        lsp_store.update(cx, |lsp_store, cx| {
11568            lsp_store
11569                .update_diagnostics(
11570                    LanguageServerId(0),
11571                    lsp::PublishDiagnosticsParams {
11572                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11573                        version: None,
11574                        diagnostics: Vec::new(),
11575                    },
11576                    &[],
11577                    cx,
11578                )
11579                .unwrap()
11580        });
11581    });
11582    executor.run_until_parked();
11583    cx.update_editor(|editor, _, _| {
11584        assert_eq!(
11585            editor.active_diagnostics, None,
11586            "After no diagnostics set to the editor, no diagnostics should be active"
11587        );
11588    });
11589    cx.assert_editor_state(indoc! {"
11590        fn func(abcˇ def: i32) -> u32 {
11591        }
11592    "});
11593
11594    cx.update_editor(|editor, window, cx| {
11595        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11596        assert_eq!(
11597            editor.active_diagnostics, None,
11598            "Should be no diagnostics to go to and activate"
11599        );
11600    });
11601    cx.assert_editor_state(indoc! {"
11602        fn func(abcˇ def: i32) -> u32 {
11603        }
11604    "});
11605}
11606
11607#[gpui::test]
11608async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11609    init_test(cx, |_| {});
11610
11611    let mut cx = EditorTestContext::new(cx).await;
11612
11613    cx.set_state(indoc! {"
11614        fn func(abˇc def: i32) -> u32 {
11615        }
11616    "});
11617    let lsp_store =
11618        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11619
11620    cx.update(|_, cx| {
11621        lsp_store.update(cx, |lsp_store, cx| {
11622            lsp_store.update_diagnostics(
11623                LanguageServerId(0),
11624                lsp::PublishDiagnosticsParams {
11625                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11626                    version: None,
11627                    diagnostics: vec![lsp::Diagnostic {
11628                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11629                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11630                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11631                        ..Default::default()
11632                    }],
11633                },
11634                &[],
11635                cx,
11636            )
11637        })
11638    }).unwrap();
11639    cx.run_until_parked();
11640    cx.update_editor(|editor, window, cx| {
11641        hover_popover::hover(editor, &Default::default(), window, cx)
11642    });
11643    cx.run_until_parked();
11644    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11645}
11646
11647#[gpui::test]
11648async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11649    init_test(cx, |_| {});
11650
11651    let mut cx = EditorTestContext::new(cx).await;
11652
11653    let diff_base = r#"
11654        use some::mod;
11655
11656        const A: u32 = 42;
11657
11658        fn main() {
11659            println!("hello");
11660
11661            println!("world");
11662        }
11663        "#
11664    .unindent();
11665
11666    // Edits are modified, removed, modified, added
11667    cx.set_state(
11668        &r#"
11669        use some::modified;
11670
11671        ˇ
11672        fn main() {
11673            println!("hello there");
11674
11675            println!("around the");
11676            println!("world");
11677        }
11678        "#
11679        .unindent(),
11680    );
11681
11682    cx.set_head_text(&diff_base);
11683    executor.run_until_parked();
11684
11685    cx.update_editor(|editor, window, cx| {
11686        //Wrap around the bottom of the buffer
11687        for _ in 0..3 {
11688            editor.go_to_next_hunk(&GoToHunk, window, cx);
11689        }
11690    });
11691
11692    cx.assert_editor_state(
11693        &r#"
11694        ˇuse some::modified;
11695
11696
11697        fn main() {
11698            println!("hello there");
11699
11700            println!("around the");
11701            println!("world");
11702        }
11703        "#
11704        .unindent(),
11705    );
11706
11707    cx.update_editor(|editor, window, cx| {
11708        //Wrap around the top of the buffer
11709        for _ in 0..2 {
11710            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11711        }
11712    });
11713
11714    cx.assert_editor_state(
11715        &r#"
11716        use some::modified;
11717
11718
11719        fn main() {
11720        ˇ    println!("hello there");
11721
11722            println!("around the");
11723            println!("world");
11724        }
11725        "#
11726        .unindent(),
11727    );
11728
11729    cx.update_editor(|editor, window, cx| {
11730        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11731    });
11732
11733    cx.assert_editor_state(
11734        &r#"
11735        use some::modified;
11736
11737        ˇ
11738        fn main() {
11739            println!("hello there");
11740
11741            println!("around the");
11742            println!("world");
11743        }
11744        "#
11745        .unindent(),
11746    );
11747
11748    cx.update_editor(|editor, window, cx| {
11749        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11750    });
11751
11752    cx.assert_editor_state(
11753        &r#"
11754        ˇuse some::modified;
11755
11756
11757        fn main() {
11758            println!("hello there");
11759
11760            println!("around the");
11761            println!("world");
11762        }
11763        "#
11764        .unindent(),
11765    );
11766
11767    cx.update_editor(|editor, window, cx| {
11768        for _ in 0..2 {
11769            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11770        }
11771    });
11772
11773    cx.assert_editor_state(
11774        &r#"
11775        use some::modified;
11776
11777
11778        fn main() {
11779        ˇ    println!("hello there");
11780
11781            println!("around the");
11782            println!("world");
11783        }
11784        "#
11785        .unindent(),
11786    );
11787
11788    cx.update_editor(|editor, window, cx| {
11789        editor.fold(&Fold, window, cx);
11790    });
11791
11792    cx.update_editor(|editor, window, cx| {
11793        editor.go_to_next_hunk(&GoToHunk, window, cx);
11794    });
11795
11796    cx.assert_editor_state(
11797        &r#"
11798        ˇuse some::modified;
11799
11800
11801        fn main() {
11802            println!("hello there");
11803
11804            println!("around the");
11805            println!("world");
11806        }
11807        "#
11808        .unindent(),
11809    );
11810}
11811
11812#[test]
11813fn test_split_words() {
11814    fn split(text: &str) -> Vec<&str> {
11815        split_words(text).collect()
11816    }
11817
11818    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11819    assert_eq!(split("hello_world"), &["hello_", "world"]);
11820    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11821    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11822    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11823    assert_eq!(split("helloworld"), &["helloworld"]);
11824
11825    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11826}
11827
11828#[gpui::test]
11829async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11830    init_test(cx, |_| {});
11831
11832    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11833    let mut assert = |before, after| {
11834        let _state_context = cx.set_state(before);
11835        cx.run_until_parked();
11836        cx.update_editor(|editor, window, cx| {
11837            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11838        });
11839        cx.assert_editor_state(after);
11840    };
11841
11842    // Outside bracket jumps to outside of matching bracket
11843    assert("console.logˇ(var);", "console.log(var)ˇ;");
11844    assert("console.log(var)ˇ;", "console.logˇ(var);");
11845
11846    // Inside bracket jumps to inside of matching bracket
11847    assert("console.log(ˇvar);", "console.log(varˇ);");
11848    assert("console.log(varˇ);", "console.log(ˇvar);");
11849
11850    // When outside a bracket and inside, favor jumping to the inside bracket
11851    assert(
11852        "console.log('foo', [1, 2, 3]ˇ);",
11853        "console.log(ˇ'foo', [1, 2, 3]);",
11854    );
11855    assert(
11856        "console.log(ˇ'foo', [1, 2, 3]);",
11857        "console.log('foo', [1, 2, 3]ˇ);",
11858    );
11859
11860    // Bias forward if two options are equally likely
11861    assert(
11862        "let result = curried_fun()ˇ();",
11863        "let result = curried_fun()()ˇ;",
11864    );
11865
11866    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11867    assert(
11868        indoc! {"
11869            function test() {
11870                console.log('test')ˇ
11871            }"},
11872        indoc! {"
11873            function test() {
11874                console.logˇ('test')
11875            }"},
11876    );
11877}
11878
11879#[gpui::test]
11880async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11881    init_test(cx, |_| {});
11882
11883    let fs = FakeFs::new(cx.executor());
11884    fs.insert_tree(
11885        path!("/a"),
11886        json!({
11887            "main.rs": "fn main() { let a = 5; }",
11888            "other.rs": "// Test file",
11889        }),
11890    )
11891    .await;
11892    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11893
11894    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11895    language_registry.add(Arc::new(Language::new(
11896        LanguageConfig {
11897            name: "Rust".into(),
11898            matcher: LanguageMatcher {
11899                path_suffixes: vec!["rs".to_string()],
11900                ..Default::default()
11901            },
11902            brackets: BracketPairConfig {
11903                pairs: vec![BracketPair {
11904                    start: "{".to_string(),
11905                    end: "}".to_string(),
11906                    close: true,
11907                    surround: true,
11908                    newline: true,
11909                }],
11910                disabled_scopes_by_bracket_ix: Vec::new(),
11911            },
11912            ..Default::default()
11913        },
11914        Some(tree_sitter_rust::LANGUAGE.into()),
11915    )));
11916    let mut fake_servers = language_registry.register_fake_lsp(
11917        "Rust",
11918        FakeLspAdapter {
11919            capabilities: lsp::ServerCapabilities {
11920                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11921                    first_trigger_character: "{".to_string(),
11922                    more_trigger_character: None,
11923                }),
11924                ..Default::default()
11925            },
11926            ..Default::default()
11927        },
11928    );
11929
11930    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11931
11932    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11933
11934    let worktree_id = workspace
11935        .update(cx, |workspace, _, cx| {
11936            workspace.project().update(cx, |project, cx| {
11937                project.worktrees(cx).next().unwrap().read(cx).id()
11938            })
11939        })
11940        .unwrap();
11941
11942    let buffer = project
11943        .update(cx, |project, cx| {
11944            project.open_local_buffer(path!("/a/main.rs"), cx)
11945        })
11946        .await
11947        .unwrap();
11948    let editor_handle = workspace
11949        .update(cx, |workspace, window, cx| {
11950            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11951        })
11952        .unwrap()
11953        .await
11954        .unwrap()
11955        .downcast::<Editor>()
11956        .unwrap();
11957
11958    cx.executor().start_waiting();
11959    let fake_server = fake_servers.next().await.unwrap();
11960
11961    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11962        assert_eq!(
11963            params.text_document_position.text_document.uri,
11964            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11965        );
11966        assert_eq!(
11967            params.text_document_position.position,
11968            lsp::Position::new(0, 21),
11969        );
11970
11971        Ok(Some(vec![lsp::TextEdit {
11972            new_text: "]".to_string(),
11973            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11974        }]))
11975    });
11976
11977    editor_handle.update_in(cx, |editor, window, cx| {
11978        window.focus(&editor.focus_handle(cx));
11979        editor.change_selections(None, window, cx, |s| {
11980            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11981        });
11982        editor.handle_input("{", window, cx);
11983    });
11984
11985    cx.executor().run_until_parked();
11986
11987    buffer.update(cx, |buffer, _| {
11988        assert_eq!(
11989            buffer.text(),
11990            "fn main() { let a = {5}; }",
11991            "No extra braces from on type formatting should appear in the buffer"
11992        )
11993    });
11994}
11995
11996#[gpui::test]
11997async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11998    init_test(cx, |_| {});
11999
12000    let fs = FakeFs::new(cx.executor());
12001    fs.insert_tree(
12002        path!("/a"),
12003        json!({
12004            "main.rs": "fn main() { let a = 5; }",
12005            "other.rs": "// Test file",
12006        }),
12007    )
12008    .await;
12009
12010    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12011
12012    let server_restarts = Arc::new(AtomicUsize::new(0));
12013    let closure_restarts = Arc::clone(&server_restarts);
12014    let language_server_name = "test language server";
12015    let language_name: LanguageName = "Rust".into();
12016
12017    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12018    language_registry.add(Arc::new(Language::new(
12019        LanguageConfig {
12020            name: language_name.clone(),
12021            matcher: LanguageMatcher {
12022                path_suffixes: vec!["rs".to_string()],
12023                ..Default::default()
12024            },
12025            ..Default::default()
12026        },
12027        Some(tree_sitter_rust::LANGUAGE.into()),
12028    )));
12029    let mut fake_servers = language_registry.register_fake_lsp(
12030        "Rust",
12031        FakeLspAdapter {
12032            name: language_server_name,
12033            initialization_options: Some(json!({
12034                "testOptionValue": true
12035            })),
12036            initializer: Some(Box::new(move |fake_server| {
12037                let task_restarts = Arc::clone(&closure_restarts);
12038                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12039                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12040                    futures::future::ready(Ok(()))
12041                });
12042            })),
12043            ..Default::default()
12044        },
12045    );
12046
12047    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12048    let _buffer = project
12049        .update(cx, |project, cx| {
12050            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12051        })
12052        .await
12053        .unwrap();
12054    let _fake_server = fake_servers.next().await.unwrap();
12055    update_test_language_settings(cx, |language_settings| {
12056        language_settings.languages.insert(
12057            language_name.clone(),
12058            LanguageSettingsContent {
12059                tab_size: NonZeroU32::new(8),
12060                ..Default::default()
12061            },
12062        );
12063    });
12064    cx.executor().run_until_parked();
12065    assert_eq!(
12066        server_restarts.load(atomic::Ordering::Acquire),
12067        0,
12068        "Should not restart LSP server on an unrelated change"
12069    );
12070
12071    update_test_project_settings(cx, |project_settings| {
12072        project_settings.lsp.insert(
12073            "Some other server name".into(),
12074            LspSettings {
12075                binary: None,
12076                settings: None,
12077                initialization_options: Some(json!({
12078                    "some other init value": false
12079                })),
12080            },
12081        );
12082    });
12083    cx.executor().run_until_parked();
12084    assert_eq!(
12085        server_restarts.load(atomic::Ordering::Acquire),
12086        0,
12087        "Should not restart LSP server on an unrelated LSP settings change"
12088    );
12089
12090    update_test_project_settings(cx, |project_settings| {
12091        project_settings.lsp.insert(
12092            language_server_name.into(),
12093            LspSettings {
12094                binary: None,
12095                settings: None,
12096                initialization_options: Some(json!({
12097                    "anotherInitValue": false
12098                })),
12099            },
12100        );
12101    });
12102    cx.executor().run_until_parked();
12103    assert_eq!(
12104        server_restarts.load(atomic::Ordering::Acquire),
12105        1,
12106        "Should restart LSP server on a related LSP settings change"
12107    );
12108
12109    update_test_project_settings(cx, |project_settings| {
12110        project_settings.lsp.insert(
12111            language_server_name.into(),
12112            LspSettings {
12113                binary: None,
12114                settings: None,
12115                initialization_options: Some(json!({
12116                    "anotherInitValue": false
12117                })),
12118            },
12119        );
12120    });
12121    cx.executor().run_until_parked();
12122    assert_eq!(
12123        server_restarts.load(atomic::Ordering::Acquire),
12124        1,
12125        "Should not restart LSP server on a related LSP settings change that is the same"
12126    );
12127
12128    update_test_project_settings(cx, |project_settings| {
12129        project_settings.lsp.insert(
12130            language_server_name.into(),
12131            LspSettings {
12132                binary: None,
12133                settings: None,
12134                initialization_options: None,
12135            },
12136        );
12137    });
12138    cx.executor().run_until_parked();
12139    assert_eq!(
12140        server_restarts.load(atomic::Ordering::Acquire),
12141        2,
12142        "Should restart LSP server on another related LSP settings change"
12143    );
12144}
12145
12146#[gpui::test]
12147async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12148    init_test(cx, |_| {});
12149
12150    let mut cx = EditorLspTestContext::new_rust(
12151        lsp::ServerCapabilities {
12152            completion_provider: Some(lsp::CompletionOptions {
12153                trigger_characters: Some(vec![".".to_string()]),
12154                resolve_provider: Some(true),
12155                ..Default::default()
12156            }),
12157            ..Default::default()
12158        },
12159        cx,
12160    )
12161    .await;
12162
12163    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12164    cx.simulate_keystroke(".");
12165    let completion_item = lsp::CompletionItem {
12166        label: "some".into(),
12167        kind: Some(lsp::CompletionItemKind::SNIPPET),
12168        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12169        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12170            kind: lsp::MarkupKind::Markdown,
12171            value: "```rust\nSome(2)\n```".to_string(),
12172        })),
12173        deprecated: Some(false),
12174        sort_text: Some("fffffff2".to_string()),
12175        filter_text: Some("some".to_string()),
12176        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12177        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12178            range: lsp::Range {
12179                start: lsp::Position {
12180                    line: 0,
12181                    character: 22,
12182                },
12183                end: lsp::Position {
12184                    line: 0,
12185                    character: 22,
12186                },
12187            },
12188            new_text: "Some(2)".to_string(),
12189        })),
12190        additional_text_edits: Some(vec![lsp::TextEdit {
12191            range: lsp::Range {
12192                start: lsp::Position {
12193                    line: 0,
12194                    character: 20,
12195                },
12196                end: lsp::Position {
12197                    line: 0,
12198                    character: 22,
12199                },
12200            },
12201            new_text: "".to_string(),
12202        }]),
12203        ..Default::default()
12204    };
12205
12206    let closure_completion_item = completion_item.clone();
12207    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12208        let task_completion_item = closure_completion_item.clone();
12209        async move {
12210            Ok(Some(lsp::CompletionResponse::Array(vec![
12211                task_completion_item,
12212            ])))
12213        }
12214    });
12215
12216    request.next().await;
12217
12218    cx.condition(|editor, _| editor.context_menu_visible())
12219        .await;
12220    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12221        editor
12222            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12223            .unwrap()
12224    });
12225    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12226
12227    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12228        let task_completion_item = completion_item.clone();
12229        async move { Ok(task_completion_item) }
12230    })
12231    .next()
12232    .await
12233    .unwrap();
12234    apply_additional_edits.await.unwrap();
12235    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12236}
12237
12238#[gpui::test]
12239async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12240    init_test(cx, |_| {});
12241
12242    let mut cx = EditorLspTestContext::new_rust(
12243        lsp::ServerCapabilities {
12244            completion_provider: Some(lsp::CompletionOptions {
12245                trigger_characters: Some(vec![".".to_string()]),
12246                resolve_provider: Some(true),
12247                ..Default::default()
12248            }),
12249            ..Default::default()
12250        },
12251        cx,
12252    )
12253    .await;
12254
12255    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12256    cx.simulate_keystroke(".");
12257
12258    let item1 = lsp::CompletionItem {
12259        label: "method id()".to_string(),
12260        filter_text: Some("id".to_string()),
12261        detail: None,
12262        documentation: None,
12263        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12264            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12265            new_text: ".id".to_string(),
12266        })),
12267        ..lsp::CompletionItem::default()
12268    };
12269
12270    let item2 = lsp::CompletionItem {
12271        label: "other".to_string(),
12272        filter_text: Some("other".to_string()),
12273        detail: None,
12274        documentation: None,
12275        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12276            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12277            new_text: ".other".to_string(),
12278        })),
12279        ..lsp::CompletionItem::default()
12280    };
12281
12282    let item1 = item1.clone();
12283    cx.handle_request::<lsp::request::Completion, _, _>({
12284        let item1 = item1.clone();
12285        move |_, _, _| {
12286            let item1 = item1.clone();
12287            let item2 = item2.clone();
12288            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12289        }
12290    })
12291    .next()
12292    .await;
12293
12294    cx.condition(|editor, _| editor.context_menu_visible())
12295        .await;
12296    cx.update_editor(|editor, _, _| {
12297        let context_menu = editor.context_menu.borrow_mut();
12298        let context_menu = context_menu
12299            .as_ref()
12300            .expect("Should have the context menu deployed");
12301        match context_menu {
12302            CodeContextMenu::Completions(completions_menu) => {
12303                let completions = completions_menu.completions.borrow_mut();
12304                assert_eq!(
12305                    completions
12306                        .iter()
12307                        .map(|completion| &completion.label.text)
12308                        .collect::<Vec<_>>(),
12309                    vec!["method id()", "other"]
12310                )
12311            }
12312            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12313        }
12314    });
12315
12316    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12317        let item1 = item1.clone();
12318        move |_, item_to_resolve, _| {
12319            let item1 = item1.clone();
12320            async move {
12321                if item1 == item_to_resolve {
12322                    Ok(lsp::CompletionItem {
12323                        label: "method id()".to_string(),
12324                        filter_text: Some("id".to_string()),
12325                        detail: Some("Now resolved!".to_string()),
12326                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12327                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12328                            range: lsp::Range::new(
12329                                lsp::Position::new(0, 22),
12330                                lsp::Position::new(0, 22),
12331                            ),
12332                            new_text: ".id".to_string(),
12333                        })),
12334                        ..lsp::CompletionItem::default()
12335                    })
12336                } else {
12337                    Ok(item_to_resolve)
12338                }
12339            }
12340        }
12341    })
12342    .next()
12343    .await
12344    .unwrap();
12345    cx.run_until_parked();
12346
12347    cx.update_editor(|editor, window, cx| {
12348        editor.context_menu_next(&Default::default(), window, cx);
12349    });
12350
12351    cx.update_editor(|editor, _, _| {
12352        let context_menu = editor.context_menu.borrow_mut();
12353        let context_menu = context_menu
12354            .as_ref()
12355            .expect("Should have the context menu deployed");
12356        match context_menu {
12357            CodeContextMenu::Completions(completions_menu) => {
12358                let completions = completions_menu.completions.borrow_mut();
12359                assert_eq!(
12360                    completions
12361                        .iter()
12362                        .map(|completion| &completion.label.text)
12363                        .collect::<Vec<_>>(),
12364                    vec!["method id() Now resolved!", "other"],
12365                    "Should update first completion label, but not second as the filter text did not match."
12366                );
12367            }
12368            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12369        }
12370    });
12371}
12372
12373#[gpui::test]
12374async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12375    init_test(cx, |_| {});
12376
12377    let mut cx = EditorLspTestContext::new_rust(
12378        lsp::ServerCapabilities {
12379            completion_provider: Some(lsp::CompletionOptions {
12380                trigger_characters: Some(vec![".".to_string()]),
12381                resolve_provider: Some(true),
12382                ..Default::default()
12383            }),
12384            ..Default::default()
12385        },
12386        cx,
12387    )
12388    .await;
12389
12390    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12391    cx.simulate_keystroke(".");
12392
12393    let unresolved_item_1 = lsp::CompletionItem {
12394        label: "id".to_string(),
12395        filter_text: Some("id".to_string()),
12396        detail: None,
12397        documentation: None,
12398        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12399            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12400            new_text: ".id".to_string(),
12401        })),
12402        ..lsp::CompletionItem::default()
12403    };
12404    let resolved_item_1 = lsp::CompletionItem {
12405        additional_text_edits: Some(vec![lsp::TextEdit {
12406            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12407            new_text: "!!".to_string(),
12408        }]),
12409        ..unresolved_item_1.clone()
12410    };
12411    let unresolved_item_2 = lsp::CompletionItem {
12412        label: "other".to_string(),
12413        filter_text: Some("other".to_string()),
12414        detail: None,
12415        documentation: None,
12416        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12417            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12418            new_text: ".other".to_string(),
12419        })),
12420        ..lsp::CompletionItem::default()
12421    };
12422    let resolved_item_2 = lsp::CompletionItem {
12423        additional_text_edits: Some(vec![lsp::TextEdit {
12424            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12425            new_text: "??".to_string(),
12426        }]),
12427        ..unresolved_item_2.clone()
12428    };
12429
12430    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12431    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12432    cx.lsp
12433        .server
12434        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12435            let unresolved_item_1 = unresolved_item_1.clone();
12436            let resolved_item_1 = resolved_item_1.clone();
12437            let unresolved_item_2 = unresolved_item_2.clone();
12438            let resolved_item_2 = resolved_item_2.clone();
12439            let resolve_requests_1 = resolve_requests_1.clone();
12440            let resolve_requests_2 = resolve_requests_2.clone();
12441            move |unresolved_request, _| {
12442                let unresolved_item_1 = unresolved_item_1.clone();
12443                let resolved_item_1 = resolved_item_1.clone();
12444                let unresolved_item_2 = unresolved_item_2.clone();
12445                let resolved_item_2 = resolved_item_2.clone();
12446                let resolve_requests_1 = resolve_requests_1.clone();
12447                let resolve_requests_2 = resolve_requests_2.clone();
12448                async move {
12449                    if unresolved_request == unresolved_item_1 {
12450                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12451                        Ok(resolved_item_1.clone())
12452                    } else if unresolved_request == unresolved_item_2 {
12453                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12454                        Ok(resolved_item_2.clone())
12455                    } else {
12456                        panic!("Unexpected completion item {unresolved_request:?}")
12457                    }
12458                }
12459            }
12460        })
12461        .detach();
12462
12463    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12464        let unresolved_item_1 = unresolved_item_1.clone();
12465        let unresolved_item_2 = unresolved_item_2.clone();
12466        async move {
12467            Ok(Some(lsp::CompletionResponse::Array(vec![
12468                unresolved_item_1,
12469                unresolved_item_2,
12470            ])))
12471        }
12472    })
12473    .next()
12474    .await;
12475
12476    cx.condition(|editor, _| editor.context_menu_visible())
12477        .await;
12478    cx.update_editor(|editor, _, _| {
12479        let context_menu = editor.context_menu.borrow_mut();
12480        let context_menu = context_menu
12481            .as_ref()
12482            .expect("Should have the context menu deployed");
12483        match context_menu {
12484            CodeContextMenu::Completions(completions_menu) => {
12485                let completions = completions_menu.completions.borrow_mut();
12486                assert_eq!(
12487                    completions
12488                        .iter()
12489                        .map(|completion| &completion.label.text)
12490                        .collect::<Vec<_>>(),
12491                    vec!["id", "other"]
12492                )
12493            }
12494            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12495        }
12496    });
12497    cx.run_until_parked();
12498
12499    cx.update_editor(|editor, window, cx| {
12500        editor.context_menu_next(&ContextMenuNext, window, cx);
12501    });
12502    cx.run_until_parked();
12503    cx.update_editor(|editor, window, cx| {
12504        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12505    });
12506    cx.run_until_parked();
12507    cx.update_editor(|editor, window, cx| {
12508        editor.context_menu_next(&ContextMenuNext, window, cx);
12509    });
12510    cx.run_until_parked();
12511    cx.update_editor(|editor, window, cx| {
12512        editor
12513            .compose_completion(&ComposeCompletion::default(), window, cx)
12514            .expect("No task returned")
12515    })
12516    .await
12517    .expect("Completion failed");
12518    cx.run_until_parked();
12519
12520    cx.update_editor(|editor, _, cx| {
12521        assert_eq!(
12522            resolve_requests_1.load(atomic::Ordering::Acquire),
12523            1,
12524            "Should always resolve once despite multiple selections"
12525        );
12526        assert_eq!(
12527            resolve_requests_2.load(atomic::Ordering::Acquire),
12528            1,
12529            "Should always resolve once after multiple selections and applying the completion"
12530        );
12531        assert_eq!(
12532            editor.text(cx),
12533            "fn main() { let a = ??.other; }",
12534            "Should use resolved data when applying the completion"
12535        );
12536    });
12537}
12538
12539#[gpui::test]
12540async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12541    init_test(cx, |_| {});
12542
12543    let item_0 = lsp::CompletionItem {
12544        label: "abs".into(),
12545        insert_text: Some("abs".into()),
12546        data: Some(json!({ "very": "special"})),
12547        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12548        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12549            lsp::InsertReplaceEdit {
12550                new_text: "abs".to_string(),
12551                insert: lsp::Range::default(),
12552                replace: lsp::Range::default(),
12553            },
12554        )),
12555        ..lsp::CompletionItem::default()
12556    };
12557    let items = iter::once(item_0.clone())
12558        .chain((11..51).map(|i| lsp::CompletionItem {
12559            label: format!("item_{}", i),
12560            insert_text: Some(format!("item_{}", i)),
12561            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12562            ..lsp::CompletionItem::default()
12563        }))
12564        .collect::<Vec<_>>();
12565
12566    let default_commit_characters = vec!["?".to_string()];
12567    let default_data = json!({ "default": "data"});
12568    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12569    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12570    let default_edit_range = lsp::Range {
12571        start: lsp::Position {
12572            line: 0,
12573            character: 5,
12574        },
12575        end: lsp::Position {
12576            line: 0,
12577            character: 5,
12578        },
12579    };
12580
12581    let mut cx = EditorLspTestContext::new_rust(
12582        lsp::ServerCapabilities {
12583            completion_provider: Some(lsp::CompletionOptions {
12584                trigger_characters: Some(vec![".".to_string()]),
12585                resolve_provider: Some(true),
12586                ..Default::default()
12587            }),
12588            ..Default::default()
12589        },
12590        cx,
12591    )
12592    .await;
12593
12594    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12595    cx.simulate_keystroke(".");
12596
12597    let completion_data = default_data.clone();
12598    let completion_characters = default_commit_characters.clone();
12599    let completion_items = items.clone();
12600    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12601        let default_data = completion_data.clone();
12602        let default_commit_characters = completion_characters.clone();
12603        let items = completion_items.clone();
12604        async move {
12605            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12606                items,
12607                item_defaults: Some(lsp::CompletionListItemDefaults {
12608                    data: Some(default_data.clone()),
12609                    commit_characters: Some(default_commit_characters.clone()),
12610                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12611                        default_edit_range,
12612                    )),
12613                    insert_text_format: Some(default_insert_text_format),
12614                    insert_text_mode: Some(default_insert_text_mode),
12615                }),
12616                ..lsp::CompletionList::default()
12617            })))
12618        }
12619    })
12620    .next()
12621    .await;
12622
12623    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12624    cx.lsp
12625        .server
12626        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12627            let closure_resolved_items = resolved_items.clone();
12628            move |item_to_resolve, _| {
12629                let closure_resolved_items = closure_resolved_items.clone();
12630                async move {
12631                    closure_resolved_items.lock().push(item_to_resolve.clone());
12632                    Ok(item_to_resolve)
12633                }
12634            }
12635        })
12636        .detach();
12637
12638    cx.condition(|editor, _| editor.context_menu_visible())
12639        .await;
12640    cx.run_until_parked();
12641    cx.update_editor(|editor, _, _| {
12642        let menu = editor.context_menu.borrow_mut();
12643        match menu.as_ref().expect("should have the completions menu") {
12644            CodeContextMenu::Completions(completions_menu) => {
12645                assert_eq!(
12646                    completions_menu
12647                        .entries
12648                        .borrow()
12649                        .iter()
12650                        .map(|mat| mat.string.clone())
12651                        .collect::<Vec<String>>(),
12652                    items
12653                        .iter()
12654                        .map(|completion| completion.label.clone())
12655                        .collect::<Vec<String>>()
12656                );
12657            }
12658            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12659        }
12660    });
12661    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12662    // with 4 from the end.
12663    assert_eq!(
12664        *resolved_items.lock(),
12665        [&items[0..16], &items[items.len() - 4..items.len()]]
12666            .concat()
12667            .iter()
12668            .cloned()
12669            .map(|mut item| {
12670                if item.data.is_none() {
12671                    item.data = Some(default_data.clone());
12672                }
12673                item
12674            })
12675            .collect::<Vec<lsp::CompletionItem>>(),
12676        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12677    );
12678    resolved_items.lock().clear();
12679
12680    cx.update_editor(|editor, window, cx| {
12681        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12682    });
12683    cx.run_until_parked();
12684    // Completions that have already been resolved are skipped.
12685    assert_eq!(
12686        *resolved_items.lock(),
12687        items[items.len() - 16..items.len() - 4]
12688            .iter()
12689            .cloned()
12690            .map(|mut item| {
12691                if item.data.is_none() {
12692                    item.data = Some(default_data.clone());
12693                }
12694                item
12695            })
12696            .collect::<Vec<lsp::CompletionItem>>()
12697    );
12698    resolved_items.lock().clear();
12699}
12700
12701#[gpui::test]
12702async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12703    init_test(cx, |_| {});
12704
12705    let mut cx = EditorLspTestContext::new(
12706        Language::new(
12707            LanguageConfig {
12708                matcher: LanguageMatcher {
12709                    path_suffixes: vec!["jsx".into()],
12710                    ..Default::default()
12711                },
12712                overrides: [(
12713                    "element".into(),
12714                    LanguageConfigOverride {
12715                        word_characters: Override::Set(['-'].into_iter().collect()),
12716                        ..Default::default()
12717                    },
12718                )]
12719                .into_iter()
12720                .collect(),
12721                ..Default::default()
12722            },
12723            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12724        )
12725        .with_override_query("(jsx_self_closing_element) @element")
12726        .unwrap(),
12727        lsp::ServerCapabilities {
12728            completion_provider: Some(lsp::CompletionOptions {
12729                trigger_characters: Some(vec![":".to_string()]),
12730                ..Default::default()
12731            }),
12732            ..Default::default()
12733        },
12734        cx,
12735    )
12736    .await;
12737
12738    cx.lsp
12739        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12740            Ok(Some(lsp::CompletionResponse::Array(vec![
12741                lsp::CompletionItem {
12742                    label: "bg-blue".into(),
12743                    ..Default::default()
12744                },
12745                lsp::CompletionItem {
12746                    label: "bg-red".into(),
12747                    ..Default::default()
12748                },
12749                lsp::CompletionItem {
12750                    label: "bg-yellow".into(),
12751                    ..Default::default()
12752                },
12753            ])))
12754        });
12755
12756    cx.set_state(r#"<p class="bgˇ" />"#);
12757
12758    // Trigger completion when typing a dash, because the dash is an extra
12759    // word character in the 'element' scope, which contains the cursor.
12760    cx.simulate_keystroke("-");
12761    cx.executor().run_until_parked();
12762    cx.update_editor(|editor, _, _| {
12763        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12764        {
12765            assert_eq!(
12766                completion_menu_entries(&menu),
12767                &["bg-red", "bg-blue", "bg-yellow"]
12768            );
12769        } else {
12770            panic!("expected completion menu to be open");
12771        }
12772    });
12773
12774    cx.simulate_keystroke("l");
12775    cx.executor().run_until_parked();
12776    cx.update_editor(|editor, _, _| {
12777        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12778        {
12779            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12780        } else {
12781            panic!("expected completion menu to be open");
12782        }
12783    });
12784
12785    // When filtering completions, consider the character after the '-' to
12786    // be the start of a subword.
12787    cx.set_state(r#"<p class="yelˇ" />"#);
12788    cx.simulate_keystroke("l");
12789    cx.executor().run_until_parked();
12790    cx.update_editor(|editor, _, _| {
12791        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12792        {
12793            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12794        } else {
12795            panic!("expected completion menu to be open");
12796        }
12797    });
12798}
12799
12800fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12801    let entries = menu.entries.borrow();
12802    entries.iter().map(|mat| mat.string.clone()).collect()
12803}
12804
12805#[gpui::test]
12806async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12807    init_test(cx, |settings| {
12808        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12809            FormatterList(vec![Formatter::Prettier].into()),
12810        ))
12811    });
12812
12813    let fs = FakeFs::new(cx.executor());
12814    fs.insert_file(path!("/file.ts"), Default::default()).await;
12815
12816    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12817    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12818
12819    language_registry.add(Arc::new(Language::new(
12820        LanguageConfig {
12821            name: "TypeScript".into(),
12822            matcher: LanguageMatcher {
12823                path_suffixes: vec!["ts".to_string()],
12824                ..Default::default()
12825            },
12826            ..Default::default()
12827        },
12828        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12829    )));
12830    update_test_language_settings(cx, |settings| {
12831        settings.defaults.prettier = Some(PrettierSettings {
12832            allowed: true,
12833            ..PrettierSettings::default()
12834        });
12835    });
12836
12837    let test_plugin = "test_plugin";
12838    let _ = language_registry.register_fake_lsp(
12839        "TypeScript",
12840        FakeLspAdapter {
12841            prettier_plugins: vec![test_plugin],
12842            ..Default::default()
12843        },
12844    );
12845
12846    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12847    let buffer = project
12848        .update(cx, |project, cx| {
12849            project.open_local_buffer(path!("/file.ts"), cx)
12850        })
12851        .await
12852        .unwrap();
12853
12854    let buffer_text = "one\ntwo\nthree\n";
12855    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12856    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12857    editor.update_in(cx, |editor, window, cx| {
12858        editor.set_text(buffer_text, window, cx)
12859    });
12860
12861    editor
12862        .update_in(cx, |editor, window, cx| {
12863            editor.perform_format(
12864                project.clone(),
12865                FormatTrigger::Manual,
12866                FormatTarget::Buffers,
12867                window,
12868                cx,
12869            )
12870        })
12871        .unwrap()
12872        .await;
12873    assert_eq!(
12874        editor.update(cx, |editor, cx| editor.text(cx)),
12875        buffer_text.to_string() + prettier_format_suffix,
12876        "Test prettier formatting was not applied to the original buffer text",
12877    );
12878
12879    update_test_language_settings(cx, |settings| {
12880        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12881    });
12882    let format = editor.update_in(cx, |editor, window, cx| {
12883        editor.perform_format(
12884            project.clone(),
12885            FormatTrigger::Manual,
12886            FormatTarget::Buffers,
12887            window,
12888            cx,
12889        )
12890    });
12891    format.await.unwrap();
12892    assert_eq!(
12893        editor.update(cx, |editor, cx| editor.text(cx)),
12894        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12895        "Autoformatting (via test prettier) was not applied to the original buffer text",
12896    );
12897}
12898
12899#[gpui::test]
12900async fn test_addition_reverts(cx: &mut TestAppContext) {
12901    init_test(cx, |_| {});
12902    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12903    let base_text = indoc! {r#"
12904        struct Row;
12905        struct Row1;
12906        struct Row2;
12907
12908        struct Row4;
12909        struct Row5;
12910        struct Row6;
12911
12912        struct Row8;
12913        struct Row9;
12914        struct Row10;"#};
12915
12916    // When addition hunks are not adjacent to carets, no hunk revert is performed
12917    assert_hunk_revert(
12918        indoc! {r#"struct Row;
12919                   struct Row1;
12920                   struct Row1.1;
12921                   struct Row1.2;
12922                   struct Row2;ˇ
12923
12924                   struct Row4;
12925                   struct Row5;
12926                   struct Row6;
12927
12928                   struct Row8;
12929                   ˇstruct Row9;
12930                   struct Row9.1;
12931                   struct Row9.2;
12932                   struct Row9.3;
12933                   struct Row10;"#},
12934        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
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        base_text,
12952        &mut cx,
12953    );
12954    // Same for selections
12955    assert_hunk_revert(
12956        indoc! {r#"struct Row;
12957                   struct Row1;
12958                   struct Row2;
12959                   struct Row2.1;
12960                   struct Row2.2;
12961                   «ˇ
12962                   struct Row4;
12963                   struct» Row5;
12964                   «struct Row6;
12965                   ˇ»
12966                   struct Row9.1;
12967                   struct Row9.2;
12968                   struct Row9.3;
12969                   struct Row8;
12970                   struct Row9;
12971                   struct Row10;"#},
12972        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
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        base_text,
12990        &mut cx,
12991    );
12992
12993    // When carets and selections intersect the addition hunks, those are reverted.
12994    // Adjacent carets got merged.
12995    assert_hunk_revert(
12996        indoc! {r#"struct Row;
12997                   ˇ// something on the top
12998                   struct Row1;
12999                   struct Row2;
13000                   struct Roˇw3.1;
13001                   struct Row2.2;
13002                   struct Row2.3;ˇ
13003
13004                   struct Row4;
13005                   struct ˇRow5.1;
13006                   struct Row5.2;
13007                   struct «Rowˇ»5.3;
13008                   struct Row5;
13009                   struct Row6;
13010                   ˇ
13011                   struct Row9.1;
13012                   struct «Rowˇ»9.2;
13013                   struct «ˇRow»9.3;
13014                   struct Row8;
13015                   struct Row9;
13016                   «ˇ// something on bottom»
13017                   struct Row10;"#},
13018        vec![
13019            DiffHunkStatusKind::Added,
13020            DiffHunkStatusKind::Added,
13021            DiffHunkStatusKind::Added,
13022            DiffHunkStatusKind::Added,
13023            DiffHunkStatusKind::Added,
13024        ],
13025        indoc! {r#"struct Row;
13026                   ˇstruct Row1;
13027                   struct Row2;
13028                   ˇ
13029                   struct Row4;
13030                   ˇstruct Row5;
13031                   struct Row6;
13032                   ˇ
13033                   ˇstruct Row8;
13034                   struct Row9;
13035                   ˇstruct Row10;"#},
13036        base_text,
13037        &mut cx,
13038    );
13039}
13040
13041#[gpui::test]
13042async fn test_modification_reverts(cx: &mut TestAppContext) {
13043    init_test(cx, |_| {});
13044    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13045    let base_text = indoc! {r#"
13046        struct Row;
13047        struct Row1;
13048        struct Row2;
13049
13050        struct Row4;
13051        struct Row5;
13052        struct Row6;
13053
13054        struct Row8;
13055        struct Row9;
13056        struct Row10;"#};
13057
13058    // Modification hunks behave the same as the addition ones.
13059    assert_hunk_revert(
13060        indoc! {r#"struct Row;
13061                   struct Row1;
13062                   struct Row33;
13063                   ˇ
13064                   struct Row4;
13065                   struct Row5;
13066                   struct Row6;
13067                   ˇ
13068                   struct Row99;
13069                   struct Row9;
13070                   struct Row10;"#},
13071        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13072        indoc! {r#"struct Row;
13073                   struct Row1;
13074                   struct Row33;
13075                   ˇ
13076                   struct Row4;
13077                   struct Row5;
13078                   struct Row6;
13079                   ˇ
13080                   struct Row99;
13081                   struct Row9;
13082                   struct Row10;"#},
13083        base_text,
13084        &mut cx,
13085    );
13086    assert_hunk_revert(
13087        indoc! {r#"struct Row;
13088                   struct Row1;
13089                   struct Row33;
13090                   «ˇ
13091                   struct Row4;
13092                   struct» Row5;
13093                   «struct Row6;
13094                   ˇ»
13095                   struct Row99;
13096                   struct Row9;
13097                   struct Row10;"#},
13098        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13099        indoc! {r#"struct Row;
13100                   struct Row1;
13101                   struct Row33;
13102                   «ˇ
13103                   struct Row4;
13104                   struct» Row5;
13105                   «struct Row6;
13106                   ˇ»
13107                   struct Row99;
13108                   struct Row9;
13109                   struct Row10;"#},
13110        base_text,
13111        &mut cx,
13112    );
13113
13114    assert_hunk_revert(
13115        indoc! {r#"ˇstruct Row1.1;
13116                   struct Row1;
13117                   «ˇstr»uct Row22;
13118
13119                   struct ˇRow44;
13120                   struct Row5;
13121                   struct «Rˇ»ow66;ˇ
13122
13123                   «struˇ»ct Row88;
13124                   struct Row9;
13125                   struct Row1011;ˇ"#},
13126        vec![
13127            DiffHunkStatusKind::Modified,
13128            DiffHunkStatusKind::Modified,
13129            DiffHunkStatusKind::Modified,
13130            DiffHunkStatusKind::Modified,
13131            DiffHunkStatusKind::Modified,
13132            DiffHunkStatusKind::Modified,
13133        ],
13134        indoc! {r#"struct Row;
13135                   ˇstruct Row1;
13136                   struct Row2;
13137                   ˇ
13138                   struct Row4;
13139                   ˇstruct Row5;
13140                   struct Row6;
13141                   ˇ
13142                   struct Row8;
13143                   ˇstruct Row9;
13144                   struct Row10;ˇ"#},
13145        base_text,
13146        &mut cx,
13147    );
13148}
13149
13150#[gpui::test]
13151async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13152    init_test(cx, |_| {});
13153    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13154    let base_text = indoc! {r#"
13155        one
13156
13157        two
13158        three
13159        "#};
13160
13161    cx.set_head_text(base_text);
13162    cx.set_state("\nˇ\n");
13163    cx.executor().run_until_parked();
13164    cx.update_editor(|editor, _window, cx| {
13165        editor.expand_selected_diff_hunks(cx);
13166    });
13167    cx.executor().run_until_parked();
13168    cx.update_editor(|editor, window, cx| {
13169        editor.backspace(&Default::default(), window, cx);
13170    });
13171    cx.run_until_parked();
13172    cx.assert_state_with_diff(
13173        indoc! {r#"
13174
13175        - two
13176        - threeˇ
13177        +
13178        "#}
13179        .to_string(),
13180    );
13181}
13182
13183#[gpui::test]
13184async fn test_deletion_reverts(cx: &mut TestAppContext) {
13185    init_test(cx, |_| {});
13186    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13187    let base_text = indoc! {r#"struct Row;
13188struct Row1;
13189struct Row2;
13190
13191struct Row4;
13192struct Row5;
13193struct Row6;
13194
13195struct Row8;
13196struct Row9;
13197struct Row10;"#};
13198
13199    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13200    assert_hunk_revert(
13201        indoc! {r#"struct Row;
13202                   struct Row2;
13203
13204                   ˇstruct Row4;
13205                   struct Row5;
13206                   struct Row6;
13207                   ˇ
13208                   struct Row8;
13209                   struct Row10;"#},
13210        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13211        indoc! {r#"struct Row;
13212                   struct Row2;
13213
13214                   ˇstruct Row4;
13215                   struct Row5;
13216                   struct Row6;
13217                   ˇ
13218                   struct Row8;
13219                   struct Row10;"#},
13220        base_text,
13221        &mut cx,
13222    );
13223    assert_hunk_revert(
13224        indoc! {r#"struct Row;
13225                   struct Row2;
13226
13227                   «ˇstruct Row4;
13228                   struct» Row5;
13229                   «struct Row6;
13230                   ˇ»
13231                   struct Row8;
13232                   struct Row10;"#},
13233        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13234        indoc! {r#"struct Row;
13235                   struct Row2;
13236
13237                   «ˇstruct Row4;
13238                   struct» Row5;
13239                   «struct Row6;
13240                   ˇ»
13241                   struct Row8;
13242                   struct Row10;"#},
13243        base_text,
13244        &mut cx,
13245    );
13246
13247    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13248    assert_hunk_revert(
13249        indoc! {r#"struct Row;
13250                   ˇstruct Row2;
13251
13252                   struct Row4;
13253                   struct Row5;
13254                   struct Row6;
13255
13256                   struct Row8;ˇ
13257                   struct Row10;"#},
13258        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13259        indoc! {r#"struct Row;
13260                   struct Row1;
13261                   ˇstruct Row2;
13262
13263                   struct Row4;
13264                   struct Row5;
13265                   struct Row6;
13266
13267                   struct Row8;ˇ
13268                   struct Row9;
13269                   struct Row10;"#},
13270        base_text,
13271        &mut cx,
13272    );
13273    assert_hunk_revert(
13274        indoc! {r#"struct Row;
13275                   struct Row2«ˇ;
13276                   struct Row4;
13277                   struct» Row5;
13278                   «struct Row6;
13279
13280                   struct Row8;ˇ»
13281                   struct Row10;"#},
13282        vec![
13283            DiffHunkStatusKind::Deleted,
13284            DiffHunkStatusKind::Deleted,
13285            DiffHunkStatusKind::Deleted,
13286        ],
13287        indoc! {r#"struct Row;
13288                   struct Row1;
13289                   struct Row2«ˇ;
13290
13291                   struct Row4;
13292                   struct» Row5;
13293                   «struct Row6;
13294
13295                   struct Row8;ˇ»
13296                   struct Row9;
13297                   struct Row10;"#},
13298        base_text,
13299        &mut cx,
13300    );
13301}
13302
13303#[gpui::test]
13304async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13305    init_test(cx, |_| {});
13306
13307    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13308    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13309    let base_text_3 =
13310        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13311
13312    let text_1 = edit_first_char_of_every_line(base_text_1);
13313    let text_2 = edit_first_char_of_every_line(base_text_2);
13314    let text_3 = edit_first_char_of_every_line(base_text_3);
13315
13316    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13317    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13318    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13319
13320    let multibuffer = cx.new(|cx| {
13321        let mut multibuffer = MultiBuffer::new(ReadWrite);
13322        multibuffer.push_excerpts(
13323            buffer_1.clone(),
13324            [
13325                ExcerptRange {
13326                    context: Point::new(0, 0)..Point::new(3, 0),
13327                    primary: None,
13328                },
13329                ExcerptRange {
13330                    context: Point::new(5, 0)..Point::new(7, 0),
13331                    primary: None,
13332                },
13333                ExcerptRange {
13334                    context: Point::new(9, 0)..Point::new(10, 4),
13335                    primary: None,
13336                },
13337            ],
13338            cx,
13339        );
13340        multibuffer.push_excerpts(
13341            buffer_2.clone(),
13342            [
13343                ExcerptRange {
13344                    context: Point::new(0, 0)..Point::new(3, 0),
13345                    primary: None,
13346                },
13347                ExcerptRange {
13348                    context: Point::new(5, 0)..Point::new(7, 0),
13349                    primary: None,
13350                },
13351                ExcerptRange {
13352                    context: Point::new(9, 0)..Point::new(10, 4),
13353                    primary: None,
13354                },
13355            ],
13356            cx,
13357        );
13358        multibuffer.push_excerpts(
13359            buffer_3.clone(),
13360            [
13361                ExcerptRange {
13362                    context: Point::new(0, 0)..Point::new(3, 0),
13363                    primary: None,
13364                },
13365                ExcerptRange {
13366                    context: Point::new(5, 0)..Point::new(7, 0),
13367                    primary: None,
13368                },
13369                ExcerptRange {
13370                    context: Point::new(9, 0)..Point::new(10, 4),
13371                    primary: None,
13372                },
13373            ],
13374            cx,
13375        );
13376        multibuffer
13377    });
13378
13379    let fs = FakeFs::new(cx.executor());
13380    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13381    let (editor, cx) = cx
13382        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13383    editor.update_in(cx, |editor, _window, cx| {
13384        for (buffer, diff_base) in [
13385            (buffer_1.clone(), base_text_1),
13386            (buffer_2.clone(), base_text_2),
13387            (buffer_3.clone(), base_text_3),
13388        ] {
13389            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13390            editor
13391                .buffer
13392                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13393        }
13394    });
13395    cx.executor().run_until_parked();
13396
13397    editor.update_in(cx, |editor, window, cx| {
13398        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}");
13399        editor.select_all(&SelectAll, window, cx);
13400        editor.git_restore(&Default::default(), window, cx);
13401    });
13402    cx.executor().run_until_parked();
13403
13404    // When all ranges are selected, all buffer hunks are reverted.
13405    editor.update(cx, |editor, cx| {
13406        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");
13407    });
13408    buffer_1.update(cx, |buffer, _| {
13409        assert_eq!(buffer.text(), base_text_1);
13410    });
13411    buffer_2.update(cx, |buffer, _| {
13412        assert_eq!(buffer.text(), base_text_2);
13413    });
13414    buffer_3.update(cx, |buffer, _| {
13415        assert_eq!(buffer.text(), base_text_3);
13416    });
13417
13418    editor.update_in(cx, |editor, window, cx| {
13419        editor.undo(&Default::default(), window, cx);
13420    });
13421
13422    editor.update_in(cx, |editor, window, cx| {
13423        editor.change_selections(None, window, cx, |s| {
13424            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13425        });
13426        editor.git_restore(&Default::default(), window, cx);
13427    });
13428
13429    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13430    // but not affect buffer_2 and its related excerpts.
13431    editor.update(cx, |editor, cx| {
13432        assert_eq!(
13433            editor.text(cx),
13434            "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}"
13435        );
13436    });
13437    buffer_1.update(cx, |buffer, _| {
13438        assert_eq!(buffer.text(), base_text_1);
13439    });
13440    buffer_2.update(cx, |buffer, _| {
13441        assert_eq!(
13442            buffer.text(),
13443            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13444        );
13445    });
13446    buffer_3.update(cx, |buffer, _| {
13447        assert_eq!(
13448            buffer.text(),
13449            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13450        );
13451    });
13452
13453    fn edit_first_char_of_every_line(text: &str) -> String {
13454        text.split('\n')
13455            .map(|line| format!("X{}", &line[1..]))
13456            .collect::<Vec<_>>()
13457            .join("\n")
13458    }
13459}
13460
13461#[gpui::test]
13462async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13463    init_test(cx, |_| {});
13464
13465    let cols = 4;
13466    let rows = 10;
13467    let sample_text_1 = sample_text(rows, cols, 'a');
13468    assert_eq!(
13469        sample_text_1,
13470        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13471    );
13472    let sample_text_2 = sample_text(rows, cols, 'l');
13473    assert_eq!(
13474        sample_text_2,
13475        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13476    );
13477    let sample_text_3 = sample_text(rows, cols, 'v');
13478    assert_eq!(
13479        sample_text_3,
13480        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13481    );
13482
13483    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13484    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13485    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13486
13487    let multi_buffer = cx.new(|cx| {
13488        let mut multibuffer = MultiBuffer::new(ReadWrite);
13489        multibuffer.push_excerpts(
13490            buffer_1.clone(),
13491            [
13492                ExcerptRange {
13493                    context: Point::new(0, 0)..Point::new(3, 0),
13494                    primary: None,
13495                },
13496                ExcerptRange {
13497                    context: Point::new(5, 0)..Point::new(7, 0),
13498                    primary: None,
13499                },
13500                ExcerptRange {
13501                    context: Point::new(9, 0)..Point::new(10, 4),
13502                    primary: None,
13503                },
13504            ],
13505            cx,
13506        );
13507        multibuffer.push_excerpts(
13508            buffer_2.clone(),
13509            [
13510                ExcerptRange {
13511                    context: Point::new(0, 0)..Point::new(3, 0),
13512                    primary: None,
13513                },
13514                ExcerptRange {
13515                    context: Point::new(5, 0)..Point::new(7, 0),
13516                    primary: None,
13517                },
13518                ExcerptRange {
13519                    context: Point::new(9, 0)..Point::new(10, 4),
13520                    primary: None,
13521                },
13522            ],
13523            cx,
13524        );
13525        multibuffer.push_excerpts(
13526            buffer_3.clone(),
13527            [
13528                ExcerptRange {
13529                    context: Point::new(0, 0)..Point::new(3, 0),
13530                    primary: None,
13531                },
13532                ExcerptRange {
13533                    context: Point::new(5, 0)..Point::new(7, 0),
13534                    primary: None,
13535                },
13536                ExcerptRange {
13537                    context: Point::new(9, 0)..Point::new(10, 4),
13538                    primary: None,
13539                },
13540            ],
13541            cx,
13542        );
13543        multibuffer
13544    });
13545
13546    let fs = FakeFs::new(cx.executor());
13547    fs.insert_tree(
13548        "/a",
13549        json!({
13550            "main.rs": sample_text_1,
13551            "other.rs": sample_text_2,
13552            "lib.rs": sample_text_3,
13553        }),
13554    )
13555    .await;
13556    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13557    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13558    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13559    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13560        Editor::new(
13561            EditorMode::Full,
13562            multi_buffer,
13563            Some(project.clone()),
13564            window,
13565            cx,
13566        )
13567    });
13568    let multibuffer_item_id = workspace
13569        .update(cx, |workspace, window, cx| {
13570            assert!(
13571                workspace.active_item(cx).is_none(),
13572                "active item should be None before the first item is added"
13573            );
13574            workspace.add_item_to_active_pane(
13575                Box::new(multi_buffer_editor.clone()),
13576                None,
13577                true,
13578                window,
13579                cx,
13580            );
13581            let active_item = workspace
13582                .active_item(cx)
13583                .expect("should have an active item after adding the multi buffer");
13584            assert!(
13585                !active_item.is_singleton(cx),
13586                "A multi buffer was expected to active after adding"
13587            );
13588            active_item.item_id()
13589        })
13590        .unwrap();
13591    cx.executor().run_until_parked();
13592
13593    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13594        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13595            s.select_ranges(Some(1..2))
13596        });
13597        editor.open_excerpts(&OpenExcerpts, window, cx);
13598    });
13599    cx.executor().run_until_parked();
13600    let first_item_id = workspace
13601        .update(cx, |workspace, window, cx| {
13602            let active_item = workspace
13603                .active_item(cx)
13604                .expect("should have an active item after navigating into the 1st buffer");
13605            let first_item_id = active_item.item_id();
13606            assert_ne!(
13607                first_item_id, multibuffer_item_id,
13608                "Should navigate into the 1st buffer and activate it"
13609            );
13610            assert!(
13611                active_item.is_singleton(cx),
13612                "New active item should be a singleton buffer"
13613            );
13614            assert_eq!(
13615                active_item
13616                    .act_as::<Editor>(cx)
13617                    .expect("should have navigated into an editor for the 1st buffer")
13618                    .read(cx)
13619                    .text(cx),
13620                sample_text_1
13621            );
13622
13623            workspace
13624                .go_back(workspace.active_pane().downgrade(), window, cx)
13625                .detach_and_log_err(cx);
13626
13627            first_item_id
13628        })
13629        .unwrap();
13630    cx.executor().run_until_parked();
13631    workspace
13632        .update(cx, |workspace, _, cx| {
13633            let active_item = workspace
13634                .active_item(cx)
13635                .expect("should have an active item after navigating back");
13636            assert_eq!(
13637                active_item.item_id(),
13638                multibuffer_item_id,
13639                "Should navigate back to the multi buffer"
13640            );
13641            assert!(!active_item.is_singleton(cx));
13642        })
13643        .unwrap();
13644
13645    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13646        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13647            s.select_ranges(Some(39..40))
13648        });
13649        editor.open_excerpts(&OpenExcerpts, window, cx);
13650    });
13651    cx.executor().run_until_parked();
13652    let second_item_id = workspace
13653        .update(cx, |workspace, window, cx| {
13654            let active_item = workspace
13655                .active_item(cx)
13656                .expect("should have an active item after navigating into the 2nd buffer");
13657            let second_item_id = active_item.item_id();
13658            assert_ne!(
13659                second_item_id, multibuffer_item_id,
13660                "Should navigate away from the multibuffer"
13661            );
13662            assert_ne!(
13663                second_item_id, first_item_id,
13664                "Should navigate into the 2nd buffer and activate it"
13665            );
13666            assert!(
13667                active_item.is_singleton(cx),
13668                "New active item should be a singleton buffer"
13669            );
13670            assert_eq!(
13671                active_item
13672                    .act_as::<Editor>(cx)
13673                    .expect("should have navigated into an editor")
13674                    .read(cx)
13675                    .text(cx),
13676                sample_text_2
13677            );
13678
13679            workspace
13680                .go_back(workspace.active_pane().downgrade(), window, cx)
13681                .detach_and_log_err(cx);
13682
13683            second_item_id
13684        })
13685        .unwrap();
13686    cx.executor().run_until_parked();
13687    workspace
13688        .update(cx, |workspace, _, cx| {
13689            let active_item = workspace
13690                .active_item(cx)
13691                .expect("should have an active item after navigating back from the 2nd buffer");
13692            assert_eq!(
13693                active_item.item_id(),
13694                multibuffer_item_id,
13695                "Should navigate back from the 2nd buffer to the multi buffer"
13696            );
13697            assert!(!active_item.is_singleton(cx));
13698        })
13699        .unwrap();
13700
13701    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13702        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13703            s.select_ranges(Some(70..70))
13704        });
13705        editor.open_excerpts(&OpenExcerpts, window, cx);
13706    });
13707    cx.executor().run_until_parked();
13708    workspace
13709        .update(cx, |workspace, window, cx| {
13710            let active_item = workspace
13711                .active_item(cx)
13712                .expect("should have an active item after navigating into the 3rd buffer");
13713            let third_item_id = active_item.item_id();
13714            assert_ne!(
13715                third_item_id, multibuffer_item_id,
13716                "Should navigate into the 3rd buffer and activate it"
13717            );
13718            assert_ne!(third_item_id, first_item_id);
13719            assert_ne!(third_item_id, second_item_id);
13720            assert!(
13721                active_item.is_singleton(cx),
13722                "New active item should be a singleton buffer"
13723            );
13724            assert_eq!(
13725                active_item
13726                    .act_as::<Editor>(cx)
13727                    .expect("should have navigated into an editor")
13728                    .read(cx)
13729                    .text(cx),
13730                sample_text_3
13731            );
13732
13733            workspace
13734                .go_back(workspace.active_pane().downgrade(), window, cx)
13735                .detach_and_log_err(cx);
13736        })
13737        .unwrap();
13738    cx.executor().run_until_parked();
13739    workspace
13740        .update(cx, |workspace, _, cx| {
13741            let active_item = workspace
13742                .active_item(cx)
13743                .expect("should have an active item after navigating back from the 3rd buffer");
13744            assert_eq!(
13745                active_item.item_id(),
13746                multibuffer_item_id,
13747                "Should navigate back from the 3rd buffer to the multi buffer"
13748            );
13749            assert!(!active_item.is_singleton(cx));
13750        })
13751        .unwrap();
13752}
13753
13754#[gpui::test]
13755async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13756    init_test(cx, |_| {});
13757
13758    let mut cx = EditorTestContext::new(cx).await;
13759
13760    let diff_base = r#"
13761        use some::mod;
13762
13763        const A: u32 = 42;
13764
13765        fn main() {
13766            println!("hello");
13767
13768            println!("world");
13769        }
13770        "#
13771    .unindent();
13772
13773    cx.set_state(
13774        &r#"
13775        use some::modified;
13776
13777        ˇ
13778        fn main() {
13779            println!("hello there");
13780
13781            println!("around the");
13782            println!("world");
13783        }
13784        "#
13785        .unindent(),
13786    );
13787
13788    cx.set_head_text(&diff_base);
13789    executor.run_until_parked();
13790
13791    cx.update_editor(|editor, window, cx| {
13792        editor.go_to_next_hunk(&GoToHunk, window, cx);
13793        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13794    });
13795    executor.run_until_parked();
13796    cx.assert_state_with_diff(
13797        r#"
13798          use some::modified;
13799
13800
13801          fn main() {
13802        -     println!("hello");
13803        + ˇ    println!("hello there");
13804
13805              println!("around the");
13806              println!("world");
13807          }
13808        "#
13809        .unindent(),
13810    );
13811
13812    cx.update_editor(|editor, window, cx| {
13813        for _ in 0..2 {
13814            editor.go_to_next_hunk(&GoToHunk, window, cx);
13815            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13816        }
13817    });
13818    executor.run_until_parked();
13819    cx.assert_state_with_diff(
13820        r#"
13821        - use some::mod;
13822        + ˇuse some::modified;
13823
13824
13825          fn main() {
13826        -     println!("hello");
13827        +     println!("hello there");
13828
13829        +     println!("around the");
13830              println!("world");
13831          }
13832        "#
13833        .unindent(),
13834    );
13835
13836    cx.update_editor(|editor, window, cx| {
13837        editor.go_to_next_hunk(&GoToHunk, window, cx);
13838        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13839    });
13840    executor.run_until_parked();
13841    cx.assert_state_with_diff(
13842        r#"
13843        - use some::mod;
13844        + use some::modified;
13845
13846        - const A: u32 = 42;
13847          ˇ
13848          fn main() {
13849        -     println!("hello");
13850        +     println!("hello there");
13851
13852        +     println!("around the");
13853              println!("world");
13854          }
13855        "#
13856        .unindent(),
13857    );
13858
13859    cx.update_editor(|editor, window, cx| {
13860        editor.cancel(&Cancel, window, cx);
13861    });
13862
13863    cx.assert_state_with_diff(
13864        r#"
13865          use some::modified;
13866
13867          ˇ
13868          fn main() {
13869              println!("hello there");
13870
13871              println!("around the");
13872              println!("world");
13873          }
13874        "#
13875        .unindent(),
13876    );
13877}
13878
13879#[gpui::test]
13880async fn test_diff_base_change_with_expanded_diff_hunks(
13881    executor: BackgroundExecutor,
13882    cx: &mut TestAppContext,
13883) {
13884    init_test(cx, |_| {});
13885
13886    let mut cx = EditorTestContext::new(cx).await;
13887
13888    let diff_base = r#"
13889        use some::mod1;
13890        use some::mod2;
13891
13892        const A: u32 = 42;
13893        const B: u32 = 42;
13894        const C: u32 = 42;
13895
13896        fn main() {
13897            println!("hello");
13898
13899            println!("world");
13900        }
13901        "#
13902    .unindent();
13903
13904    cx.set_state(
13905        &r#"
13906        use some::mod2;
13907
13908        const A: u32 = 42;
13909        const C: u32 = 42;
13910
13911        fn main(ˇ) {
13912            //println!("hello");
13913
13914            println!("world");
13915            //
13916            //
13917        }
13918        "#
13919        .unindent(),
13920    );
13921
13922    cx.set_head_text(&diff_base);
13923    executor.run_until_parked();
13924
13925    cx.update_editor(|editor, window, cx| {
13926        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13927    });
13928    executor.run_until_parked();
13929    cx.assert_state_with_diff(
13930        r#"
13931        - use some::mod1;
13932          use some::mod2;
13933
13934          const A: u32 = 42;
13935        - const B: u32 = 42;
13936          const C: u32 = 42;
13937
13938          fn main(ˇ) {
13939        -     println!("hello");
13940        +     //println!("hello");
13941
13942              println!("world");
13943        +     //
13944        +     //
13945          }
13946        "#
13947        .unindent(),
13948    );
13949
13950    cx.set_head_text("new diff base!");
13951    executor.run_until_parked();
13952    cx.assert_state_with_diff(
13953        r#"
13954        - new diff base!
13955        + use some::mod2;
13956        +
13957        + const A: u32 = 42;
13958        + const C: u32 = 42;
13959        +
13960        + fn main(ˇ) {
13961        +     //println!("hello");
13962        +
13963        +     println!("world");
13964        +     //
13965        +     //
13966        + }
13967        "#
13968        .unindent(),
13969    );
13970}
13971
13972#[gpui::test]
13973async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13974    init_test(cx, |_| {});
13975
13976    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13977    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13978    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13979    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13980    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13981    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13982
13983    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13984    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13985    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13986
13987    let multi_buffer = cx.new(|cx| {
13988        let mut multibuffer = MultiBuffer::new(ReadWrite);
13989        multibuffer.push_excerpts(
13990            buffer_1.clone(),
13991            [
13992                ExcerptRange {
13993                    context: Point::new(0, 0)..Point::new(3, 0),
13994                    primary: None,
13995                },
13996                ExcerptRange {
13997                    context: Point::new(5, 0)..Point::new(7, 0),
13998                    primary: None,
13999                },
14000                ExcerptRange {
14001                    context: Point::new(9, 0)..Point::new(10, 3),
14002                    primary: None,
14003                },
14004            ],
14005            cx,
14006        );
14007        multibuffer.push_excerpts(
14008            buffer_2.clone(),
14009            [
14010                ExcerptRange {
14011                    context: Point::new(0, 0)..Point::new(3, 0),
14012                    primary: None,
14013                },
14014                ExcerptRange {
14015                    context: Point::new(5, 0)..Point::new(7, 0),
14016                    primary: None,
14017                },
14018                ExcerptRange {
14019                    context: Point::new(9, 0)..Point::new(10, 3),
14020                    primary: None,
14021                },
14022            ],
14023            cx,
14024        );
14025        multibuffer.push_excerpts(
14026            buffer_3.clone(),
14027            [
14028                ExcerptRange {
14029                    context: Point::new(0, 0)..Point::new(3, 0),
14030                    primary: None,
14031                },
14032                ExcerptRange {
14033                    context: Point::new(5, 0)..Point::new(7, 0),
14034                    primary: None,
14035                },
14036                ExcerptRange {
14037                    context: Point::new(9, 0)..Point::new(10, 3),
14038                    primary: None,
14039                },
14040            ],
14041            cx,
14042        );
14043        multibuffer
14044    });
14045
14046    let editor =
14047        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14048    editor
14049        .update(cx, |editor, _window, cx| {
14050            for (buffer, diff_base) in [
14051                (buffer_1.clone(), file_1_old),
14052                (buffer_2.clone(), file_2_old),
14053                (buffer_3.clone(), file_3_old),
14054            ] {
14055                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14056                editor
14057                    .buffer
14058                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14059            }
14060        })
14061        .unwrap();
14062
14063    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14064    cx.run_until_parked();
14065
14066    cx.assert_editor_state(
14067        &"
14068            ˇaaa
14069            ccc
14070            ddd
14071
14072            ggg
14073            hhh
14074
14075
14076            lll
14077            mmm
14078            NNN
14079
14080            qqq
14081            rrr
14082
14083            uuu
14084            111
14085            222
14086            333
14087
14088            666
14089            777
14090
14091            000
14092            !!!"
14093        .unindent(),
14094    );
14095
14096    cx.update_editor(|editor, window, cx| {
14097        editor.select_all(&SelectAll, window, cx);
14098        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14099    });
14100    cx.executor().run_until_parked();
14101
14102    cx.assert_state_with_diff(
14103        "
14104            «aaa
14105          - bbb
14106            ccc
14107            ddd
14108
14109            ggg
14110            hhh
14111
14112
14113            lll
14114            mmm
14115          - nnn
14116          + NNN
14117
14118            qqq
14119            rrr
14120
14121            uuu
14122            111
14123            222
14124            333
14125
14126          + 666
14127            777
14128
14129            000
14130            !!!ˇ»"
14131            .unindent(),
14132    );
14133}
14134
14135#[gpui::test]
14136async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14137    init_test(cx, |_| {});
14138
14139    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14140    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14141
14142    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14143    let multi_buffer = cx.new(|cx| {
14144        let mut multibuffer = MultiBuffer::new(ReadWrite);
14145        multibuffer.push_excerpts(
14146            buffer.clone(),
14147            [
14148                ExcerptRange {
14149                    context: Point::new(0, 0)..Point::new(2, 0),
14150                    primary: None,
14151                },
14152                ExcerptRange {
14153                    context: Point::new(4, 0)..Point::new(7, 0),
14154                    primary: None,
14155                },
14156                ExcerptRange {
14157                    context: Point::new(9, 0)..Point::new(10, 0),
14158                    primary: None,
14159                },
14160            ],
14161            cx,
14162        );
14163        multibuffer
14164    });
14165
14166    let editor =
14167        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14168    editor
14169        .update(cx, |editor, _window, cx| {
14170            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14171            editor
14172                .buffer
14173                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14174        })
14175        .unwrap();
14176
14177    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14178    cx.run_until_parked();
14179
14180    cx.update_editor(|editor, window, cx| {
14181        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14182    });
14183    cx.executor().run_until_parked();
14184
14185    // When the start of a hunk coincides with the start of its excerpt,
14186    // the hunk is expanded. When the start of a a hunk is earlier than
14187    // the start of its excerpt, the hunk is not expanded.
14188    cx.assert_state_with_diff(
14189        "
14190            ˇaaa
14191          - bbb
14192          + BBB
14193
14194          - ddd
14195          - eee
14196          + DDD
14197          + EEE
14198            fff
14199
14200            iii
14201        "
14202        .unindent(),
14203    );
14204}
14205
14206#[gpui::test]
14207async fn test_edits_around_expanded_insertion_hunks(
14208    executor: BackgroundExecutor,
14209    cx: &mut TestAppContext,
14210) {
14211    init_test(cx, |_| {});
14212
14213    let mut cx = EditorTestContext::new(cx).await;
14214
14215    let diff_base = r#"
14216        use some::mod1;
14217        use some::mod2;
14218
14219        const A: u32 = 42;
14220
14221        fn main() {
14222            println!("hello");
14223
14224            println!("world");
14225        }
14226        "#
14227    .unindent();
14228    executor.run_until_parked();
14229    cx.set_state(
14230        &r#"
14231        use some::mod1;
14232        use some::mod2;
14233
14234        const A: u32 = 42;
14235        const B: u32 = 42;
14236        const C: u32 = 42;
14237        ˇ
14238
14239        fn main() {
14240            println!("hello");
14241
14242            println!("world");
14243        }
14244        "#
14245        .unindent(),
14246    );
14247
14248    cx.set_head_text(&diff_base);
14249    executor.run_until_parked();
14250
14251    cx.update_editor(|editor, window, cx| {
14252        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14253    });
14254    executor.run_until_parked();
14255
14256    cx.assert_state_with_diff(
14257        r#"
14258        use some::mod1;
14259        use some::mod2;
14260
14261        const A: u32 = 42;
14262      + const B: u32 = 42;
14263      + const C: u32 = 42;
14264      + ˇ
14265
14266        fn main() {
14267            println!("hello");
14268
14269            println!("world");
14270        }
14271      "#
14272        .unindent(),
14273    );
14274
14275    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14276    executor.run_until_parked();
14277
14278    cx.assert_state_with_diff(
14279        r#"
14280        use some::mod1;
14281        use some::mod2;
14282
14283        const A: u32 = 42;
14284      + const B: u32 = 42;
14285      + const C: u32 = 42;
14286      + const D: u32 = 42;
14287      + ˇ
14288
14289        fn main() {
14290            println!("hello");
14291
14292            println!("world");
14293        }
14294      "#
14295        .unindent(),
14296    );
14297
14298    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14299    executor.run_until_parked();
14300
14301    cx.assert_state_with_diff(
14302        r#"
14303        use some::mod1;
14304        use some::mod2;
14305
14306        const A: u32 = 42;
14307      + const B: u32 = 42;
14308      + const C: u32 = 42;
14309      + const D: u32 = 42;
14310      + const E: u32 = 42;
14311      + ˇ
14312
14313        fn main() {
14314            println!("hello");
14315
14316            println!("world");
14317        }
14318      "#
14319        .unindent(),
14320    );
14321
14322    cx.update_editor(|editor, window, cx| {
14323        editor.delete_line(&DeleteLine, window, cx);
14324    });
14325    executor.run_until_parked();
14326
14327    cx.assert_state_with_diff(
14328        r#"
14329        use some::mod1;
14330        use some::mod2;
14331
14332        const A: u32 = 42;
14333      + const B: u32 = 42;
14334      + const C: u32 = 42;
14335      + const D: u32 = 42;
14336      + const E: u32 = 42;
14337        ˇ
14338        fn main() {
14339            println!("hello");
14340
14341            println!("world");
14342        }
14343      "#
14344        .unindent(),
14345    );
14346
14347    cx.update_editor(|editor, window, cx| {
14348        editor.move_up(&MoveUp, window, cx);
14349        editor.delete_line(&DeleteLine, window, cx);
14350        editor.move_up(&MoveUp, window, cx);
14351        editor.delete_line(&DeleteLine, window, cx);
14352        editor.move_up(&MoveUp, window, cx);
14353        editor.delete_line(&DeleteLine, window, cx);
14354    });
14355    executor.run_until_parked();
14356    cx.assert_state_with_diff(
14357        r#"
14358        use some::mod1;
14359        use some::mod2;
14360
14361        const A: u32 = 42;
14362      + const B: u32 = 42;
14363        ˇ
14364        fn main() {
14365            println!("hello");
14366
14367            println!("world");
14368        }
14369      "#
14370        .unindent(),
14371    );
14372
14373    cx.update_editor(|editor, window, cx| {
14374        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14375        editor.delete_line(&DeleteLine, window, cx);
14376    });
14377    executor.run_until_parked();
14378    cx.assert_state_with_diff(
14379        r#"
14380        ˇ
14381        fn main() {
14382            println!("hello");
14383
14384            println!("world");
14385        }
14386      "#
14387        .unindent(),
14388    );
14389}
14390
14391#[gpui::test]
14392async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14393    init_test(cx, |_| {});
14394
14395    let mut cx = EditorTestContext::new(cx).await;
14396    cx.set_head_text(indoc! { "
14397        one
14398        two
14399        three
14400        four
14401        five
14402        "
14403    });
14404    cx.set_state(indoc! { "
14405        one
14406        ˇthree
14407        five
14408    "});
14409    cx.run_until_parked();
14410    cx.update_editor(|editor, window, cx| {
14411        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14412    });
14413    cx.assert_state_with_diff(
14414        indoc! { "
14415        one
14416      - two
14417        ˇthree
14418      - four
14419        five
14420    "}
14421        .to_string(),
14422    );
14423    cx.update_editor(|editor, window, cx| {
14424        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14425    });
14426
14427    cx.assert_state_with_diff(
14428        indoc! { "
14429        one
14430        ˇthree
14431        five
14432    "}
14433        .to_string(),
14434    );
14435
14436    cx.set_state(indoc! { "
14437        one
14438        ˇTWO
14439        three
14440        four
14441        five
14442    "});
14443    cx.run_until_parked();
14444    cx.update_editor(|editor, window, cx| {
14445        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14446    });
14447
14448    cx.assert_state_with_diff(
14449        indoc! { "
14450            one
14451          - two
14452          + ˇTWO
14453            three
14454            four
14455            five
14456        "}
14457        .to_string(),
14458    );
14459    cx.update_editor(|editor, window, cx| {
14460        editor.move_up(&Default::default(), window, cx);
14461        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14462    });
14463    cx.assert_state_with_diff(
14464        indoc! { "
14465            one
14466            ˇTWO
14467            three
14468            four
14469            five
14470        "}
14471        .to_string(),
14472    );
14473}
14474
14475#[gpui::test]
14476async fn test_edits_around_expanded_deletion_hunks(
14477    executor: BackgroundExecutor,
14478    cx: &mut TestAppContext,
14479) {
14480    init_test(cx, |_| {});
14481
14482    let mut cx = EditorTestContext::new(cx).await;
14483
14484    let diff_base = r#"
14485        use some::mod1;
14486        use some::mod2;
14487
14488        const A: u32 = 42;
14489        const B: u32 = 42;
14490        const C: u32 = 42;
14491
14492
14493        fn main() {
14494            println!("hello");
14495
14496            println!("world");
14497        }
14498    "#
14499    .unindent();
14500    executor.run_until_parked();
14501    cx.set_state(
14502        &r#"
14503        use some::mod1;
14504        use some::mod2;
14505
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    );
14518
14519    cx.set_head_text(&diff_base);
14520    executor.run_until_parked();
14521
14522    cx.update_editor(|editor, window, cx| {
14523        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14524    });
14525    executor.run_until_parked();
14526
14527    cx.assert_state_with_diff(
14528        r#"
14529        use some::mod1;
14530        use some::mod2;
14531
14532      - const A: u32 = 42;
14533        ˇconst B: u32 = 42;
14534        const C: u32 = 42;
14535
14536
14537        fn main() {
14538            println!("hello");
14539
14540            println!("world");
14541        }
14542      "#
14543        .unindent(),
14544    );
14545
14546    cx.update_editor(|editor, window, cx| {
14547        editor.delete_line(&DeleteLine, window, cx);
14548    });
14549    executor.run_until_parked();
14550    cx.assert_state_with_diff(
14551        r#"
14552        use some::mod1;
14553        use some::mod2;
14554
14555      - const A: u32 = 42;
14556      - const B: u32 = 42;
14557        ˇconst C: u32 = 42;
14558
14559
14560        fn main() {
14561            println!("hello");
14562
14563            println!("world");
14564        }
14565      "#
14566        .unindent(),
14567    );
14568
14569    cx.update_editor(|editor, window, cx| {
14570        editor.delete_line(&DeleteLine, window, cx);
14571    });
14572    executor.run_until_parked();
14573    cx.assert_state_with_diff(
14574        r#"
14575        use some::mod1;
14576        use some::mod2;
14577
14578      - const A: u32 = 42;
14579      - const B: u32 = 42;
14580      - const C: u32 = 42;
14581        ˇ
14582
14583        fn main() {
14584            println!("hello");
14585
14586            println!("world");
14587        }
14588      "#
14589        .unindent(),
14590    );
14591
14592    cx.update_editor(|editor, window, cx| {
14593        editor.handle_input("replacement", window, cx);
14594    });
14595    executor.run_until_parked();
14596    cx.assert_state_with_diff(
14597        r#"
14598        use some::mod1;
14599        use some::mod2;
14600
14601      - const A: u32 = 42;
14602      - const B: u32 = 42;
14603      - const C: u32 = 42;
14604      -
14605      + replacementˇ
14606
14607        fn main() {
14608            println!("hello");
14609
14610            println!("world");
14611        }
14612      "#
14613        .unindent(),
14614    );
14615}
14616
14617#[gpui::test]
14618async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14619    init_test(cx, |_| {});
14620
14621    let mut cx = EditorTestContext::new(cx).await;
14622
14623    let base_text = r#"
14624        one
14625        two
14626        three
14627        four
14628        five
14629    "#
14630    .unindent();
14631    executor.run_until_parked();
14632    cx.set_state(
14633        &r#"
14634        one
14635        two
14636        fˇour
14637        five
14638        "#
14639        .unindent(),
14640    );
14641
14642    cx.set_head_text(&base_text);
14643    executor.run_until_parked();
14644
14645    cx.update_editor(|editor, window, cx| {
14646        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14647    });
14648    executor.run_until_parked();
14649
14650    cx.assert_state_with_diff(
14651        r#"
14652          one
14653          two
14654        - three
14655          fˇour
14656          five
14657        "#
14658        .unindent(),
14659    );
14660
14661    cx.update_editor(|editor, window, cx| {
14662        editor.backspace(&Backspace, window, cx);
14663        editor.backspace(&Backspace, window, cx);
14664    });
14665    executor.run_until_parked();
14666    cx.assert_state_with_diff(
14667        r#"
14668          one
14669          two
14670        - threeˇ
14671        - four
14672        + our
14673          five
14674        "#
14675        .unindent(),
14676    );
14677}
14678
14679#[gpui::test]
14680async fn test_edit_after_expanded_modification_hunk(
14681    executor: BackgroundExecutor,
14682    cx: &mut TestAppContext,
14683) {
14684    init_test(cx, |_| {});
14685
14686    let mut cx = EditorTestContext::new(cx).await;
14687
14688    let diff_base = r#"
14689        use some::mod1;
14690        use some::mod2;
14691
14692        const A: u32 = 42;
14693        const B: u32 = 42;
14694        const C: u32 = 42;
14695        const D: u32 = 42;
14696
14697
14698        fn main() {
14699            println!("hello");
14700
14701            println!("world");
14702        }"#
14703    .unindent();
14704
14705    cx.set_state(
14706        &r#"
14707        use some::mod1;
14708        use some::mod2;
14709
14710        const A: u32 = 42;
14711        const B: u32 = 42;
14712        const C: u32 = 43ˇ
14713        const D: u32 = 42;
14714
14715
14716        fn main() {
14717            println!("hello");
14718
14719            println!("world");
14720        }"#
14721        .unindent(),
14722    );
14723
14724    cx.set_head_text(&diff_base);
14725    executor.run_until_parked();
14726    cx.update_editor(|editor, window, cx| {
14727        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14728    });
14729    executor.run_until_parked();
14730
14731    cx.assert_state_with_diff(
14732        r#"
14733        use some::mod1;
14734        use some::mod2;
14735
14736        const A: u32 = 42;
14737        const B: u32 = 42;
14738      - const C: u32 = 42;
14739      + const C: u32 = 43ˇ
14740        const D: u32 = 42;
14741
14742
14743        fn main() {
14744            println!("hello");
14745
14746            println!("world");
14747        }"#
14748        .unindent(),
14749    );
14750
14751    cx.update_editor(|editor, window, cx| {
14752        editor.handle_input("\nnew_line\n", window, cx);
14753    });
14754    executor.run_until_parked();
14755
14756    cx.assert_state_with_diff(
14757        r#"
14758        use some::mod1;
14759        use some::mod2;
14760
14761        const A: u32 = 42;
14762        const B: u32 = 42;
14763      - const C: u32 = 42;
14764      + const C: u32 = 43
14765      + new_line
14766      + ˇ
14767        const D: u32 = 42;
14768
14769
14770        fn main() {
14771            println!("hello");
14772
14773            println!("world");
14774        }"#
14775        .unindent(),
14776    );
14777}
14778
14779#[gpui::test]
14780async fn test_stage_and_unstage_added_file_hunk(
14781    executor: BackgroundExecutor,
14782    cx: &mut TestAppContext,
14783) {
14784    init_test(cx, |_| {});
14785
14786    let mut cx = EditorTestContext::new(cx).await;
14787    cx.update_editor(|editor, _, cx| {
14788        editor.set_expand_all_diff_hunks(cx);
14789    });
14790
14791    let working_copy = r#"
14792            ˇfn main() {
14793                println!("hello, world!");
14794            }
14795        "#
14796    .unindent();
14797
14798    cx.set_state(&working_copy);
14799    executor.run_until_parked();
14800
14801    cx.assert_state_with_diff(
14802        r#"
14803            + ˇfn main() {
14804            +     println!("hello, world!");
14805            + }
14806        "#
14807        .unindent(),
14808    );
14809    cx.assert_index_text(None);
14810
14811    cx.update_editor(|editor, window, cx| {
14812        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14813    });
14814    executor.run_until_parked();
14815    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14816    cx.assert_state_with_diff(
14817        r#"
14818            + ˇfn main() {
14819            +     println!("hello, world!");
14820            + }
14821        "#
14822        .unindent(),
14823    );
14824
14825    cx.update_editor(|editor, window, cx| {
14826        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14827    });
14828    executor.run_until_parked();
14829    cx.assert_index_text(None);
14830}
14831
14832async fn setup_indent_guides_editor(
14833    text: &str,
14834    cx: &mut TestAppContext,
14835) -> (BufferId, EditorTestContext) {
14836    init_test(cx, |_| {});
14837
14838    let mut cx = EditorTestContext::new(cx).await;
14839
14840    let buffer_id = cx.update_editor(|editor, window, cx| {
14841        editor.set_text(text, window, cx);
14842        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14843
14844        buffer_ids[0]
14845    });
14846
14847    (buffer_id, cx)
14848}
14849
14850fn assert_indent_guides(
14851    range: Range<u32>,
14852    expected: Vec<IndentGuide>,
14853    active_indices: Option<Vec<usize>>,
14854    cx: &mut EditorTestContext,
14855) {
14856    let indent_guides = cx.update_editor(|editor, window, cx| {
14857        let snapshot = editor.snapshot(window, cx).display_snapshot;
14858        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14859            editor,
14860            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14861            true,
14862            &snapshot,
14863            cx,
14864        );
14865
14866        indent_guides.sort_by(|a, b| {
14867            a.depth.cmp(&b.depth).then(
14868                a.start_row
14869                    .cmp(&b.start_row)
14870                    .then(a.end_row.cmp(&b.end_row)),
14871            )
14872        });
14873        indent_guides
14874    });
14875
14876    if let Some(expected) = active_indices {
14877        let active_indices = cx.update_editor(|editor, window, cx| {
14878            let snapshot = editor.snapshot(window, cx).display_snapshot;
14879            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14880        });
14881
14882        assert_eq!(
14883            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14884            expected,
14885            "Active indent guide indices do not match"
14886        );
14887    }
14888
14889    assert_eq!(indent_guides, expected, "Indent guides do not match");
14890}
14891
14892fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14893    IndentGuide {
14894        buffer_id,
14895        start_row: MultiBufferRow(start_row),
14896        end_row: MultiBufferRow(end_row),
14897        depth,
14898        tab_size: 4,
14899        settings: IndentGuideSettings {
14900            enabled: true,
14901            line_width: 1,
14902            active_line_width: 1,
14903            ..Default::default()
14904        },
14905    }
14906}
14907
14908#[gpui::test]
14909async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14910    let (buffer_id, mut cx) = setup_indent_guides_editor(
14911        &"
14912    fn main() {
14913        let a = 1;
14914    }"
14915        .unindent(),
14916        cx,
14917    )
14918    .await;
14919
14920    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14921}
14922
14923#[gpui::test]
14924async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14925    let (buffer_id, mut cx) = setup_indent_guides_editor(
14926        &"
14927    fn main() {
14928        let a = 1;
14929        let b = 2;
14930    }"
14931        .unindent(),
14932        cx,
14933    )
14934    .await;
14935
14936    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14937}
14938
14939#[gpui::test]
14940async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14941    let (buffer_id, mut cx) = setup_indent_guides_editor(
14942        &"
14943    fn main() {
14944        let a = 1;
14945        if a == 3 {
14946            let b = 2;
14947        } else {
14948            let c = 3;
14949        }
14950    }"
14951        .unindent(),
14952        cx,
14953    )
14954    .await;
14955
14956    assert_indent_guides(
14957        0..8,
14958        vec![
14959            indent_guide(buffer_id, 1, 6, 0),
14960            indent_guide(buffer_id, 3, 3, 1),
14961            indent_guide(buffer_id, 5, 5, 1),
14962        ],
14963        None,
14964        &mut cx,
14965    );
14966}
14967
14968#[gpui::test]
14969async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14970    let (buffer_id, mut cx) = setup_indent_guides_editor(
14971        &"
14972    fn main() {
14973        let a = 1;
14974            let b = 2;
14975        let c = 3;
14976    }"
14977        .unindent(),
14978        cx,
14979    )
14980    .await;
14981
14982    assert_indent_guides(
14983        0..5,
14984        vec![
14985            indent_guide(buffer_id, 1, 3, 0),
14986            indent_guide(buffer_id, 2, 2, 1),
14987        ],
14988        None,
14989        &mut cx,
14990    );
14991}
14992
14993#[gpui::test]
14994async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14995    let (buffer_id, mut cx) = setup_indent_guides_editor(
14996        &"
14997        fn main() {
14998            let a = 1;
14999
15000            let c = 3;
15001        }"
15002        .unindent(),
15003        cx,
15004    )
15005    .await;
15006
15007    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15008}
15009
15010#[gpui::test]
15011async fn test_indent_guide_complex(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            if a == 3 {
15020                let b = 2;
15021            } else {
15022                let c = 3;
15023            }
15024        }"
15025        .unindent(),
15026        cx,
15027    )
15028    .await;
15029
15030    assert_indent_guides(
15031        0..11,
15032        vec![
15033            indent_guide(buffer_id, 1, 9, 0),
15034            indent_guide(buffer_id, 6, 6, 1),
15035            indent_guide(buffer_id, 8, 8, 1),
15036        ],
15037        None,
15038        &mut cx,
15039    );
15040}
15041
15042#[gpui::test]
15043async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15044    let (buffer_id, mut cx) = setup_indent_guides_editor(
15045        &"
15046        fn main() {
15047            let a = 1;
15048
15049            let c = 3;
15050
15051            if a == 3 {
15052                let b = 2;
15053            } else {
15054                let c = 3;
15055            }
15056        }"
15057        .unindent(),
15058        cx,
15059    )
15060    .await;
15061
15062    assert_indent_guides(
15063        1..11,
15064        vec![
15065            indent_guide(buffer_id, 1, 9, 0),
15066            indent_guide(buffer_id, 6, 6, 1),
15067            indent_guide(buffer_id, 8, 8, 1),
15068        ],
15069        None,
15070        &mut cx,
15071    );
15072}
15073
15074#[gpui::test]
15075async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15076    let (buffer_id, mut cx) = setup_indent_guides_editor(
15077        &"
15078        fn main() {
15079            let a = 1;
15080
15081            let c = 3;
15082
15083            if a == 3 {
15084                let b = 2;
15085            } else {
15086                let c = 3;
15087            }
15088        }"
15089        .unindent(),
15090        cx,
15091    )
15092    .await;
15093
15094    assert_indent_guides(
15095        1..10,
15096        vec![
15097            indent_guide(buffer_id, 1, 9, 0),
15098            indent_guide(buffer_id, 6, 6, 1),
15099            indent_guide(buffer_id, 8, 8, 1),
15100        ],
15101        None,
15102        &mut cx,
15103    );
15104}
15105
15106#[gpui::test]
15107async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15108    let (buffer_id, mut cx) = setup_indent_guides_editor(
15109        &"
15110        block1
15111            block2
15112                block3
15113                    block4
15114            block2
15115        block1
15116        block1"
15117            .unindent(),
15118        cx,
15119    )
15120    .await;
15121
15122    assert_indent_guides(
15123        1..10,
15124        vec![
15125            indent_guide(buffer_id, 1, 4, 0),
15126            indent_guide(buffer_id, 2, 3, 1),
15127            indent_guide(buffer_id, 3, 3, 2),
15128        ],
15129        None,
15130        &mut cx,
15131    );
15132}
15133
15134#[gpui::test]
15135async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15136    let (buffer_id, mut cx) = setup_indent_guides_editor(
15137        &"
15138        block1
15139            block2
15140                block3
15141
15142        block1
15143        block1"
15144            .unindent(),
15145        cx,
15146    )
15147    .await;
15148
15149    assert_indent_guides(
15150        0..6,
15151        vec![
15152            indent_guide(buffer_id, 1, 2, 0),
15153            indent_guide(buffer_id, 2, 2, 1),
15154        ],
15155        None,
15156        &mut cx,
15157    );
15158}
15159
15160#[gpui::test]
15161async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15162    let (buffer_id, mut cx) = setup_indent_guides_editor(
15163        &"
15164        block1
15165
15166
15167
15168            block2
15169        "
15170        .unindent(),
15171        cx,
15172    )
15173    .await;
15174
15175    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15176}
15177
15178#[gpui::test]
15179async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15180    let (buffer_id, mut cx) = setup_indent_guides_editor(
15181        &"
15182        def a:
15183        \tb = 3
15184        \tif True:
15185        \t\tc = 4
15186        \t\td = 5
15187        \tprint(b)
15188        "
15189        .unindent(),
15190        cx,
15191    )
15192    .await;
15193
15194    assert_indent_guides(
15195        0..6,
15196        vec![
15197            indent_guide(buffer_id, 1, 6, 0),
15198            indent_guide(buffer_id, 3, 4, 1),
15199        ],
15200        None,
15201        &mut cx,
15202    );
15203}
15204
15205#[gpui::test]
15206async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15207    let (buffer_id, mut cx) = setup_indent_guides_editor(
15208        &"
15209    fn main() {
15210        let a = 1;
15211    }"
15212        .unindent(),
15213        cx,
15214    )
15215    .await;
15216
15217    cx.update_editor(|editor, window, cx| {
15218        editor.change_selections(None, window, cx, |s| {
15219            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15220        });
15221    });
15222
15223    assert_indent_guides(
15224        0..3,
15225        vec![indent_guide(buffer_id, 1, 1, 0)],
15226        Some(vec![0]),
15227        &mut cx,
15228    );
15229}
15230
15231#[gpui::test]
15232async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15233    let (buffer_id, mut cx) = setup_indent_guides_editor(
15234        &"
15235    fn main() {
15236        if 1 == 2 {
15237            let a = 1;
15238        }
15239    }"
15240        .unindent(),
15241        cx,
15242    )
15243    .await;
15244
15245    cx.update_editor(|editor, window, cx| {
15246        editor.change_selections(None, window, cx, |s| {
15247            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15248        });
15249    });
15250
15251    assert_indent_guides(
15252        0..4,
15253        vec![
15254            indent_guide(buffer_id, 1, 3, 0),
15255            indent_guide(buffer_id, 2, 2, 1),
15256        ],
15257        Some(vec![1]),
15258        &mut cx,
15259    );
15260
15261    cx.update_editor(|editor, window, cx| {
15262        editor.change_selections(None, window, cx, |s| {
15263            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15264        });
15265    });
15266
15267    assert_indent_guides(
15268        0..4,
15269        vec![
15270            indent_guide(buffer_id, 1, 3, 0),
15271            indent_guide(buffer_id, 2, 2, 1),
15272        ],
15273        Some(vec![1]),
15274        &mut cx,
15275    );
15276
15277    cx.update_editor(|editor, window, cx| {
15278        editor.change_selections(None, window, cx, |s| {
15279            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15280        });
15281    });
15282
15283    assert_indent_guides(
15284        0..4,
15285        vec![
15286            indent_guide(buffer_id, 1, 3, 0),
15287            indent_guide(buffer_id, 2, 2, 1),
15288        ],
15289        Some(vec![0]),
15290        &mut cx,
15291    );
15292}
15293
15294#[gpui::test]
15295async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15296    let (buffer_id, mut cx) = setup_indent_guides_editor(
15297        &"
15298    fn main() {
15299        let a = 1;
15300
15301        let b = 2;
15302    }"
15303        .unindent(),
15304        cx,
15305    )
15306    .await;
15307
15308    cx.update_editor(|editor, window, cx| {
15309        editor.change_selections(None, window, cx, |s| {
15310            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15311        });
15312    });
15313
15314    assert_indent_guides(
15315        0..5,
15316        vec![indent_guide(buffer_id, 1, 3, 0)],
15317        Some(vec![0]),
15318        &mut cx,
15319    );
15320}
15321
15322#[gpui::test]
15323async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15324    let (buffer_id, mut cx) = setup_indent_guides_editor(
15325        &"
15326    def m:
15327        a = 1
15328        pass"
15329            .unindent(),
15330        cx,
15331    )
15332    .await;
15333
15334    cx.update_editor(|editor, window, cx| {
15335        editor.change_selections(None, window, cx, |s| {
15336            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15337        });
15338    });
15339
15340    assert_indent_guides(
15341        0..3,
15342        vec![indent_guide(buffer_id, 1, 2, 0)],
15343        Some(vec![0]),
15344        &mut cx,
15345    );
15346}
15347
15348#[gpui::test]
15349async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15350    init_test(cx, |_| {});
15351    let mut cx = EditorTestContext::new(cx).await;
15352    let text = indoc! {
15353        "
15354        impl A {
15355            fn b() {
15356                0;
15357                3;
15358                5;
15359                6;
15360                7;
15361            }
15362        }
15363        "
15364    };
15365    let base_text = indoc! {
15366        "
15367        impl A {
15368            fn b() {
15369                0;
15370                1;
15371                2;
15372                3;
15373                4;
15374            }
15375            fn c() {
15376                5;
15377                6;
15378                7;
15379            }
15380        }
15381        "
15382    };
15383
15384    cx.update_editor(|editor, window, cx| {
15385        editor.set_text(text, window, cx);
15386
15387        editor.buffer().update(cx, |multibuffer, cx| {
15388            let buffer = multibuffer.as_singleton().unwrap();
15389            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15390
15391            multibuffer.set_all_diff_hunks_expanded(cx);
15392            multibuffer.add_diff(diff, cx);
15393
15394            buffer.read(cx).remote_id()
15395        })
15396    });
15397    cx.run_until_parked();
15398
15399    cx.assert_state_with_diff(
15400        indoc! { "
15401          impl A {
15402              fn b() {
15403                  0;
15404        -         1;
15405        -         2;
15406                  3;
15407        -         4;
15408        -     }
15409        -     fn c() {
15410                  5;
15411                  6;
15412                  7;
15413              }
15414          }
15415          ˇ"
15416        }
15417        .to_string(),
15418    );
15419
15420    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15421        editor
15422            .snapshot(window, cx)
15423            .buffer_snapshot
15424            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15425            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15426            .collect::<Vec<_>>()
15427    });
15428    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15429    assert_eq!(
15430        actual_guides,
15431        vec![
15432            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15433            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15434            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15435        ]
15436    );
15437}
15438
15439#[gpui::test]
15440async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15441    init_test(cx, |_| {});
15442    let mut cx = EditorTestContext::new(cx).await;
15443
15444    let diff_base = r#"
15445        a
15446        b
15447        c
15448        "#
15449    .unindent();
15450
15451    cx.set_state(
15452        &r#"
15453        ˇA
15454        b
15455        C
15456        "#
15457        .unindent(),
15458    );
15459    cx.set_head_text(&diff_base);
15460    cx.update_editor(|editor, window, cx| {
15461        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15462    });
15463    executor.run_until_parked();
15464
15465    let both_hunks_expanded = r#"
15466        - a
15467        + ˇA
15468          b
15469        - c
15470        + C
15471        "#
15472    .unindent();
15473
15474    cx.assert_state_with_diff(both_hunks_expanded.clone());
15475
15476    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15477        let snapshot = editor.snapshot(window, cx);
15478        let hunks = editor
15479            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15480            .collect::<Vec<_>>();
15481        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15482        let buffer_id = hunks[0].buffer_id;
15483        hunks
15484            .into_iter()
15485            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15486            .collect::<Vec<_>>()
15487    });
15488    assert_eq!(hunk_ranges.len(), 2);
15489
15490    cx.update_editor(|editor, _, cx| {
15491        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15492    });
15493    executor.run_until_parked();
15494
15495    let second_hunk_expanded = r#"
15496          ˇA
15497          b
15498        - c
15499        + C
15500        "#
15501    .unindent();
15502
15503    cx.assert_state_with_diff(second_hunk_expanded);
15504
15505    cx.update_editor(|editor, _, cx| {
15506        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15507    });
15508    executor.run_until_parked();
15509
15510    cx.assert_state_with_diff(both_hunks_expanded.clone());
15511
15512    cx.update_editor(|editor, _, cx| {
15513        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15514    });
15515    executor.run_until_parked();
15516
15517    let first_hunk_expanded = r#"
15518        - a
15519        + ˇA
15520          b
15521          C
15522        "#
15523    .unindent();
15524
15525    cx.assert_state_with_diff(first_hunk_expanded);
15526
15527    cx.update_editor(|editor, _, cx| {
15528        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15529    });
15530    executor.run_until_parked();
15531
15532    cx.assert_state_with_diff(both_hunks_expanded);
15533
15534    cx.set_state(
15535        &r#"
15536        ˇA
15537        b
15538        "#
15539        .unindent(),
15540    );
15541    cx.run_until_parked();
15542
15543    // TODO this cursor position seems bad
15544    cx.assert_state_with_diff(
15545        r#"
15546        - ˇa
15547        + A
15548          b
15549        "#
15550        .unindent(),
15551    );
15552
15553    cx.update_editor(|editor, window, cx| {
15554        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15555    });
15556
15557    cx.assert_state_with_diff(
15558        r#"
15559            - ˇa
15560            + A
15561              b
15562            - c
15563            "#
15564        .unindent(),
15565    );
15566
15567    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15568        let snapshot = editor.snapshot(window, cx);
15569        let hunks = editor
15570            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15571            .collect::<Vec<_>>();
15572        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15573        let buffer_id = hunks[0].buffer_id;
15574        hunks
15575            .into_iter()
15576            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15577            .collect::<Vec<_>>()
15578    });
15579    assert_eq!(hunk_ranges.len(), 2);
15580
15581    cx.update_editor(|editor, _, cx| {
15582        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15583    });
15584    executor.run_until_parked();
15585
15586    cx.assert_state_with_diff(
15587        r#"
15588        - ˇa
15589        + A
15590          b
15591        "#
15592        .unindent(),
15593    );
15594}
15595
15596#[gpui::test]
15597async fn test_toggle_deletion_hunk_at_start_of_file(
15598    executor: BackgroundExecutor,
15599    cx: &mut TestAppContext,
15600) {
15601    init_test(cx, |_| {});
15602    let mut cx = EditorTestContext::new(cx).await;
15603
15604    let diff_base = r#"
15605        a
15606        b
15607        c
15608        "#
15609    .unindent();
15610
15611    cx.set_state(
15612        &r#"
15613        ˇb
15614        c
15615        "#
15616        .unindent(),
15617    );
15618    cx.set_head_text(&diff_base);
15619    cx.update_editor(|editor, window, cx| {
15620        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15621    });
15622    executor.run_until_parked();
15623
15624    let hunk_expanded = r#"
15625        - a
15626          ˇb
15627          c
15628        "#
15629    .unindent();
15630
15631    cx.assert_state_with_diff(hunk_expanded.clone());
15632
15633    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15634        let snapshot = editor.snapshot(window, cx);
15635        let hunks = editor
15636            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15637            .collect::<Vec<_>>();
15638        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15639        let buffer_id = hunks[0].buffer_id;
15640        hunks
15641            .into_iter()
15642            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15643            .collect::<Vec<_>>()
15644    });
15645    assert_eq!(hunk_ranges.len(), 1);
15646
15647    cx.update_editor(|editor, _, cx| {
15648        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15649    });
15650    executor.run_until_parked();
15651
15652    let hunk_collapsed = r#"
15653          ˇb
15654          c
15655        "#
15656    .unindent();
15657
15658    cx.assert_state_with_diff(hunk_collapsed);
15659
15660    cx.update_editor(|editor, _, cx| {
15661        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15662    });
15663    executor.run_until_parked();
15664
15665    cx.assert_state_with_diff(hunk_expanded.clone());
15666}
15667
15668#[gpui::test]
15669async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15670    init_test(cx, |_| {});
15671
15672    let fs = FakeFs::new(cx.executor());
15673    fs.insert_tree(
15674        path!("/test"),
15675        json!({
15676            ".git": {},
15677            "file-1": "ONE\n",
15678            "file-2": "TWO\n",
15679            "file-3": "THREE\n",
15680        }),
15681    )
15682    .await;
15683
15684    fs.set_head_for_repo(
15685        path!("/test/.git").as_ref(),
15686        &[
15687            ("file-1".into(), "one\n".into()),
15688            ("file-2".into(), "two\n".into()),
15689            ("file-3".into(), "three\n".into()),
15690        ],
15691    );
15692
15693    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15694    let mut buffers = vec![];
15695    for i in 1..=3 {
15696        let buffer = project
15697            .update(cx, |project, cx| {
15698                let path = format!(path!("/test/file-{}"), i);
15699                project.open_local_buffer(path, cx)
15700            })
15701            .await
15702            .unwrap();
15703        buffers.push(buffer);
15704    }
15705
15706    let multibuffer = cx.new(|cx| {
15707        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15708        multibuffer.set_all_diff_hunks_expanded(cx);
15709        for buffer in &buffers {
15710            let snapshot = buffer.read(cx).snapshot();
15711            multibuffer.set_excerpts_for_path(
15712                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15713                buffer.clone(),
15714                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15715                DEFAULT_MULTIBUFFER_CONTEXT,
15716                cx,
15717            );
15718        }
15719        multibuffer
15720    });
15721
15722    let editor = cx.add_window(|window, cx| {
15723        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15724    });
15725    cx.run_until_parked();
15726
15727    let snapshot = editor
15728        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15729        .unwrap();
15730    let hunks = snapshot
15731        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15732        .map(|hunk| match hunk {
15733            DisplayDiffHunk::Unfolded {
15734                display_row_range, ..
15735            } => display_row_range,
15736            DisplayDiffHunk::Folded { .. } => unreachable!(),
15737        })
15738        .collect::<Vec<_>>();
15739    assert_eq!(
15740        hunks,
15741        [
15742            DisplayRow(2)..DisplayRow(4),
15743            DisplayRow(7)..DisplayRow(9),
15744            DisplayRow(12)..DisplayRow(14),
15745        ]
15746    );
15747}
15748
15749#[gpui::test]
15750async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15751    init_test(cx, |_| {});
15752
15753    let mut cx = EditorTestContext::new(cx).await;
15754    cx.set_head_text(indoc! { "
15755        one
15756        two
15757        three
15758        four
15759        five
15760        "
15761    });
15762    cx.set_index_text(indoc! { "
15763        one
15764        two
15765        three
15766        four
15767        five
15768        "
15769    });
15770    cx.set_state(indoc! {"
15771        one
15772        TWO
15773        ˇTHREE
15774        FOUR
15775        five
15776    "});
15777    cx.run_until_parked();
15778    cx.update_editor(|editor, window, cx| {
15779        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15780    });
15781    cx.run_until_parked();
15782    cx.assert_index_text(Some(indoc! {"
15783        one
15784        TWO
15785        THREE
15786        FOUR
15787        five
15788    "}));
15789    cx.set_state(indoc! { "
15790        one
15791        TWO
15792        ˇTHREE-HUNDRED
15793        FOUR
15794        five
15795    "});
15796    cx.run_until_parked();
15797    cx.update_editor(|editor, window, cx| {
15798        let snapshot = editor.snapshot(window, cx);
15799        let hunks = editor
15800            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15801            .collect::<Vec<_>>();
15802        assert_eq!(hunks.len(), 1);
15803        assert_eq!(
15804            hunks[0].status(),
15805            DiffHunkStatus {
15806                kind: DiffHunkStatusKind::Modified,
15807                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15808            }
15809        );
15810
15811        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15812    });
15813    cx.run_until_parked();
15814    cx.assert_index_text(Some(indoc! {"
15815        one
15816        TWO
15817        THREE-HUNDRED
15818        FOUR
15819        five
15820    "}));
15821}
15822
15823#[gpui::test]
15824fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15825    init_test(cx, |_| {});
15826
15827    let editor = cx.add_window(|window, cx| {
15828        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15829        build_editor(buffer, window, cx)
15830    });
15831
15832    let render_args = Arc::new(Mutex::new(None));
15833    let snapshot = editor
15834        .update(cx, |editor, window, cx| {
15835            let snapshot = editor.buffer().read(cx).snapshot(cx);
15836            let range =
15837                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15838
15839            struct RenderArgs {
15840                row: MultiBufferRow,
15841                folded: bool,
15842                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15843            }
15844
15845            let crease = Crease::inline(
15846                range,
15847                FoldPlaceholder::test(),
15848                {
15849                    let toggle_callback = render_args.clone();
15850                    move |row, folded, callback, _window, _cx| {
15851                        *toggle_callback.lock() = Some(RenderArgs {
15852                            row,
15853                            folded,
15854                            callback,
15855                        });
15856                        div()
15857                    }
15858                },
15859                |_row, _folded, _window, _cx| div(),
15860            );
15861
15862            editor.insert_creases(Some(crease), cx);
15863            let snapshot = editor.snapshot(window, cx);
15864            let _div = snapshot.render_crease_toggle(
15865                MultiBufferRow(1),
15866                false,
15867                cx.entity().clone(),
15868                window,
15869                cx,
15870            );
15871            snapshot
15872        })
15873        .unwrap();
15874
15875    let render_args = render_args.lock().take().unwrap();
15876    assert_eq!(render_args.row, MultiBufferRow(1));
15877    assert!(!render_args.folded);
15878    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15879
15880    cx.update_window(*editor, |_, window, cx| {
15881        (render_args.callback)(true, window, cx)
15882    })
15883    .unwrap();
15884    let snapshot = editor
15885        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15886        .unwrap();
15887    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15888
15889    cx.update_window(*editor, |_, window, cx| {
15890        (render_args.callback)(false, window, cx)
15891    })
15892    .unwrap();
15893    let snapshot = editor
15894        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15895        .unwrap();
15896    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15897}
15898
15899#[gpui::test]
15900async fn test_input_text(cx: &mut TestAppContext) {
15901    init_test(cx, |_| {});
15902    let mut cx = EditorTestContext::new(cx).await;
15903
15904    cx.set_state(
15905        &r#"ˇone
15906        two
15907
15908        three
15909        fourˇ
15910        five
15911
15912        siˇx"#
15913            .unindent(),
15914    );
15915
15916    cx.dispatch_action(HandleInput(String::new()));
15917    cx.assert_editor_state(
15918        &r#"ˇone
15919        two
15920
15921        three
15922        fourˇ
15923        five
15924
15925        siˇx"#
15926            .unindent(),
15927    );
15928
15929    cx.dispatch_action(HandleInput("AAAA".to_string()));
15930    cx.assert_editor_state(
15931        &r#"AAAAˇone
15932        two
15933
15934        three
15935        fourAAAAˇ
15936        five
15937
15938        siAAAAˇx"#
15939            .unindent(),
15940    );
15941}
15942
15943#[gpui::test]
15944async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15945    init_test(cx, |_| {});
15946
15947    let mut cx = EditorTestContext::new(cx).await;
15948    cx.set_state(
15949        r#"let foo = 1;
15950let foo = 2;
15951let foo = 3;
15952let fooˇ = 4;
15953let foo = 5;
15954let foo = 6;
15955let foo = 7;
15956let foo = 8;
15957let foo = 9;
15958let foo = 10;
15959let foo = 11;
15960let foo = 12;
15961let foo = 13;
15962let foo = 14;
15963let foo = 15;"#,
15964    );
15965
15966    cx.update_editor(|e, window, cx| {
15967        assert_eq!(
15968            e.next_scroll_position,
15969            NextScrollCursorCenterTopBottom::Center,
15970            "Default next scroll direction is center",
15971        );
15972
15973        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15974        assert_eq!(
15975            e.next_scroll_position,
15976            NextScrollCursorCenterTopBottom::Top,
15977            "After center, next scroll direction should be top",
15978        );
15979
15980        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15981        assert_eq!(
15982            e.next_scroll_position,
15983            NextScrollCursorCenterTopBottom::Bottom,
15984            "After top, next scroll direction should be bottom",
15985        );
15986
15987        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15988        assert_eq!(
15989            e.next_scroll_position,
15990            NextScrollCursorCenterTopBottom::Center,
15991            "After bottom, scrolling should start over",
15992        );
15993
15994        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15995        assert_eq!(
15996            e.next_scroll_position,
15997            NextScrollCursorCenterTopBottom::Top,
15998            "Scrolling continues if retriggered fast enough"
15999        );
16000    });
16001
16002    cx.executor()
16003        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16004    cx.executor().run_until_parked();
16005    cx.update_editor(|e, _, _| {
16006        assert_eq!(
16007            e.next_scroll_position,
16008            NextScrollCursorCenterTopBottom::Center,
16009            "If scrolling is not triggered fast enough, it should reset"
16010        );
16011    });
16012}
16013
16014#[gpui::test]
16015async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16016    init_test(cx, |_| {});
16017    let mut cx = EditorLspTestContext::new_rust(
16018        lsp::ServerCapabilities {
16019            definition_provider: Some(lsp::OneOf::Left(true)),
16020            references_provider: Some(lsp::OneOf::Left(true)),
16021            ..lsp::ServerCapabilities::default()
16022        },
16023        cx,
16024    )
16025    .await;
16026
16027    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16028        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16029            move |params, _| async move {
16030                if empty_go_to_definition {
16031                    Ok(None)
16032                } else {
16033                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16034                        uri: params.text_document_position_params.text_document.uri,
16035                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16036                    })))
16037                }
16038            },
16039        );
16040        let references =
16041            cx.lsp
16042                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16043                    Ok(Some(vec![lsp::Location {
16044                        uri: params.text_document_position.text_document.uri,
16045                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16046                    }]))
16047                });
16048        (go_to_definition, references)
16049    };
16050
16051    cx.set_state(
16052        &r#"fn one() {
16053            let mut a = ˇtwo();
16054        }
16055
16056        fn two() {}"#
16057            .unindent(),
16058    );
16059    set_up_lsp_handlers(false, &mut cx);
16060    let navigated = cx
16061        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16062        .await
16063        .expect("Failed to navigate to definition");
16064    assert_eq!(
16065        navigated,
16066        Navigated::Yes,
16067        "Should have navigated to definition from the GetDefinition response"
16068    );
16069    cx.assert_editor_state(
16070        &r#"fn one() {
16071            let mut a = two();
16072        }
16073
16074        fn «twoˇ»() {}"#
16075            .unindent(),
16076    );
16077
16078    let editors = cx.update_workspace(|workspace, _, cx| {
16079        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16080    });
16081    cx.update_editor(|_, _, test_editor_cx| {
16082        assert_eq!(
16083            editors.len(),
16084            1,
16085            "Initially, only one, test, editor should be open in the workspace"
16086        );
16087        assert_eq!(
16088            test_editor_cx.entity(),
16089            editors.last().expect("Asserted len is 1").clone()
16090        );
16091    });
16092
16093    set_up_lsp_handlers(true, &mut cx);
16094    let navigated = cx
16095        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16096        .await
16097        .expect("Failed to navigate to lookup references");
16098    assert_eq!(
16099        navigated,
16100        Navigated::Yes,
16101        "Should have navigated to references as a fallback after empty GoToDefinition response"
16102    );
16103    // We should not change the selections in the existing file,
16104    // if opening another milti buffer with the references
16105    cx.assert_editor_state(
16106        &r#"fn one() {
16107            let mut a = two();
16108        }
16109
16110        fn «twoˇ»() {}"#
16111            .unindent(),
16112    );
16113    let editors = cx.update_workspace(|workspace, _, cx| {
16114        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16115    });
16116    cx.update_editor(|_, _, test_editor_cx| {
16117        assert_eq!(
16118            editors.len(),
16119            2,
16120            "After falling back to references search, we open a new editor with the results"
16121        );
16122        let references_fallback_text = editors
16123            .into_iter()
16124            .find(|new_editor| *new_editor != test_editor_cx.entity())
16125            .expect("Should have one non-test editor now")
16126            .read(test_editor_cx)
16127            .text(test_editor_cx);
16128        assert_eq!(
16129            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16130            "Should use the range from the references response and not the GoToDefinition one"
16131        );
16132    });
16133}
16134
16135#[gpui::test]
16136async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16137    init_test(cx, |_| {});
16138
16139    let language = Arc::new(Language::new(
16140        LanguageConfig::default(),
16141        Some(tree_sitter_rust::LANGUAGE.into()),
16142    ));
16143
16144    let text = r#"
16145        #[cfg(test)]
16146        mod tests() {
16147            #[test]
16148            fn runnable_1() {
16149                let a = 1;
16150            }
16151
16152            #[test]
16153            fn runnable_2() {
16154                let a = 1;
16155                let b = 2;
16156            }
16157        }
16158    "#
16159    .unindent();
16160
16161    let fs = FakeFs::new(cx.executor());
16162    fs.insert_file("/file.rs", Default::default()).await;
16163
16164    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16165    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16166    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16167    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16168    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16169
16170    let editor = cx.new_window_entity(|window, cx| {
16171        Editor::new(
16172            EditorMode::Full,
16173            multi_buffer,
16174            Some(project.clone()),
16175            window,
16176            cx,
16177        )
16178    });
16179
16180    editor.update_in(cx, |editor, window, cx| {
16181        let snapshot = editor.buffer().read(cx).snapshot(cx);
16182        editor.tasks.insert(
16183            (buffer.read(cx).remote_id(), 3),
16184            RunnableTasks {
16185                templates: vec![],
16186                offset: snapshot.anchor_before(43),
16187                column: 0,
16188                extra_variables: HashMap::default(),
16189                context_range: BufferOffset(43)..BufferOffset(85),
16190            },
16191        );
16192        editor.tasks.insert(
16193            (buffer.read(cx).remote_id(), 8),
16194            RunnableTasks {
16195                templates: vec![],
16196                offset: snapshot.anchor_before(86),
16197                column: 0,
16198                extra_variables: HashMap::default(),
16199                context_range: BufferOffset(86)..BufferOffset(191),
16200            },
16201        );
16202
16203        // Test finding task when cursor is inside function body
16204        editor.change_selections(None, window, cx, |s| {
16205            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16206        });
16207        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16208        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16209
16210        // Test finding task when cursor is on function name
16211        editor.change_selections(None, window, cx, |s| {
16212            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16213        });
16214        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16215        assert_eq!(row, 8, "Should find task when cursor is on function name");
16216    });
16217}
16218
16219#[gpui::test]
16220async fn test_folding_buffers(cx: &mut TestAppContext) {
16221    init_test(cx, |_| {});
16222
16223    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16224    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16225    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16226
16227    let fs = FakeFs::new(cx.executor());
16228    fs.insert_tree(
16229        path!("/a"),
16230        json!({
16231            "first.rs": sample_text_1,
16232            "second.rs": sample_text_2,
16233            "third.rs": sample_text_3,
16234        }),
16235    )
16236    .await;
16237    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16238    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16239    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16240    let worktree = project.update(cx, |project, cx| {
16241        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16242        assert_eq!(worktrees.len(), 1);
16243        worktrees.pop().unwrap()
16244    });
16245    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16246
16247    let buffer_1 = project
16248        .update(cx, |project, cx| {
16249            project.open_buffer((worktree_id, "first.rs"), cx)
16250        })
16251        .await
16252        .unwrap();
16253    let buffer_2 = project
16254        .update(cx, |project, cx| {
16255            project.open_buffer((worktree_id, "second.rs"), cx)
16256        })
16257        .await
16258        .unwrap();
16259    let buffer_3 = project
16260        .update(cx, |project, cx| {
16261            project.open_buffer((worktree_id, "third.rs"), cx)
16262        })
16263        .await
16264        .unwrap();
16265
16266    let multi_buffer = cx.new(|cx| {
16267        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16268        multi_buffer.push_excerpts(
16269            buffer_1.clone(),
16270            [
16271                ExcerptRange {
16272                    context: Point::new(0, 0)..Point::new(3, 0),
16273                    primary: None,
16274                },
16275                ExcerptRange {
16276                    context: Point::new(5, 0)..Point::new(7, 0),
16277                    primary: None,
16278                },
16279                ExcerptRange {
16280                    context: Point::new(9, 0)..Point::new(10, 4),
16281                    primary: None,
16282                },
16283            ],
16284            cx,
16285        );
16286        multi_buffer.push_excerpts(
16287            buffer_2.clone(),
16288            [
16289                ExcerptRange {
16290                    context: Point::new(0, 0)..Point::new(3, 0),
16291                    primary: None,
16292                },
16293                ExcerptRange {
16294                    context: Point::new(5, 0)..Point::new(7, 0),
16295                    primary: None,
16296                },
16297                ExcerptRange {
16298                    context: Point::new(9, 0)..Point::new(10, 4),
16299                    primary: None,
16300                },
16301            ],
16302            cx,
16303        );
16304        multi_buffer.push_excerpts(
16305            buffer_3.clone(),
16306            [
16307                ExcerptRange {
16308                    context: Point::new(0, 0)..Point::new(3, 0),
16309                    primary: None,
16310                },
16311                ExcerptRange {
16312                    context: Point::new(5, 0)..Point::new(7, 0),
16313                    primary: None,
16314                },
16315                ExcerptRange {
16316                    context: Point::new(9, 0)..Point::new(10, 4),
16317                    primary: None,
16318                },
16319            ],
16320            cx,
16321        );
16322        multi_buffer
16323    });
16324    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16325        Editor::new(
16326            EditorMode::Full,
16327            multi_buffer.clone(),
16328            Some(project.clone()),
16329            window,
16330            cx,
16331        )
16332    });
16333
16334    assert_eq!(
16335        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16336        "\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",
16337    );
16338
16339    multi_buffer_editor.update(cx, |editor, cx| {
16340        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16341    });
16342    assert_eq!(
16343        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16344        "\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",
16345        "After folding the first buffer, its text should not be displayed"
16346    );
16347
16348    multi_buffer_editor.update(cx, |editor, cx| {
16349        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16350    });
16351    assert_eq!(
16352        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16353        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16354        "After folding the second buffer, its text should not be displayed"
16355    );
16356
16357    multi_buffer_editor.update(cx, |editor, cx| {
16358        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16359    });
16360    assert_eq!(
16361        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16362        "\n\n\n\n\n",
16363        "After folding the third buffer, its text should not be displayed"
16364    );
16365
16366    // Emulate selection inside the fold logic, that should work
16367    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16368        editor
16369            .snapshot(window, cx)
16370            .next_line_boundary(Point::new(0, 4));
16371    });
16372
16373    multi_buffer_editor.update(cx, |editor, cx| {
16374        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16375    });
16376    assert_eq!(
16377        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16378        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16379        "After unfolding the second buffer, its text should be displayed"
16380    );
16381
16382    // Typing inside of buffer 1 causes that buffer to be unfolded.
16383    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16384        assert_eq!(
16385            multi_buffer
16386                .read(cx)
16387                .snapshot(cx)
16388                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16389                .collect::<String>(),
16390            "bbbb"
16391        );
16392        editor.change_selections(None, window, cx, |selections| {
16393            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16394        });
16395        editor.handle_input("B", window, cx);
16396    });
16397
16398    assert_eq!(
16399        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16400        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16401        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16402    );
16403
16404    multi_buffer_editor.update(cx, |editor, cx| {
16405        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16406    });
16407    assert_eq!(
16408        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16409        "\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",
16410        "After unfolding the all buffers, all original text should be displayed"
16411    );
16412}
16413
16414#[gpui::test]
16415async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16416    init_test(cx, |_| {});
16417
16418    let sample_text_1 = "1111\n2222\n3333".to_string();
16419    let sample_text_2 = "4444\n5555\n6666".to_string();
16420    let sample_text_3 = "7777\n8888\n9999".to_string();
16421
16422    let fs = FakeFs::new(cx.executor());
16423    fs.insert_tree(
16424        path!("/a"),
16425        json!({
16426            "first.rs": sample_text_1,
16427            "second.rs": sample_text_2,
16428            "third.rs": sample_text_3,
16429        }),
16430    )
16431    .await;
16432    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16433    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16434    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16435    let worktree = project.update(cx, |project, cx| {
16436        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16437        assert_eq!(worktrees.len(), 1);
16438        worktrees.pop().unwrap()
16439    });
16440    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16441
16442    let buffer_1 = project
16443        .update(cx, |project, cx| {
16444            project.open_buffer((worktree_id, "first.rs"), cx)
16445        })
16446        .await
16447        .unwrap();
16448    let buffer_2 = project
16449        .update(cx, |project, cx| {
16450            project.open_buffer((worktree_id, "second.rs"), cx)
16451        })
16452        .await
16453        .unwrap();
16454    let buffer_3 = project
16455        .update(cx, |project, cx| {
16456            project.open_buffer((worktree_id, "third.rs"), cx)
16457        })
16458        .await
16459        .unwrap();
16460
16461    let multi_buffer = cx.new(|cx| {
16462        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16463        multi_buffer.push_excerpts(
16464            buffer_1.clone(),
16465            [ExcerptRange {
16466                context: Point::new(0, 0)..Point::new(3, 0),
16467                primary: None,
16468            }],
16469            cx,
16470        );
16471        multi_buffer.push_excerpts(
16472            buffer_2.clone(),
16473            [ExcerptRange {
16474                context: Point::new(0, 0)..Point::new(3, 0),
16475                primary: None,
16476            }],
16477            cx,
16478        );
16479        multi_buffer.push_excerpts(
16480            buffer_3.clone(),
16481            [ExcerptRange {
16482                context: Point::new(0, 0)..Point::new(3, 0),
16483                primary: None,
16484            }],
16485            cx,
16486        );
16487        multi_buffer
16488    });
16489
16490    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16491        Editor::new(
16492            EditorMode::Full,
16493            multi_buffer,
16494            Some(project.clone()),
16495            window,
16496            cx,
16497        )
16498    });
16499
16500    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16501    assert_eq!(
16502        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16503        full_text,
16504    );
16505
16506    multi_buffer_editor.update(cx, |editor, cx| {
16507        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16508    });
16509    assert_eq!(
16510        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16511        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16512        "After folding the first buffer, its text should not be displayed"
16513    );
16514
16515    multi_buffer_editor.update(cx, |editor, cx| {
16516        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16517    });
16518
16519    assert_eq!(
16520        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16521        "\n\n\n\n\n\n7777\n8888\n9999",
16522        "After folding the second buffer, its text should not be displayed"
16523    );
16524
16525    multi_buffer_editor.update(cx, |editor, cx| {
16526        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16527    });
16528    assert_eq!(
16529        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16530        "\n\n\n\n\n",
16531        "After folding the third buffer, its text should not be displayed"
16532    );
16533
16534    multi_buffer_editor.update(cx, |editor, cx| {
16535        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16536    });
16537    assert_eq!(
16538        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16539        "\n\n\n\n4444\n5555\n6666\n\n",
16540        "After unfolding the second buffer, its text should be displayed"
16541    );
16542
16543    multi_buffer_editor.update(cx, |editor, cx| {
16544        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16545    });
16546    assert_eq!(
16547        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16548        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16549        "After unfolding the first buffer, its text should be displayed"
16550    );
16551
16552    multi_buffer_editor.update(cx, |editor, cx| {
16553        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16554    });
16555    assert_eq!(
16556        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16557        full_text,
16558        "After unfolding all buffers, all original text should be displayed"
16559    );
16560}
16561
16562#[gpui::test]
16563async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16564    init_test(cx, |_| {});
16565
16566    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16567
16568    let fs = FakeFs::new(cx.executor());
16569    fs.insert_tree(
16570        path!("/a"),
16571        json!({
16572            "main.rs": sample_text,
16573        }),
16574    )
16575    .await;
16576    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16577    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16578    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16579    let worktree = project.update(cx, |project, cx| {
16580        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16581        assert_eq!(worktrees.len(), 1);
16582        worktrees.pop().unwrap()
16583    });
16584    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16585
16586    let buffer_1 = project
16587        .update(cx, |project, cx| {
16588            project.open_buffer((worktree_id, "main.rs"), cx)
16589        })
16590        .await
16591        .unwrap();
16592
16593    let multi_buffer = cx.new(|cx| {
16594        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16595        multi_buffer.push_excerpts(
16596            buffer_1.clone(),
16597            [ExcerptRange {
16598                context: Point::new(0, 0)
16599                    ..Point::new(
16600                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16601                        0,
16602                    ),
16603                primary: None,
16604            }],
16605            cx,
16606        );
16607        multi_buffer
16608    });
16609    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16610        Editor::new(
16611            EditorMode::Full,
16612            multi_buffer,
16613            Some(project.clone()),
16614            window,
16615            cx,
16616        )
16617    });
16618
16619    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16620    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16621        enum TestHighlight {}
16622        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16623        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16624        editor.highlight_text::<TestHighlight>(
16625            vec![highlight_range.clone()],
16626            HighlightStyle::color(Hsla::green()),
16627            cx,
16628        );
16629        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16630    });
16631
16632    let full_text = format!("\n\n{sample_text}");
16633    assert_eq!(
16634        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16635        full_text,
16636    );
16637}
16638
16639#[gpui::test]
16640async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16641    init_test(cx, |_| {});
16642    cx.update(|cx| {
16643        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16644            "keymaps/default-linux.json",
16645            cx,
16646        )
16647        .unwrap();
16648        cx.bind_keys(default_key_bindings);
16649    });
16650
16651    let (editor, cx) = cx.add_window_view(|window, cx| {
16652        let multi_buffer = MultiBuffer::build_multi(
16653            [
16654                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16655                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16656                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16657                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16658            ],
16659            cx,
16660        );
16661        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16662
16663        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16664        // fold all but the second buffer, so that we test navigating between two
16665        // adjacent folded buffers, as well as folded buffers at the start and
16666        // end the multibuffer
16667        editor.fold_buffer(buffer_ids[0], cx);
16668        editor.fold_buffer(buffer_ids[2], cx);
16669        editor.fold_buffer(buffer_ids[3], cx);
16670
16671        editor
16672    });
16673    cx.simulate_resize(size(px(1000.), px(1000.)));
16674
16675    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16676    cx.assert_excerpts_with_selections(indoc! {"
16677        [EXCERPT]
16678        ˇ[FOLDED]
16679        [EXCERPT]
16680        a1
16681        b1
16682        [EXCERPT]
16683        [FOLDED]
16684        [EXCERPT]
16685        [FOLDED]
16686        "
16687    });
16688    cx.simulate_keystroke("down");
16689    cx.assert_excerpts_with_selections(indoc! {"
16690        [EXCERPT]
16691        [FOLDED]
16692        [EXCERPT]
16693        ˇa1
16694        b1
16695        [EXCERPT]
16696        [FOLDED]
16697        [EXCERPT]
16698        [FOLDED]
16699        "
16700    });
16701    cx.simulate_keystroke("down");
16702    cx.assert_excerpts_with_selections(indoc! {"
16703        [EXCERPT]
16704        [FOLDED]
16705        [EXCERPT]
16706        a1
16707        ˇb1
16708        [EXCERPT]
16709        [FOLDED]
16710        [EXCERPT]
16711        [FOLDED]
16712        "
16713    });
16714    cx.simulate_keystroke("down");
16715    cx.assert_excerpts_with_selections(indoc! {"
16716        [EXCERPT]
16717        [FOLDED]
16718        [EXCERPT]
16719        a1
16720        b1
16721        ˇ[EXCERPT]
16722        [FOLDED]
16723        [EXCERPT]
16724        [FOLDED]
16725        "
16726    });
16727    cx.simulate_keystroke("down");
16728    cx.assert_excerpts_with_selections(indoc! {"
16729        [EXCERPT]
16730        [FOLDED]
16731        [EXCERPT]
16732        a1
16733        b1
16734        [EXCERPT]
16735        ˇ[FOLDED]
16736        [EXCERPT]
16737        [FOLDED]
16738        "
16739    });
16740    for _ in 0..5 {
16741        cx.simulate_keystroke("down");
16742        cx.assert_excerpts_with_selections(indoc! {"
16743            [EXCERPT]
16744            [FOLDED]
16745            [EXCERPT]
16746            a1
16747            b1
16748            [EXCERPT]
16749            [FOLDED]
16750            [EXCERPT]
16751            ˇ[FOLDED]
16752            "
16753        });
16754    }
16755
16756    cx.simulate_keystroke("up");
16757    cx.assert_excerpts_with_selections(indoc! {"
16758        [EXCERPT]
16759        [FOLDED]
16760        [EXCERPT]
16761        a1
16762        b1
16763        [EXCERPT]
16764        ˇ[FOLDED]
16765        [EXCERPT]
16766        [FOLDED]
16767        "
16768    });
16769    cx.simulate_keystroke("up");
16770    cx.assert_excerpts_with_selections(indoc! {"
16771        [EXCERPT]
16772        [FOLDED]
16773        [EXCERPT]
16774        a1
16775        b1
16776        ˇ[EXCERPT]
16777        [FOLDED]
16778        [EXCERPT]
16779        [FOLDED]
16780        "
16781    });
16782    cx.simulate_keystroke("up");
16783    cx.assert_excerpts_with_selections(indoc! {"
16784        [EXCERPT]
16785        [FOLDED]
16786        [EXCERPT]
16787        a1
16788        ˇb1
16789        [EXCERPT]
16790        [FOLDED]
16791        [EXCERPT]
16792        [FOLDED]
16793        "
16794    });
16795    cx.simulate_keystroke("up");
16796    cx.assert_excerpts_with_selections(indoc! {"
16797        [EXCERPT]
16798        [FOLDED]
16799        [EXCERPT]
16800        ˇa1
16801        b1
16802        [EXCERPT]
16803        [FOLDED]
16804        [EXCERPT]
16805        [FOLDED]
16806        "
16807    });
16808    for _ in 0..5 {
16809        cx.simulate_keystroke("up");
16810        cx.assert_excerpts_with_selections(indoc! {"
16811            [EXCERPT]
16812            ˇ[FOLDED]
16813            [EXCERPT]
16814            a1
16815            b1
16816            [EXCERPT]
16817            [FOLDED]
16818            [EXCERPT]
16819            [FOLDED]
16820            "
16821        });
16822    }
16823}
16824
16825#[gpui::test]
16826async fn test_inline_completion_text(cx: &mut TestAppContext) {
16827    init_test(cx, |_| {});
16828
16829    // Simple insertion
16830    assert_highlighted_edits(
16831        "Hello, world!",
16832        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16833        true,
16834        cx,
16835        |highlighted_edits, cx| {
16836            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16837            assert_eq!(highlighted_edits.highlights.len(), 1);
16838            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16839            assert_eq!(
16840                highlighted_edits.highlights[0].1.background_color,
16841                Some(cx.theme().status().created_background)
16842            );
16843        },
16844    )
16845    .await;
16846
16847    // Replacement
16848    assert_highlighted_edits(
16849        "This is a test.",
16850        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16851        false,
16852        cx,
16853        |highlighted_edits, cx| {
16854            assert_eq!(highlighted_edits.text, "That is a test.");
16855            assert_eq!(highlighted_edits.highlights.len(), 1);
16856            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16857            assert_eq!(
16858                highlighted_edits.highlights[0].1.background_color,
16859                Some(cx.theme().status().created_background)
16860            );
16861        },
16862    )
16863    .await;
16864
16865    // Multiple edits
16866    assert_highlighted_edits(
16867        "Hello, world!",
16868        vec![
16869            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16870            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16871        ],
16872        false,
16873        cx,
16874        |highlighted_edits, cx| {
16875            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16876            assert_eq!(highlighted_edits.highlights.len(), 2);
16877            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16878            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16879            assert_eq!(
16880                highlighted_edits.highlights[0].1.background_color,
16881                Some(cx.theme().status().created_background)
16882            );
16883            assert_eq!(
16884                highlighted_edits.highlights[1].1.background_color,
16885                Some(cx.theme().status().created_background)
16886            );
16887        },
16888    )
16889    .await;
16890
16891    // Multiple lines with edits
16892    assert_highlighted_edits(
16893        "First line\nSecond line\nThird line\nFourth line",
16894        vec![
16895            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16896            (
16897                Point::new(2, 0)..Point::new(2, 10),
16898                "New third line".to_string(),
16899            ),
16900            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16901        ],
16902        false,
16903        cx,
16904        |highlighted_edits, cx| {
16905            assert_eq!(
16906                highlighted_edits.text,
16907                "Second modified\nNew third line\nFourth updated line"
16908            );
16909            assert_eq!(highlighted_edits.highlights.len(), 3);
16910            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16911            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16912            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16913            for highlight in &highlighted_edits.highlights {
16914                assert_eq!(
16915                    highlight.1.background_color,
16916                    Some(cx.theme().status().created_background)
16917                );
16918            }
16919        },
16920    )
16921    .await;
16922}
16923
16924#[gpui::test]
16925async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16926    init_test(cx, |_| {});
16927
16928    // Deletion
16929    assert_highlighted_edits(
16930        "Hello, world!",
16931        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16932        true,
16933        cx,
16934        |highlighted_edits, cx| {
16935            assert_eq!(highlighted_edits.text, "Hello, world!");
16936            assert_eq!(highlighted_edits.highlights.len(), 1);
16937            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16938            assert_eq!(
16939                highlighted_edits.highlights[0].1.background_color,
16940                Some(cx.theme().status().deleted_background)
16941            );
16942        },
16943    )
16944    .await;
16945
16946    // Insertion
16947    assert_highlighted_edits(
16948        "Hello, world!",
16949        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16950        true,
16951        cx,
16952        |highlighted_edits, cx| {
16953            assert_eq!(highlighted_edits.highlights.len(), 1);
16954            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16955            assert_eq!(
16956                highlighted_edits.highlights[0].1.background_color,
16957                Some(cx.theme().status().created_background)
16958            );
16959        },
16960    )
16961    .await;
16962}
16963
16964async fn assert_highlighted_edits(
16965    text: &str,
16966    edits: Vec<(Range<Point>, String)>,
16967    include_deletions: bool,
16968    cx: &mut TestAppContext,
16969    assertion_fn: impl Fn(HighlightedText, &App),
16970) {
16971    let window = cx.add_window(|window, cx| {
16972        let buffer = MultiBuffer::build_simple(text, cx);
16973        Editor::new(EditorMode::Full, buffer, None, window, cx)
16974    });
16975    let cx = &mut VisualTestContext::from_window(*window, cx);
16976
16977    let (buffer, snapshot) = window
16978        .update(cx, |editor, _window, cx| {
16979            (
16980                editor.buffer().clone(),
16981                editor.buffer().read(cx).snapshot(cx),
16982            )
16983        })
16984        .unwrap();
16985
16986    let edits = edits
16987        .into_iter()
16988        .map(|(range, edit)| {
16989            (
16990                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16991                edit,
16992            )
16993        })
16994        .collect::<Vec<_>>();
16995
16996    let text_anchor_edits = edits
16997        .clone()
16998        .into_iter()
16999        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17000        .collect::<Vec<_>>();
17001
17002    let edit_preview = window
17003        .update(cx, |_, _window, cx| {
17004            buffer
17005                .read(cx)
17006                .as_singleton()
17007                .unwrap()
17008                .read(cx)
17009                .preview_edits(text_anchor_edits.into(), cx)
17010        })
17011        .unwrap()
17012        .await;
17013
17014    cx.update(|_window, cx| {
17015        let highlighted_edits = inline_completion_edit_text(
17016            &snapshot.as_singleton().unwrap().2,
17017            &edits,
17018            &edit_preview,
17019            include_deletions,
17020            cx,
17021        );
17022        assertion_fn(highlighted_edits, cx)
17023    });
17024}
17025
17026#[gpui::test]
17027async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17028    init_test(cx, |_| {});
17029    let capabilities = lsp::ServerCapabilities {
17030        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17031            prepare_provider: Some(true),
17032            work_done_progress_options: Default::default(),
17033        })),
17034        ..Default::default()
17035    };
17036    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17037
17038    cx.set_state(indoc! {"
17039        struct Fˇoo {}
17040    "});
17041
17042    cx.update_editor(|editor, _, cx| {
17043        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17044        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17045        editor.highlight_background::<DocumentHighlightRead>(
17046            &[highlight_range],
17047            |c| c.editor_document_highlight_read_background,
17048            cx,
17049        );
17050    });
17051
17052    let mut prepare_rename_handler =
17053        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17054            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17055                start: lsp::Position {
17056                    line: 0,
17057                    character: 7,
17058                },
17059                end: lsp::Position {
17060                    line: 0,
17061                    character: 10,
17062                },
17063            })))
17064        });
17065    let prepare_rename_task = cx
17066        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17067        .expect("Prepare rename was not started");
17068    prepare_rename_handler.next().await.unwrap();
17069    prepare_rename_task.await.expect("Prepare rename failed");
17070
17071    let mut rename_handler =
17072        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17073            let edit = lsp::TextEdit {
17074                range: lsp::Range {
17075                    start: lsp::Position {
17076                        line: 0,
17077                        character: 7,
17078                    },
17079                    end: lsp::Position {
17080                        line: 0,
17081                        character: 10,
17082                    },
17083                },
17084                new_text: "FooRenamed".to_string(),
17085            };
17086            Ok(Some(lsp::WorkspaceEdit::new(
17087                // Specify the same edit twice
17088                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17089            )))
17090        });
17091    let rename_task = cx
17092        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17093        .expect("Confirm rename was not started");
17094    rename_handler.next().await.unwrap();
17095    rename_task.await.expect("Confirm rename failed");
17096    cx.run_until_parked();
17097
17098    // Despite two edits, only one is actually applied as those are identical
17099    cx.assert_editor_state(indoc! {"
17100        struct FooRenamedˇ {}
17101    "});
17102}
17103
17104#[gpui::test]
17105async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17106    init_test(cx, |_| {});
17107    // These capabilities indicate that the server does not support prepare rename.
17108    let capabilities = lsp::ServerCapabilities {
17109        rename_provider: Some(lsp::OneOf::Left(true)),
17110        ..Default::default()
17111    };
17112    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17113
17114    cx.set_state(indoc! {"
17115        struct Fˇoo {}
17116    "});
17117
17118    cx.update_editor(|editor, _window, cx| {
17119        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17120        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17121        editor.highlight_background::<DocumentHighlightRead>(
17122            &[highlight_range],
17123            |c| c.editor_document_highlight_read_background,
17124            cx,
17125        );
17126    });
17127
17128    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17129        .expect("Prepare rename was not started")
17130        .await
17131        .expect("Prepare rename failed");
17132
17133    let mut rename_handler =
17134        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17135            let edit = lsp::TextEdit {
17136                range: lsp::Range {
17137                    start: lsp::Position {
17138                        line: 0,
17139                        character: 7,
17140                    },
17141                    end: lsp::Position {
17142                        line: 0,
17143                        character: 10,
17144                    },
17145                },
17146                new_text: "FooRenamed".to_string(),
17147            };
17148            Ok(Some(lsp::WorkspaceEdit::new(
17149                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17150            )))
17151        });
17152    let rename_task = cx
17153        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17154        .expect("Confirm rename was not started");
17155    rename_handler.next().await.unwrap();
17156    rename_task.await.expect("Confirm rename failed");
17157    cx.run_until_parked();
17158
17159    // Correct range is renamed, as `surrounding_word` is used to find it.
17160    cx.assert_editor_state(indoc! {"
17161        struct FooRenamedˇ {}
17162    "});
17163}
17164
17165#[gpui::test]
17166async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17167    init_test(cx, |_| {});
17168    let mut cx = EditorTestContext::new(cx).await;
17169
17170    let language = Arc::new(
17171        Language::new(
17172            LanguageConfig::default(),
17173            Some(tree_sitter_html::LANGUAGE.into()),
17174        )
17175        .with_brackets_query(
17176            r#"
17177            ("<" @open "/>" @close)
17178            ("</" @open ">" @close)
17179            ("<" @open ">" @close)
17180            ("\"" @open "\"" @close)
17181            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17182        "#,
17183        )
17184        .unwrap(),
17185    );
17186    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17187
17188    cx.set_state(indoc! {"
17189        <span>ˇ</span>
17190    "});
17191    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17192    cx.assert_editor_state(indoc! {"
17193        <span>
17194        ˇ
17195        </span>
17196    "});
17197
17198    cx.set_state(indoc! {"
17199        <span><span></span>ˇ</span>
17200    "});
17201    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17202    cx.assert_editor_state(indoc! {"
17203        <span><span></span>
17204        ˇ</span>
17205    "});
17206
17207    cx.set_state(indoc! {"
17208        <span>ˇ
17209        </span>
17210    "});
17211    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17212    cx.assert_editor_state(indoc! {"
17213        <span>
17214        ˇ
17215        </span>
17216    "});
17217}
17218
17219mod autoclose_tags {
17220    use super::*;
17221    use language::language_settings::JsxTagAutoCloseSettings;
17222    use languages::language;
17223
17224    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17225        init_test(cx, |settings| {
17226            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17227        });
17228
17229        let mut cx = EditorTestContext::new(cx).await;
17230        cx.update_buffer(|buffer, cx| {
17231            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17232
17233            buffer.set_language(Some(language), cx)
17234        });
17235
17236        cx
17237    }
17238
17239    macro_rules! check {
17240        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17241            #[gpui::test]
17242            async fn $name(cx: &mut TestAppContext) {
17243                let mut cx = test_setup(cx).await;
17244                cx.set_state($initial);
17245                cx.run_until_parked();
17246
17247                cx.update_editor(|editor, window, cx| {
17248                    editor.handle_input($input, window, cx);
17249                });
17250                cx.run_until_parked();
17251                cx.assert_editor_state($expected);
17252            }
17253        };
17254    }
17255
17256    check!(
17257        test_basic,
17258        "<divˇ" + ">" => "<div>ˇ</div>"
17259    );
17260
17261    check!(
17262        test_basic_nested,
17263        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17264    );
17265
17266    check!(
17267        test_basic_ignore_already_closed,
17268        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17269    );
17270
17271    check!(
17272        test_doesnt_autoclose_closing_tag,
17273        "</divˇ" + ">" => "</div>ˇ"
17274    );
17275
17276    check!(
17277        test_jsx_attr,
17278        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17279    );
17280
17281    check!(
17282        test_ignores_closing_tags_in_expr_block,
17283        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17284    );
17285
17286    check!(
17287        test_doesnt_autoclose_on_gt_in_expr,
17288        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17289    );
17290
17291    check!(
17292        test_ignores_closing_tags_with_different_tag_names,
17293        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17294    );
17295
17296    check!(
17297        test_autocloses_in_jsx_expression,
17298        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17299    );
17300
17301    check!(
17302        test_doesnt_autoclose_already_closed_in_jsx_expression,
17303        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17304    );
17305
17306    check!(
17307        test_autocloses_fragment,
17308        "" + ">" => "<>ˇ</>"
17309    );
17310
17311    check!(
17312        test_does_not_include_type_argument_in_autoclose_tag_name,
17313        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17314    );
17315
17316    check!(
17317        test_does_not_autoclose_doctype,
17318        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17319    );
17320
17321    check!(
17322        test_does_not_autoclose_comment,
17323        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17324    );
17325
17326    check!(
17327        test_multi_cursor_autoclose_same_tag,
17328        r#"
17329        <divˇ
17330        <divˇ
17331        "#
17332        + ">" =>
17333        r#"
17334        <div>ˇ</div>
17335        <div>ˇ</div>
17336        "#
17337    );
17338
17339    check!(
17340        test_multi_cursor_autoclose_different_tags,
17341        r#"
17342        <divˇ
17343        <spanˇ
17344        "#
17345        + ">" =>
17346        r#"
17347        <div>ˇ</div>
17348        <span>ˇ</span>
17349        "#
17350    );
17351
17352    check!(
17353        test_multi_cursor_autoclose_some_dont_autoclose_others,
17354        r#"
17355        <divˇ
17356        <div /ˇ
17357        <spanˇ</span>
17358        <!DOCTYPE htmlˇ
17359        </headˇ
17360        <Component<T>ˇ
17361        ˇ
17362        "#
17363        + ">" =>
17364        r#"
17365        <div>ˇ</div>
17366        <div />ˇ
17367        <span>ˇ</span>
17368        <!DOCTYPE html>ˇ
17369        </head>ˇ
17370        <Component<T>>ˇ</Component>
1737117372        "#
17373    );
17374
17375    check!(
17376        test_doesnt_mess_up_trailing_text,
17377        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17378    );
17379
17380    #[gpui::test]
17381    async fn test_multibuffer(cx: &mut TestAppContext) {
17382        init_test(cx, |settings| {
17383            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17384        });
17385
17386        let buffer_a = cx.new(|cx| {
17387            let mut buf = language::Buffer::local("<div", cx);
17388            buf.set_language(
17389                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17390                cx,
17391            );
17392            buf
17393        });
17394        let buffer_b = cx.new(|cx| {
17395            let mut buf = language::Buffer::local("<pre", cx);
17396            buf.set_language(
17397                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17398                cx,
17399            );
17400            buf
17401        });
17402        let buffer_c = cx.new(|cx| {
17403            let buf = language::Buffer::local("<span", cx);
17404            buf
17405        });
17406        let buffer = cx.new(|cx| {
17407            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17408            buf.push_excerpts(
17409                buffer_a,
17410                [ExcerptRange {
17411                    context: text::Anchor::MIN..text::Anchor::MAX,
17412                    primary: None,
17413                }],
17414                cx,
17415            );
17416            buf.push_excerpts(
17417                buffer_b,
17418                [ExcerptRange {
17419                    context: text::Anchor::MIN..text::Anchor::MAX,
17420                    primary: None,
17421                }],
17422                cx,
17423            );
17424            buf.push_excerpts(
17425                buffer_c,
17426                [ExcerptRange {
17427                    context: text::Anchor::MIN..text::Anchor::MAX,
17428                    primary: None,
17429                }],
17430                cx,
17431            );
17432            buf
17433        });
17434        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17435
17436        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17437
17438        cx.update_editor(|editor, window, cx| {
17439            editor.change_selections(None, window, cx, |selections| {
17440                selections.select(vec![
17441                    Selection::from_offset(4),
17442                    Selection::from_offset(9),
17443                    Selection::from_offset(15),
17444                ])
17445            })
17446        });
17447        cx.run_until_parked();
17448
17449        cx.update_editor(|editor, window, cx| {
17450            editor.handle_input(">", window, cx);
17451        });
17452        cx.run_until_parked();
17453
17454        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17455    }
17456}
17457
17458fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17459    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17460    point..point
17461}
17462
17463fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17464    let (text, ranges) = marked_text_ranges(marked_text, true);
17465    assert_eq!(editor.text(cx), text);
17466    assert_eq!(
17467        editor.selections.ranges(cx),
17468        ranges,
17469        "Assert selections are {}",
17470        marked_text
17471    );
17472}
17473
17474pub fn handle_signature_help_request(
17475    cx: &mut EditorLspTestContext,
17476    mocked_response: lsp::SignatureHelp,
17477) -> impl Future<Output = ()> {
17478    let mut request =
17479        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17480            let mocked_response = mocked_response.clone();
17481            async move { Ok(Some(mocked_response)) }
17482        });
17483
17484    async move {
17485        request.next().await;
17486    }
17487}
17488
17489/// Handle completion request passing a marked string specifying where the completion
17490/// should be triggered from using '|' character, what range should be replaced, and what completions
17491/// should be returned using '<' and '>' to delimit the range
17492pub fn handle_completion_request(
17493    cx: &mut EditorLspTestContext,
17494    marked_string: &str,
17495    completions: Vec<&'static str>,
17496    counter: Arc<AtomicUsize>,
17497) -> impl Future<Output = ()> {
17498    let complete_from_marker: TextRangeMarker = '|'.into();
17499    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17500    let (_, mut marked_ranges) = marked_text_ranges_by(
17501        marked_string,
17502        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17503    );
17504
17505    let complete_from_position =
17506        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17507    let replace_range =
17508        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17509
17510    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17511        let completions = completions.clone();
17512        counter.fetch_add(1, atomic::Ordering::Release);
17513        async move {
17514            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17515            assert_eq!(
17516                params.text_document_position.position,
17517                complete_from_position
17518            );
17519            Ok(Some(lsp::CompletionResponse::Array(
17520                completions
17521                    .iter()
17522                    .map(|completion_text| lsp::CompletionItem {
17523                        label: completion_text.to_string(),
17524                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17525                            range: replace_range,
17526                            new_text: completion_text.to_string(),
17527                        })),
17528                        ..Default::default()
17529                    })
17530                    .collect(),
17531            )))
17532        }
17533    });
17534
17535    async move {
17536        request.next().await;
17537    }
17538}
17539
17540fn handle_resolve_completion_request(
17541    cx: &mut EditorLspTestContext,
17542    edits: Option<Vec<(&'static str, &'static str)>>,
17543) -> impl Future<Output = ()> {
17544    let edits = edits.map(|edits| {
17545        edits
17546            .iter()
17547            .map(|(marked_string, new_text)| {
17548                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17549                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17550                lsp::TextEdit::new(replace_range, new_text.to_string())
17551            })
17552            .collect::<Vec<_>>()
17553    });
17554
17555    let mut request =
17556        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17557            let edits = edits.clone();
17558            async move {
17559                Ok(lsp::CompletionItem {
17560                    additional_text_edits: edits,
17561                    ..Default::default()
17562                })
17563            }
17564        });
17565
17566    async move {
17567        request.next().await;
17568    }
17569}
17570
17571pub(crate) fn update_test_language_settings(
17572    cx: &mut TestAppContext,
17573    f: impl Fn(&mut AllLanguageSettingsContent),
17574) {
17575    cx.update(|cx| {
17576        SettingsStore::update_global(cx, |store, cx| {
17577            store.update_user_settings::<AllLanguageSettings>(cx, f);
17578        });
17579    });
17580}
17581
17582pub(crate) fn update_test_project_settings(
17583    cx: &mut TestAppContext,
17584    f: impl Fn(&mut ProjectSettings),
17585) {
17586    cx.update(|cx| {
17587        SettingsStore::update_global(cx, |store, cx| {
17588            store.update_user_settings::<ProjectSettings>(cx, f);
17589        });
17590    });
17591}
17592
17593pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17594    cx.update(|cx| {
17595        assets::Assets.load_test_fonts(cx);
17596        let store = SettingsStore::test(cx);
17597        cx.set_global(store);
17598        theme::init(theme::LoadThemes::JustBase, cx);
17599        release_channel::init(SemanticVersion::default(), cx);
17600        client::init_settings(cx);
17601        language::init(cx);
17602        Project::init_settings(cx);
17603        workspace::init_settings(cx);
17604        crate::init(cx);
17605    });
17606
17607    update_test_language_settings(cx, f);
17608}
17609
17610#[track_caller]
17611fn assert_hunk_revert(
17612    not_reverted_text_with_selections: &str,
17613    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17614    expected_reverted_text_with_selections: &str,
17615    base_text: &str,
17616    cx: &mut EditorLspTestContext,
17617) {
17618    cx.set_state(not_reverted_text_with_selections);
17619    cx.set_head_text(base_text);
17620    cx.executor().run_until_parked();
17621
17622    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17623        let snapshot = editor.snapshot(window, cx);
17624        let reverted_hunk_statuses = snapshot
17625            .buffer_snapshot
17626            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17627            .map(|hunk| hunk.status().kind)
17628            .collect::<Vec<_>>();
17629
17630        editor.git_restore(&Default::default(), window, cx);
17631        reverted_hunk_statuses
17632    });
17633    cx.executor().run_until_parked();
17634    cx.assert_editor_state(expected_reverted_text_with_selections);
17635    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17636}