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_multiline_completion(cx: &mut TestAppContext) {
 9357    init_test(cx, |_| {});
 9358
 9359    let fs = FakeFs::new(cx.executor());
 9360    fs.insert_tree(
 9361        path!("/a"),
 9362        json!({
 9363            "main.ts": "a",
 9364        }),
 9365    )
 9366    .await;
 9367
 9368    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9369    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9370    let typescript_language = Arc::new(Language::new(
 9371        LanguageConfig {
 9372            name: "TypeScript".into(),
 9373            matcher: LanguageMatcher {
 9374                path_suffixes: vec!["ts".to_string()],
 9375                ..LanguageMatcher::default()
 9376            },
 9377            line_comments: vec!["// ".into()],
 9378            ..LanguageConfig::default()
 9379        },
 9380        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9381    ));
 9382    language_registry.add(typescript_language.clone());
 9383    let mut fake_servers = language_registry.register_fake_lsp(
 9384        "TypeScript",
 9385        FakeLspAdapter {
 9386            capabilities: lsp::ServerCapabilities {
 9387                completion_provider: Some(lsp::CompletionOptions {
 9388                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9389                    ..lsp::CompletionOptions::default()
 9390                }),
 9391                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9392                ..lsp::ServerCapabilities::default()
 9393            },
 9394            // Emulate vtsls label generation
 9395            label_for_completion: Some(Box::new(|item, _| {
 9396                let text = if let Some(description) = item
 9397                    .label_details
 9398                    .as_ref()
 9399                    .and_then(|label_details| label_details.description.as_ref())
 9400                {
 9401                    format!("{} {}", item.label, description)
 9402                } else if let Some(detail) = &item.detail {
 9403                    format!("{} {}", item.label, detail)
 9404                } else {
 9405                    item.label.clone()
 9406                };
 9407                let len = text.len();
 9408                Some(language::CodeLabel {
 9409                    text,
 9410                    runs: Vec::new(),
 9411                    filter_range: 0..len,
 9412                })
 9413            })),
 9414            ..FakeLspAdapter::default()
 9415        },
 9416    );
 9417    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9418    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9419    let worktree_id = workspace
 9420        .update(cx, |workspace, _window, cx| {
 9421            workspace.project().update(cx, |project, cx| {
 9422                project.worktrees(cx).next().unwrap().read(cx).id()
 9423            })
 9424        })
 9425        .unwrap();
 9426    let _buffer = project
 9427        .update(cx, |project, cx| {
 9428            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9429        })
 9430        .await
 9431        .unwrap();
 9432    let editor = workspace
 9433        .update(cx, |workspace, window, cx| {
 9434            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9435        })
 9436        .unwrap()
 9437        .await
 9438        .unwrap()
 9439        .downcast::<Editor>()
 9440        .unwrap();
 9441    let fake_server = fake_servers.next().await.unwrap();
 9442
 9443    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9444    let multiline_label_2 = "a\nb\nc\n";
 9445    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9446    let multiline_description = "d\ne\nf\n";
 9447    let multiline_detail_2 = "g\nh\ni\n";
 9448
 9449    let mut completion_handle =
 9450        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9451            Ok(Some(lsp::CompletionResponse::Array(vec![
 9452                lsp::CompletionItem {
 9453                    label: multiline_label.to_string(),
 9454                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9455                        range: lsp::Range {
 9456                            start: lsp::Position {
 9457                                line: params.text_document_position.position.line,
 9458                                character: params.text_document_position.position.character,
 9459                            },
 9460                            end: lsp::Position {
 9461                                line: params.text_document_position.position.line,
 9462                                character: params.text_document_position.position.character,
 9463                            },
 9464                        },
 9465                        new_text: "new_text_1".to_string(),
 9466                    })),
 9467                    ..lsp::CompletionItem::default()
 9468                },
 9469                lsp::CompletionItem {
 9470                    label: "single line label 1".to_string(),
 9471                    detail: Some(multiline_detail.to_string()),
 9472                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9473                        range: lsp::Range {
 9474                            start: lsp::Position {
 9475                                line: params.text_document_position.position.line,
 9476                                character: params.text_document_position.position.character,
 9477                            },
 9478                            end: lsp::Position {
 9479                                line: params.text_document_position.position.line,
 9480                                character: params.text_document_position.position.character,
 9481                            },
 9482                        },
 9483                        new_text: "new_text_2".to_string(),
 9484                    })),
 9485                    ..lsp::CompletionItem::default()
 9486                },
 9487                lsp::CompletionItem {
 9488                    label: "single line label 2".to_string(),
 9489                    label_details: Some(lsp::CompletionItemLabelDetails {
 9490                        description: Some(multiline_description.to_string()),
 9491                        detail: None,
 9492                    }),
 9493                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9494                        range: lsp::Range {
 9495                            start: lsp::Position {
 9496                                line: params.text_document_position.position.line,
 9497                                character: params.text_document_position.position.character,
 9498                            },
 9499                            end: lsp::Position {
 9500                                line: params.text_document_position.position.line,
 9501                                character: params.text_document_position.position.character,
 9502                            },
 9503                        },
 9504                        new_text: "new_text_2".to_string(),
 9505                    })),
 9506                    ..lsp::CompletionItem::default()
 9507                },
 9508                lsp::CompletionItem {
 9509                    label: multiline_label_2.to_string(),
 9510                    detail: Some(multiline_detail_2.to_string()),
 9511                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9512                        range: lsp::Range {
 9513                            start: lsp::Position {
 9514                                line: params.text_document_position.position.line,
 9515                                character: params.text_document_position.position.character,
 9516                            },
 9517                            end: lsp::Position {
 9518                                line: params.text_document_position.position.line,
 9519                                character: params.text_document_position.position.character,
 9520                            },
 9521                        },
 9522                        new_text: "new_text_3".to_string(),
 9523                    })),
 9524                    ..lsp::CompletionItem::default()
 9525                },
 9526                lsp::CompletionItem {
 9527                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9528                    detail: Some(
 9529                        "Details with many     spaces and \t but without newlines".to_string(),
 9530                    ),
 9531                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9532                        range: lsp::Range {
 9533                            start: lsp::Position {
 9534                                line: params.text_document_position.position.line,
 9535                                character: params.text_document_position.position.character,
 9536                            },
 9537                            end: lsp::Position {
 9538                                line: params.text_document_position.position.line,
 9539                                character: params.text_document_position.position.character,
 9540                            },
 9541                        },
 9542                        new_text: "new_text_4".to_string(),
 9543                    })),
 9544                    ..lsp::CompletionItem::default()
 9545                },
 9546            ])))
 9547        });
 9548
 9549    editor.update_in(cx, |editor, window, cx| {
 9550        cx.focus_self(window);
 9551        editor.move_to_end(&MoveToEnd, window, cx);
 9552        editor.handle_input(".", window, cx);
 9553    });
 9554    cx.run_until_parked();
 9555    completion_handle.next().await.unwrap();
 9556
 9557    editor.update(cx, |editor, _| {
 9558        assert!(editor.context_menu_visible());
 9559        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9560        {
 9561            let completion_labels = menu
 9562                .completions
 9563                .borrow()
 9564                .iter()
 9565                .map(|c| c.label.text.clone())
 9566                .collect::<Vec<_>>();
 9567            assert_eq!(
 9568                completion_labels,
 9569                &[
 9570                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9571                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9572                    "single line label 2 d e f ",
 9573                    "a b c g h i ",
 9574                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9575                ],
 9576                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9577            );
 9578
 9579            for completion in menu
 9580                .completions
 9581                .borrow()
 9582                .iter() {
 9583                    assert_eq!(
 9584                        completion.label.filter_range,
 9585                        0..completion.label.text.len(),
 9586                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9587                    );
 9588                }
 9589
 9590        } else {
 9591            panic!("expected completion menu to be open");
 9592        }
 9593    });
 9594}
 9595
 9596#[gpui::test]
 9597async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9598    init_test(cx, |_| {});
 9599    let mut cx = EditorLspTestContext::new_rust(
 9600        lsp::ServerCapabilities {
 9601            completion_provider: Some(lsp::CompletionOptions {
 9602                trigger_characters: Some(vec![".".to_string()]),
 9603                ..Default::default()
 9604            }),
 9605            ..Default::default()
 9606        },
 9607        cx,
 9608    )
 9609    .await;
 9610    cx.lsp
 9611        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9612            Ok(Some(lsp::CompletionResponse::Array(vec![
 9613                lsp::CompletionItem {
 9614                    label: "first".into(),
 9615                    ..Default::default()
 9616                },
 9617                lsp::CompletionItem {
 9618                    label: "last".into(),
 9619                    ..Default::default()
 9620                },
 9621            ])))
 9622        });
 9623    cx.set_state("variableˇ");
 9624    cx.simulate_keystroke(".");
 9625    cx.executor().run_until_parked();
 9626
 9627    cx.update_editor(|editor, _, _| {
 9628        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9629        {
 9630            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9631        } else {
 9632            panic!("expected completion menu to be open");
 9633        }
 9634    });
 9635
 9636    cx.update_editor(|editor, window, cx| {
 9637        editor.move_page_down(&MovePageDown::default(), window, cx);
 9638        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9639        {
 9640            assert!(
 9641                menu.selected_item == 1,
 9642                "expected PageDown to select the last item from the context menu"
 9643            );
 9644        } else {
 9645            panic!("expected completion menu to stay open after PageDown");
 9646        }
 9647    });
 9648
 9649    cx.update_editor(|editor, window, cx| {
 9650        editor.move_page_up(&MovePageUp::default(), window, cx);
 9651        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9652        {
 9653            assert!(
 9654                menu.selected_item == 0,
 9655                "expected PageUp to select the first item from the context menu"
 9656            );
 9657        } else {
 9658            panic!("expected completion menu to stay open after PageUp");
 9659        }
 9660    });
 9661}
 9662
 9663#[gpui::test]
 9664async fn test_completion_sort(cx: &mut TestAppContext) {
 9665    init_test(cx, |_| {});
 9666    let mut cx = EditorLspTestContext::new_rust(
 9667        lsp::ServerCapabilities {
 9668            completion_provider: Some(lsp::CompletionOptions {
 9669                trigger_characters: Some(vec![".".to_string()]),
 9670                ..Default::default()
 9671            }),
 9672            ..Default::default()
 9673        },
 9674        cx,
 9675    )
 9676    .await;
 9677    cx.lsp
 9678        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9679            Ok(Some(lsp::CompletionResponse::Array(vec![
 9680                lsp::CompletionItem {
 9681                    label: "Range".into(),
 9682                    sort_text: Some("a".into()),
 9683                    ..Default::default()
 9684                },
 9685                lsp::CompletionItem {
 9686                    label: "r".into(),
 9687                    sort_text: Some("b".into()),
 9688                    ..Default::default()
 9689                },
 9690                lsp::CompletionItem {
 9691                    label: "ret".into(),
 9692                    sort_text: Some("c".into()),
 9693                    ..Default::default()
 9694                },
 9695                lsp::CompletionItem {
 9696                    label: "return".into(),
 9697                    sort_text: Some("d".into()),
 9698                    ..Default::default()
 9699                },
 9700                lsp::CompletionItem {
 9701                    label: "slice".into(),
 9702                    sort_text: Some("d".into()),
 9703                    ..Default::default()
 9704                },
 9705            ])))
 9706        });
 9707    cx.set_state("");
 9708    cx.executor().run_until_parked();
 9709    cx.update_editor(|editor, window, cx| {
 9710        editor.show_completions(
 9711            &ShowCompletions {
 9712                trigger: Some("r".into()),
 9713            },
 9714            window,
 9715            cx,
 9716        );
 9717    });
 9718    cx.executor().run_until_parked();
 9719
 9720    cx.update_editor(|editor, _, _| {
 9721        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9722        {
 9723            assert_eq!(
 9724                completion_menu_entries(&menu),
 9725                &["r", "ret", "Range", "return"]
 9726            );
 9727        } else {
 9728            panic!("expected completion menu to be open");
 9729        }
 9730    });
 9731}
 9732
 9733#[gpui::test]
 9734async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9735    init_test(cx, |_| {});
 9736
 9737    let mut cx = EditorLspTestContext::new_rust(
 9738        lsp::ServerCapabilities {
 9739            completion_provider: Some(lsp::CompletionOptions {
 9740                trigger_characters: Some(vec![".".to_string()]),
 9741                resolve_provider: Some(true),
 9742                ..Default::default()
 9743            }),
 9744            ..Default::default()
 9745        },
 9746        cx,
 9747    )
 9748    .await;
 9749
 9750    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9751    cx.simulate_keystroke(".");
 9752    let completion_item = lsp::CompletionItem {
 9753        label: "Some".into(),
 9754        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9755        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9756        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9757            kind: lsp::MarkupKind::Markdown,
 9758            value: "```rust\nSome(2)\n```".to_string(),
 9759        })),
 9760        deprecated: Some(false),
 9761        sort_text: Some("Some".to_string()),
 9762        filter_text: Some("Some".to_string()),
 9763        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9764        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9765            range: lsp::Range {
 9766                start: lsp::Position {
 9767                    line: 0,
 9768                    character: 22,
 9769                },
 9770                end: lsp::Position {
 9771                    line: 0,
 9772                    character: 22,
 9773                },
 9774            },
 9775            new_text: "Some(2)".to_string(),
 9776        })),
 9777        additional_text_edits: Some(vec![lsp::TextEdit {
 9778            range: lsp::Range {
 9779                start: lsp::Position {
 9780                    line: 0,
 9781                    character: 20,
 9782                },
 9783                end: lsp::Position {
 9784                    line: 0,
 9785                    character: 22,
 9786                },
 9787            },
 9788            new_text: "".to_string(),
 9789        }]),
 9790        ..Default::default()
 9791    };
 9792
 9793    let closure_completion_item = completion_item.clone();
 9794    let counter = Arc::new(AtomicUsize::new(0));
 9795    let counter_clone = counter.clone();
 9796    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9797        let task_completion_item = closure_completion_item.clone();
 9798        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9799        async move {
 9800            Ok(Some(lsp::CompletionResponse::Array(vec![
 9801                task_completion_item,
 9802            ])))
 9803        }
 9804    });
 9805
 9806    cx.condition(|editor, _| editor.context_menu_visible())
 9807        .await;
 9808    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9809    assert!(request.next().await.is_some());
 9810    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9811
 9812    cx.simulate_keystroke("S");
 9813    cx.simulate_keystroke("o");
 9814    cx.simulate_keystroke("m");
 9815    cx.condition(|editor, _| editor.context_menu_visible())
 9816        .await;
 9817    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9818    assert!(request.next().await.is_some());
 9819    assert!(request.next().await.is_some());
 9820    assert!(request.next().await.is_some());
 9821    request.close();
 9822    assert!(request.next().await.is_none());
 9823    assert_eq!(
 9824        counter.load(atomic::Ordering::Acquire),
 9825        4,
 9826        "With the completions menu open, only one LSP request should happen per input"
 9827    );
 9828}
 9829
 9830#[gpui::test]
 9831async fn test_toggle_comment(cx: &mut TestAppContext) {
 9832    init_test(cx, |_| {});
 9833    let mut cx = EditorTestContext::new(cx).await;
 9834    let language = Arc::new(Language::new(
 9835        LanguageConfig {
 9836            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9837            ..Default::default()
 9838        },
 9839        Some(tree_sitter_rust::LANGUAGE.into()),
 9840    ));
 9841    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9842
 9843    // If multiple selections intersect a line, the line is only toggled once.
 9844    cx.set_state(indoc! {"
 9845        fn a() {
 9846            «//b();
 9847            ˇ»// «c();
 9848            //ˇ»  d();
 9849        }
 9850    "});
 9851
 9852    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9853
 9854    cx.assert_editor_state(indoc! {"
 9855        fn a() {
 9856            «b();
 9857            c();
 9858            ˇ» d();
 9859        }
 9860    "});
 9861
 9862    // The comment prefix is inserted at the same column for every line in a
 9863    // selection.
 9864    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9865
 9866    cx.assert_editor_state(indoc! {"
 9867        fn a() {
 9868            // «b();
 9869            // c();
 9870            ˇ»//  d();
 9871        }
 9872    "});
 9873
 9874    // If a selection ends at the beginning of a line, that line is not toggled.
 9875    cx.set_selections_state(indoc! {"
 9876        fn a() {
 9877            // b();
 9878            «// c();
 9879        ˇ»    //  d();
 9880        }
 9881    "});
 9882
 9883    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9884
 9885    cx.assert_editor_state(indoc! {"
 9886        fn a() {
 9887            // b();
 9888            «c();
 9889        ˇ»    //  d();
 9890        }
 9891    "});
 9892
 9893    // If a selection span a single line and is empty, the line is toggled.
 9894    cx.set_state(indoc! {"
 9895        fn a() {
 9896            a();
 9897            b();
 9898        ˇ
 9899        }
 9900    "});
 9901
 9902    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9903
 9904    cx.assert_editor_state(indoc! {"
 9905        fn a() {
 9906            a();
 9907            b();
 9908        //•ˇ
 9909        }
 9910    "});
 9911
 9912    // If a selection span multiple lines, empty lines are not toggled.
 9913    cx.set_state(indoc! {"
 9914        fn a() {
 9915            «a();
 9916
 9917            c();ˇ»
 9918        }
 9919    "});
 9920
 9921    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9922
 9923    cx.assert_editor_state(indoc! {"
 9924        fn a() {
 9925            // «a();
 9926
 9927            // c();ˇ»
 9928        }
 9929    "});
 9930
 9931    // If a selection includes multiple comment prefixes, all lines are uncommented.
 9932    cx.set_state(indoc! {"
 9933        fn a() {
 9934            «// a();
 9935            /// b();
 9936            //! c();ˇ»
 9937        }
 9938    "});
 9939
 9940    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
 9941
 9942    cx.assert_editor_state(indoc! {"
 9943        fn a() {
 9944            «a();
 9945            b();
 9946            c();ˇ»
 9947        }
 9948    "});
 9949}
 9950
 9951#[gpui::test]
 9952async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
 9953    init_test(cx, |_| {});
 9954    let mut cx = EditorTestContext::new(cx).await;
 9955    let language = Arc::new(Language::new(
 9956        LanguageConfig {
 9957            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9958            ..Default::default()
 9959        },
 9960        Some(tree_sitter_rust::LANGUAGE.into()),
 9961    ));
 9962    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9963
 9964    let toggle_comments = &ToggleComments {
 9965        advance_downwards: false,
 9966        ignore_indent: true,
 9967    };
 9968
 9969    // If multiple selections intersect a line, the line is only toggled once.
 9970    cx.set_state(indoc! {"
 9971        fn a() {
 9972        //    «b();
 9973        //    c();
 9974        //    ˇ» d();
 9975        }
 9976    "});
 9977
 9978    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9979
 9980    cx.assert_editor_state(indoc! {"
 9981        fn a() {
 9982            «b();
 9983            c();
 9984            ˇ» d();
 9985        }
 9986    "});
 9987
 9988    // The comment prefix is inserted at the beginning of each line
 9989    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
 9990
 9991    cx.assert_editor_state(indoc! {"
 9992        fn a() {
 9993        //    «b();
 9994        //    c();
 9995        //    ˇ» d();
 9996        }
 9997    "});
 9998
 9999    // If a selection ends at the beginning of a line, that line is not toggled.
10000    cx.set_selections_state(indoc! {"
10001        fn a() {
10002        //    b();
10003        //    «c();
10004        ˇ»//     d();
10005        }
10006    "});
10007
10008    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10009
10010    cx.assert_editor_state(indoc! {"
10011        fn a() {
10012        //    b();
10013            «c();
10014        ˇ»//     d();
10015        }
10016    "});
10017
10018    // If a selection span a single line and is empty, the line is toggled.
10019    cx.set_state(indoc! {"
10020        fn a() {
10021            a();
10022            b();
10023        ˇ
10024        }
10025    "});
10026
10027    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10028
10029    cx.assert_editor_state(indoc! {"
10030        fn a() {
10031            a();
10032            b();
10033        //ˇ
10034        }
10035    "});
10036
10037    // If a selection span multiple lines, empty lines are not toggled.
10038    cx.set_state(indoc! {"
10039        fn a() {
10040            «a();
10041
10042            c();ˇ»
10043        }
10044    "});
10045
10046    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10047
10048    cx.assert_editor_state(indoc! {"
10049        fn a() {
10050        //    «a();
10051
10052        //    c();ˇ»
10053        }
10054    "});
10055
10056    // If a selection includes multiple comment prefixes, all lines are uncommented.
10057    cx.set_state(indoc! {"
10058        fn a() {
10059        //    «a();
10060        ///    b();
10061        //!    c();ˇ»
10062        }
10063    "});
10064
10065    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10066
10067    cx.assert_editor_state(indoc! {"
10068        fn a() {
10069            «a();
10070            b();
10071            c();ˇ»
10072        }
10073    "});
10074}
10075
10076#[gpui::test]
10077async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10078    init_test(cx, |_| {});
10079
10080    let language = Arc::new(Language::new(
10081        LanguageConfig {
10082            line_comments: vec!["// ".into()],
10083            ..Default::default()
10084        },
10085        Some(tree_sitter_rust::LANGUAGE.into()),
10086    ));
10087
10088    let mut cx = EditorTestContext::new(cx).await;
10089
10090    cx.language_registry().add(language.clone());
10091    cx.update_buffer(|buffer, cx| {
10092        buffer.set_language(Some(language), cx);
10093    });
10094
10095    let toggle_comments = &ToggleComments {
10096        advance_downwards: true,
10097        ignore_indent: false,
10098    };
10099
10100    // Single cursor on one line -> advance
10101    // Cursor moves horizontally 3 characters as well on non-blank line
10102    cx.set_state(indoc!(
10103        "fn a() {
10104             ˇdog();
10105             cat();
10106        }"
10107    ));
10108    cx.update_editor(|editor, window, cx| {
10109        editor.toggle_comments(toggle_comments, window, cx);
10110    });
10111    cx.assert_editor_state(indoc!(
10112        "fn a() {
10113             // dog();
10114             catˇ();
10115        }"
10116    ));
10117
10118    // Single selection on one line -> don't advance
10119    cx.set_state(indoc!(
10120        "fn a() {
10121             «dog()ˇ»;
10122             cat();
10123        }"
10124    ));
10125    cx.update_editor(|editor, window, cx| {
10126        editor.toggle_comments(toggle_comments, window, cx);
10127    });
10128    cx.assert_editor_state(indoc!(
10129        "fn a() {
10130             // «dog()ˇ»;
10131             cat();
10132        }"
10133    ));
10134
10135    // Multiple cursors on one line -> advance
10136    cx.set_state(indoc!(
10137        "fn a() {
10138             ˇdˇog();
10139             cat();
10140        }"
10141    ));
10142    cx.update_editor(|editor, window, cx| {
10143        editor.toggle_comments(toggle_comments, window, cx);
10144    });
10145    cx.assert_editor_state(indoc!(
10146        "fn a() {
10147             // dog();
10148             catˇ(ˇ);
10149        }"
10150    ));
10151
10152    // Multiple cursors on one line, with selection -> don't advance
10153    cx.set_state(indoc!(
10154        "fn a() {
10155             ˇdˇog«()ˇ»;
10156             cat();
10157        }"
10158    ));
10159    cx.update_editor(|editor, window, cx| {
10160        editor.toggle_comments(toggle_comments, window, cx);
10161    });
10162    cx.assert_editor_state(indoc!(
10163        "fn a() {
10164             // ˇdˇog«()ˇ»;
10165             cat();
10166        }"
10167    ));
10168
10169    // Single cursor on one line -> advance
10170    // Cursor moves to column 0 on blank line
10171    cx.set_state(indoc!(
10172        "fn a() {
10173             ˇdog();
10174
10175             cat();
10176        }"
10177    ));
10178    cx.update_editor(|editor, window, cx| {
10179        editor.toggle_comments(toggle_comments, window, cx);
10180    });
10181    cx.assert_editor_state(indoc!(
10182        "fn a() {
10183             // dog();
10184        ˇ
10185             cat();
10186        }"
10187    ));
10188
10189    // Single cursor on one line -> advance
10190    // Cursor starts and ends at column 0
10191    cx.set_state(indoc!(
10192        "fn a() {
10193         ˇ    dog();
10194             cat();
10195        }"
10196    ));
10197    cx.update_editor(|editor, window, cx| {
10198        editor.toggle_comments(toggle_comments, window, cx);
10199    });
10200    cx.assert_editor_state(indoc!(
10201        "fn a() {
10202             // dog();
10203         ˇ    cat();
10204        }"
10205    ));
10206}
10207
10208#[gpui::test]
10209async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10210    init_test(cx, |_| {});
10211
10212    let mut cx = EditorTestContext::new(cx).await;
10213
10214    let html_language = Arc::new(
10215        Language::new(
10216            LanguageConfig {
10217                name: "HTML".into(),
10218                block_comment: Some(("<!-- ".into(), " -->".into())),
10219                ..Default::default()
10220            },
10221            Some(tree_sitter_html::LANGUAGE.into()),
10222        )
10223        .with_injection_query(
10224            r#"
10225            (script_element
10226                (raw_text) @injection.content
10227                (#set! injection.language "javascript"))
10228            "#,
10229        )
10230        .unwrap(),
10231    );
10232
10233    let javascript_language = Arc::new(Language::new(
10234        LanguageConfig {
10235            name: "JavaScript".into(),
10236            line_comments: vec!["// ".into()],
10237            ..Default::default()
10238        },
10239        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10240    ));
10241
10242    cx.language_registry().add(html_language.clone());
10243    cx.language_registry().add(javascript_language.clone());
10244    cx.update_buffer(|buffer, cx| {
10245        buffer.set_language(Some(html_language), cx);
10246    });
10247
10248    // Toggle comments for empty selections
10249    cx.set_state(
10250        &r#"
10251            <p>A</p>ˇ
10252            <p>B</p>ˇ
10253            <p>C</p>ˇ
10254        "#
10255        .unindent(),
10256    );
10257    cx.update_editor(|editor, window, cx| {
10258        editor.toggle_comments(&ToggleComments::default(), window, cx)
10259    });
10260    cx.assert_editor_state(
10261        &r#"
10262            <!-- <p>A</p>ˇ -->
10263            <!-- <p>B</p>ˇ -->
10264            <!-- <p>C</p>ˇ -->
10265        "#
10266        .unindent(),
10267    );
10268    cx.update_editor(|editor, window, cx| {
10269        editor.toggle_comments(&ToggleComments::default(), window, cx)
10270    });
10271    cx.assert_editor_state(
10272        &r#"
10273            <p>A</p>ˇ
10274            <p>B</p>ˇ
10275            <p>C</p>ˇ
10276        "#
10277        .unindent(),
10278    );
10279
10280    // Toggle comments for mixture of empty and non-empty selections, where
10281    // multiple selections occupy a given line.
10282    cx.set_state(
10283        &r#"
10284            <p>A«</p>
10285            <p>ˇ»B</p>ˇ
10286            <p>C«</p>
10287            <p>ˇ»D</p>ˇ
10288        "#
10289        .unindent(),
10290    );
10291
10292    cx.update_editor(|editor, window, cx| {
10293        editor.toggle_comments(&ToggleComments::default(), window, cx)
10294    });
10295    cx.assert_editor_state(
10296        &r#"
10297            <!-- <p>A«</p>
10298            <p>ˇ»B</p>ˇ -->
10299            <!-- <p>C«</p>
10300            <p>ˇ»D</p>ˇ -->
10301        "#
10302        .unindent(),
10303    );
10304    cx.update_editor(|editor, window, cx| {
10305        editor.toggle_comments(&ToggleComments::default(), window, cx)
10306    });
10307    cx.assert_editor_state(
10308        &r#"
10309            <p>A«</p>
10310            <p>ˇ»B</p>ˇ
10311            <p>C«</p>
10312            <p>ˇ»D</p>ˇ
10313        "#
10314        .unindent(),
10315    );
10316
10317    // Toggle comments when different languages are active for different
10318    // selections.
10319    cx.set_state(
10320        &r#"
10321            ˇ<script>
10322                ˇvar x = new Y();
10323            ˇ</script>
10324        "#
10325        .unindent(),
10326    );
10327    cx.executor().run_until_parked();
10328    cx.update_editor(|editor, window, cx| {
10329        editor.toggle_comments(&ToggleComments::default(), window, cx)
10330    });
10331    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10332    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10333    cx.assert_editor_state(
10334        &r#"
10335            <!-- ˇ<script> -->
10336                // ˇvar x = new Y();
10337            <!-- ˇ</script> -->
10338        "#
10339        .unindent(),
10340    );
10341}
10342
10343#[gpui::test]
10344fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10345    init_test(cx, |_| {});
10346
10347    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10348    let multibuffer = cx.new(|cx| {
10349        let mut multibuffer = MultiBuffer::new(ReadWrite);
10350        multibuffer.push_excerpts(
10351            buffer.clone(),
10352            [
10353                ExcerptRange {
10354                    context: Point::new(0, 0)..Point::new(0, 4),
10355                    primary: None,
10356                },
10357                ExcerptRange {
10358                    context: Point::new(1, 0)..Point::new(1, 4),
10359                    primary: None,
10360                },
10361            ],
10362            cx,
10363        );
10364        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10365        multibuffer
10366    });
10367
10368    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10369    editor.update_in(cx, |editor, window, cx| {
10370        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10371        editor.change_selections(None, window, cx, |s| {
10372            s.select_ranges([
10373                Point::new(0, 0)..Point::new(0, 0),
10374                Point::new(1, 0)..Point::new(1, 0),
10375            ])
10376        });
10377
10378        editor.handle_input("X", window, cx);
10379        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10380        assert_eq!(
10381            editor.selections.ranges(cx),
10382            [
10383                Point::new(0, 1)..Point::new(0, 1),
10384                Point::new(1, 1)..Point::new(1, 1),
10385            ]
10386        );
10387
10388        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10389        editor.change_selections(None, window, cx, |s| {
10390            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10391        });
10392        editor.backspace(&Default::default(), window, cx);
10393        assert_eq!(editor.text(cx), "Xa\nbbb");
10394        assert_eq!(
10395            editor.selections.ranges(cx),
10396            [Point::new(1, 0)..Point::new(1, 0)]
10397        );
10398
10399        editor.change_selections(None, window, cx, |s| {
10400            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10401        });
10402        editor.backspace(&Default::default(), window, cx);
10403        assert_eq!(editor.text(cx), "X\nbb");
10404        assert_eq!(
10405            editor.selections.ranges(cx),
10406            [Point::new(0, 1)..Point::new(0, 1)]
10407        );
10408    });
10409}
10410
10411#[gpui::test]
10412fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10413    init_test(cx, |_| {});
10414
10415    let markers = vec![('[', ']').into(), ('(', ')').into()];
10416    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10417        indoc! {"
10418            [aaaa
10419            (bbbb]
10420            cccc)",
10421        },
10422        markers.clone(),
10423    );
10424    let excerpt_ranges = markers.into_iter().map(|marker| {
10425        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10426        ExcerptRange {
10427            context,
10428            primary: None,
10429        }
10430    });
10431    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10432    let multibuffer = cx.new(|cx| {
10433        let mut multibuffer = MultiBuffer::new(ReadWrite);
10434        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10435        multibuffer
10436    });
10437
10438    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10439    editor.update_in(cx, |editor, window, cx| {
10440        let (expected_text, selection_ranges) = marked_text_ranges(
10441            indoc! {"
10442                aaaa
10443                bˇbbb
10444                bˇbbˇb
10445                cccc"
10446            },
10447            true,
10448        );
10449        assert_eq!(editor.text(cx), expected_text);
10450        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10451
10452        editor.handle_input("X", window, cx);
10453
10454        let (expected_text, expected_selections) = marked_text_ranges(
10455            indoc! {"
10456                aaaa
10457                bXˇbbXb
10458                bXˇbbXˇb
10459                cccc"
10460            },
10461            false,
10462        );
10463        assert_eq!(editor.text(cx), expected_text);
10464        assert_eq!(editor.selections.ranges(cx), expected_selections);
10465
10466        editor.newline(&Newline, window, cx);
10467        let (expected_text, expected_selections) = marked_text_ranges(
10468            indoc! {"
10469                aaaa
10470                bX
10471                ˇbbX
10472                b
10473                bX
10474                ˇbbX
10475                ˇb
10476                cccc"
10477            },
10478            false,
10479        );
10480        assert_eq!(editor.text(cx), expected_text);
10481        assert_eq!(editor.selections.ranges(cx), expected_selections);
10482    });
10483}
10484
10485#[gpui::test]
10486fn test_refresh_selections(cx: &mut TestAppContext) {
10487    init_test(cx, |_| {});
10488
10489    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10490    let mut excerpt1_id = None;
10491    let multibuffer = cx.new(|cx| {
10492        let mut multibuffer = MultiBuffer::new(ReadWrite);
10493        excerpt1_id = multibuffer
10494            .push_excerpts(
10495                buffer.clone(),
10496                [
10497                    ExcerptRange {
10498                        context: Point::new(0, 0)..Point::new(1, 4),
10499                        primary: None,
10500                    },
10501                    ExcerptRange {
10502                        context: Point::new(1, 0)..Point::new(2, 4),
10503                        primary: None,
10504                    },
10505                ],
10506                cx,
10507            )
10508            .into_iter()
10509            .next();
10510        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10511        multibuffer
10512    });
10513
10514    let editor = cx.add_window(|window, cx| {
10515        let mut editor = build_editor(multibuffer.clone(), window, cx);
10516        let snapshot = editor.snapshot(window, cx);
10517        editor.change_selections(None, window, cx, |s| {
10518            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10519        });
10520        editor.begin_selection(
10521            Point::new(2, 1).to_display_point(&snapshot),
10522            true,
10523            1,
10524            window,
10525            cx,
10526        );
10527        assert_eq!(
10528            editor.selections.ranges(cx),
10529            [
10530                Point::new(1, 3)..Point::new(1, 3),
10531                Point::new(2, 1)..Point::new(2, 1),
10532            ]
10533        );
10534        editor
10535    });
10536
10537    // Refreshing selections is a no-op when excerpts haven't changed.
10538    _ = editor.update(cx, |editor, window, cx| {
10539        editor.change_selections(None, window, cx, |s| s.refresh());
10540        assert_eq!(
10541            editor.selections.ranges(cx),
10542            [
10543                Point::new(1, 3)..Point::new(1, 3),
10544                Point::new(2, 1)..Point::new(2, 1),
10545            ]
10546        );
10547    });
10548
10549    multibuffer.update(cx, |multibuffer, cx| {
10550        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10551    });
10552    _ = editor.update(cx, |editor, window, cx| {
10553        // Removing an excerpt causes the first selection to become degenerate.
10554        assert_eq!(
10555            editor.selections.ranges(cx),
10556            [
10557                Point::new(0, 0)..Point::new(0, 0),
10558                Point::new(0, 1)..Point::new(0, 1)
10559            ]
10560        );
10561
10562        // Refreshing selections will relocate the first selection to the original buffer
10563        // location.
10564        editor.change_selections(None, window, cx, |s| s.refresh());
10565        assert_eq!(
10566            editor.selections.ranges(cx),
10567            [
10568                Point::new(0, 1)..Point::new(0, 1),
10569                Point::new(0, 3)..Point::new(0, 3)
10570            ]
10571        );
10572        assert!(editor.selections.pending_anchor().is_some());
10573    });
10574}
10575
10576#[gpui::test]
10577fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10578    init_test(cx, |_| {});
10579
10580    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10581    let mut excerpt1_id = None;
10582    let multibuffer = cx.new(|cx| {
10583        let mut multibuffer = MultiBuffer::new(ReadWrite);
10584        excerpt1_id = multibuffer
10585            .push_excerpts(
10586                buffer.clone(),
10587                [
10588                    ExcerptRange {
10589                        context: Point::new(0, 0)..Point::new(1, 4),
10590                        primary: None,
10591                    },
10592                    ExcerptRange {
10593                        context: Point::new(1, 0)..Point::new(2, 4),
10594                        primary: None,
10595                    },
10596                ],
10597                cx,
10598            )
10599            .into_iter()
10600            .next();
10601        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10602        multibuffer
10603    });
10604
10605    let editor = cx.add_window(|window, cx| {
10606        let mut editor = build_editor(multibuffer.clone(), window, cx);
10607        let snapshot = editor.snapshot(window, cx);
10608        editor.begin_selection(
10609            Point::new(1, 3).to_display_point(&snapshot),
10610            false,
10611            1,
10612            window,
10613            cx,
10614        );
10615        assert_eq!(
10616            editor.selections.ranges(cx),
10617            [Point::new(1, 3)..Point::new(1, 3)]
10618        );
10619        editor
10620    });
10621
10622    multibuffer.update(cx, |multibuffer, cx| {
10623        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10624    });
10625    _ = editor.update(cx, |editor, window, cx| {
10626        assert_eq!(
10627            editor.selections.ranges(cx),
10628            [Point::new(0, 0)..Point::new(0, 0)]
10629        );
10630
10631        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10632        editor.change_selections(None, window, cx, |s| s.refresh());
10633        assert_eq!(
10634            editor.selections.ranges(cx),
10635            [Point::new(0, 3)..Point::new(0, 3)]
10636        );
10637        assert!(editor.selections.pending_anchor().is_some());
10638    });
10639}
10640
10641#[gpui::test]
10642async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10643    init_test(cx, |_| {});
10644
10645    let language = Arc::new(
10646        Language::new(
10647            LanguageConfig {
10648                brackets: BracketPairConfig {
10649                    pairs: vec![
10650                        BracketPair {
10651                            start: "{".to_string(),
10652                            end: "}".to_string(),
10653                            close: true,
10654                            surround: true,
10655                            newline: true,
10656                        },
10657                        BracketPair {
10658                            start: "/* ".to_string(),
10659                            end: " */".to_string(),
10660                            close: true,
10661                            surround: true,
10662                            newline: true,
10663                        },
10664                    ],
10665                    ..Default::default()
10666                },
10667                ..Default::default()
10668            },
10669            Some(tree_sitter_rust::LANGUAGE.into()),
10670        )
10671        .with_indents_query("")
10672        .unwrap(),
10673    );
10674
10675    let text = concat!(
10676        "{   }\n",     //
10677        "  x\n",       //
10678        "  /*   */\n", //
10679        "x\n",         //
10680        "{{} }\n",     //
10681    );
10682
10683    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10684    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10685    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10686    editor
10687        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10688        .await;
10689
10690    editor.update_in(cx, |editor, window, cx| {
10691        editor.change_selections(None, window, cx, |s| {
10692            s.select_display_ranges([
10693                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10694                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10695                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10696            ])
10697        });
10698        editor.newline(&Newline, window, cx);
10699
10700        assert_eq!(
10701            editor.buffer().read(cx).read(cx).text(),
10702            concat!(
10703                "{ \n",    // Suppress rustfmt
10704                "\n",      //
10705                "}\n",     //
10706                "  x\n",   //
10707                "  /* \n", //
10708                "  \n",    //
10709                "  */\n",  //
10710                "x\n",     //
10711                "{{} \n",  //
10712                "}\n",     //
10713            )
10714        );
10715    });
10716}
10717
10718#[gpui::test]
10719fn test_highlighted_ranges(cx: &mut TestAppContext) {
10720    init_test(cx, |_| {});
10721
10722    let editor = cx.add_window(|window, cx| {
10723        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10724        build_editor(buffer.clone(), window, cx)
10725    });
10726
10727    _ = editor.update(cx, |editor, window, cx| {
10728        struct Type1;
10729        struct Type2;
10730
10731        let buffer = editor.buffer.read(cx).snapshot(cx);
10732
10733        let anchor_range =
10734            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10735
10736        editor.highlight_background::<Type1>(
10737            &[
10738                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10739                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10740                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10741                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10742            ],
10743            |_| Hsla::red(),
10744            cx,
10745        );
10746        editor.highlight_background::<Type2>(
10747            &[
10748                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10749                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10750                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10751                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10752            ],
10753            |_| Hsla::green(),
10754            cx,
10755        );
10756
10757        let snapshot = editor.snapshot(window, cx);
10758        let mut highlighted_ranges = editor.background_highlights_in_range(
10759            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10760            &snapshot,
10761            cx.theme().colors(),
10762        );
10763        // Enforce a consistent ordering based on color without relying on the ordering of the
10764        // highlight's `TypeId` which is non-executor.
10765        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10766        assert_eq!(
10767            highlighted_ranges,
10768            &[
10769                (
10770                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10771                    Hsla::red(),
10772                ),
10773                (
10774                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10775                    Hsla::red(),
10776                ),
10777                (
10778                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10779                    Hsla::green(),
10780                ),
10781                (
10782                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10783                    Hsla::green(),
10784                ),
10785            ]
10786        );
10787        assert_eq!(
10788            editor.background_highlights_in_range(
10789                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10790                &snapshot,
10791                cx.theme().colors(),
10792            ),
10793            &[(
10794                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10795                Hsla::red(),
10796            )]
10797        );
10798    });
10799}
10800
10801#[gpui::test]
10802async fn test_following(cx: &mut TestAppContext) {
10803    init_test(cx, |_| {});
10804
10805    let fs = FakeFs::new(cx.executor());
10806    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10807
10808    let buffer = project.update(cx, |project, cx| {
10809        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10810        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10811    });
10812    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10813    let follower = cx.update(|cx| {
10814        cx.open_window(
10815            WindowOptions {
10816                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10817                    gpui::Point::new(px(0.), px(0.)),
10818                    gpui::Point::new(px(10.), px(80.)),
10819                ))),
10820                ..Default::default()
10821            },
10822            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10823        )
10824        .unwrap()
10825    });
10826
10827    let is_still_following = Rc::new(RefCell::new(true));
10828    let follower_edit_event_count = Rc::new(RefCell::new(0));
10829    let pending_update = Rc::new(RefCell::new(None));
10830    let leader_entity = leader.root(cx).unwrap();
10831    let follower_entity = follower.root(cx).unwrap();
10832    _ = follower.update(cx, {
10833        let update = pending_update.clone();
10834        let is_still_following = is_still_following.clone();
10835        let follower_edit_event_count = follower_edit_event_count.clone();
10836        |_, window, cx| {
10837            cx.subscribe_in(
10838                &leader_entity,
10839                window,
10840                move |_, leader, event, window, cx| {
10841                    leader.read(cx).add_event_to_update_proto(
10842                        event,
10843                        &mut update.borrow_mut(),
10844                        window,
10845                        cx,
10846                    );
10847                },
10848            )
10849            .detach();
10850
10851            cx.subscribe_in(
10852                &follower_entity,
10853                window,
10854                move |_, _, event: &EditorEvent, _window, _cx| {
10855                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
10856                        *is_still_following.borrow_mut() = false;
10857                    }
10858
10859                    if let EditorEvent::BufferEdited = event {
10860                        *follower_edit_event_count.borrow_mut() += 1;
10861                    }
10862                },
10863            )
10864            .detach();
10865        }
10866    });
10867
10868    // Update the selections only
10869    _ = leader.update(cx, |leader, window, cx| {
10870        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10871    });
10872    follower
10873        .update(cx, |follower, window, cx| {
10874            follower.apply_update_proto(
10875                &project,
10876                pending_update.borrow_mut().take().unwrap(),
10877                window,
10878                cx,
10879            )
10880        })
10881        .unwrap()
10882        .await
10883        .unwrap();
10884    _ = follower.update(cx, |follower, _, cx| {
10885        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
10886    });
10887    assert!(*is_still_following.borrow());
10888    assert_eq!(*follower_edit_event_count.borrow(), 0);
10889
10890    // Update the scroll position only
10891    _ = leader.update(cx, |leader, window, cx| {
10892        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10893    });
10894    follower
10895        .update(cx, |follower, window, cx| {
10896            follower.apply_update_proto(
10897                &project,
10898                pending_update.borrow_mut().take().unwrap(),
10899                window,
10900                cx,
10901            )
10902        })
10903        .unwrap()
10904        .await
10905        .unwrap();
10906    assert_eq!(
10907        follower
10908            .update(cx, |follower, _, cx| follower.scroll_position(cx))
10909            .unwrap(),
10910        gpui::Point::new(1.5, 3.5)
10911    );
10912    assert!(*is_still_following.borrow());
10913    assert_eq!(*follower_edit_event_count.borrow(), 0);
10914
10915    // Update the selections and scroll position. The follower's scroll position is updated
10916    // via autoscroll, not via the leader's exact scroll position.
10917    _ = leader.update(cx, |leader, window, cx| {
10918        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
10919        leader.request_autoscroll(Autoscroll::newest(), cx);
10920        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
10921    });
10922    follower
10923        .update(cx, |follower, window, cx| {
10924            follower.apply_update_proto(
10925                &project,
10926                pending_update.borrow_mut().take().unwrap(),
10927                window,
10928                cx,
10929            )
10930        })
10931        .unwrap()
10932        .await
10933        .unwrap();
10934    _ = follower.update(cx, |follower, _, cx| {
10935        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
10936        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
10937    });
10938    assert!(*is_still_following.borrow());
10939
10940    // Creating a pending selection that precedes another selection
10941    _ = leader.update(cx, |leader, window, cx| {
10942        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
10943        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
10944    });
10945    follower
10946        .update(cx, |follower, window, cx| {
10947            follower.apply_update_proto(
10948                &project,
10949                pending_update.borrow_mut().take().unwrap(),
10950                window,
10951                cx,
10952            )
10953        })
10954        .unwrap()
10955        .await
10956        .unwrap();
10957    _ = follower.update(cx, |follower, _, cx| {
10958        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
10959    });
10960    assert!(*is_still_following.borrow());
10961
10962    // Extend the pending selection so that it surrounds another selection
10963    _ = leader.update(cx, |leader, window, cx| {
10964        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
10965    });
10966    follower
10967        .update(cx, |follower, window, cx| {
10968            follower.apply_update_proto(
10969                &project,
10970                pending_update.borrow_mut().take().unwrap(),
10971                window,
10972                cx,
10973            )
10974        })
10975        .unwrap()
10976        .await
10977        .unwrap();
10978    _ = follower.update(cx, |follower, _, cx| {
10979        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
10980    });
10981
10982    // Scrolling locally breaks the follow
10983    _ = follower.update(cx, |follower, window, cx| {
10984        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
10985        follower.set_scroll_anchor(
10986            ScrollAnchor {
10987                anchor: top_anchor,
10988                offset: gpui::Point::new(0.0, 0.5),
10989            },
10990            window,
10991            cx,
10992        );
10993    });
10994    assert!(!(*is_still_following.borrow()));
10995}
10996
10997#[gpui::test]
10998async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
10999    init_test(cx, |_| {});
11000
11001    let fs = FakeFs::new(cx.executor());
11002    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11003    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11004    let pane = workspace
11005        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11006        .unwrap();
11007
11008    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11009
11010    let leader = pane.update_in(cx, |_, window, cx| {
11011        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11012        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11013    });
11014
11015    // Start following the editor when it has no excerpts.
11016    let mut state_message =
11017        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11018    let workspace_entity = workspace.root(cx).unwrap();
11019    let follower_1 = cx
11020        .update_window(*workspace.deref(), |_, window, cx| {
11021            Editor::from_state_proto(
11022                workspace_entity,
11023                ViewId {
11024                    creator: Default::default(),
11025                    id: 0,
11026                },
11027                &mut state_message,
11028                window,
11029                cx,
11030            )
11031        })
11032        .unwrap()
11033        .unwrap()
11034        .await
11035        .unwrap();
11036
11037    let update_message = Rc::new(RefCell::new(None));
11038    follower_1.update_in(cx, {
11039        let update = update_message.clone();
11040        |_, window, cx| {
11041            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11042                leader.read(cx).add_event_to_update_proto(
11043                    event,
11044                    &mut update.borrow_mut(),
11045                    window,
11046                    cx,
11047                );
11048            })
11049            .detach();
11050        }
11051    });
11052
11053    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11054        (
11055            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11056            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11057        )
11058    });
11059
11060    // Insert some excerpts.
11061    leader.update(cx, |leader, cx| {
11062        leader.buffer.update(cx, |multibuffer, cx| {
11063            let excerpt_ids = multibuffer.push_excerpts(
11064                buffer_1.clone(),
11065                [
11066                    ExcerptRange {
11067                        context: 1..6,
11068                        primary: None,
11069                    },
11070                    ExcerptRange {
11071                        context: 12..15,
11072                        primary: None,
11073                    },
11074                    ExcerptRange {
11075                        context: 0..3,
11076                        primary: None,
11077                    },
11078                ],
11079                cx,
11080            );
11081            multibuffer.insert_excerpts_after(
11082                excerpt_ids[0],
11083                buffer_2.clone(),
11084                [
11085                    ExcerptRange {
11086                        context: 8..12,
11087                        primary: None,
11088                    },
11089                    ExcerptRange {
11090                        context: 0..6,
11091                        primary: None,
11092                    },
11093                ],
11094                cx,
11095            );
11096        });
11097    });
11098
11099    // Apply the update of adding the excerpts.
11100    follower_1
11101        .update_in(cx, |follower, window, cx| {
11102            follower.apply_update_proto(
11103                &project,
11104                update_message.borrow().clone().unwrap(),
11105                window,
11106                cx,
11107            )
11108        })
11109        .await
11110        .unwrap();
11111    assert_eq!(
11112        follower_1.update(cx, |editor, cx| editor.text(cx)),
11113        leader.update(cx, |editor, cx| editor.text(cx))
11114    );
11115    update_message.borrow_mut().take();
11116
11117    // Start following separately after it already has excerpts.
11118    let mut state_message =
11119        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11120    let workspace_entity = workspace.root(cx).unwrap();
11121    let follower_2 = cx
11122        .update_window(*workspace.deref(), |_, window, cx| {
11123            Editor::from_state_proto(
11124                workspace_entity,
11125                ViewId {
11126                    creator: Default::default(),
11127                    id: 0,
11128                },
11129                &mut state_message,
11130                window,
11131                cx,
11132            )
11133        })
11134        .unwrap()
11135        .unwrap()
11136        .await
11137        .unwrap();
11138    assert_eq!(
11139        follower_2.update(cx, |editor, cx| editor.text(cx)),
11140        leader.update(cx, |editor, cx| editor.text(cx))
11141    );
11142
11143    // Remove some excerpts.
11144    leader.update(cx, |leader, cx| {
11145        leader.buffer.update(cx, |multibuffer, cx| {
11146            let excerpt_ids = multibuffer.excerpt_ids();
11147            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11148            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11149        });
11150    });
11151
11152    // Apply the update of removing the excerpts.
11153    follower_1
11154        .update_in(cx, |follower, window, cx| {
11155            follower.apply_update_proto(
11156                &project,
11157                update_message.borrow().clone().unwrap(),
11158                window,
11159                cx,
11160            )
11161        })
11162        .await
11163        .unwrap();
11164    follower_2
11165        .update_in(cx, |follower, window, cx| {
11166            follower.apply_update_proto(
11167                &project,
11168                update_message.borrow().clone().unwrap(),
11169                window,
11170                cx,
11171            )
11172        })
11173        .await
11174        .unwrap();
11175    update_message.borrow_mut().take();
11176    assert_eq!(
11177        follower_1.update(cx, |editor, cx| editor.text(cx)),
11178        leader.update(cx, |editor, cx| editor.text(cx))
11179    );
11180}
11181
11182#[gpui::test]
11183async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11184    init_test(cx, |_| {});
11185
11186    let mut cx = EditorTestContext::new(cx).await;
11187    let lsp_store =
11188        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11189
11190    cx.set_state(indoc! {"
11191        ˇfn func(abc def: i32) -> u32 {
11192        }
11193    "});
11194
11195    cx.update(|_, cx| {
11196        lsp_store.update(cx, |lsp_store, cx| {
11197            lsp_store
11198                .update_diagnostics(
11199                    LanguageServerId(0),
11200                    lsp::PublishDiagnosticsParams {
11201                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11202                        version: None,
11203                        diagnostics: vec![
11204                            lsp::Diagnostic {
11205                                range: lsp::Range::new(
11206                                    lsp::Position::new(0, 11),
11207                                    lsp::Position::new(0, 12),
11208                                ),
11209                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11210                                ..Default::default()
11211                            },
11212                            lsp::Diagnostic {
11213                                range: lsp::Range::new(
11214                                    lsp::Position::new(0, 12),
11215                                    lsp::Position::new(0, 15),
11216                                ),
11217                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11218                                ..Default::default()
11219                            },
11220                            lsp::Diagnostic {
11221                                range: lsp::Range::new(
11222                                    lsp::Position::new(0, 25),
11223                                    lsp::Position::new(0, 28),
11224                                ),
11225                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11226                                ..Default::default()
11227                            },
11228                        ],
11229                    },
11230                    &[],
11231                    cx,
11232                )
11233                .unwrap()
11234        });
11235    });
11236
11237    executor.run_until_parked();
11238
11239    cx.update_editor(|editor, window, cx| {
11240        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11241    });
11242
11243    cx.assert_editor_state(indoc! {"
11244        fn func(abc def: i32) -> ˇu32 {
11245        }
11246    "});
11247
11248    cx.update_editor(|editor, window, cx| {
11249        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11250    });
11251
11252    cx.assert_editor_state(indoc! {"
11253        fn func(abc ˇdef: i32) -> u32 {
11254        }
11255    "});
11256
11257    cx.update_editor(|editor, window, cx| {
11258        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11259    });
11260
11261    cx.assert_editor_state(indoc! {"
11262        fn func(abcˇ def: i32) -> u32 {
11263        }
11264    "});
11265
11266    cx.update_editor(|editor, window, cx| {
11267        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11268    });
11269
11270    cx.assert_editor_state(indoc! {"
11271        fn func(abc def: i32) -> ˇu32 {
11272        }
11273    "});
11274}
11275
11276#[gpui::test]
11277async fn cycle_through_same_place_diagnostics(
11278    executor: BackgroundExecutor,
11279    cx: &mut TestAppContext,
11280) {
11281    init_test(cx, |_| {});
11282
11283    let mut cx = EditorTestContext::new(cx).await;
11284    let lsp_store =
11285        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11286
11287    cx.set_state(indoc! {"
11288        ˇfn func(abc def: i32) -> u32 {
11289        }
11290    "});
11291
11292    cx.update(|_, cx| {
11293        lsp_store.update(cx, |lsp_store, cx| {
11294            lsp_store
11295                .update_diagnostics(
11296                    LanguageServerId(0),
11297                    lsp::PublishDiagnosticsParams {
11298                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11299                        version: None,
11300                        diagnostics: vec![
11301                            lsp::Diagnostic {
11302                                range: lsp::Range::new(
11303                                    lsp::Position::new(0, 11),
11304                                    lsp::Position::new(0, 12),
11305                                ),
11306                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11307                                ..Default::default()
11308                            },
11309                            lsp::Diagnostic {
11310                                range: lsp::Range::new(
11311                                    lsp::Position::new(0, 12),
11312                                    lsp::Position::new(0, 15),
11313                                ),
11314                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11315                                ..Default::default()
11316                            },
11317                            lsp::Diagnostic {
11318                                range: lsp::Range::new(
11319                                    lsp::Position::new(0, 12),
11320                                    lsp::Position::new(0, 15),
11321                                ),
11322                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11323                                ..Default::default()
11324                            },
11325                            lsp::Diagnostic {
11326                                range: lsp::Range::new(
11327                                    lsp::Position::new(0, 25),
11328                                    lsp::Position::new(0, 28),
11329                                ),
11330                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11331                                ..Default::default()
11332                            },
11333                        ],
11334                    },
11335                    &[],
11336                    cx,
11337                )
11338                .unwrap()
11339        });
11340    });
11341    executor.run_until_parked();
11342
11343    //// Backward
11344
11345    // Fourth diagnostic
11346    cx.update_editor(|editor, window, cx| {
11347        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11348    });
11349    cx.assert_editor_state(indoc! {"
11350        fn func(abc def: i32) -> ˇu32 {
11351        }
11352    "});
11353
11354    // Third diagnostic
11355    cx.update_editor(|editor, window, cx| {
11356        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11357    });
11358    cx.assert_editor_state(indoc! {"
11359        fn func(abc ˇdef: i32) -> u32 {
11360        }
11361    "});
11362
11363    // Second diagnostic, same place
11364    cx.update_editor(|editor, window, cx| {
11365        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11366    });
11367    cx.assert_editor_state(indoc! {"
11368        fn func(abc ˇdef: i32) -> u32 {
11369        }
11370    "});
11371
11372    // First diagnostic
11373    cx.update_editor(|editor, window, cx| {
11374        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11375    });
11376    cx.assert_editor_state(indoc! {"
11377        fn func(abcˇ def: i32) -> u32 {
11378        }
11379    "});
11380
11381    // Wrapped over, fourth diagnostic
11382    cx.update_editor(|editor, window, cx| {
11383        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11384    });
11385    cx.assert_editor_state(indoc! {"
11386        fn func(abc def: i32) -> ˇu32 {
11387        }
11388    "});
11389
11390    cx.update_editor(|editor, window, cx| {
11391        editor.move_to_beginning(&MoveToBeginning, window, cx);
11392    });
11393    cx.assert_editor_state(indoc! {"
11394        ˇfn func(abc def: i32) -> u32 {
11395        }
11396    "});
11397
11398    //// Forward
11399
11400    // First diagnostic
11401    cx.update_editor(|editor, window, cx| {
11402        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11403    });
11404    cx.assert_editor_state(indoc! {"
11405        fn func(abcˇ def: i32) -> u32 {
11406        }
11407    "});
11408
11409    // Second diagnostic
11410    cx.update_editor(|editor, window, cx| {
11411        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11412    });
11413    cx.assert_editor_state(indoc! {"
11414        fn func(abc ˇdef: i32) -> u32 {
11415        }
11416    "});
11417
11418    // Third diagnostic, same place
11419    cx.update_editor(|editor, window, cx| {
11420        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11421    });
11422    cx.assert_editor_state(indoc! {"
11423        fn func(abc ˇdef: i32) -> u32 {
11424        }
11425    "});
11426
11427    // Fourth diagnostic
11428    cx.update_editor(|editor, window, cx| {
11429        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11430    });
11431    cx.assert_editor_state(indoc! {"
11432        fn func(abc def: i32) -> ˇu32 {
11433        }
11434    "});
11435
11436    // Wrapped around, first diagnostic
11437    cx.update_editor(|editor, window, cx| {
11438        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11439    });
11440    cx.assert_editor_state(indoc! {"
11441        fn func(abcˇ def: i32) -> u32 {
11442        }
11443    "});
11444}
11445
11446#[gpui::test]
11447async fn active_diagnostics_dismiss_after_invalidation(
11448    executor: BackgroundExecutor,
11449    cx: &mut TestAppContext,
11450) {
11451    init_test(cx, |_| {});
11452
11453    let mut cx = EditorTestContext::new(cx).await;
11454    let lsp_store =
11455        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11456
11457    cx.set_state(indoc! {"
11458        ˇfn func(abc def: i32) -> u32 {
11459        }
11460    "});
11461
11462    let message = "Something's wrong!";
11463    cx.update(|_, cx| {
11464        lsp_store.update(cx, |lsp_store, cx| {
11465            lsp_store
11466                .update_diagnostics(
11467                    LanguageServerId(0),
11468                    lsp::PublishDiagnosticsParams {
11469                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11470                        version: None,
11471                        diagnostics: vec![lsp::Diagnostic {
11472                            range: lsp::Range::new(
11473                                lsp::Position::new(0, 11),
11474                                lsp::Position::new(0, 12),
11475                            ),
11476                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11477                            message: message.to_string(),
11478                            ..Default::default()
11479                        }],
11480                    },
11481                    &[],
11482                    cx,
11483                )
11484                .unwrap()
11485        });
11486    });
11487    executor.run_until_parked();
11488
11489    cx.update_editor(|editor, window, cx| {
11490        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11491        assert_eq!(
11492            editor
11493                .active_diagnostics
11494                .as_ref()
11495                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11496            Some(message),
11497            "Should have a diagnostics group activated"
11498        );
11499    });
11500    cx.assert_editor_state(indoc! {"
11501        fn func(abcˇ def: i32) -> u32 {
11502        }
11503    "});
11504
11505    cx.update(|_, cx| {
11506        lsp_store.update(cx, |lsp_store, cx| {
11507            lsp_store
11508                .update_diagnostics(
11509                    LanguageServerId(0),
11510                    lsp::PublishDiagnosticsParams {
11511                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11512                        version: None,
11513                        diagnostics: Vec::new(),
11514                    },
11515                    &[],
11516                    cx,
11517                )
11518                .unwrap()
11519        });
11520    });
11521    executor.run_until_parked();
11522    cx.update_editor(|editor, _, _| {
11523        assert_eq!(
11524            editor.active_diagnostics, None,
11525            "After no diagnostics set to the editor, no diagnostics should be active"
11526        );
11527    });
11528    cx.assert_editor_state(indoc! {"
11529        fn func(abcˇ def: i32) -> u32 {
11530        }
11531    "});
11532
11533    cx.update_editor(|editor, window, cx| {
11534        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11535        assert_eq!(
11536            editor.active_diagnostics, None,
11537            "Should be no diagnostics to go to and activate"
11538        );
11539    });
11540    cx.assert_editor_state(indoc! {"
11541        fn func(abcˇ def: i32) -> u32 {
11542        }
11543    "});
11544}
11545
11546#[gpui::test]
11547async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11548    init_test(cx, |_| {});
11549
11550    let mut cx = EditorTestContext::new(cx).await;
11551
11552    cx.set_state(indoc! {"
11553        fn func(abˇc def: i32) -> u32 {
11554        }
11555    "});
11556    let lsp_store =
11557        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11558
11559    cx.update(|_, cx| {
11560        lsp_store.update(cx, |lsp_store, cx| {
11561            lsp_store.update_diagnostics(
11562                LanguageServerId(0),
11563                lsp::PublishDiagnosticsParams {
11564                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11565                    version: None,
11566                    diagnostics: vec![lsp::Diagnostic {
11567                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11568                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11569                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11570                        ..Default::default()
11571                    }],
11572                },
11573                &[],
11574                cx,
11575            )
11576        })
11577    }).unwrap();
11578    cx.run_until_parked();
11579    cx.update_editor(|editor, window, cx| {
11580        hover_popover::hover(editor, &Default::default(), window, cx)
11581    });
11582    cx.run_until_parked();
11583    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11584}
11585
11586#[gpui::test]
11587async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11588    init_test(cx, |_| {});
11589
11590    let mut cx = EditorTestContext::new(cx).await;
11591
11592    let diff_base = r#"
11593        use some::mod;
11594
11595        const A: u32 = 42;
11596
11597        fn main() {
11598            println!("hello");
11599
11600            println!("world");
11601        }
11602        "#
11603    .unindent();
11604
11605    // Edits are modified, removed, modified, added
11606    cx.set_state(
11607        &r#"
11608        use some::modified;
11609
11610        ˇ
11611        fn main() {
11612            println!("hello there");
11613
11614            println!("around the");
11615            println!("world");
11616        }
11617        "#
11618        .unindent(),
11619    );
11620
11621    cx.set_head_text(&diff_base);
11622    executor.run_until_parked();
11623
11624    cx.update_editor(|editor, window, cx| {
11625        //Wrap around the bottom of the buffer
11626        for _ in 0..3 {
11627            editor.go_to_next_hunk(&GoToHunk, window, cx);
11628        }
11629    });
11630
11631    cx.assert_editor_state(
11632        &r#"
11633        ˇuse some::modified;
11634
11635
11636        fn main() {
11637            println!("hello there");
11638
11639            println!("around the");
11640            println!("world");
11641        }
11642        "#
11643        .unindent(),
11644    );
11645
11646    cx.update_editor(|editor, window, cx| {
11647        //Wrap around the top of the buffer
11648        for _ in 0..2 {
11649            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11650        }
11651    });
11652
11653    cx.assert_editor_state(
11654        &r#"
11655        use some::modified;
11656
11657
11658        fn main() {
11659        ˇ    println!("hello there");
11660
11661            println!("around the");
11662            println!("world");
11663        }
11664        "#
11665        .unindent(),
11666    );
11667
11668    cx.update_editor(|editor, window, cx| {
11669        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11670    });
11671
11672    cx.assert_editor_state(
11673        &r#"
11674        use some::modified;
11675
11676        ˇ
11677        fn main() {
11678            println!("hello there");
11679
11680            println!("around the");
11681            println!("world");
11682        }
11683        "#
11684        .unindent(),
11685    );
11686
11687    cx.update_editor(|editor, window, cx| {
11688        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11689    });
11690
11691    cx.assert_editor_state(
11692        &r#"
11693        ˇuse some::modified;
11694
11695
11696        fn main() {
11697            println!("hello there");
11698
11699            println!("around the");
11700            println!("world");
11701        }
11702        "#
11703        .unindent(),
11704    );
11705
11706    cx.update_editor(|editor, window, cx| {
11707        for _ in 0..2 {
11708            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11709        }
11710    });
11711
11712    cx.assert_editor_state(
11713        &r#"
11714        use some::modified;
11715
11716
11717        fn main() {
11718        ˇ    println!("hello there");
11719
11720            println!("around the");
11721            println!("world");
11722        }
11723        "#
11724        .unindent(),
11725    );
11726
11727    cx.update_editor(|editor, window, cx| {
11728        editor.fold(&Fold, window, cx);
11729    });
11730
11731    cx.update_editor(|editor, window, cx| {
11732        editor.go_to_next_hunk(&GoToHunk, window, cx);
11733    });
11734
11735    cx.assert_editor_state(
11736        &r#"
11737        ˇuse some::modified;
11738
11739
11740        fn main() {
11741            println!("hello there");
11742
11743            println!("around the");
11744            println!("world");
11745        }
11746        "#
11747        .unindent(),
11748    );
11749}
11750
11751#[test]
11752fn test_split_words() {
11753    fn split(text: &str) -> Vec<&str> {
11754        split_words(text).collect()
11755    }
11756
11757    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11758    assert_eq!(split("hello_world"), &["hello_", "world"]);
11759    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11760    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11761    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11762    assert_eq!(split("helloworld"), &["helloworld"]);
11763
11764    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11765}
11766
11767#[gpui::test]
11768async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11769    init_test(cx, |_| {});
11770
11771    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11772    let mut assert = |before, after| {
11773        let _state_context = cx.set_state(before);
11774        cx.run_until_parked();
11775        cx.update_editor(|editor, window, cx| {
11776            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11777        });
11778        cx.assert_editor_state(after);
11779    };
11780
11781    // Outside bracket jumps to outside of matching bracket
11782    assert("console.logˇ(var);", "console.log(var)ˇ;");
11783    assert("console.log(var)ˇ;", "console.logˇ(var);");
11784
11785    // Inside bracket jumps to inside of matching bracket
11786    assert("console.log(ˇvar);", "console.log(varˇ);");
11787    assert("console.log(varˇ);", "console.log(ˇvar);");
11788
11789    // When outside a bracket and inside, favor jumping to the inside bracket
11790    assert(
11791        "console.log('foo', [1, 2, 3]ˇ);",
11792        "console.log(ˇ'foo', [1, 2, 3]);",
11793    );
11794    assert(
11795        "console.log(ˇ'foo', [1, 2, 3]);",
11796        "console.log('foo', [1, 2, 3]ˇ);",
11797    );
11798
11799    // Bias forward if two options are equally likely
11800    assert(
11801        "let result = curried_fun()ˇ();",
11802        "let result = curried_fun()()ˇ;",
11803    );
11804
11805    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11806    assert(
11807        indoc! {"
11808            function test() {
11809                console.log('test')ˇ
11810            }"},
11811        indoc! {"
11812            function test() {
11813                console.logˇ('test')
11814            }"},
11815    );
11816}
11817
11818#[gpui::test]
11819async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11820    init_test(cx, |_| {});
11821
11822    let fs = FakeFs::new(cx.executor());
11823    fs.insert_tree(
11824        path!("/a"),
11825        json!({
11826            "main.rs": "fn main() { let a = 5; }",
11827            "other.rs": "// Test file",
11828        }),
11829    )
11830    .await;
11831    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11832
11833    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11834    language_registry.add(Arc::new(Language::new(
11835        LanguageConfig {
11836            name: "Rust".into(),
11837            matcher: LanguageMatcher {
11838                path_suffixes: vec!["rs".to_string()],
11839                ..Default::default()
11840            },
11841            brackets: BracketPairConfig {
11842                pairs: vec![BracketPair {
11843                    start: "{".to_string(),
11844                    end: "}".to_string(),
11845                    close: true,
11846                    surround: true,
11847                    newline: true,
11848                }],
11849                disabled_scopes_by_bracket_ix: Vec::new(),
11850            },
11851            ..Default::default()
11852        },
11853        Some(tree_sitter_rust::LANGUAGE.into()),
11854    )));
11855    let mut fake_servers = language_registry.register_fake_lsp(
11856        "Rust",
11857        FakeLspAdapter {
11858            capabilities: lsp::ServerCapabilities {
11859                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
11860                    first_trigger_character: "{".to_string(),
11861                    more_trigger_character: None,
11862                }),
11863                ..Default::default()
11864            },
11865            ..Default::default()
11866        },
11867    );
11868
11869    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11870
11871    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11872
11873    let worktree_id = workspace
11874        .update(cx, |workspace, _, cx| {
11875            workspace.project().update(cx, |project, cx| {
11876                project.worktrees(cx).next().unwrap().read(cx).id()
11877            })
11878        })
11879        .unwrap();
11880
11881    let buffer = project
11882        .update(cx, |project, cx| {
11883            project.open_local_buffer(path!("/a/main.rs"), cx)
11884        })
11885        .await
11886        .unwrap();
11887    let editor_handle = workspace
11888        .update(cx, |workspace, window, cx| {
11889            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
11890        })
11891        .unwrap()
11892        .await
11893        .unwrap()
11894        .downcast::<Editor>()
11895        .unwrap();
11896
11897    cx.executor().start_waiting();
11898    let fake_server = fake_servers.next().await.unwrap();
11899
11900    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
11901        assert_eq!(
11902            params.text_document_position.text_document.uri,
11903            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
11904        );
11905        assert_eq!(
11906            params.text_document_position.position,
11907            lsp::Position::new(0, 21),
11908        );
11909
11910        Ok(Some(vec![lsp::TextEdit {
11911            new_text: "]".to_string(),
11912            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
11913        }]))
11914    });
11915
11916    editor_handle.update_in(cx, |editor, window, cx| {
11917        window.focus(&editor.focus_handle(cx));
11918        editor.change_selections(None, window, cx, |s| {
11919            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
11920        });
11921        editor.handle_input("{", window, cx);
11922    });
11923
11924    cx.executor().run_until_parked();
11925
11926    buffer.update(cx, |buffer, _| {
11927        assert_eq!(
11928            buffer.text(),
11929            "fn main() { let a = {5}; }",
11930            "No extra braces from on type formatting should appear in the buffer"
11931        )
11932    });
11933}
11934
11935#[gpui::test]
11936async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
11937    init_test(cx, |_| {});
11938
11939    let fs = FakeFs::new(cx.executor());
11940    fs.insert_tree(
11941        path!("/a"),
11942        json!({
11943            "main.rs": "fn main() { let a = 5; }",
11944            "other.rs": "// Test file",
11945        }),
11946    )
11947    .await;
11948
11949    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11950
11951    let server_restarts = Arc::new(AtomicUsize::new(0));
11952    let closure_restarts = Arc::clone(&server_restarts);
11953    let language_server_name = "test language server";
11954    let language_name: LanguageName = "Rust".into();
11955
11956    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11957    language_registry.add(Arc::new(Language::new(
11958        LanguageConfig {
11959            name: language_name.clone(),
11960            matcher: LanguageMatcher {
11961                path_suffixes: vec!["rs".to_string()],
11962                ..Default::default()
11963            },
11964            ..Default::default()
11965        },
11966        Some(tree_sitter_rust::LANGUAGE.into()),
11967    )));
11968    let mut fake_servers = language_registry.register_fake_lsp(
11969        "Rust",
11970        FakeLspAdapter {
11971            name: language_server_name,
11972            initialization_options: Some(json!({
11973                "testOptionValue": true
11974            })),
11975            initializer: Some(Box::new(move |fake_server| {
11976                let task_restarts = Arc::clone(&closure_restarts);
11977                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
11978                    task_restarts.fetch_add(1, atomic::Ordering::Release);
11979                    futures::future::ready(Ok(()))
11980                });
11981            })),
11982            ..Default::default()
11983        },
11984    );
11985
11986    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11987    let _buffer = project
11988        .update(cx, |project, cx| {
11989            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
11990        })
11991        .await
11992        .unwrap();
11993    let _fake_server = fake_servers.next().await.unwrap();
11994    update_test_language_settings(cx, |language_settings| {
11995        language_settings.languages.insert(
11996            language_name.clone(),
11997            LanguageSettingsContent {
11998                tab_size: NonZeroU32::new(8),
11999                ..Default::default()
12000            },
12001        );
12002    });
12003    cx.executor().run_until_parked();
12004    assert_eq!(
12005        server_restarts.load(atomic::Ordering::Acquire),
12006        0,
12007        "Should not restart LSP server on an unrelated change"
12008    );
12009
12010    update_test_project_settings(cx, |project_settings| {
12011        project_settings.lsp.insert(
12012            "Some other server name".into(),
12013            LspSettings {
12014                binary: None,
12015                settings: None,
12016                initialization_options: Some(json!({
12017                    "some other init value": false
12018                })),
12019            },
12020        );
12021    });
12022    cx.executor().run_until_parked();
12023    assert_eq!(
12024        server_restarts.load(atomic::Ordering::Acquire),
12025        0,
12026        "Should not restart LSP server on an unrelated LSP settings change"
12027    );
12028
12029    update_test_project_settings(cx, |project_settings| {
12030        project_settings.lsp.insert(
12031            language_server_name.into(),
12032            LspSettings {
12033                binary: None,
12034                settings: None,
12035                initialization_options: Some(json!({
12036                    "anotherInitValue": false
12037                })),
12038            },
12039        );
12040    });
12041    cx.executor().run_until_parked();
12042    assert_eq!(
12043        server_restarts.load(atomic::Ordering::Acquire),
12044        1,
12045        "Should restart LSP server on a related LSP settings change"
12046    );
12047
12048    update_test_project_settings(cx, |project_settings| {
12049        project_settings.lsp.insert(
12050            language_server_name.into(),
12051            LspSettings {
12052                binary: None,
12053                settings: None,
12054                initialization_options: Some(json!({
12055                    "anotherInitValue": false
12056                })),
12057            },
12058        );
12059    });
12060    cx.executor().run_until_parked();
12061    assert_eq!(
12062        server_restarts.load(atomic::Ordering::Acquire),
12063        1,
12064        "Should not restart LSP server on a related LSP settings change that is the same"
12065    );
12066
12067    update_test_project_settings(cx, |project_settings| {
12068        project_settings.lsp.insert(
12069            language_server_name.into(),
12070            LspSettings {
12071                binary: None,
12072                settings: None,
12073                initialization_options: None,
12074            },
12075        );
12076    });
12077    cx.executor().run_until_parked();
12078    assert_eq!(
12079        server_restarts.load(atomic::Ordering::Acquire),
12080        2,
12081        "Should restart LSP server on another related LSP settings change"
12082    );
12083}
12084
12085#[gpui::test]
12086async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12087    init_test(cx, |_| {});
12088
12089    let mut cx = EditorLspTestContext::new_rust(
12090        lsp::ServerCapabilities {
12091            completion_provider: Some(lsp::CompletionOptions {
12092                trigger_characters: Some(vec![".".to_string()]),
12093                resolve_provider: Some(true),
12094                ..Default::default()
12095            }),
12096            ..Default::default()
12097        },
12098        cx,
12099    )
12100    .await;
12101
12102    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12103    cx.simulate_keystroke(".");
12104    let completion_item = lsp::CompletionItem {
12105        label: "some".into(),
12106        kind: Some(lsp::CompletionItemKind::SNIPPET),
12107        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12108        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12109            kind: lsp::MarkupKind::Markdown,
12110            value: "```rust\nSome(2)\n```".to_string(),
12111        })),
12112        deprecated: Some(false),
12113        sort_text: Some("fffffff2".to_string()),
12114        filter_text: Some("some".to_string()),
12115        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12116        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12117            range: lsp::Range {
12118                start: lsp::Position {
12119                    line: 0,
12120                    character: 22,
12121                },
12122                end: lsp::Position {
12123                    line: 0,
12124                    character: 22,
12125                },
12126            },
12127            new_text: "Some(2)".to_string(),
12128        })),
12129        additional_text_edits: Some(vec![lsp::TextEdit {
12130            range: lsp::Range {
12131                start: lsp::Position {
12132                    line: 0,
12133                    character: 20,
12134                },
12135                end: lsp::Position {
12136                    line: 0,
12137                    character: 22,
12138                },
12139            },
12140            new_text: "".to_string(),
12141        }]),
12142        ..Default::default()
12143    };
12144
12145    let closure_completion_item = completion_item.clone();
12146    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12147        let task_completion_item = closure_completion_item.clone();
12148        async move {
12149            Ok(Some(lsp::CompletionResponse::Array(vec![
12150                task_completion_item,
12151            ])))
12152        }
12153    });
12154
12155    request.next().await;
12156
12157    cx.condition(|editor, _| editor.context_menu_visible())
12158        .await;
12159    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12160        editor
12161            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12162            .unwrap()
12163    });
12164    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12165
12166    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12167        let task_completion_item = completion_item.clone();
12168        async move { Ok(task_completion_item) }
12169    })
12170    .next()
12171    .await
12172    .unwrap();
12173    apply_additional_edits.await.unwrap();
12174    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12175}
12176
12177#[gpui::test]
12178async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12179    init_test(cx, |_| {});
12180
12181    let mut cx = EditorLspTestContext::new_rust(
12182        lsp::ServerCapabilities {
12183            completion_provider: Some(lsp::CompletionOptions {
12184                trigger_characters: Some(vec![".".to_string()]),
12185                resolve_provider: Some(true),
12186                ..Default::default()
12187            }),
12188            ..Default::default()
12189        },
12190        cx,
12191    )
12192    .await;
12193
12194    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12195    cx.simulate_keystroke(".");
12196
12197    let item1 = lsp::CompletionItem {
12198        label: "method id()".to_string(),
12199        filter_text: Some("id".to_string()),
12200        detail: None,
12201        documentation: None,
12202        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12203            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12204            new_text: ".id".to_string(),
12205        })),
12206        ..lsp::CompletionItem::default()
12207    };
12208
12209    let item2 = lsp::CompletionItem {
12210        label: "other".to_string(),
12211        filter_text: Some("other".to_string()),
12212        detail: None,
12213        documentation: None,
12214        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12215            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12216            new_text: ".other".to_string(),
12217        })),
12218        ..lsp::CompletionItem::default()
12219    };
12220
12221    let item1 = item1.clone();
12222    cx.handle_request::<lsp::request::Completion, _, _>({
12223        let item1 = item1.clone();
12224        move |_, _, _| {
12225            let item1 = item1.clone();
12226            let item2 = item2.clone();
12227            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12228        }
12229    })
12230    .next()
12231    .await;
12232
12233    cx.condition(|editor, _| editor.context_menu_visible())
12234        .await;
12235    cx.update_editor(|editor, _, _| {
12236        let context_menu = editor.context_menu.borrow_mut();
12237        let context_menu = context_menu
12238            .as_ref()
12239            .expect("Should have the context menu deployed");
12240        match context_menu {
12241            CodeContextMenu::Completions(completions_menu) => {
12242                let completions = completions_menu.completions.borrow_mut();
12243                assert_eq!(
12244                    completions
12245                        .iter()
12246                        .map(|completion| &completion.label.text)
12247                        .collect::<Vec<_>>(),
12248                    vec!["method id()", "other"]
12249                )
12250            }
12251            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12252        }
12253    });
12254
12255    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12256        let item1 = item1.clone();
12257        move |_, item_to_resolve, _| {
12258            let item1 = item1.clone();
12259            async move {
12260                if item1 == item_to_resolve {
12261                    Ok(lsp::CompletionItem {
12262                        label: "method id()".to_string(),
12263                        filter_text: Some("id".to_string()),
12264                        detail: Some("Now resolved!".to_string()),
12265                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12266                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12267                            range: lsp::Range::new(
12268                                lsp::Position::new(0, 22),
12269                                lsp::Position::new(0, 22),
12270                            ),
12271                            new_text: ".id".to_string(),
12272                        })),
12273                        ..lsp::CompletionItem::default()
12274                    })
12275                } else {
12276                    Ok(item_to_resolve)
12277                }
12278            }
12279        }
12280    })
12281    .next()
12282    .await
12283    .unwrap();
12284    cx.run_until_parked();
12285
12286    cx.update_editor(|editor, window, cx| {
12287        editor.context_menu_next(&Default::default(), window, cx);
12288    });
12289
12290    cx.update_editor(|editor, _, _| {
12291        let context_menu = editor.context_menu.borrow_mut();
12292        let context_menu = context_menu
12293            .as_ref()
12294            .expect("Should have the context menu deployed");
12295        match context_menu {
12296            CodeContextMenu::Completions(completions_menu) => {
12297                let completions = completions_menu.completions.borrow_mut();
12298                assert_eq!(
12299                    completions
12300                        .iter()
12301                        .map(|completion| &completion.label.text)
12302                        .collect::<Vec<_>>(),
12303                    vec!["method id() Now resolved!", "other"],
12304                    "Should update first completion label, but not second as the filter text did not match."
12305                );
12306            }
12307            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12308        }
12309    });
12310}
12311
12312#[gpui::test]
12313async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12314    init_test(cx, |_| {});
12315
12316    let mut cx = EditorLspTestContext::new_rust(
12317        lsp::ServerCapabilities {
12318            completion_provider: Some(lsp::CompletionOptions {
12319                trigger_characters: Some(vec![".".to_string()]),
12320                resolve_provider: Some(true),
12321                ..Default::default()
12322            }),
12323            ..Default::default()
12324        },
12325        cx,
12326    )
12327    .await;
12328
12329    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12330    cx.simulate_keystroke(".");
12331
12332    let unresolved_item_1 = lsp::CompletionItem {
12333        label: "id".to_string(),
12334        filter_text: Some("id".to_string()),
12335        detail: None,
12336        documentation: None,
12337        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12338            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12339            new_text: ".id".to_string(),
12340        })),
12341        ..lsp::CompletionItem::default()
12342    };
12343    let resolved_item_1 = lsp::CompletionItem {
12344        additional_text_edits: Some(vec![lsp::TextEdit {
12345            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12346            new_text: "!!".to_string(),
12347        }]),
12348        ..unresolved_item_1.clone()
12349    };
12350    let unresolved_item_2 = lsp::CompletionItem {
12351        label: "other".to_string(),
12352        filter_text: Some("other".to_string()),
12353        detail: None,
12354        documentation: None,
12355        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12356            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12357            new_text: ".other".to_string(),
12358        })),
12359        ..lsp::CompletionItem::default()
12360    };
12361    let resolved_item_2 = lsp::CompletionItem {
12362        additional_text_edits: Some(vec![lsp::TextEdit {
12363            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12364            new_text: "??".to_string(),
12365        }]),
12366        ..unresolved_item_2.clone()
12367    };
12368
12369    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12370    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12371    cx.lsp
12372        .server
12373        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12374            let unresolved_item_1 = unresolved_item_1.clone();
12375            let resolved_item_1 = resolved_item_1.clone();
12376            let unresolved_item_2 = unresolved_item_2.clone();
12377            let resolved_item_2 = resolved_item_2.clone();
12378            let resolve_requests_1 = resolve_requests_1.clone();
12379            let resolve_requests_2 = resolve_requests_2.clone();
12380            move |unresolved_request, _| {
12381                let unresolved_item_1 = unresolved_item_1.clone();
12382                let resolved_item_1 = resolved_item_1.clone();
12383                let unresolved_item_2 = unresolved_item_2.clone();
12384                let resolved_item_2 = resolved_item_2.clone();
12385                let resolve_requests_1 = resolve_requests_1.clone();
12386                let resolve_requests_2 = resolve_requests_2.clone();
12387                async move {
12388                    if unresolved_request == unresolved_item_1 {
12389                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12390                        Ok(resolved_item_1.clone())
12391                    } else if unresolved_request == unresolved_item_2 {
12392                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12393                        Ok(resolved_item_2.clone())
12394                    } else {
12395                        panic!("Unexpected completion item {unresolved_request:?}")
12396                    }
12397                }
12398            }
12399        })
12400        .detach();
12401
12402    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12403        let unresolved_item_1 = unresolved_item_1.clone();
12404        let unresolved_item_2 = unresolved_item_2.clone();
12405        async move {
12406            Ok(Some(lsp::CompletionResponse::Array(vec![
12407                unresolved_item_1,
12408                unresolved_item_2,
12409            ])))
12410        }
12411    })
12412    .next()
12413    .await;
12414
12415    cx.condition(|editor, _| editor.context_menu_visible())
12416        .await;
12417    cx.update_editor(|editor, _, _| {
12418        let context_menu = editor.context_menu.borrow_mut();
12419        let context_menu = context_menu
12420            .as_ref()
12421            .expect("Should have the context menu deployed");
12422        match context_menu {
12423            CodeContextMenu::Completions(completions_menu) => {
12424                let completions = completions_menu.completions.borrow_mut();
12425                assert_eq!(
12426                    completions
12427                        .iter()
12428                        .map(|completion| &completion.label.text)
12429                        .collect::<Vec<_>>(),
12430                    vec!["id", "other"]
12431                )
12432            }
12433            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12434        }
12435    });
12436    cx.run_until_parked();
12437
12438    cx.update_editor(|editor, window, cx| {
12439        editor.context_menu_next(&ContextMenuNext, window, cx);
12440    });
12441    cx.run_until_parked();
12442    cx.update_editor(|editor, window, cx| {
12443        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12444    });
12445    cx.run_until_parked();
12446    cx.update_editor(|editor, window, cx| {
12447        editor.context_menu_next(&ContextMenuNext, window, cx);
12448    });
12449    cx.run_until_parked();
12450    cx.update_editor(|editor, window, cx| {
12451        editor
12452            .compose_completion(&ComposeCompletion::default(), window, cx)
12453            .expect("No task returned")
12454    })
12455    .await
12456    .expect("Completion failed");
12457    cx.run_until_parked();
12458
12459    cx.update_editor(|editor, _, cx| {
12460        assert_eq!(
12461            resolve_requests_1.load(atomic::Ordering::Acquire),
12462            1,
12463            "Should always resolve once despite multiple selections"
12464        );
12465        assert_eq!(
12466            resolve_requests_2.load(atomic::Ordering::Acquire),
12467            1,
12468            "Should always resolve once after multiple selections and applying the completion"
12469        );
12470        assert_eq!(
12471            editor.text(cx),
12472            "fn main() { let a = ??.other; }",
12473            "Should use resolved data when applying the completion"
12474        );
12475    });
12476}
12477
12478#[gpui::test]
12479async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12480    init_test(cx, |_| {});
12481
12482    let item_0 = lsp::CompletionItem {
12483        label: "abs".into(),
12484        insert_text: Some("abs".into()),
12485        data: Some(json!({ "very": "special"})),
12486        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12487        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12488            lsp::InsertReplaceEdit {
12489                new_text: "abs".to_string(),
12490                insert: lsp::Range::default(),
12491                replace: lsp::Range::default(),
12492            },
12493        )),
12494        ..lsp::CompletionItem::default()
12495    };
12496    let items = iter::once(item_0.clone())
12497        .chain((11..51).map(|i| lsp::CompletionItem {
12498            label: format!("item_{}", i),
12499            insert_text: Some(format!("item_{}", i)),
12500            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12501            ..lsp::CompletionItem::default()
12502        }))
12503        .collect::<Vec<_>>();
12504
12505    let default_commit_characters = vec!["?".to_string()];
12506    let default_data = json!({ "default": "data"});
12507    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12508    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12509    let default_edit_range = lsp::Range {
12510        start: lsp::Position {
12511            line: 0,
12512            character: 5,
12513        },
12514        end: lsp::Position {
12515            line: 0,
12516            character: 5,
12517        },
12518    };
12519
12520    let mut cx = EditorLspTestContext::new_rust(
12521        lsp::ServerCapabilities {
12522            completion_provider: Some(lsp::CompletionOptions {
12523                trigger_characters: Some(vec![".".to_string()]),
12524                resolve_provider: Some(true),
12525                ..Default::default()
12526            }),
12527            ..Default::default()
12528        },
12529        cx,
12530    )
12531    .await;
12532
12533    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12534    cx.simulate_keystroke(".");
12535
12536    let completion_data = default_data.clone();
12537    let completion_characters = default_commit_characters.clone();
12538    let completion_items = items.clone();
12539    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12540        let default_data = completion_data.clone();
12541        let default_commit_characters = completion_characters.clone();
12542        let items = completion_items.clone();
12543        async move {
12544            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12545                items,
12546                item_defaults: Some(lsp::CompletionListItemDefaults {
12547                    data: Some(default_data.clone()),
12548                    commit_characters: Some(default_commit_characters.clone()),
12549                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12550                        default_edit_range,
12551                    )),
12552                    insert_text_format: Some(default_insert_text_format),
12553                    insert_text_mode: Some(default_insert_text_mode),
12554                }),
12555                ..lsp::CompletionList::default()
12556            })))
12557        }
12558    })
12559    .next()
12560    .await;
12561
12562    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12563    cx.lsp
12564        .server
12565        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12566            let closure_resolved_items = resolved_items.clone();
12567            move |item_to_resolve, _| {
12568                let closure_resolved_items = closure_resolved_items.clone();
12569                async move {
12570                    closure_resolved_items.lock().push(item_to_resolve.clone());
12571                    Ok(item_to_resolve)
12572                }
12573            }
12574        })
12575        .detach();
12576
12577    cx.condition(|editor, _| editor.context_menu_visible())
12578        .await;
12579    cx.run_until_parked();
12580    cx.update_editor(|editor, _, _| {
12581        let menu = editor.context_menu.borrow_mut();
12582        match menu.as_ref().expect("should have the completions menu") {
12583            CodeContextMenu::Completions(completions_menu) => {
12584                assert_eq!(
12585                    completions_menu
12586                        .entries
12587                        .borrow()
12588                        .iter()
12589                        .map(|mat| mat.string.clone())
12590                        .collect::<Vec<String>>(),
12591                    items
12592                        .iter()
12593                        .map(|completion| completion.label.clone())
12594                        .collect::<Vec<String>>()
12595                );
12596            }
12597            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12598        }
12599    });
12600    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12601    // with 4 from the end.
12602    assert_eq!(
12603        *resolved_items.lock(),
12604        [&items[0..16], &items[items.len() - 4..items.len()]]
12605            .concat()
12606            .iter()
12607            .cloned()
12608            .map(|mut item| {
12609                if item.data.is_none() {
12610                    item.data = Some(default_data.clone());
12611                }
12612                item
12613            })
12614            .collect::<Vec<lsp::CompletionItem>>(),
12615        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12616    );
12617    resolved_items.lock().clear();
12618
12619    cx.update_editor(|editor, window, cx| {
12620        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12621    });
12622    cx.run_until_parked();
12623    // Completions that have already been resolved are skipped.
12624    assert_eq!(
12625        *resolved_items.lock(),
12626        items[items.len() - 16..items.len() - 4]
12627            .iter()
12628            .cloned()
12629            .map(|mut item| {
12630                if item.data.is_none() {
12631                    item.data = Some(default_data.clone());
12632                }
12633                item
12634            })
12635            .collect::<Vec<lsp::CompletionItem>>()
12636    );
12637    resolved_items.lock().clear();
12638}
12639
12640#[gpui::test]
12641async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12642    init_test(cx, |_| {});
12643
12644    let mut cx = EditorLspTestContext::new(
12645        Language::new(
12646            LanguageConfig {
12647                matcher: LanguageMatcher {
12648                    path_suffixes: vec!["jsx".into()],
12649                    ..Default::default()
12650                },
12651                overrides: [(
12652                    "element".into(),
12653                    LanguageConfigOverride {
12654                        word_characters: Override::Set(['-'].into_iter().collect()),
12655                        ..Default::default()
12656                    },
12657                )]
12658                .into_iter()
12659                .collect(),
12660                ..Default::default()
12661            },
12662            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12663        )
12664        .with_override_query("(jsx_self_closing_element) @element")
12665        .unwrap(),
12666        lsp::ServerCapabilities {
12667            completion_provider: Some(lsp::CompletionOptions {
12668                trigger_characters: Some(vec![":".to_string()]),
12669                ..Default::default()
12670            }),
12671            ..Default::default()
12672        },
12673        cx,
12674    )
12675    .await;
12676
12677    cx.lsp
12678        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12679            Ok(Some(lsp::CompletionResponse::Array(vec![
12680                lsp::CompletionItem {
12681                    label: "bg-blue".into(),
12682                    ..Default::default()
12683                },
12684                lsp::CompletionItem {
12685                    label: "bg-red".into(),
12686                    ..Default::default()
12687                },
12688                lsp::CompletionItem {
12689                    label: "bg-yellow".into(),
12690                    ..Default::default()
12691                },
12692            ])))
12693        });
12694
12695    cx.set_state(r#"<p class="bgˇ" />"#);
12696
12697    // Trigger completion when typing a dash, because the dash is an extra
12698    // word character in the 'element' scope, which contains the cursor.
12699    cx.simulate_keystroke("-");
12700    cx.executor().run_until_parked();
12701    cx.update_editor(|editor, _, _| {
12702        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12703        {
12704            assert_eq!(
12705                completion_menu_entries(&menu),
12706                &["bg-red", "bg-blue", "bg-yellow"]
12707            );
12708        } else {
12709            panic!("expected completion menu to be open");
12710        }
12711    });
12712
12713    cx.simulate_keystroke("l");
12714    cx.executor().run_until_parked();
12715    cx.update_editor(|editor, _, _| {
12716        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12717        {
12718            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12719        } else {
12720            panic!("expected completion menu to be open");
12721        }
12722    });
12723
12724    // When filtering completions, consider the character after the '-' to
12725    // be the start of a subword.
12726    cx.set_state(r#"<p class="yelˇ" />"#);
12727    cx.simulate_keystroke("l");
12728    cx.executor().run_until_parked();
12729    cx.update_editor(|editor, _, _| {
12730        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12731        {
12732            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12733        } else {
12734            panic!("expected completion menu to be open");
12735        }
12736    });
12737}
12738
12739fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12740    let entries = menu.entries.borrow();
12741    entries.iter().map(|mat| mat.string.clone()).collect()
12742}
12743
12744#[gpui::test]
12745async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12746    init_test(cx, |settings| {
12747        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12748            FormatterList(vec![Formatter::Prettier].into()),
12749        ))
12750    });
12751
12752    let fs = FakeFs::new(cx.executor());
12753    fs.insert_file(path!("/file.ts"), Default::default()).await;
12754
12755    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12756    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12757
12758    language_registry.add(Arc::new(Language::new(
12759        LanguageConfig {
12760            name: "TypeScript".into(),
12761            matcher: LanguageMatcher {
12762                path_suffixes: vec!["ts".to_string()],
12763                ..Default::default()
12764            },
12765            ..Default::default()
12766        },
12767        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12768    )));
12769    update_test_language_settings(cx, |settings| {
12770        settings.defaults.prettier = Some(PrettierSettings {
12771            allowed: true,
12772            ..PrettierSettings::default()
12773        });
12774    });
12775
12776    let test_plugin = "test_plugin";
12777    let _ = language_registry.register_fake_lsp(
12778        "TypeScript",
12779        FakeLspAdapter {
12780            prettier_plugins: vec![test_plugin],
12781            ..Default::default()
12782        },
12783    );
12784
12785    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12786    let buffer = project
12787        .update(cx, |project, cx| {
12788            project.open_local_buffer(path!("/file.ts"), cx)
12789        })
12790        .await
12791        .unwrap();
12792
12793    let buffer_text = "one\ntwo\nthree\n";
12794    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12795    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12796    editor.update_in(cx, |editor, window, cx| {
12797        editor.set_text(buffer_text, window, cx)
12798    });
12799
12800    editor
12801        .update_in(cx, |editor, window, cx| {
12802            editor.perform_format(
12803                project.clone(),
12804                FormatTrigger::Manual,
12805                FormatTarget::Buffers,
12806                window,
12807                cx,
12808            )
12809        })
12810        .unwrap()
12811        .await;
12812    assert_eq!(
12813        editor.update(cx, |editor, cx| editor.text(cx)),
12814        buffer_text.to_string() + prettier_format_suffix,
12815        "Test prettier formatting was not applied to the original buffer text",
12816    );
12817
12818    update_test_language_settings(cx, |settings| {
12819        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12820    });
12821    let format = editor.update_in(cx, |editor, window, cx| {
12822        editor.perform_format(
12823            project.clone(),
12824            FormatTrigger::Manual,
12825            FormatTarget::Buffers,
12826            window,
12827            cx,
12828        )
12829    });
12830    format.await.unwrap();
12831    assert_eq!(
12832        editor.update(cx, |editor, cx| editor.text(cx)),
12833        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12834        "Autoformatting (via test prettier) was not applied to the original buffer text",
12835    );
12836}
12837
12838#[gpui::test]
12839async fn test_addition_reverts(cx: &mut TestAppContext) {
12840    init_test(cx, |_| {});
12841    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12842    let base_text = indoc! {r#"
12843        struct Row;
12844        struct Row1;
12845        struct Row2;
12846
12847        struct Row4;
12848        struct Row5;
12849        struct Row6;
12850
12851        struct Row8;
12852        struct Row9;
12853        struct Row10;"#};
12854
12855    // When addition hunks are not adjacent to carets, no hunk revert is performed
12856    assert_hunk_revert(
12857        indoc! {r#"struct Row;
12858                   struct Row1;
12859                   struct Row1.1;
12860                   struct Row1.2;
12861                   struct Row2;ˇ
12862
12863                   struct Row4;
12864                   struct Row5;
12865                   struct Row6;
12866
12867                   struct Row8;
12868                   ˇstruct Row9;
12869                   struct Row9.1;
12870                   struct Row9.2;
12871                   struct Row9.3;
12872                   struct Row10;"#},
12873        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12874        indoc! {r#"struct Row;
12875                   struct Row1;
12876                   struct Row1.1;
12877                   struct Row1.2;
12878                   struct Row2;ˇ
12879
12880                   struct Row4;
12881                   struct Row5;
12882                   struct Row6;
12883
12884                   struct Row8;
12885                   ˇstruct Row9;
12886                   struct Row9.1;
12887                   struct Row9.2;
12888                   struct Row9.3;
12889                   struct Row10;"#},
12890        base_text,
12891        &mut cx,
12892    );
12893    // Same for selections
12894    assert_hunk_revert(
12895        indoc! {r#"struct Row;
12896                   struct Row1;
12897                   struct Row2;
12898                   struct Row2.1;
12899                   struct Row2.2;
12900                   «ˇ
12901                   struct Row4;
12902                   struct» Row5;
12903                   «struct Row6;
12904                   ˇ»
12905                   struct Row9.1;
12906                   struct Row9.2;
12907                   struct Row9.3;
12908                   struct Row8;
12909                   struct Row9;
12910                   struct Row10;"#},
12911        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
12912        indoc! {r#"struct Row;
12913                   struct Row1;
12914                   struct Row2;
12915                   struct Row2.1;
12916                   struct Row2.2;
12917                   «ˇ
12918                   struct Row4;
12919                   struct» Row5;
12920                   «struct Row6;
12921                   ˇ»
12922                   struct Row9.1;
12923                   struct Row9.2;
12924                   struct Row9.3;
12925                   struct Row8;
12926                   struct Row9;
12927                   struct Row10;"#},
12928        base_text,
12929        &mut cx,
12930    );
12931
12932    // When carets and selections intersect the addition hunks, those are reverted.
12933    // Adjacent carets got merged.
12934    assert_hunk_revert(
12935        indoc! {r#"struct Row;
12936                   ˇ// something on the top
12937                   struct Row1;
12938                   struct Row2;
12939                   struct Roˇw3.1;
12940                   struct Row2.2;
12941                   struct Row2.3;ˇ
12942
12943                   struct Row4;
12944                   struct ˇRow5.1;
12945                   struct Row5.2;
12946                   struct «Rowˇ»5.3;
12947                   struct Row5;
12948                   struct Row6;
12949                   ˇ
12950                   struct Row9.1;
12951                   struct «Rowˇ»9.2;
12952                   struct «ˇRow»9.3;
12953                   struct Row8;
12954                   struct Row9;
12955                   «ˇ// something on bottom»
12956                   struct Row10;"#},
12957        vec![
12958            DiffHunkStatusKind::Added,
12959            DiffHunkStatusKind::Added,
12960            DiffHunkStatusKind::Added,
12961            DiffHunkStatusKind::Added,
12962            DiffHunkStatusKind::Added,
12963        ],
12964        indoc! {r#"struct Row;
12965                   ˇstruct Row1;
12966                   struct Row2;
12967                   ˇ
12968                   struct Row4;
12969                   ˇstruct Row5;
12970                   struct Row6;
12971                   ˇ
12972                   ˇstruct Row8;
12973                   struct Row9;
12974                   ˇstruct Row10;"#},
12975        base_text,
12976        &mut cx,
12977    );
12978}
12979
12980#[gpui::test]
12981async fn test_modification_reverts(cx: &mut TestAppContext) {
12982    init_test(cx, |_| {});
12983    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12984    let base_text = indoc! {r#"
12985        struct Row;
12986        struct Row1;
12987        struct Row2;
12988
12989        struct Row4;
12990        struct Row5;
12991        struct Row6;
12992
12993        struct Row8;
12994        struct Row9;
12995        struct Row10;"#};
12996
12997    // Modification hunks behave the same as the addition ones.
12998    assert_hunk_revert(
12999        indoc! {r#"struct Row;
13000                   struct Row1;
13001                   struct Row33;
13002                   ˇ
13003                   struct Row4;
13004                   struct Row5;
13005                   struct Row6;
13006                   ˇ
13007                   struct Row99;
13008                   struct Row9;
13009                   struct Row10;"#},
13010        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13011        indoc! {r#"struct Row;
13012                   struct Row1;
13013                   struct Row33;
13014                   ˇ
13015                   struct Row4;
13016                   struct Row5;
13017                   struct Row6;
13018                   ˇ
13019                   struct Row99;
13020                   struct Row9;
13021                   struct Row10;"#},
13022        base_text,
13023        &mut cx,
13024    );
13025    assert_hunk_revert(
13026        indoc! {r#"struct Row;
13027                   struct Row1;
13028                   struct Row33;
13029                   «ˇ
13030                   struct Row4;
13031                   struct» Row5;
13032                   «struct Row6;
13033                   ˇ»
13034                   struct Row99;
13035                   struct Row9;
13036                   struct Row10;"#},
13037        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13038        indoc! {r#"struct Row;
13039                   struct Row1;
13040                   struct Row33;
13041                   «ˇ
13042                   struct Row4;
13043                   struct» Row5;
13044                   «struct Row6;
13045                   ˇ»
13046                   struct Row99;
13047                   struct Row9;
13048                   struct Row10;"#},
13049        base_text,
13050        &mut cx,
13051    );
13052
13053    assert_hunk_revert(
13054        indoc! {r#"ˇstruct Row1.1;
13055                   struct Row1;
13056                   «ˇstr»uct Row22;
13057
13058                   struct ˇRow44;
13059                   struct Row5;
13060                   struct «Rˇ»ow66;ˇ
13061
13062                   «struˇ»ct Row88;
13063                   struct Row9;
13064                   struct Row1011;ˇ"#},
13065        vec![
13066            DiffHunkStatusKind::Modified,
13067            DiffHunkStatusKind::Modified,
13068            DiffHunkStatusKind::Modified,
13069            DiffHunkStatusKind::Modified,
13070            DiffHunkStatusKind::Modified,
13071            DiffHunkStatusKind::Modified,
13072        ],
13073        indoc! {r#"struct Row;
13074                   ˇstruct Row1;
13075                   struct Row2;
13076                   ˇ
13077                   struct Row4;
13078                   ˇstruct Row5;
13079                   struct Row6;
13080                   ˇ
13081                   struct Row8;
13082                   ˇstruct Row9;
13083                   struct Row10;ˇ"#},
13084        base_text,
13085        &mut cx,
13086    );
13087}
13088
13089#[gpui::test]
13090async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13091    init_test(cx, |_| {});
13092    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13093    let base_text = indoc! {r#"
13094        one
13095
13096        two
13097        three
13098        "#};
13099
13100    cx.set_head_text(base_text);
13101    cx.set_state("\nˇ\n");
13102    cx.executor().run_until_parked();
13103    cx.update_editor(|editor, _window, cx| {
13104        editor.expand_selected_diff_hunks(cx);
13105    });
13106    cx.executor().run_until_parked();
13107    cx.update_editor(|editor, window, cx| {
13108        editor.backspace(&Default::default(), window, cx);
13109    });
13110    cx.run_until_parked();
13111    cx.assert_state_with_diff(
13112        indoc! {r#"
13113
13114        - two
13115        - threeˇ
13116        +
13117        "#}
13118        .to_string(),
13119    );
13120}
13121
13122#[gpui::test]
13123async fn test_deletion_reverts(cx: &mut TestAppContext) {
13124    init_test(cx, |_| {});
13125    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13126    let base_text = indoc! {r#"struct Row;
13127struct Row1;
13128struct Row2;
13129
13130struct Row4;
13131struct Row5;
13132struct Row6;
13133
13134struct Row8;
13135struct Row9;
13136struct Row10;"#};
13137
13138    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13139    assert_hunk_revert(
13140        indoc! {r#"struct Row;
13141                   struct Row2;
13142
13143                   ˇstruct Row4;
13144                   struct Row5;
13145                   struct Row6;
13146                   ˇ
13147                   struct Row8;
13148                   struct Row10;"#},
13149        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13150        indoc! {r#"struct Row;
13151                   struct Row2;
13152
13153                   ˇstruct Row4;
13154                   struct Row5;
13155                   struct Row6;
13156                   ˇ
13157                   struct Row8;
13158                   struct Row10;"#},
13159        base_text,
13160        &mut cx,
13161    );
13162    assert_hunk_revert(
13163        indoc! {r#"struct Row;
13164                   struct Row2;
13165
13166                   «ˇstruct Row4;
13167                   struct» Row5;
13168                   «struct Row6;
13169                   ˇ»
13170                   struct Row8;
13171                   struct Row10;"#},
13172        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13173        indoc! {r#"struct Row;
13174                   struct Row2;
13175
13176                   «ˇstruct Row4;
13177                   struct» Row5;
13178                   «struct Row6;
13179                   ˇ»
13180                   struct Row8;
13181                   struct Row10;"#},
13182        base_text,
13183        &mut cx,
13184    );
13185
13186    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13187    assert_hunk_revert(
13188        indoc! {r#"struct Row;
13189                   ˇstruct Row2;
13190
13191                   struct Row4;
13192                   struct Row5;
13193                   struct Row6;
13194
13195                   struct Row8;ˇ
13196                   struct Row10;"#},
13197        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13198        indoc! {r#"struct Row;
13199                   struct Row1;
13200                   ˇstruct Row2;
13201
13202                   struct Row4;
13203                   struct Row5;
13204                   struct Row6;
13205
13206                   struct Row8;ˇ
13207                   struct Row9;
13208                   struct Row10;"#},
13209        base_text,
13210        &mut cx,
13211    );
13212    assert_hunk_revert(
13213        indoc! {r#"struct Row;
13214                   struct Row2«ˇ;
13215                   struct Row4;
13216                   struct» Row5;
13217                   «struct Row6;
13218
13219                   struct Row8;ˇ»
13220                   struct Row10;"#},
13221        vec![
13222            DiffHunkStatusKind::Deleted,
13223            DiffHunkStatusKind::Deleted,
13224            DiffHunkStatusKind::Deleted,
13225        ],
13226        indoc! {r#"struct Row;
13227                   struct Row1;
13228                   struct Row2«ˇ;
13229
13230                   struct Row4;
13231                   struct» Row5;
13232                   «struct Row6;
13233
13234                   struct Row8;ˇ»
13235                   struct Row9;
13236                   struct Row10;"#},
13237        base_text,
13238        &mut cx,
13239    );
13240}
13241
13242#[gpui::test]
13243async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13244    init_test(cx, |_| {});
13245
13246    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13247    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13248    let base_text_3 =
13249        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13250
13251    let text_1 = edit_first_char_of_every_line(base_text_1);
13252    let text_2 = edit_first_char_of_every_line(base_text_2);
13253    let text_3 = edit_first_char_of_every_line(base_text_3);
13254
13255    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13256    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13257    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13258
13259    let multibuffer = cx.new(|cx| {
13260        let mut multibuffer = MultiBuffer::new(ReadWrite);
13261        multibuffer.push_excerpts(
13262            buffer_1.clone(),
13263            [
13264                ExcerptRange {
13265                    context: Point::new(0, 0)..Point::new(3, 0),
13266                    primary: None,
13267                },
13268                ExcerptRange {
13269                    context: Point::new(5, 0)..Point::new(7, 0),
13270                    primary: None,
13271                },
13272                ExcerptRange {
13273                    context: Point::new(9, 0)..Point::new(10, 4),
13274                    primary: None,
13275                },
13276            ],
13277            cx,
13278        );
13279        multibuffer.push_excerpts(
13280            buffer_2.clone(),
13281            [
13282                ExcerptRange {
13283                    context: Point::new(0, 0)..Point::new(3, 0),
13284                    primary: None,
13285                },
13286                ExcerptRange {
13287                    context: Point::new(5, 0)..Point::new(7, 0),
13288                    primary: None,
13289                },
13290                ExcerptRange {
13291                    context: Point::new(9, 0)..Point::new(10, 4),
13292                    primary: None,
13293                },
13294            ],
13295            cx,
13296        );
13297        multibuffer.push_excerpts(
13298            buffer_3.clone(),
13299            [
13300                ExcerptRange {
13301                    context: Point::new(0, 0)..Point::new(3, 0),
13302                    primary: None,
13303                },
13304                ExcerptRange {
13305                    context: Point::new(5, 0)..Point::new(7, 0),
13306                    primary: None,
13307                },
13308                ExcerptRange {
13309                    context: Point::new(9, 0)..Point::new(10, 4),
13310                    primary: None,
13311                },
13312            ],
13313            cx,
13314        );
13315        multibuffer
13316    });
13317
13318    let fs = FakeFs::new(cx.executor());
13319    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13320    let (editor, cx) = cx
13321        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13322    editor.update_in(cx, |editor, _window, cx| {
13323        for (buffer, diff_base) in [
13324            (buffer_1.clone(), base_text_1),
13325            (buffer_2.clone(), base_text_2),
13326            (buffer_3.clone(), base_text_3),
13327        ] {
13328            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13329            editor
13330                .buffer
13331                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13332        }
13333    });
13334    cx.executor().run_until_parked();
13335
13336    editor.update_in(cx, |editor, window, cx| {
13337        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}");
13338        editor.select_all(&SelectAll, window, cx);
13339        editor.git_restore(&Default::default(), window, cx);
13340    });
13341    cx.executor().run_until_parked();
13342
13343    // When all ranges are selected, all buffer hunks are reverted.
13344    editor.update(cx, |editor, cx| {
13345        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");
13346    });
13347    buffer_1.update(cx, |buffer, _| {
13348        assert_eq!(buffer.text(), base_text_1);
13349    });
13350    buffer_2.update(cx, |buffer, _| {
13351        assert_eq!(buffer.text(), base_text_2);
13352    });
13353    buffer_3.update(cx, |buffer, _| {
13354        assert_eq!(buffer.text(), base_text_3);
13355    });
13356
13357    editor.update_in(cx, |editor, window, cx| {
13358        editor.undo(&Default::default(), window, cx);
13359    });
13360
13361    editor.update_in(cx, |editor, window, cx| {
13362        editor.change_selections(None, window, cx, |s| {
13363            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13364        });
13365        editor.git_restore(&Default::default(), window, cx);
13366    });
13367
13368    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13369    // but not affect buffer_2 and its related excerpts.
13370    editor.update(cx, |editor, cx| {
13371        assert_eq!(
13372            editor.text(cx),
13373            "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}"
13374        );
13375    });
13376    buffer_1.update(cx, |buffer, _| {
13377        assert_eq!(buffer.text(), base_text_1);
13378    });
13379    buffer_2.update(cx, |buffer, _| {
13380        assert_eq!(
13381            buffer.text(),
13382            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13383        );
13384    });
13385    buffer_3.update(cx, |buffer, _| {
13386        assert_eq!(
13387            buffer.text(),
13388            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13389        );
13390    });
13391
13392    fn edit_first_char_of_every_line(text: &str) -> String {
13393        text.split('\n')
13394            .map(|line| format!("X{}", &line[1..]))
13395            .collect::<Vec<_>>()
13396            .join("\n")
13397    }
13398}
13399
13400#[gpui::test]
13401async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13402    init_test(cx, |_| {});
13403
13404    let cols = 4;
13405    let rows = 10;
13406    let sample_text_1 = sample_text(rows, cols, 'a');
13407    assert_eq!(
13408        sample_text_1,
13409        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13410    );
13411    let sample_text_2 = sample_text(rows, cols, 'l');
13412    assert_eq!(
13413        sample_text_2,
13414        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13415    );
13416    let sample_text_3 = sample_text(rows, cols, 'v');
13417    assert_eq!(
13418        sample_text_3,
13419        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13420    );
13421
13422    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13423    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13424    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13425
13426    let multi_buffer = cx.new(|cx| {
13427        let mut multibuffer = MultiBuffer::new(ReadWrite);
13428        multibuffer.push_excerpts(
13429            buffer_1.clone(),
13430            [
13431                ExcerptRange {
13432                    context: Point::new(0, 0)..Point::new(3, 0),
13433                    primary: None,
13434                },
13435                ExcerptRange {
13436                    context: Point::new(5, 0)..Point::new(7, 0),
13437                    primary: None,
13438                },
13439                ExcerptRange {
13440                    context: Point::new(9, 0)..Point::new(10, 4),
13441                    primary: None,
13442                },
13443            ],
13444            cx,
13445        );
13446        multibuffer.push_excerpts(
13447            buffer_2.clone(),
13448            [
13449                ExcerptRange {
13450                    context: Point::new(0, 0)..Point::new(3, 0),
13451                    primary: None,
13452                },
13453                ExcerptRange {
13454                    context: Point::new(5, 0)..Point::new(7, 0),
13455                    primary: None,
13456                },
13457                ExcerptRange {
13458                    context: Point::new(9, 0)..Point::new(10, 4),
13459                    primary: None,
13460                },
13461            ],
13462            cx,
13463        );
13464        multibuffer.push_excerpts(
13465            buffer_3.clone(),
13466            [
13467                ExcerptRange {
13468                    context: Point::new(0, 0)..Point::new(3, 0),
13469                    primary: None,
13470                },
13471                ExcerptRange {
13472                    context: Point::new(5, 0)..Point::new(7, 0),
13473                    primary: None,
13474                },
13475                ExcerptRange {
13476                    context: Point::new(9, 0)..Point::new(10, 4),
13477                    primary: None,
13478                },
13479            ],
13480            cx,
13481        );
13482        multibuffer
13483    });
13484
13485    let fs = FakeFs::new(cx.executor());
13486    fs.insert_tree(
13487        "/a",
13488        json!({
13489            "main.rs": sample_text_1,
13490            "other.rs": sample_text_2,
13491            "lib.rs": sample_text_3,
13492        }),
13493    )
13494    .await;
13495    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13496    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13497    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13498    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13499        Editor::new(
13500            EditorMode::Full,
13501            multi_buffer,
13502            Some(project.clone()),
13503            window,
13504            cx,
13505        )
13506    });
13507    let multibuffer_item_id = workspace
13508        .update(cx, |workspace, window, cx| {
13509            assert!(
13510                workspace.active_item(cx).is_none(),
13511                "active item should be None before the first item is added"
13512            );
13513            workspace.add_item_to_active_pane(
13514                Box::new(multi_buffer_editor.clone()),
13515                None,
13516                true,
13517                window,
13518                cx,
13519            );
13520            let active_item = workspace
13521                .active_item(cx)
13522                .expect("should have an active item after adding the multi buffer");
13523            assert!(
13524                !active_item.is_singleton(cx),
13525                "A multi buffer was expected to active after adding"
13526            );
13527            active_item.item_id()
13528        })
13529        .unwrap();
13530    cx.executor().run_until_parked();
13531
13532    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13533        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13534            s.select_ranges(Some(1..2))
13535        });
13536        editor.open_excerpts(&OpenExcerpts, window, cx);
13537    });
13538    cx.executor().run_until_parked();
13539    let first_item_id = workspace
13540        .update(cx, |workspace, window, cx| {
13541            let active_item = workspace
13542                .active_item(cx)
13543                .expect("should have an active item after navigating into the 1st buffer");
13544            let first_item_id = active_item.item_id();
13545            assert_ne!(
13546                first_item_id, multibuffer_item_id,
13547                "Should navigate into the 1st buffer and activate it"
13548            );
13549            assert!(
13550                active_item.is_singleton(cx),
13551                "New active item should be a singleton buffer"
13552            );
13553            assert_eq!(
13554                active_item
13555                    .act_as::<Editor>(cx)
13556                    .expect("should have navigated into an editor for the 1st buffer")
13557                    .read(cx)
13558                    .text(cx),
13559                sample_text_1
13560            );
13561
13562            workspace
13563                .go_back(workspace.active_pane().downgrade(), window, cx)
13564                .detach_and_log_err(cx);
13565
13566            first_item_id
13567        })
13568        .unwrap();
13569    cx.executor().run_until_parked();
13570    workspace
13571        .update(cx, |workspace, _, cx| {
13572            let active_item = workspace
13573                .active_item(cx)
13574                .expect("should have an active item after navigating back");
13575            assert_eq!(
13576                active_item.item_id(),
13577                multibuffer_item_id,
13578                "Should navigate back to the multi buffer"
13579            );
13580            assert!(!active_item.is_singleton(cx));
13581        })
13582        .unwrap();
13583
13584    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13585        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13586            s.select_ranges(Some(39..40))
13587        });
13588        editor.open_excerpts(&OpenExcerpts, window, cx);
13589    });
13590    cx.executor().run_until_parked();
13591    let second_item_id = workspace
13592        .update(cx, |workspace, window, cx| {
13593            let active_item = workspace
13594                .active_item(cx)
13595                .expect("should have an active item after navigating into the 2nd buffer");
13596            let second_item_id = active_item.item_id();
13597            assert_ne!(
13598                second_item_id, multibuffer_item_id,
13599                "Should navigate away from the multibuffer"
13600            );
13601            assert_ne!(
13602                second_item_id, first_item_id,
13603                "Should navigate into the 2nd buffer and activate it"
13604            );
13605            assert!(
13606                active_item.is_singleton(cx),
13607                "New active item should be a singleton buffer"
13608            );
13609            assert_eq!(
13610                active_item
13611                    .act_as::<Editor>(cx)
13612                    .expect("should have navigated into an editor")
13613                    .read(cx)
13614                    .text(cx),
13615                sample_text_2
13616            );
13617
13618            workspace
13619                .go_back(workspace.active_pane().downgrade(), window, cx)
13620                .detach_and_log_err(cx);
13621
13622            second_item_id
13623        })
13624        .unwrap();
13625    cx.executor().run_until_parked();
13626    workspace
13627        .update(cx, |workspace, _, cx| {
13628            let active_item = workspace
13629                .active_item(cx)
13630                .expect("should have an active item after navigating back from the 2nd buffer");
13631            assert_eq!(
13632                active_item.item_id(),
13633                multibuffer_item_id,
13634                "Should navigate back from the 2nd buffer to the multi buffer"
13635            );
13636            assert!(!active_item.is_singleton(cx));
13637        })
13638        .unwrap();
13639
13640    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13641        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13642            s.select_ranges(Some(70..70))
13643        });
13644        editor.open_excerpts(&OpenExcerpts, window, cx);
13645    });
13646    cx.executor().run_until_parked();
13647    workspace
13648        .update(cx, |workspace, window, cx| {
13649            let active_item = workspace
13650                .active_item(cx)
13651                .expect("should have an active item after navigating into the 3rd buffer");
13652            let third_item_id = active_item.item_id();
13653            assert_ne!(
13654                third_item_id, multibuffer_item_id,
13655                "Should navigate into the 3rd buffer and activate it"
13656            );
13657            assert_ne!(third_item_id, first_item_id);
13658            assert_ne!(third_item_id, second_item_id);
13659            assert!(
13660                active_item.is_singleton(cx),
13661                "New active item should be a singleton buffer"
13662            );
13663            assert_eq!(
13664                active_item
13665                    .act_as::<Editor>(cx)
13666                    .expect("should have navigated into an editor")
13667                    .read(cx)
13668                    .text(cx),
13669                sample_text_3
13670            );
13671
13672            workspace
13673                .go_back(workspace.active_pane().downgrade(), window, cx)
13674                .detach_and_log_err(cx);
13675        })
13676        .unwrap();
13677    cx.executor().run_until_parked();
13678    workspace
13679        .update(cx, |workspace, _, cx| {
13680            let active_item = workspace
13681                .active_item(cx)
13682                .expect("should have an active item after navigating back from the 3rd buffer");
13683            assert_eq!(
13684                active_item.item_id(),
13685                multibuffer_item_id,
13686                "Should navigate back from the 3rd buffer to the multi buffer"
13687            );
13688            assert!(!active_item.is_singleton(cx));
13689        })
13690        .unwrap();
13691}
13692
13693#[gpui::test]
13694async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13695    init_test(cx, |_| {});
13696
13697    let mut cx = EditorTestContext::new(cx).await;
13698
13699    let diff_base = r#"
13700        use some::mod;
13701
13702        const A: u32 = 42;
13703
13704        fn main() {
13705            println!("hello");
13706
13707            println!("world");
13708        }
13709        "#
13710    .unindent();
13711
13712    cx.set_state(
13713        &r#"
13714        use some::modified;
13715
13716        ˇ
13717        fn main() {
13718            println!("hello there");
13719
13720            println!("around the");
13721            println!("world");
13722        }
13723        "#
13724        .unindent(),
13725    );
13726
13727    cx.set_head_text(&diff_base);
13728    executor.run_until_parked();
13729
13730    cx.update_editor(|editor, window, cx| {
13731        editor.go_to_next_hunk(&GoToHunk, window, cx);
13732        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13733    });
13734    executor.run_until_parked();
13735    cx.assert_state_with_diff(
13736        r#"
13737          use some::modified;
13738
13739
13740          fn main() {
13741        -     println!("hello");
13742        + ˇ    println!("hello there");
13743
13744              println!("around the");
13745              println!("world");
13746          }
13747        "#
13748        .unindent(),
13749    );
13750
13751    cx.update_editor(|editor, window, cx| {
13752        for _ in 0..2 {
13753            editor.go_to_next_hunk(&GoToHunk, window, cx);
13754            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13755        }
13756    });
13757    executor.run_until_parked();
13758    cx.assert_state_with_diff(
13759        r#"
13760        - use some::mod;
13761        + ˇuse some::modified;
13762
13763
13764          fn main() {
13765        -     println!("hello");
13766        +     println!("hello there");
13767
13768        +     println!("around the");
13769              println!("world");
13770          }
13771        "#
13772        .unindent(),
13773    );
13774
13775    cx.update_editor(|editor, window, cx| {
13776        editor.go_to_next_hunk(&GoToHunk, window, cx);
13777        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13778    });
13779    executor.run_until_parked();
13780    cx.assert_state_with_diff(
13781        r#"
13782        - use some::mod;
13783        + use some::modified;
13784
13785        - const A: u32 = 42;
13786          ˇ
13787          fn main() {
13788        -     println!("hello");
13789        +     println!("hello there");
13790
13791        +     println!("around the");
13792              println!("world");
13793          }
13794        "#
13795        .unindent(),
13796    );
13797
13798    cx.update_editor(|editor, window, cx| {
13799        editor.cancel(&Cancel, window, cx);
13800    });
13801
13802    cx.assert_state_with_diff(
13803        r#"
13804          use some::modified;
13805
13806          ˇ
13807          fn main() {
13808              println!("hello there");
13809
13810              println!("around the");
13811              println!("world");
13812          }
13813        "#
13814        .unindent(),
13815    );
13816}
13817
13818#[gpui::test]
13819async fn test_diff_base_change_with_expanded_diff_hunks(
13820    executor: BackgroundExecutor,
13821    cx: &mut TestAppContext,
13822) {
13823    init_test(cx, |_| {});
13824
13825    let mut cx = EditorTestContext::new(cx).await;
13826
13827    let diff_base = r#"
13828        use some::mod1;
13829        use some::mod2;
13830
13831        const A: u32 = 42;
13832        const B: u32 = 42;
13833        const C: u32 = 42;
13834
13835        fn main() {
13836            println!("hello");
13837
13838            println!("world");
13839        }
13840        "#
13841    .unindent();
13842
13843    cx.set_state(
13844        &r#"
13845        use some::mod2;
13846
13847        const A: u32 = 42;
13848        const C: u32 = 42;
13849
13850        fn main(ˇ) {
13851            //println!("hello");
13852
13853            println!("world");
13854            //
13855            //
13856        }
13857        "#
13858        .unindent(),
13859    );
13860
13861    cx.set_head_text(&diff_base);
13862    executor.run_until_parked();
13863
13864    cx.update_editor(|editor, window, cx| {
13865        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
13866    });
13867    executor.run_until_parked();
13868    cx.assert_state_with_diff(
13869        r#"
13870        - use some::mod1;
13871          use some::mod2;
13872
13873          const A: u32 = 42;
13874        - const B: u32 = 42;
13875          const C: u32 = 42;
13876
13877          fn main(ˇ) {
13878        -     println!("hello");
13879        +     //println!("hello");
13880
13881              println!("world");
13882        +     //
13883        +     //
13884          }
13885        "#
13886        .unindent(),
13887    );
13888
13889    cx.set_head_text("new diff base!");
13890    executor.run_until_parked();
13891    cx.assert_state_with_diff(
13892        r#"
13893        - new diff base!
13894        + use some::mod2;
13895        +
13896        + const A: u32 = 42;
13897        + const C: u32 = 42;
13898        +
13899        + fn main(ˇ) {
13900        +     //println!("hello");
13901        +
13902        +     println!("world");
13903        +     //
13904        +     //
13905        + }
13906        "#
13907        .unindent(),
13908    );
13909}
13910
13911#[gpui::test]
13912async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
13913    init_test(cx, |_| {});
13914
13915    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13916    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
13917    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13918    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
13919    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
13920    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
13921
13922    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
13923    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
13924    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
13925
13926    let multi_buffer = cx.new(|cx| {
13927        let mut multibuffer = MultiBuffer::new(ReadWrite);
13928        multibuffer.push_excerpts(
13929            buffer_1.clone(),
13930            [
13931                ExcerptRange {
13932                    context: Point::new(0, 0)..Point::new(3, 0),
13933                    primary: None,
13934                },
13935                ExcerptRange {
13936                    context: Point::new(5, 0)..Point::new(7, 0),
13937                    primary: None,
13938                },
13939                ExcerptRange {
13940                    context: Point::new(9, 0)..Point::new(10, 3),
13941                    primary: None,
13942                },
13943            ],
13944            cx,
13945        );
13946        multibuffer.push_excerpts(
13947            buffer_2.clone(),
13948            [
13949                ExcerptRange {
13950                    context: Point::new(0, 0)..Point::new(3, 0),
13951                    primary: None,
13952                },
13953                ExcerptRange {
13954                    context: Point::new(5, 0)..Point::new(7, 0),
13955                    primary: None,
13956                },
13957                ExcerptRange {
13958                    context: Point::new(9, 0)..Point::new(10, 3),
13959                    primary: None,
13960                },
13961            ],
13962            cx,
13963        );
13964        multibuffer.push_excerpts(
13965            buffer_3.clone(),
13966            [
13967                ExcerptRange {
13968                    context: Point::new(0, 0)..Point::new(3, 0),
13969                    primary: None,
13970                },
13971                ExcerptRange {
13972                    context: Point::new(5, 0)..Point::new(7, 0),
13973                    primary: None,
13974                },
13975                ExcerptRange {
13976                    context: Point::new(9, 0)..Point::new(10, 3),
13977                    primary: None,
13978                },
13979            ],
13980            cx,
13981        );
13982        multibuffer
13983    });
13984
13985    let editor =
13986        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
13987    editor
13988        .update(cx, |editor, _window, cx| {
13989            for (buffer, diff_base) in [
13990                (buffer_1.clone(), file_1_old),
13991                (buffer_2.clone(), file_2_old),
13992                (buffer_3.clone(), file_3_old),
13993            ] {
13994                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13995                editor
13996                    .buffer
13997                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13998            }
13999        })
14000        .unwrap();
14001
14002    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14003    cx.run_until_parked();
14004
14005    cx.assert_editor_state(
14006        &"
14007            ˇaaa
14008            ccc
14009            ddd
14010
14011            ggg
14012            hhh
14013
14014
14015            lll
14016            mmm
14017            NNN
14018
14019            qqq
14020            rrr
14021
14022            uuu
14023            111
14024            222
14025            333
14026
14027            666
14028            777
14029
14030            000
14031            !!!"
14032        .unindent(),
14033    );
14034
14035    cx.update_editor(|editor, window, cx| {
14036        editor.select_all(&SelectAll, window, cx);
14037        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14038    });
14039    cx.executor().run_until_parked();
14040
14041    cx.assert_state_with_diff(
14042        "
14043            «aaa
14044          - bbb
14045            ccc
14046            ddd
14047
14048            ggg
14049            hhh
14050
14051
14052            lll
14053            mmm
14054          - nnn
14055          + NNN
14056
14057            qqq
14058            rrr
14059
14060            uuu
14061            111
14062            222
14063            333
14064
14065          + 666
14066            777
14067
14068            000
14069            !!!ˇ»"
14070            .unindent(),
14071    );
14072}
14073
14074#[gpui::test]
14075async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14076    init_test(cx, |_| {});
14077
14078    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14079    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14080
14081    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14082    let multi_buffer = cx.new(|cx| {
14083        let mut multibuffer = MultiBuffer::new(ReadWrite);
14084        multibuffer.push_excerpts(
14085            buffer.clone(),
14086            [
14087                ExcerptRange {
14088                    context: Point::new(0, 0)..Point::new(2, 0),
14089                    primary: None,
14090                },
14091                ExcerptRange {
14092                    context: Point::new(4, 0)..Point::new(7, 0),
14093                    primary: None,
14094                },
14095                ExcerptRange {
14096                    context: Point::new(9, 0)..Point::new(10, 0),
14097                    primary: None,
14098                },
14099            ],
14100            cx,
14101        );
14102        multibuffer
14103    });
14104
14105    let editor =
14106        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14107    editor
14108        .update(cx, |editor, _window, cx| {
14109            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14110            editor
14111                .buffer
14112                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14113        })
14114        .unwrap();
14115
14116    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14117    cx.run_until_parked();
14118
14119    cx.update_editor(|editor, window, cx| {
14120        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14121    });
14122    cx.executor().run_until_parked();
14123
14124    // When the start of a hunk coincides with the start of its excerpt,
14125    // the hunk is expanded. When the start of a a hunk is earlier than
14126    // the start of its excerpt, the hunk is not expanded.
14127    cx.assert_state_with_diff(
14128        "
14129            ˇaaa
14130          - bbb
14131          + BBB
14132
14133          - ddd
14134          - eee
14135          + DDD
14136          + EEE
14137            fff
14138
14139            iii
14140        "
14141        .unindent(),
14142    );
14143}
14144
14145#[gpui::test]
14146async fn test_edits_around_expanded_insertion_hunks(
14147    executor: BackgroundExecutor,
14148    cx: &mut TestAppContext,
14149) {
14150    init_test(cx, |_| {});
14151
14152    let mut cx = EditorTestContext::new(cx).await;
14153
14154    let diff_base = r#"
14155        use some::mod1;
14156        use some::mod2;
14157
14158        const A: u32 = 42;
14159
14160        fn main() {
14161            println!("hello");
14162
14163            println!("world");
14164        }
14165        "#
14166    .unindent();
14167    executor.run_until_parked();
14168    cx.set_state(
14169        &r#"
14170        use some::mod1;
14171        use some::mod2;
14172
14173        const A: u32 = 42;
14174        const B: u32 = 42;
14175        const C: u32 = 42;
14176        ˇ
14177
14178        fn main() {
14179            println!("hello");
14180
14181            println!("world");
14182        }
14183        "#
14184        .unindent(),
14185    );
14186
14187    cx.set_head_text(&diff_base);
14188    executor.run_until_parked();
14189
14190    cx.update_editor(|editor, window, cx| {
14191        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14192    });
14193    executor.run_until_parked();
14194
14195    cx.assert_state_with_diff(
14196        r#"
14197        use some::mod1;
14198        use some::mod2;
14199
14200        const A: u32 = 42;
14201      + const B: u32 = 42;
14202      + const C: u32 = 42;
14203      + ˇ
14204
14205        fn main() {
14206            println!("hello");
14207
14208            println!("world");
14209        }
14210      "#
14211        .unindent(),
14212    );
14213
14214    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14215    executor.run_until_parked();
14216
14217    cx.assert_state_with_diff(
14218        r#"
14219        use some::mod1;
14220        use some::mod2;
14221
14222        const A: u32 = 42;
14223      + const B: u32 = 42;
14224      + const C: u32 = 42;
14225      + const D: u32 = 42;
14226      + ˇ
14227
14228        fn main() {
14229            println!("hello");
14230
14231            println!("world");
14232        }
14233      "#
14234        .unindent(),
14235    );
14236
14237    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14238    executor.run_until_parked();
14239
14240    cx.assert_state_with_diff(
14241        r#"
14242        use some::mod1;
14243        use some::mod2;
14244
14245        const A: u32 = 42;
14246      + const B: u32 = 42;
14247      + const C: u32 = 42;
14248      + const D: u32 = 42;
14249      + const E: u32 = 42;
14250      + ˇ
14251
14252        fn main() {
14253            println!("hello");
14254
14255            println!("world");
14256        }
14257      "#
14258        .unindent(),
14259    );
14260
14261    cx.update_editor(|editor, window, cx| {
14262        editor.delete_line(&DeleteLine, window, cx);
14263    });
14264    executor.run_until_parked();
14265
14266    cx.assert_state_with_diff(
14267        r#"
14268        use some::mod1;
14269        use some::mod2;
14270
14271        const A: u32 = 42;
14272      + const B: u32 = 42;
14273      + const C: u32 = 42;
14274      + const D: u32 = 42;
14275      + const E: u32 = 42;
14276        ˇ
14277        fn main() {
14278            println!("hello");
14279
14280            println!("world");
14281        }
14282      "#
14283        .unindent(),
14284    );
14285
14286    cx.update_editor(|editor, window, cx| {
14287        editor.move_up(&MoveUp, window, cx);
14288        editor.delete_line(&DeleteLine, window, cx);
14289        editor.move_up(&MoveUp, window, cx);
14290        editor.delete_line(&DeleteLine, window, cx);
14291        editor.move_up(&MoveUp, window, cx);
14292        editor.delete_line(&DeleteLine, window, cx);
14293    });
14294    executor.run_until_parked();
14295    cx.assert_state_with_diff(
14296        r#"
14297        use some::mod1;
14298        use some::mod2;
14299
14300        const A: u32 = 42;
14301      + const B: u32 = 42;
14302        ˇ
14303        fn main() {
14304            println!("hello");
14305
14306            println!("world");
14307        }
14308      "#
14309        .unindent(),
14310    );
14311
14312    cx.update_editor(|editor, window, cx| {
14313        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14314        editor.delete_line(&DeleteLine, window, cx);
14315    });
14316    executor.run_until_parked();
14317    cx.assert_state_with_diff(
14318        r#"
14319        ˇ
14320        fn main() {
14321            println!("hello");
14322
14323            println!("world");
14324        }
14325      "#
14326        .unindent(),
14327    );
14328}
14329
14330#[gpui::test]
14331async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14332    init_test(cx, |_| {});
14333
14334    let mut cx = EditorTestContext::new(cx).await;
14335    cx.set_head_text(indoc! { "
14336        one
14337        two
14338        three
14339        four
14340        five
14341        "
14342    });
14343    cx.set_state(indoc! { "
14344        one
14345        ˇthree
14346        five
14347    "});
14348    cx.run_until_parked();
14349    cx.update_editor(|editor, window, cx| {
14350        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14351    });
14352    cx.assert_state_with_diff(
14353        indoc! { "
14354        one
14355      - two
14356        ˇthree
14357      - four
14358        five
14359    "}
14360        .to_string(),
14361    );
14362    cx.update_editor(|editor, window, cx| {
14363        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14364    });
14365
14366    cx.assert_state_with_diff(
14367        indoc! { "
14368        one
14369        ˇthree
14370        five
14371    "}
14372        .to_string(),
14373    );
14374
14375    cx.set_state(indoc! { "
14376        one
14377        ˇTWO
14378        three
14379        four
14380        five
14381    "});
14382    cx.run_until_parked();
14383    cx.update_editor(|editor, window, cx| {
14384        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14385    });
14386
14387    cx.assert_state_with_diff(
14388        indoc! { "
14389            one
14390          - two
14391          + ˇTWO
14392            three
14393            four
14394            five
14395        "}
14396        .to_string(),
14397    );
14398    cx.update_editor(|editor, window, cx| {
14399        editor.move_up(&Default::default(), window, cx);
14400        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14401    });
14402    cx.assert_state_with_diff(
14403        indoc! { "
14404            one
14405            ˇTWO
14406            three
14407            four
14408            five
14409        "}
14410        .to_string(),
14411    );
14412}
14413
14414#[gpui::test]
14415async fn test_edits_around_expanded_deletion_hunks(
14416    executor: BackgroundExecutor,
14417    cx: &mut TestAppContext,
14418) {
14419    init_test(cx, |_| {});
14420
14421    let mut cx = EditorTestContext::new(cx).await;
14422
14423    let diff_base = r#"
14424        use some::mod1;
14425        use some::mod2;
14426
14427        const A: u32 = 42;
14428        const B: u32 = 42;
14429        const C: u32 = 42;
14430
14431
14432        fn main() {
14433            println!("hello");
14434
14435            println!("world");
14436        }
14437    "#
14438    .unindent();
14439    executor.run_until_parked();
14440    cx.set_state(
14441        &r#"
14442        use some::mod1;
14443        use some::mod2;
14444
14445        ˇconst B: u32 = 42;
14446        const C: u32 = 42;
14447
14448
14449        fn main() {
14450            println!("hello");
14451
14452            println!("world");
14453        }
14454        "#
14455        .unindent(),
14456    );
14457
14458    cx.set_head_text(&diff_base);
14459    executor.run_until_parked();
14460
14461    cx.update_editor(|editor, window, cx| {
14462        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14463    });
14464    executor.run_until_parked();
14465
14466    cx.assert_state_with_diff(
14467        r#"
14468        use some::mod1;
14469        use some::mod2;
14470
14471      - const A: u32 = 42;
14472        ˇconst B: u32 = 42;
14473        const C: u32 = 42;
14474
14475
14476        fn main() {
14477            println!("hello");
14478
14479            println!("world");
14480        }
14481      "#
14482        .unindent(),
14483    );
14484
14485    cx.update_editor(|editor, window, cx| {
14486        editor.delete_line(&DeleteLine, window, cx);
14487    });
14488    executor.run_until_parked();
14489    cx.assert_state_with_diff(
14490        r#"
14491        use some::mod1;
14492        use some::mod2;
14493
14494      - const A: u32 = 42;
14495      - const B: u32 = 42;
14496        ˇconst C: u32 = 42;
14497
14498
14499        fn main() {
14500            println!("hello");
14501
14502            println!("world");
14503        }
14504      "#
14505        .unindent(),
14506    );
14507
14508    cx.update_editor(|editor, window, cx| {
14509        editor.delete_line(&DeleteLine, window, cx);
14510    });
14511    executor.run_until_parked();
14512    cx.assert_state_with_diff(
14513        r#"
14514        use some::mod1;
14515        use some::mod2;
14516
14517      - const A: u32 = 42;
14518      - const B: u32 = 42;
14519      - const C: u32 = 42;
14520        ˇ
14521
14522        fn main() {
14523            println!("hello");
14524
14525            println!("world");
14526        }
14527      "#
14528        .unindent(),
14529    );
14530
14531    cx.update_editor(|editor, window, cx| {
14532        editor.handle_input("replacement", window, cx);
14533    });
14534    executor.run_until_parked();
14535    cx.assert_state_with_diff(
14536        r#"
14537        use some::mod1;
14538        use some::mod2;
14539
14540      - const A: u32 = 42;
14541      - const B: u32 = 42;
14542      - const C: u32 = 42;
14543      -
14544      + replacementˇ
14545
14546        fn main() {
14547            println!("hello");
14548
14549            println!("world");
14550        }
14551      "#
14552        .unindent(),
14553    );
14554}
14555
14556#[gpui::test]
14557async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14558    init_test(cx, |_| {});
14559
14560    let mut cx = EditorTestContext::new(cx).await;
14561
14562    let base_text = r#"
14563        one
14564        two
14565        three
14566        four
14567        five
14568    "#
14569    .unindent();
14570    executor.run_until_parked();
14571    cx.set_state(
14572        &r#"
14573        one
14574        two
14575        fˇour
14576        five
14577        "#
14578        .unindent(),
14579    );
14580
14581    cx.set_head_text(&base_text);
14582    executor.run_until_parked();
14583
14584    cx.update_editor(|editor, window, cx| {
14585        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14586    });
14587    executor.run_until_parked();
14588
14589    cx.assert_state_with_diff(
14590        r#"
14591          one
14592          two
14593        - three
14594          fˇour
14595          five
14596        "#
14597        .unindent(),
14598    );
14599
14600    cx.update_editor(|editor, window, cx| {
14601        editor.backspace(&Backspace, window, cx);
14602        editor.backspace(&Backspace, window, cx);
14603    });
14604    executor.run_until_parked();
14605    cx.assert_state_with_diff(
14606        r#"
14607          one
14608          two
14609        - threeˇ
14610        - four
14611        + our
14612          five
14613        "#
14614        .unindent(),
14615    );
14616}
14617
14618#[gpui::test]
14619async fn test_edit_after_expanded_modification_hunk(
14620    executor: BackgroundExecutor,
14621    cx: &mut TestAppContext,
14622) {
14623    init_test(cx, |_| {});
14624
14625    let mut cx = EditorTestContext::new(cx).await;
14626
14627    let diff_base = r#"
14628        use some::mod1;
14629        use some::mod2;
14630
14631        const A: u32 = 42;
14632        const B: u32 = 42;
14633        const C: u32 = 42;
14634        const D: u32 = 42;
14635
14636
14637        fn main() {
14638            println!("hello");
14639
14640            println!("world");
14641        }"#
14642    .unindent();
14643
14644    cx.set_state(
14645        &r#"
14646        use some::mod1;
14647        use some::mod2;
14648
14649        const A: u32 = 42;
14650        const B: u32 = 42;
14651        const C: u32 = 43ˇ
14652        const D: u32 = 42;
14653
14654
14655        fn main() {
14656            println!("hello");
14657
14658            println!("world");
14659        }"#
14660        .unindent(),
14661    );
14662
14663    cx.set_head_text(&diff_base);
14664    executor.run_until_parked();
14665    cx.update_editor(|editor, window, cx| {
14666        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14667    });
14668    executor.run_until_parked();
14669
14670    cx.assert_state_with_diff(
14671        r#"
14672        use some::mod1;
14673        use some::mod2;
14674
14675        const A: u32 = 42;
14676        const B: u32 = 42;
14677      - const C: u32 = 42;
14678      + const C: u32 = 43ˇ
14679        const D: u32 = 42;
14680
14681
14682        fn main() {
14683            println!("hello");
14684
14685            println!("world");
14686        }"#
14687        .unindent(),
14688    );
14689
14690    cx.update_editor(|editor, window, cx| {
14691        editor.handle_input("\nnew_line\n", window, cx);
14692    });
14693    executor.run_until_parked();
14694
14695    cx.assert_state_with_diff(
14696        r#"
14697        use some::mod1;
14698        use some::mod2;
14699
14700        const A: u32 = 42;
14701        const B: u32 = 42;
14702      - const C: u32 = 42;
14703      + const C: u32 = 43
14704      + new_line
14705      + ˇ
14706        const D: u32 = 42;
14707
14708
14709        fn main() {
14710            println!("hello");
14711
14712            println!("world");
14713        }"#
14714        .unindent(),
14715    );
14716}
14717
14718#[gpui::test]
14719async fn test_stage_and_unstage_added_file_hunk(
14720    executor: BackgroundExecutor,
14721    cx: &mut TestAppContext,
14722) {
14723    init_test(cx, |_| {});
14724
14725    let mut cx = EditorTestContext::new(cx).await;
14726    cx.update_editor(|editor, _, cx| {
14727        editor.set_expand_all_diff_hunks(cx);
14728    });
14729
14730    let working_copy = r#"
14731            ˇfn main() {
14732                println!("hello, world!");
14733            }
14734        "#
14735    .unindent();
14736
14737    cx.set_state(&working_copy);
14738    executor.run_until_parked();
14739
14740    cx.assert_state_with_diff(
14741        r#"
14742            + ˇfn main() {
14743            +     println!("hello, world!");
14744            + }
14745        "#
14746        .unindent(),
14747    );
14748    cx.assert_index_text(None);
14749
14750    cx.update_editor(|editor, window, cx| {
14751        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14752    });
14753    executor.run_until_parked();
14754    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14755    cx.assert_state_with_diff(
14756        r#"
14757            + ˇfn main() {
14758            +     println!("hello, world!");
14759            + }
14760        "#
14761        .unindent(),
14762    );
14763
14764    cx.update_editor(|editor, window, cx| {
14765        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14766    });
14767    executor.run_until_parked();
14768    cx.assert_index_text(None);
14769}
14770
14771async fn setup_indent_guides_editor(
14772    text: &str,
14773    cx: &mut TestAppContext,
14774) -> (BufferId, EditorTestContext) {
14775    init_test(cx, |_| {});
14776
14777    let mut cx = EditorTestContext::new(cx).await;
14778
14779    let buffer_id = cx.update_editor(|editor, window, cx| {
14780        editor.set_text(text, window, cx);
14781        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14782
14783        buffer_ids[0]
14784    });
14785
14786    (buffer_id, cx)
14787}
14788
14789fn assert_indent_guides(
14790    range: Range<u32>,
14791    expected: Vec<IndentGuide>,
14792    active_indices: Option<Vec<usize>>,
14793    cx: &mut EditorTestContext,
14794) {
14795    let indent_guides = cx.update_editor(|editor, window, cx| {
14796        let snapshot = editor.snapshot(window, cx).display_snapshot;
14797        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14798            editor,
14799            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14800            true,
14801            &snapshot,
14802            cx,
14803        );
14804
14805        indent_guides.sort_by(|a, b| {
14806            a.depth.cmp(&b.depth).then(
14807                a.start_row
14808                    .cmp(&b.start_row)
14809                    .then(a.end_row.cmp(&b.end_row)),
14810            )
14811        });
14812        indent_guides
14813    });
14814
14815    if let Some(expected) = active_indices {
14816        let active_indices = cx.update_editor(|editor, window, cx| {
14817            let snapshot = editor.snapshot(window, cx).display_snapshot;
14818            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14819        });
14820
14821        assert_eq!(
14822            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14823            expected,
14824            "Active indent guide indices do not match"
14825        );
14826    }
14827
14828    assert_eq!(indent_guides, expected, "Indent guides do not match");
14829}
14830
14831fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14832    IndentGuide {
14833        buffer_id,
14834        start_row: MultiBufferRow(start_row),
14835        end_row: MultiBufferRow(end_row),
14836        depth,
14837        tab_size: 4,
14838        settings: IndentGuideSettings {
14839            enabled: true,
14840            line_width: 1,
14841            active_line_width: 1,
14842            ..Default::default()
14843        },
14844    }
14845}
14846
14847#[gpui::test]
14848async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14849    let (buffer_id, mut cx) = setup_indent_guides_editor(
14850        &"
14851    fn main() {
14852        let a = 1;
14853    }"
14854        .unindent(),
14855        cx,
14856    )
14857    .await;
14858
14859    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
14860}
14861
14862#[gpui::test]
14863async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
14864    let (buffer_id, mut cx) = setup_indent_guides_editor(
14865        &"
14866    fn main() {
14867        let a = 1;
14868        let b = 2;
14869    }"
14870        .unindent(),
14871        cx,
14872    )
14873    .await;
14874
14875    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
14876}
14877
14878#[gpui::test]
14879async fn test_indent_guide_nested(cx: &mut TestAppContext) {
14880    let (buffer_id, mut cx) = setup_indent_guides_editor(
14881        &"
14882    fn main() {
14883        let a = 1;
14884        if a == 3 {
14885            let b = 2;
14886        } else {
14887            let c = 3;
14888        }
14889    }"
14890        .unindent(),
14891        cx,
14892    )
14893    .await;
14894
14895    assert_indent_guides(
14896        0..8,
14897        vec![
14898            indent_guide(buffer_id, 1, 6, 0),
14899            indent_guide(buffer_id, 3, 3, 1),
14900            indent_guide(buffer_id, 5, 5, 1),
14901        ],
14902        None,
14903        &mut cx,
14904    );
14905}
14906
14907#[gpui::test]
14908async fn test_indent_guide_tab(cx: &mut TestAppContext) {
14909    let (buffer_id, mut cx) = setup_indent_guides_editor(
14910        &"
14911    fn main() {
14912        let a = 1;
14913            let b = 2;
14914        let c = 3;
14915    }"
14916        .unindent(),
14917        cx,
14918    )
14919    .await;
14920
14921    assert_indent_guides(
14922        0..5,
14923        vec![
14924            indent_guide(buffer_id, 1, 3, 0),
14925            indent_guide(buffer_id, 2, 2, 1),
14926        ],
14927        None,
14928        &mut cx,
14929    );
14930}
14931
14932#[gpui::test]
14933async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
14934    let (buffer_id, mut cx) = setup_indent_guides_editor(
14935        &"
14936        fn main() {
14937            let a = 1;
14938
14939            let c = 3;
14940        }"
14941        .unindent(),
14942        cx,
14943    )
14944    .await;
14945
14946    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
14947}
14948
14949#[gpui::test]
14950async fn test_indent_guide_complex(cx: &mut TestAppContext) {
14951    let (buffer_id, mut cx) = setup_indent_guides_editor(
14952        &"
14953        fn main() {
14954            let a = 1;
14955
14956            let c = 3;
14957
14958            if a == 3 {
14959                let b = 2;
14960            } else {
14961                let c = 3;
14962            }
14963        }"
14964        .unindent(),
14965        cx,
14966    )
14967    .await;
14968
14969    assert_indent_guides(
14970        0..11,
14971        vec![
14972            indent_guide(buffer_id, 1, 9, 0),
14973            indent_guide(buffer_id, 6, 6, 1),
14974            indent_guide(buffer_id, 8, 8, 1),
14975        ],
14976        None,
14977        &mut cx,
14978    );
14979}
14980
14981#[gpui::test]
14982async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
14983    let (buffer_id, mut cx) = setup_indent_guides_editor(
14984        &"
14985        fn main() {
14986            let a = 1;
14987
14988            let c = 3;
14989
14990            if a == 3 {
14991                let b = 2;
14992            } else {
14993                let c = 3;
14994            }
14995        }"
14996        .unindent(),
14997        cx,
14998    )
14999    .await;
15000
15001    assert_indent_guides(
15002        1..11,
15003        vec![
15004            indent_guide(buffer_id, 1, 9, 0),
15005            indent_guide(buffer_id, 6, 6, 1),
15006            indent_guide(buffer_id, 8, 8, 1),
15007        ],
15008        None,
15009        &mut cx,
15010    );
15011}
15012
15013#[gpui::test]
15014async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15015    let (buffer_id, mut cx) = setup_indent_guides_editor(
15016        &"
15017        fn main() {
15018            let a = 1;
15019
15020            let c = 3;
15021
15022            if a == 3 {
15023                let b = 2;
15024            } else {
15025                let c = 3;
15026            }
15027        }"
15028        .unindent(),
15029        cx,
15030    )
15031    .await;
15032
15033    assert_indent_guides(
15034        1..10,
15035        vec![
15036            indent_guide(buffer_id, 1, 9, 0),
15037            indent_guide(buffer_id, 6, 6, 1),
15038            indent_guide(buffer_id, 8, 8, 1),
15039        ],
15040        None,
15041        &mut cx,
15042    );
15043}
15044
15045#[gpui::test]
15046async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15047    let (buffer_id, mut cx) = setup_indent_guides_editor(
15048        &"
15049        block1
15050            block2
15051                block3
15052                    block4
15053            block2
15054        block1
15055        block1"
15056            .unindent(),
15057        cx,
15058    )
15059    .await;
15060
15061    assert_indent_guides(
15062        1..10,
15063        vec![
15064            indent_guide(buffer_id, 1, 4, 0),
15065            indent_guide(buffer_id, 2, 3, 1),
15066            indent_guide(buffer_id, 3, 3, 2),
15067        ],
15068        None,
15069        &mut cx,
15070    );
15071}
15072
15073#[gpui::test]
15074async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15075    let (buffer_id, mut cx) = setup_indent_guides_editor(
15076        &"
15077        block1
15078            block2
15079                block3
15080
15081        block1
15082        block1"
15083            .unindent(),
15084        cx,
15085    )
15086    .await;
15087
15088    assert_indent_guides(
15089        0..6,
15090        vec![
15091            indent_guide(buffer_id, 1, 2, 0),
15092            indent_guide(buffer_id, 2, 2, 1),
15093        ],
15094        None,
15095        &mut cx,
15096    );
15097}
15098
15099#[gpui::test]
15100async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15101    let (buffer_id, mut cx) = setup_indent_guides_editor(
15102        &"
15103        block1
15104
15105
15106
15107            block2
15108        "
15109        .unindent(),
15110        cx,
15111    )
15112    .await;
15113
15114    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15115}
15116
15117#[gpui::test]
15118async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15119    let (buffer_id, mut cx) = setup_indent_guides_editor(
15120        &"
15121        def a:
15122        \tb = 3
15123        \tif True:
15124        \t\tc = 4
15125        \t\td = 5
15126        \tprint(b)
15127        "
15128        .unindent(),
15129        cx,
15130    )
15131    .await;
15132
15133    assert_indent_guides(
15134        0..6,
15135        vec![
15136            indent_guide(buffer_id, 1, 6, 0),
15137            indent_guide(buffer_id, 3, 4, 1),
15138        ],
15139        None,
15140        &mut cx,
15141    );
15142}
15143
15144#[gpui::test]
15145async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15146    let (buffer_id, mut cx) = setup_indent_guides_editor(
15147        &"
15148    fn main() {
15149        let a = 1;
15150    }"
15151        .unindent(),
15152        cx,
15153    )
15154    .await;
15155
15156    cx.update_editor(|editor, window, cx| {
15157        editor.change_selections(None, window, cx, |s| {
15158            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15159        });
15160    });
15161
15162    assert_indent_guides(
15163        0..3,
15164        vec![indent_guide(buffer_id, 1, 1, 0)],
15165        Some(vec![0]),
15166        &mut cx,
15167    );
15168}
15169
15170#[gpui::test]
15171async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15172    let (buffer_id, mut cx) = setup_indent_guides_editor(
15173        &"
15174    fn main() {
15175        if 1 == 2 {
15176            let a = 1;
15177        }
15178    }"
15179        .unindent(),
15180        cx,
15181    )
15182    .await;
15183
15184    cx.update_editor(|editor, window, cx| {
15185        editor.change_selections(None, window, cx, |s| {
15186            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15187        });
15188    });
15189
15190    assert_indent_guides(
15191        0..4,
15192        vec![
15193            indent_guide(buffer_id, 1, 3, 0),
15194            indent_guide(buffer_id, 2, 2, 1),
15195        ],
15196        Some(vec![1]),
15197        &mut cx,
15198    );
15199
15200    cx.update_editor(|editor, window, cx| {
15201        editor.change_selections(None, window, cx, |s| {
15202            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15203        });
15204    });
15205
15206    assert_indent_guides(
15207        0..4,
15208        vec![
15209            indent_guide(buffer_id, 1, 3, 0),
15210            indent_guide(buffer_id, 2, 2, 1),
15211        ],
15212        Some(vec![1]),
15213        &mut cx,
15214    );
15215
15216    cx.update_editor(|editor, window, cx| {
15217        editor.change_selections(None, window, cx, |s| {
15218            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15219        });
15220    });
15221
15222    assert_indent_guides(
15223        0..4,
15224        vec![
15225            indent_guide(buffer_id, 1, 3, 0),
15226            indent_guide(buffer_id, 2, 2, 1),
15227        ],
15228        Some(vec![0]),
15229        &mut cx,
15230    );
15231}
15232
15233#[gpui::test]
15234async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15235    let (buffer_id, mut cx) = setup_indent_guides_editor(
15236        &"
15237    fn main() {
15238        let a = 1;
15239
15240        let b = 2;
15241    }"
15242        .unindent(),
15243        cx,
15244    )
15245    .await;
15246
15247    cx.update_editor(|editor, window, cx| {
15248        editor.change_selections(None, window, cx, |s| {
15249            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15250        });
15251    });
15252
15253    assert_indent_guides(
15254        0..5,
15255        vec![indent_guide(buffer_id, 1, 3, 0)],
15256        Some(vec![0]),
15257        &mut cx,
15258    );
15259}
15260
15261#[gpui::test]
15262async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15263    let (buffer_id, mut cx) = setup_indent_guides_editor(
15264        &"
15265    def m:
15266        a = 1
15267        pass"
15268            .unindent(),
15269        cx,
15270    )
15271    .await;
15272
15273    cx.update_editor(|editor, window, cx| {
15274        editor.change_selections(None, window, cx, |s| {
15275            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15276        });
15277    });
15278
15279    assert_indent_guides(
15280        0..3,
15281        vec![indent_guide(buffer_id, 1, 2, 0)],
15282        Some(vec![0]),
15283        &mut cx,
15284    );
15285}
15286
15287#[gpui::test]
15288async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15289    init_test(cx, |_| {});
15290    let mut cx = EditorTestContext::new(cx).await;
15291    let text = indoc! {
15292        "
15293        impl A {
15294            fn b() {
15295                0;
15296                3;
15297                5;
15298                6;
15299                7;
15300            }
15301        }
15302        "
15303    };
15304    let base_text = indoc! {
15305        "
15306        impl A {
15307            fn b() {
15308                0;
15309                1;
15310                2;
15311                3;
15312                4;
15313            }
15314            fn c() {
15315                5;
15316                6;
15317                7;
15318            }
15319        }
15320        "
15321    };
15322
15323    cx.update_editor(|editor, window, cx| {
15324        editor.set_text(text, window, cx);
15325
15326        editor.buffer().update(cx, |multibuffer, cx| {
15327            let buffer = multibuffer.as_singleton().unwrap();
15328            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15329
15330            multibuffer.set_all_diff_hunks_expanded(cx);
15331            multibuffer.add_diff(diff, cx);
15332
15333            buffer.read(cx).remote_id()
15334        })
15335    });
15336    cx.run_until_parked();
15337
15338    cx.assert_state_with_diff(
15339        indoc! { "
15340          impl A {
15341              fn b() {
15342                  0;
15343        -         1;
15344        -         2;
15345                  3;
15346        -         4;
15347        -     }
15348        -     fn c() {
15349                  5;
15350                  6;
15351                  7;
15352              }
15353          }
15354          ˇ"
15355        }
15356        .to_string(),
15357    );
15358
15359    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15360        editor
15361            .snapshot(window, cx)
15362            .buffer_snapshot
15363            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15364            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15365            .collect::<Vec<_>>()
15366    });
15367    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15368    assert_eq!(
15369        actual_guides,
15370        vec![
15371            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15372            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15373            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15374        ]
15375    );
15376}
15377
15378#[gpui::test]
15379async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15380    init_test(cx, |_| {});
15381    let mut cx = EditorTestContext::new(cx).await;
15382
15383    let diff_base = r#"
15384        a
15385        b
15386        c
15387        "#
15388    .unindent();
15389
15390    cx.set_state(
15391        &r#"
15392        ˇA
15393        b
15394        C
15395        "#
15396        .unindent(),
15397    );
15398    cx.set_head_text(&diff_base);
15399    cx.update_editor(|editor, window, cx| {
15400        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15401    });
15402    executor.run_until_parked();
15403
15404    let both_hunks_expanded = r#"
15405        - a
15406        + ˇA
15407          b
15408        - c
15409        + C
15410        "#
15411    .unindent();
15412
15413    cx.assert_state_with_diff(both_hunks_expanded.clone());
15414
15415    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15416        let snapshot = editor.snapshot(window, cx);
15417        let hunks = editor
15418            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15419            .collect::<Vec<_>>();
15420        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15421        let buffer_id = hunks[0].buffer_id;
15422        hunks
15423            .into_iter()
15424            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15425            .collect::<Vec<_>>()
15426    });
15427    assert_eq!(hunk_ranges.len(), 2);
15428
15429    cx.update_editor(|editor, _, cx| {
15430        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15431    });
15432    executor.run_until_parked();
15433
15434    let second_hunk_expanded = r#"
15435          ˇA
15436          b
15437        - c
15438        + C
15439        "#
15440    .unindent();
15441
15442    cx.assert_state_with_diff(second_hunk_expanded);
15443
15444    cx.update_editor(|editor, _, cx| {
15445        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15446    });
15447    executor.run_until_parked();
15448
15449    cx.assert_state_with_diff(both_hunks_expanded.clone());
15450
15451    cx.update_editor(|editor, _, cx| {
15452        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15453    });
15454    executor.run_until_parked();
15455
15456    let first_hunk_expanded = r#"
15457        - a
15458        + ˇA
15459          b
15460          C
15461        "#
15462    .unindent();
15463
15464    cx.assert_state_with_diff(first_hunk_expanded);
15465
15466    cx.update_editor(|editor, _, cx| {
15467        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15468    });
15469    executor.run_until_parked();
15470
15471    cx.assert_state_with_diff(both_hunks_expanded);
15472
15473    cx.set_state(
15474        &r#"
15475        ˇA
15476        b
15477        "#
15478        .unindent(),
15479    );
15480    cx.run_until_parked();
15481
15482    // TODO this cursor position seems bad
15483    cx.assert_state_with_diff(
15484        r#"
15485        - ˇa
15486        + A
15487          b
15488        "#
15489        .unindent(),
15490    );
15491
15492    cx.update_editor(|editor, window, cx| {
15493        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15494    });
15495
15496    cx.assert_state_with_diff(
15497        r#"
15498            - ˇa
15499            + A
15500              b
15501            - c
15502            "#
15503        .unindent(),
15504    );
15505
15506    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15507        let snapshot = editor.snapshot(window, cx);
15508        let hunks = editor
15509            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15510            .collect::<Vec<_>>();
15511        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15512        let buffer_id = hunks[0].buffer_id;
15513        hunks
15514            .into_iter()
15515            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15516            .collect::<Vec<_>>()
15517    });
15518    assert_eq!(hunk_ranges.len(), 2);
15519
15520    cx.update_editor(|editor, _, cx| {
15521        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15522    });
15523    executor.run_until_parked();
15524
15525    cx.assert_state_with_diff(
15526        r#"
15527        - ˇa
15528        + A
15529          b
15530        "#
15531        .unindent(),
15532    );
15533}
15534
15535#[gpui::test]
15536async fn test_toggle_deletion_hunk_at_start_of_file(
15537    executor: BackgroundExecutor,
15538    cx: &mut TestAppContext,
15539) {
15540    init_test(cx, |_| {});
15541    let mut cx = EditorTestContext::new(cx).await;
15542
15543    let diff_base = r#"
15544        a
15545        b
15546        c
15547        "#
15548    .unindent();
15549
15550    cx.set_state(
15551        &r#"
15552        ˇb
15553        c
15554        "#
15555        .unindent(),
15556    );
15557    cx.set_head_text(&diff_base);
15558    cx.update_editor(|editor, window, cx| {
15559        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15560    });
15561    executor.run_until_parked();
15562
15563    let hunk_expanded = r#"
15564        - a
15565          ˇb
15566          c
15567        "#
15568    .unindent();
15569
15570    cx.assert_state_with_diff(hunk_expanded.clone());
15571
15572    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15573        let snapshot = editor.snapshot(window, cx);
15574        let hunks = editor
15575            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15576            .collect::<Vec<_>>();
15577        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15578        let buffer_id = hunks[0].buffer_id;
15579        hunks
15580            .into_iter()
15581            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15582            .collect::<Vec<_>>()
15583    });
15584    assert_eq!(hunk_ranges.len(), 1);
15585
15586    cx.update_editor(|editor, _, cx| {
15587        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15588    });
15589    executor.run_until_parked();
15590
15591    let hunk_collapsed = r#"
15592          ˇb
15593          c
15594        "#
15595    .unindent();
15596
15597    cx.assert_state_with_diff(hunk_collapsed);
15598
15599    cx.update_editor(|editor, _, cx| {
15600        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15601    });
15602    executor.run_until_parked();
15603
15604    cx.assert_state_with_diff(hunk_expanded.clone());
15605}
15606
15607#[gpui::test]
15608async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15609    init_test(cx, |_| {});
15610
15611    let fs = FakeFs::new(cx.executor());
15612    fs.insert_tree(
15613        path!("/test"),
15614        json!({
15615            ".git": {},
15616            "file-1": "ONE\n",
15617            "file-2": "TWO\n",
15618            "file-3": "THREE\n",
15619        }),
15620    )
15621    .await;
15622
15623    fs.set_head_for_repo(
15624        path!("/test/.git").as_ref(),
15625        &[
15626            ("file-1".into(), "one\n".into()),
15627            ("file-2".into(), "two\n".into()),
15628            ("file-3".into(), "three\n".into()),
15629        ],
15630    );
15631
15632    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15633    let mut buffers = vec![];
15634    for i in 1..=3 {
15635        let buffer = project
15636            .update(cx, |project, cx| {
15637                let path = format!(path!("/test/file-{}"), i);
15638                project.open_local_buffer(path, cx)
15639            })
15640            .await
15641            .unwrap();
15642        buffers.push(buffer);
15643    }
15644
15645    let multibuffer = cx.new(|cx| {
15646        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15647        multibuffer.set_all_diff_hunks_expanded(cx);
15648        for buffer in &buffers {
15649            let snapshot = buffer.read(cx).snapshot();
15650            multibuffer.set_excerpts_for_path(
15651                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15652                buffer.clone(),
15653                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15654                DEFAULT_MULTIBUFFER_CONTEXT,
15655                cx,
15656            );
15657        }
15658        multibuffer
15659    });
15660
15661    let editor = cx.add_window(|window, cx| {
15662        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15663    });
15664    cx.run_until_parked();
15665
15666    let snapshot = editor
15667        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15668        .unwrap();
15669    let hunks = snapshot
15670        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15671        .map(|hunk| match hunk {
15672            DisplayDiffHunk::Unfolded {
15673                display_row_range, ..
15674            } => display_row_range,
15675            DisplayDiffHunk::Folded { .. } => unreachable!(),
15676        })
15677        .collect::<Vec<_>>();
15678    assert_eq!(
15679        hunks,
15680        [
15681            DisplayRow(2)..DisplayRow(4),
15682            DisplayRow(7)..DisplayRow(9),
15683            DisplayRow(12)..DisplayRow(14),
15684        ]
15685    );
15686}
15687
15688#[gpui::test]
15689async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15690    init_test(cx, |_| {});
15691
15692    let mut cx = EditorTestContext::new(cx).await;
15693    cx.set_head_text(indoc! { "
15694        one
15695        two
15696        three
15697        four
15698        five
15699        "
15700    });
15701    cx.set_index_text(indoc! { "
15702        one
15703        two
15704        three
15705        four
15706        five
15707        "
15708    });
15709    cx.set_state(indoc! {"
15710        one
15711        TWO
15712        ˇTHREE
15713        FOUR
15714        five
15715    "});
15716    cx.run_until_parked();
15717    cx.update_editor(|editor, window, cx| {
15718        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15719    });
15720    cx.run_until_parked();
15721    cx.assert_index_text(Some(indoc! {"
15722        one
15723        TWO
15724        THREE
15725        FOUR
15726        five
15727    "}));
15728    cx.set_state(indoc! { "
15729        one
15730        TWO
15731        ˇTHREE-HUNDRED
15732        FOUR
15733        five
15734    "});
15735    cx.run_until_parked();
15736    cx.update_editor(|editor, window, cx| {
15737        let snapshot = editor.snapshot(window, cx);
15738        let hunks = editor
15739            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15740            .collect::<Vec<_>>();
15741        assert_eq!(hunks.len(), 1);
15742        assert_eq!(
15743            hunks[0].status(),
15744            DiffHunkStatus {
15745                kind: DiffHunkStatusKind::Modified,
15746                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15747            }
15748        );
15749
15750        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15751    });
15752    cx.run_until_parked();
15753    cx.assert_index_text(Some(indoc! {"
15754        one
15755        TWO
15756        THREE-HUNDRED
15757        FOUR
15758        five
15759    "}));
15760}
15761
15762#[gpui::test]
15763fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15764    init_test(cx, |_| {});
15765
15766    let editor = cx.add_window(|window, cx| {
15767        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15768        build_editor(buffer, window, cx)
15769    });
15770
15771    let render_args = Arc::new(Mutex::new(None));
15772    let snapshot = editor
15773        .update(cx, |editor, window, cx| {
15774            let snapshot = editor.buffer().read(cx).snapshot(cx);
15775            let range =
15776                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15777
15778            struct RenderArgs {
15779                row: MultiBufferRow,
15780                folded: bool,
15781                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15782            }
15783
15784            let crease = Crease::inline(
15785                range,
15786                FoldPlaceholder::test(),
15787                {
15788                    let toggle_callback = render_args.clone();
15789                    move |row, folded, callback, _window, _cx| {
15790                        *toggle_callback.lock() = Some(RenderArgs {
15791                            row,
15792                            folded,
15793                            callback,
15794                        });
15795                        div()
15796                    }
15797                },
15798                |_row, _folded, _window, _cx| div(),
15799            );
15800
15801            editor.insert_creases(Some(crease), cx);
15802            let snapshot = editor.snapshot(window, cx);
15803            let _div = snapshot.render_crease_toggle(
15804                MultiBufferRow(1),
15805                false,
15806                cx.entity().clone(),
15807                window,
15808                cx,
15809            );
15810            snapshot
15811        })
15812        .unwrap();
15813
15814    let render_args = render_args.lock().take().unwrap();
15815    assert_eq!(render_args.row, MultiBufferRow(1));
15816    assert!(!render_args.folded);
15817    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15818
15819    cx.update_window(*editor, |_, window, cx| {
15820        (render_args.callback)(true, window, cx)
15821    })
15822    .unwrap();
15823    let snapshot = editor
15824        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15825        .unwrap();
15826    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15827
15828    cx.update_window(*editor, |_, window, cx| {
15829        (render_args.callback)(false, window, cx)
15830    })
15831    .unwrap();
15832    let snapshot = editor
15833        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15834        .unwrap();
15835    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15836}
15837
15838#[gpui::test]
15839async fn test_input_text(cx: &mut TestAppContext) {
15840    init_test(cx, |_| {});
15841    let mut cx = EditorTestContext::new(cx).await;
15842
15843    cx.set_state(
15844        &r#"ˇone
15845        two
15846
15847        three
15848        fourˇ
15849        five
15850
15851        siˇx"#
15852            .unindent(),
15853    );
15854
15855    cx.dispatch_action(HandleInput(String::new()));
15856    cx.assert_editor_state(
15857        &r#"ˇone
15858        two
15859
15860        three
15861        fourˇ
15862        five
15863
15864        siˇx"#
15865            .unindent(),
15866    );
15867
15868    cx.dispatch_action(HandleInput("AAAA".to_string()));
15869    cx.assert_editor_state(
15870        &r#"AAAAˇone
15871        two
15872
15873        three
15874        fourAAAAˇ
15875        five
15876
15877        siAAAAˇx"#
15878            .unindent(),
15879    );
15880}
15881
15882#[gpui::test]
15883async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
15884    init_test(cx, |_| {});
15885
15886    let mut cx = EditorTestContext::new(cx).await;
15887    cx.set_state(
15888        r#"let foo = 1;
15889let foo = 2;
15890let foo = 3;
15891let fooˇ = 4;
15892let foo = 5;
15893let foo = 6;
15894let foo = 7;
15895let foo = 8;
15896let foo = 9;
15897let foo = 10;
15898let foo = 11;
15899let foo = 12;
15900let foo = 13;
15901let foo = 14;
15902let foo = 15;"#,
15903    );
15904
15905    cx.update_editor(|e, window, cx| {
15906        assert_eq!(
15907            e.next_scroll_position,
15908            NextScrollCursorCenterTopBottom::Center,
15909            "Default next scroll direction is center",
15910        );
15911
15912        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15913        assert_eq!(
15914            e.next_scroll_position,
15915            NextScrollCursorCenterTopBottom::Top,
15916            "After center, next scroll direction should be top",
15917        );
15918
15919        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15920        assert_eq!(
15921            e.next_scroll_position,
15922            NextScrollCursorCenterTopBottom::Bottom,
15923            "After top, next scroll direction should be bottom",
15924        );
15925
15926        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15927        assert_eq!(
15928            e.next_scroll_position,
15929            NextScrollCursorCenterTopBottom::Center,
15930            "After bottom, scrolling should start over",
15931        );
15932
15933        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
15934        assert_eq!(
15935            e.next_scroll_position,
15936            NextScrollCursorCenterTopBottom::Top,
15937            "Scrolling continues if retriggered fast enough"
15938        );
15939    });
15940
15941    cx.executor()
15942        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
15943    cx.executor().run_until_parked();
15944    cx.update_editor(|e, _, _| {
15945        assert_eq!(
15946            e.next_scroll_position,
15947            NextScrollCursorCenterTopBottom::Center,
15948            "If scrolling is not triggered fast enough, it should reset"
15949        );
15950    });
15951}
15952
15953#[gpui::test]
15954async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
15955    init_test(cx, |_| {});
15956    let mut cx = EditorLspTestContext::new_rust(
15957        lsp::ServerCapabilities {
15958            definition_provider: Some(lsp::OneOf::Left(true)),
15959            references_provider: Some(lsp::OneOf::Left(true)),
15960            ..lsp::ServerCapabilities::default()
15961        },
15962        cx,
15963    )
15964    .await;
15965
15966    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
15967        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
15968            move |params, _| async move {
15969                if empty_go_to_definition {
15970                    Ok(None)
15971                } else {
15972                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
15973                        uri: params.text_document_position_params.text_document.uri,
15974                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
15975                    })))
15976                }
15977            },
15978        );
15979        let references =
15980            cx.lsp
15981                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
15982                    Ok(Some(vec![lsp::Location {
15983                        uri: params.text_document_position.text_document.uri,
15984                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
15985                    }]))
15986                });
15987        (go_to_definition, references)
15988    };
15989
15990    cx.set_state(
15991        &r#"fn one() {
15992            let mut a = ˇtwo();
15993        }
15994
15995        fn two() {}"#
15996            .unindent(),
15997    );
15998    set_up_lsp_handlers(false, &mut cx);
15999    let navigated = cx
16000        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16001        .await
16002        .expect("Failed to navigate to definition");
16003    assert_eq!(
16004        navigated,
16005        Navigated::Yes,
16006        "Should have navigated to definition from the GetDefinition response"
16007    );
16008    cx.assert_editor_state(
16009        &r#"fn one() {
16010            let mut a = two();
16011        }
16012
16013        fn «twoˇ»() {}"#
16014            .unindent(),
16015    );
16016
16017    let editors = cx.update_workspace(|workspace, _, cx| {
16018        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16019    });
16020    cx.update_editor(|_, _, test_editor_cx| {
16021        assert_eq!(
16022            editors.len(),
16023            1,
16024            "Initially, only one, test, editor should be open in the workspace"
16025        );
16026        assert_eq!(
16027            test_editor_cx.entity(),
16028            editors.last().expect("Asserted len is 1").clone()
16029        );
16030    });
16031
16032    set_up_lsp_handlers(true, &mut cx);
16033    let navigated = cx
16034        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16035        .await
16036        .expect("Failed to navigate to lookup references");
16037    assert_eq!(
16038        navigated,
16039        Navigated::Yes,
16040        "Should have navigated to references as a fallback after empty GoToDefinition response"
16041    );
16042    // We should not change the selections in the existing file,
16043    // if opening another milti buffer with the references
16044    cx.assert_editor_state(
16045        &r#"fn one() {
16046            let mut a = two();
16047        }
16048
16049        fn «twoˇ»() {}"#
16050            .unindent(),
16051    );
16052    let editors = cx.update_workspace(|workspace, _, cx| {
16053        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16054    });
16055    cx.update_editor(|_, _, test_editor_cx| {
16056        assert_eq!(
16057            editors.len(),
16058            2,
16059            "After falling back to references search, we open a new editor with the results"
16060        );
16061        let references_fallback_text = editors
16062            .into_iter()
16063            .find(|new_editor| *new_editor != test_editor_cx.entity())
16064            .expect("Should have one non-test editor now")
16065            .read(test_editor_cx)
16066            .text(test_editor_cx);
16067        assert_eq!(
16068            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16069            "Should use the range from the references response and not the GoToDefinition one"
16070        );
16071    });
16072}
16073
16074#[gpui::test]
16075async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16076    init_test(cx, |_| {});
16077
16078    let language = Arc::new(Language::new(
16079        LanguageConfig::default(),
16080        Some(tree_sitter_rust::LANGUAGE.into()),
16081    ));
16082
16083    let text = r#"
16084        #[cfg(test)]
16085        mod tests() {
16086            #[test]
16087            fn runnable_1() {
16088                let a = 1;
16089            }
16090
16091            #[test]
16092            fn runnable_2() {
16093                let a = 1;
16094                let b = 2;
16095            }
16096        }
16097    "#
16098    .unindent();
16099
16100    let fs = FakeFs::new(cx.executor());
16101    fs.insert_file("/file.rs", Default::default()).await;
16102
16103    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16104    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16105    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16106    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16107    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16108
16109    let editor = cx.new_window_entity(|window, cx| {
16110        Editor::new(
16111            EditorMode::Full,
16112            multi_buffer,
16113            Some(project.clone()),
16114            window,
16115            cx,
16116        )
16117    });
16118
16119    editor.update_in(cx, |editor, window, cx| {
16120        let snapshot = editor.buffer().read(cx).snapshot(cx);
16121        editor.tasks.insert(
16122            (buffer.read(cx).remote_id(), 3),
16123            RunnableTasks {
16124                templates: vec![],
16125                offset: snapshot.anchor_before(43),
16126                column: 0,
16127                extra_variables: HashMap::default(),
16128                context_range: BufferOffset(43)..BufferOffset(85),
16129            },
16130        );
16131        editor.tasks.insert(
16132            (buffer.read(cx).remote_id(), 8),
16133            RunnableTasks {
16134                templates: vec![],
16135                offset: snapshot.anchor_before(86),
16136                column: 0,
16137                extra_variables: HashMap::default(),
16138                context_range: BufferOffset(86)..BufferOffset(191),
16139            },
16140        );
16141
16142        // Test finding task when cursor is inside function body
16143        editor.change_selections(None, window, cx, |s| {
16144            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16145        });
16146        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16147        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16148
16149        // Test finding task when cursor is on function name
16150        editor.change_selections(None, window, cx, |s| {
16151            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16152        });
16153        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16154        assert_eq!(row, 8, "Should find task when cursor is on function name");
16155    });
16156}
16157
16158#[gpui::test]
16159async fn test_folding_buffers(cx: &mut TestAppContext) {
16160    init_test(cx, |_| {});
16161
16162    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16163    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16164    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16165
16166    let fs = FakeFs::new(cx.executor());
16167    fs.insert_tree(
16168        path!("/a"),
16169        json!({
16170            "first.rs": sample_text_1,
16171            "second.rs": sample_text_2,
16172            "third.rs": sample_text_3,
16173        }),
16174    )
16175    .await;
16176    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16177    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16178    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16179    let worktree = project.update(cx, |project, cx| {
16180        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16181        assert_eq!(worktrees.len(), 1);
16182        worktrees.pop().unwrap()
16183    });
16184    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16185
16186    let buffer_1 = project
16187        .update(cx, |project, cx| {
16188            project.open_buffer((worktree_id, "first.rs"), cx)
16189        })
16190        .await
16191        .unwrap();
16192    let buffer_2 = project
16193        .update(cx, |project, cx| {
16194            project.open_buffer((worktree_id, "second.rs"), cx)
16195        })
16196        .await
16197        .unwrap();
16198    let buffer_3 = project
16199        .update(cx, |project, cx| {
16200            project.open_buffer((worktree_id, "third.rs"), cx)
16201        })
16202        .await
16203        .unwrap();
16204
16205    let multi_buffer = cx.new(|cx| {
16206        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16207        multi_buffer.push_excerpts(
16208            buffer_1.clone(),
16209            [
16210                ExcerptRange {
16211                    context: Point::new(0, 0)..Point::new(3, 0),
16212                    primary: None,
16213                },
16214                ExcerptRange {
16215                    context: Point::new(5, 0)..Point::new(7, 0),
16216                    primary: None,
16217                },
16218                ExcerptRange {
16219                    context: Point::new(9, 0)..Point::new(10, 4),
16220                    primary: None,
16221                },
16222            ],
16223            cx,
16224        );
16225        multi_buffer.push_excerpts(
16226            buffer_2.clone(),
16227            [
16228                ExcerptRange {
16229                    context: Point::new(0, 0)..Point::new(3, 0),
16230                    primary: None,
16231                },
16232                ExcerptRange {
16233                    context: Point::new(5, 0)..Point::new(7, 0),
16234                    primary: None,
16235                },
16236                ExcerptRange {
16237                    context: Point::new(9, 0)..Point::new(10, 4),
16238                    primary: None,
16239                },
16240            ],
16241            cx,
16242        );
16243        multi_buffer.push_excerpts(
16244            buffer_3.clone(),
16245            [
16246                ExcerptRange {
16247                    context: Point::new(0, 0)..Point::new(3, 0),
16248                    primary: None,
16249                },
16250                ExcerptRange {
16251                    context: Point::new(5, 0)..Point::new(7, 0),
16252                    primary: None,
16253                },
16254                ExcerptRange {
16255                    context: Point::new(9, 0)..Point::new(10, 4),
16256                    primary: None,
16257                },
16258            ],
16259            cx,
16260        );
16261        multi_buffer
16262    });
16263    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16264        Editor::new(
16265            EditorMode::Full,
16266            multi_buffer.clone(),
16267            Some(project.clone()),
16268            window,
16269            cx,
16270        )
16271    });
16272
16273    assert_eq!(
16274        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16275        "\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",
16276    );
16277
16278    multi_buffer_editor.update(cx, |editor, cx| {
16279        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16280    });
16281    assert_eq!(
16282        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16283        "\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",
16284        "After folding the first buffer, its text should not be displayed"
16285    );
16286
16287    multi_buffer_editor.update(cx, |editor, cx| {
16288        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16289    });
16290    assert_eq!(
16291        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16292        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16293        "After folding the second buffer, its text should not be displayed"
16294    );
16295
16296    multi_buffer_editor.update(cx, |editor, cx| {
16297        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16298    });
16299    assert_eq!(
16300        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16301        "\n\n\n\n\n",
16302        "After folding the third buffer, its text should not be displayed"
16303    );
16304
16305    // Emulate selection inside the fold logic, that should work
16306    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16307        editor
16308            .snapshot(window, cx)
16309            .next_line_boundary(Point::new(0, 4));
16310    });
16311
16312    multi_buffer_editor.update(cx, |editor, cx| {
16313        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16314    });
16315    assert_eq!(
16316        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16317        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16318        "After unfolding the second buffer, its text should be displayed"
16319    );
16320
16321    // Typing inside of buffer 1 causes that buffer to be unfolded.
16322    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16323        assert_eq!(
16324            multi_buffer
16325                .read(cx)
16326                .snapshot(cx)
16327                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16328                .collect::<String>(),
16329            "bbbb"
16330        );
16331        editor.change_selections(None, window, cx, |selections| {
16332            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16333        });
16334        editor.handle_input("B", window, cx);
16335    });
16336
16337    assert_eq!(
16338        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16339        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16340        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16341    );
16342
16343    multi_buffer_editor.update(cx, |editor, cx| {
16344        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16345    });
16346    assert_eq!(
16347        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16348        "\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",
16349        "After unfolding the all buffers, all original text should be displayed"
16350    );
16351}
16352
16353#[gpui::test]
16354async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16355    init_test(cx, |_| {});
16356
16357    let sample_text_1 = "1111\n2222\n3333".to_string();
16358    let sample_text_2 = "4444\n5555\n6666".to_string();
16359    let sample_text_3 = "7777\n8888\n9999".to_string();
16360
16361    let fs = FakeFs::new(cx.executor());
16362    fs.insert_tree(
16363        path!("/a"),
16364        json!({
16365            "first.rs": sample_text_1,
16366            "second.rs": sample_text_2,
16367            "third.rs": sample_text_3,
16368        }),
16369    )
16370    .await;
16371    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16372    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16373    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16374    let worktree = project.update(cx, |project, cx| {
16375        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16376        assert_eq!(worktrees.len(), 1);
16377        worktrees.pop().unwrap()
16378    });
16379    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16380
16381    let buffer_1 = project
16382        .update(cx, |project, cx| {
16383            project.open_buffer((worktree_id, "first.rs"), cx)
16384        })
16385        .await
16386        .unwrap();
16387    let buffer_2 = project
16388        .update(cx, |project, cx| {
16389            project.open_buffer((worktree_id, "second.rs"), cx)
16390        })
16391        .await
16392        .unwrap();
16393    let buffer_3 = project
16394        .update(cx, |project, cx| {
16395            project.open_buffer((worktree_id, "third.rs"), cx)
16396        })
16397        .await
16398        .unwrap();
16399
16400    let multi_buffer = cx.new(|cx| {
16401        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16402        multi_buffer.push_excerpts(
16403            buffer_1.clone(),
16404            [ExcerptRange {
16405                context: Point::new(0, 0)..Point::new(3, 0),
16406                primary: None,
16407            }],
16408            cx,
16409        );
16410        multi_buffer.push_excerpts(
16411            buffer_2.clone(),
16412            [ExcerptRange {
16413                context: Point::new(0, 0)..Point::new(3, 0),
16414                primary: None,
16415            }],
16416            cx,
16417        );
16418        multi_buffer.push_excerpts(
16419            buffer_3.clone(),
16420            [ExcerptRange {
16421                context: Point::new(0, 0)..Point::new(3, 0),
16422                primary: None,
16423            }],
16424            cx,
16425        );
16426        multi_buffer
16427    });
16428
16429    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16430        Editor::new(
16431            EditorMode::Full,
16432            multi_buffer,
16433            Some(project.clone()),
16434            window,
16435            cx,
16436        )
16437    });
16438
16439    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16440    assert_eq!(
16441        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16442        full_text,
16443    );
16444
16445    multi_buffer_editor.update(cx, |editor, cx| {
16446        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16447    });
16448    assert_eq!(
16449        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16450        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16451        "After folding the first buffer, its text should not be displayed"
16452    );
16453
16454    multi_buffer_editor.update(cx, |editor, cx| {
16455        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16456    });
16457
16458    assert_eq!(
16459        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16460        "\n\n\n\n\n\n7777\n8888\n9999",
16461        "After folding the second buffer, its text should not be displayed"
16462    );
16463
16464    multi_buffer_editor.update(cx, |editor, cx| {
16465        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16466    });
16467    assert_eq!(
16468        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16469        "\n\n\n\n\n",
16470        "After folding the third buffer, its text should not be displayed"
16471    );
16472
16473    multi_buffer_editor.update(cx, |editor, cx| {
16474        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16475    });
16476    assert_eq!(
16477        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16478        "\n\n\n\n4444\n5555\n6666\n\n",
16479        "After unfolding the second buffer, its text should be displayed"
16480    );
16481
16482    multi_buffer_editor.update(cx, |editor, cx| {
16483        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16484    });
16485    assert_eq!(
16486        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16487        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16488        "After unfolding the first buffer, its text should be displayed"
16489    );
16490
16491    multi_buffer_editor.update(cx, |editor, cx| {
16492        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16493    });
16494    assert_eq!(
16495        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16496        full_text,
16497        "After unfolding all buffers, all original text should be displayed"
16498    );
16499}
16500
16501#[gpui::test]
16502async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16503    init_test(cx, |_| {});
16504
16505    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16506
16507    let fs = FakeFs::new(cx.executor());
16508    fs.insert_tree(
16509        path!("/a"),
16510        json!({
16511            "main.rs": sample_text,
16512        }),
16513    )
16514    .await;
16515    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16516    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16517    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16518    let worktree = project.update(cx, |project, cx| {
16519        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16520        assert_eq!(worktrees.len(), 1);
16521        worktrees.pop().unwrap()
16522    });
16523    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16524
16525    let buffer_1 = project
16526        .update(cx, |project, cx| {
16527            project.open_buffer((worktree_id, "main.rs"), cx)
16528        })
16529        .await
16530        .unwrap();
16531
16532    let multi_buffer = cx.new(|cx| {
16533        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16534        multi_buffer.push_excerpts(
16535            buffer_1.clone(),
16536            [ExcerptRange {
16537                context: Point::new(0, 0)
16538                    ..Point::new(
16539                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16540                        0,
16541                    ),
16542                primary: None,
16543            }],
16544            cx,
16545        );
16546        multi_buffer
16547    });
16548    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16549        Editor::new(
16550            EditorMode::Full,
16551            multi_buffer,
16552            Some(project.clone()),
16553            window,
16554            cx,
16555        )
16556    });
16557
16558    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16559    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16560        enum TestHighlight {}
16561        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16562        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16563        editor.highlight_text::<TestHighlight>(
16564            vec![highlight_range.clone()],
16565            HighlightStyle::color(Hsla::green()),
16566            cx,
16567        );
16568        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16569    });
16570
16571    let full_text = format!("\n\n{sample_text}");
16572    assert_eq!(
16573        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16574        full_text,
16575    );
16576}
16577
16578#[gpui::test]
16579async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16580    init_test(cx, |_| {});
16581    cx.update(|cx| {
16582        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16583            "keymaps/default-linux.json",
16584            cx,
16585        )
16586        .unwrap();
16587        cx.bind_keys(default_key_bindings);
16588    });
16589
16590    let (editor, cx) = cx.add_window_view(|window, cx| {
16591        let multi_buffer = MultiBuffer::build_multi(
16592            [
16593                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16594                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16595                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16596                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16597            ],
16598            cx,
16599        );
16600        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16601
16602        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16603        // fold all but the second buffer, so that we test navigating between two
16604        // adjacent folded buffers, as well as folded buffers at the start and
16605        // end the multibuffer
16606        editor.fold_buffer(buffer_ids[0], cx);
16607        editor.fold_buffer(buffer_ids[2], cx);
16608        editor.fold_buffer(buffer_ids[3], cx);
16609
16610        editor
16611    });
16612    cx.simulate_resize(size(px(1000.), px(1000.)));
16613
16614    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16615    cx.assert_excerpts_with_selections(indoc! {"
16616        [EXCERPT]
16617        ˇ[FOLDED]
16618        [EXCERPT]
16619        a1
16620        b1
16621        [EXCERPT]
16622        [FOLDED]
16623        [EXCERPT]
16624        [FOLDED]
16625        "
16626    });
16627    cx.simulate_keystroke("down");
16628    cx.assert_excerpts_with_selections(indoc! {"
16629        [EXCERPT]
16630        [FOLDED]
16631        [EXCERPT]
16632        ˇa1
16633        b1
16634        [EXCERPT]
16635        [FOLDED]
16636        [EXCERPT]
16637        [FOLDED]
16638        "
16639    });
16640    cx.simulate_keystroke("down");
16641    cx.assert_excerpts_with_selections(indoc! {"
16642        [EXCERPT]
16643        [FOLDED]
16644        [EXCERPT]
16645        a1
16646        ˇb1
16647        [EXCERPT]
16648        [FOLDED]
16649        [EXCERPT]
16650        [FOLDED]
16651        "
16652    });
16653    cx.simulate_keystroke("down");
16654    cx.assert_excerpts_with_selections(indoc! {"
16655        [EXCERPT]
16656        [FOLDED]
16657        [EXCERPT]
16658        a1
16659        b1
16660        ˇ[EXCERPT]
16661        [FOLDED]
16662        [EXCERPT]
16663        [FOLDED]
16664        "
16665    });
16666    cx.simulate_keystroke("down");
16667    cx.assert_excerpts_with_selections(indoc! {"
16668        [EXCERPT]
16669        [FOLDED]
16670        [EXCERPT]
16671        a1
16672        b1
16673        [EXCERPT]
16674        ˇ[FOLDED]
16675        [EXCERPT]
16676        [FOLDED]
16677        "
16678    });
16679    for _ in 0..5 {
16680        cx.simulate_keystroke("down");
16681        cx.assert_excerpts_with_selections(indoc! {"
16682            [EXCERPT]
16683            [FOLDED]
16684            [EXCERPT]
16685            a1
16686            b1
16687            [EXCERPT]
16688            [FOLDED]
16689            [EXCERPT]
16690            ˇ[FOLDED]
16691            "
16692        });
16693    }
16694
16695    cx.simulate_keystroke("up");
16696    cx.assert_excerpts_with_selections(indoc! {"
16697        [EXCERPT]
16698        [FOLDED]
16699        [EXCERPT]
16700        a1
16701        b1
16702        [EXCERPT]
16703        ˇ[FOLDED]
16704        [EXCERPT]
16705        [FOLDED]
16706        "
16707    });
16708    cx.simulate_keystroke("up");
16709    cx.assert_excerpts_with_selections(indoc! {"
16710        [EXCERPT]
16711        [FOLDED]
16712        [EXCERPT]
16713        a1
16714        b1
16715        ˇ[EXCERPT]
16716        [FOLDED]
16717        [EXCERPT]
16718        [FOLDED]
16719        "
16720    });
16721    cx.simulate_keystroke("up");
16722    cx.assert_excerpts_with_selections(indoc! {"
16723        [EXCERPT]
16724        [FOLDED]
16725        [EXCERPT]
16726        a1
16727        ˇb1
16728        [EXCERPT]
16729        [FOLDED]
16730        [EXCERPT]
16731        [FOLDED]
16732        "
16733    });
16734    cx.simulate_keystroke("up");
16735    cx.assert_excerpts_with_selections(indoc! {"
16736        [EXCERPT]
16737        [FOLDED]
16738        [EXCERPT]
16739        ˇa1
16740        b1
16741        [EXCERPT]
16742        [FOLDED]
16743        [EXCERPT]
16744        [FOLDED]
16745        "
16746    });
16747    for _ in 0..5 {
16748        cx.simulate_keystroke("up");
16749        cx.assert_excerpts_with_selections(indoc! {"
16750            [EXCERPT]
16751            ˇ[FOLDED]
16752            [EXCERPT]
16753            a1
16754            b1
16755            [EXCERPT]
16756            [FOLDED]
16757            [EXCERPT]
16758            [FOLDED]
16759            "
16760        });
16761    }
16762}
16763
16764#[gpui::test]
16765async fn test_inline_completion_text(cx: &mut TestAppContext) {
16766    init_test(cx, |_| {});
16767
16768    // Simple insertion
16769    assert_highlighted_edits(
16770        "Hello, world!",
16771        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16772        true,
16773        cx,
16774        |highlighted_edits, cx| {
16775            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16776            assert_eq!(highlighted_edits.highlights.len(), 1);
16777            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16778            assert_eq!(
16779                highlighted_edits.highlights[0].1.background_color,
16780                Some(cx.theme().status().created_background)
16781            );
16782        },
16783    )
16784    .await;
16785
16786    // Replacement
16787    assert_highlighted_edits(
16788        "This is a test.",
16789        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16790        false,
16791        cx,
16792        |highlighted_edits, cx| {
16793            assert_eq!(highlighted_edits.text, "That is a test.");
16794            assert_eq!(highlighted_edits.highlights.len(), 1);
16795            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16796            assert_eq!(
16797                highlighted_edits.highlights[0].1.background_color,
16798                Some(cx.theme().status().created_background)
16799            );
16800        },
16801    )
16802    .await;
16803
16804    // Multiple edits
16805    assert_highlighted_edits(
16806        "Hello, world!",
16807        vec![
16808            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16809            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16810        ],
16811        false,
16812        cx,
16813        |highlighted_edits, cx| {
16814            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16815            assert_eq!(highlighted_edits.highlights.len(), 2);
16816            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16817            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16818            assert_eq!(
16819                highlighted_edits.highlights[0].1.background_color,
16820                Some(cx.theme().status().created_background)
16821            );
16822            assert_eq!(
16823                highlighted_edits.highlights[1].1.background_color,
16824                Some(cx.theme().status().created_background)
16825            );
16826        },
16827    )
16828    .await;
16829
16830    // Multiple lines with edits
16831    assert_highlighted_edits(
16832        "First line\nSecond line\nThird line\nFourth line",
16833        vec![
16834            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16835            (
16836                Point::new(2, 0)..Point::new(2, 10),
16837                "New third line".to_string(),
16838            ),
16839            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16840        ],
16841        false,
16842        cx,
16843        |highlighted_edits, cx| {
16844            assert_eq!(
16845                highlighted_edits.text,
16846                "Second modified\nNew third line\nFourth updated line"
16847            );
16848            assert_eq!(highlighted_edits.highlights.len(), 3);
16849            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16850            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
16851            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
16852            for highlight in &highlighted_edits.highlights {
16853                assert_eq!(
16854                    highlight.1.background_color,
16855                    Some(cx.theme().status().created_background)
16856                );
16857            }
16858        },
16859    )
16860    .await;
16861}
16862
16863#[gpui::test]
16864async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
16865    init_test(cx, |_| {});
16866
16867    // Deletion
16868    assert_highlighted_edits(
16869        "Hello, world!",
16870        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
16871        true,
16872        cx,
16873        |highlighted_edits, cx| {
16874            assert_eq!(highlighted_edits.text, "Hello, world!");
16875            assert_eq!(highlighted_edits.highlights.len(), 1);
16876            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
16877            assert_eq!(
16878                highlighted_edits.highlights[0].1.background_color,
16879                Some(cx.theme().status().deleted_background)
16880            );
16881        },
16882    )
16883    .await;
16884
16885    // Insertion
16886    assert_highlighted_edits(
16887        "Hello, world!",
16888        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
16889        true,
16890        cx,
16891        |highlighted_edits, cx| {
16892            assert_eq!(highlighted_edits.highlights.len(), 1);
16893            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
16894            assert_eq!(
16895                highlighted_edits.highlights[0].1.background_color,
16896                Some(cx.theme().status().created_background)
16897            );
16898        },
16899    )
16900    .await;
16901}
16902
16903async fn assert_highlighted_edits(
16904    text: &str,
16905    edits: Vec<(Range<Point>, String)>,
16906    include_deletions: bool,
16907    cx: &mut TestAppContext,
16908    assertion_fn: impl Fn(HighlightedText, &App),
16909) {
16910    let window = cx.add_window(|window, cx| {
16911        let buffer = MultiBuffer::build_simple(text, cx);
16912        Editor::new(EditorMode::Full, buffer, None, window, cx)
16913    });
16914    let cx = &mut VisualTestContext::from_window(*window, cx);
16915
16916    let (buffer, snapshot) = window
16917        .update(cx, |editor, _window, cx| {
16918            (
16919                editor.buffer().clone(),
16920                editor.buffer().read(cx).snapshot(cx),
16921            )
16922        })
16923        .unwrap();
16924
16925    let edits = edits
16926        .into_iter()
16927        .map(|(range, edit)| {
16928            (
16929                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
16930                edit,
16931            )
16932        })
16933        .collect::<Vec<_>>();
16934
16935    let text_anchor_edits = edits
16936        .clone()
16937        .into_iter()
16938        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
16939        .collect::<Vec<_>>();
16940
16941    let edit_preview = window
16942        .update(cx, |_, _window, cx| {
16943            buffer
16944                .read(cx)
16945                .as_singleton()
16946                .unwrap()
16947                .read(cx)
16948                .preview_edits(text_anchor_edits.into(), cx)
16949        })
16950        .unwrap()
16951        .await;
16952
16953    cx.update(|_window, cx| {
16954        let highlighted_edits = inline_completion_edit_text(
16955            &snapshot.as_singleton().unwrap().2,
16956            &edits,
16957            &edit_preview,
16958            include_deletions,
16959            cx,
16960        );
16961        assertion_fn(highlighted_edits, cx)
16962    });
16963}
16964
16965#[gpui::test]
16966async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
16967    init_test(cx, |_| {});
16968    let capabilities = lsp::ServerCapabilities {
16969        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
16970            prepare_provider: Some(true),
16971            work_done_progress_options: Default::default(),
16972        })),
16973        ..Default::default()
16974    };
16975    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
16976
16977    cx.set_state(indoc! {"
16978        struct Fˇoo {}
16979    "});
16980
16981    cx.update_editor(|editor, _, cx| {
16982        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
16983        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
16984        editor.highlight_background::<DocumentHighlightRead>(
16985            &[highlight_range],
16986            |c| c.editor_document_highlight_read_background,
16987            cx,
16988        );
16989    });
16990
16991    let mut prepare_rename_handler =
16992        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
16993            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
16994                start: lsp::Position {
16995                    line: 0,
16996                    character: 7,
16997                },
16998                end: lsp::Position {
16999                    line: 0,
17000                    character: 10,
17001                },
17002            })))
17003        });
17004    let prepare_rename_task = cx
17005        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17006        .expect("Prepare rename was not started");
17007    prepare_rename_handler.next().await.unwrap();
17008    prepare_rename_task.await.expect("Prepare rename failed");
17009
17010    let mut rename_handler =
17011        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17012            let edit = lsp::TextEdit {
17013                range: lsp::Range {
17014                    start: lsp::Position {
17015                        line: 0,
17016                        character: 7,
17017                    },
17018                    end: lsp::Position {
17019                        line: 0,
17020                        character: 10,
17021                    },
17022                },
17023                new_text: "FooRenamed".to_string(),
17024            };
17025            Ok(Some(lsp::WorkspaceEdit::new(
17026                // Specify the same edit twice
17027                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17028            )))
17029        });
17030    let rename_task = cx
17031        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17032        .expect("Confirm rename was not started");
17033    rename_handler.next().await.unwrap();
17034    rename_task.await.expect("Confirm rename failed");
17035    cx.run_until_parked();
17036
17037    // Despite two edits, only one is actually applied as those are identical
17038    cx.assert_editor_state(indoc! {"
17039        struct FooRenamedˇ {}
17040    "});
17041}
17042
17043#[gpui::test]
17044async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17045    init_test(cx, |_| {});
17046    // These capabilities indicate that the server does not support prepare rename.
17047    let capabilities = lsp::ServerCapabilities {
17048        rename_provider: Some(lsp::OneOf::Left(true)),
17049        ..Default::default()
17050    };
17051    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17052
17053    cx.set_state(indoc! {"
17054        struct Fˇoo {}
17055    "});
17056
17057    cx.update_editor(|editor, _window, cx| {
17058        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17059        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17060        editor.highlight_background::<DocumentHighlightRead>(
17061            &[highlight_range],
17062            |c| c.editor_document_highlight_read_background,
17063            cx,
17064        );
17065    });
17066
17067    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17068        .expect("Prepare rename was not started")
17069        .await
17070        .expect("Prepare rename failed");
17071
17072    let mut rename_handler =
17073        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17074            let edit = lsp::TextEdit {
17075                range: lsp::Range {
17076                    start: lsp::Position {
17077                        line: 0,
17078                        character: 7,
17079                    },
17080                    end: lsp::Position {
17081                        line: 0,
17082                        character: 10,
17083                    },
17084                },
17085                new_text: "FooRenamed".to_string(),
17086            };
17087            Ok(Some(lsp::WorkspaceEdit::new(
17088                std::collections::HashMap::from_iter(Some((url, vec![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    // Correct range is renamed, as `surrounding_word` is used to find it.
17099    cx.assert_editor_state(indoc! {"
17100        struct FooRenamedˇ {}
17101    "});
17102}
17103
17104#[gpui::test]
17105async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17106    init_test(cx, |_| {});
17107    let mut cx = EditorTestContext::new(cx).await;
17108
17109    let language = Arc::new(
17110        Language::new(
17111            LanguageConfig::default(),
17112            Some(tree_sitter_html::LANGUAGE.into()),
17113        )
17114        .with_brackets_query(
17115            r#"
17116            ("<" @open "/>" @close)
17117            ("</" @open ">" @close)
17118            ("<" @open ">" @close)
17119            ("\"" @open "\"" @close)
17120            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17121        "#,
17122        )
17123        .unwrap(),
17124    );
17125    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17126
17127    cx.set_state(indoc! {"
17128        <span>ˇ</span>
17129    "});
17130    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17131    cx.assert_editor_state(indoc! {"
17132        <span>
17133        ˇ
17134        </span>
17135    "});
17136
17137    cx.set_state(indoc! {"
17138        <span><span></span>ˇ</span>
17139    "});
17140    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17141    cx.assert_editor_state(indoc! {"
17142        <span><span></span>
17143        ˇ</span>
17144    "});
17145
17146    cx.set_state(indoc! {"
17147        <span>ˇ
17148        </span>
17149    "});
17150    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17151    cx.assert_editor_state(indoc! {"
17152        <span>
17153        ˇ
17154        </span>
17155    "});
17156}
17157
17158mod autoclose_tags {
17159    use super::*;
17160    use language::language_settings::JsxTagAutoCloseSettings;
17161    use languages::language;
17162
17163    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17164        init_test(cx, |settings| {
17165            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17166        });
17167
17168        let mut cx = EditorTestContext::new(cx).await;
17169        cx.update_buffer(|buffer, cx| {
17170            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17171
17172            buffer.set_language(Some(language), cx)
17173        });
17174
17175        cx
17176    }
17177
17178    macro_rules! check {
17179        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17180            #[gpui::test]
17181            async fn $name(cx: &mut TestAppContext) {
17182                let mut cx = test_setup(cx).await;
17183                cx.set_state($initial);
17184                cx.run_until_parked();
17185
17186                cx.update_editor(|editor, window, cx| {
17187                    editor.handle_input($input, window, cx);
17188                });
17189                cx.run_until_parked();
17190                cx.assert_editor_state($expected);
17191            }
17192        };
17193    }
17194
17195    check!(
17196        test_basic,
17197        "<divˇ" + ">" => "<div>ˇ</div>"
17198    );
17199
17200    check!(
17201        test_basic_nested,
17202        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17203    );
17204
17205    check!(
17206        test_basic_ignore_already_closed,
17207        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17208    );
17209
17210    check!(
17211        test_doesnt_autoclose_closing_tag,
17212        "</divˇ" + ">" => "</div>ˇ"
17213    );
17214
17215    check!(
17216        test_jsx_attr,
17217        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17218    );
17219
17220    check!(
17221        test_ignores_closing_tags_in_expr_block,
17222        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17223    );
17224
17225    check!(
17226        test_doesnt_autoclose_on_gt_in_expr,
17227        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17228    );
17229
17230    check!(
17231        test_ignores_closing_tags_with_different_tag_names,
17232        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17233    );
17234
17235    check!(
17236        test_autocloses_in_jsx_expression,
17237        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17238    );
17239
17240    check!(
17241        test_doesnt_autoclose_already_closed_in_jsx_expression,
17242        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17243    );
17244
17245    check!(
17246        test_autocloses_fragment,
17247        "" + ">" => "<>ˇ</>"
17248    );
17249
17250    check!(
17251        test_does_not_include_type_argument_in_autoclose_tag_name,
17252        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17253    );
17254
17255    check!(
17256        test_does_not_autoclose_doctype,
17257        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17258    );
17259
17260    check!(
17261        test_does_not_autoclose_comment,
17262        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17263    );
17264
17265    check!(
17266        test_multi_cursor_autoclose_same_tag,
17267        r#"
17268        <divˇ
17269        <divˇ
17270        "#
17271        + ">" =>
17272        r#"
17273        <div>ˇ</div>
17274        <div>ˇ</div>
17275        "#
17276    );
17277
17278    check!(
17279        test_multi_cursor_autoclose_different_tags,
17280        r#"
17281        <divˇ
17282        <spanˇ
17283        "#
17284        + ">" =>
17285        r#"
17286        <div>ˇ</div>
17287        <span>ˇ</span>
17288        "#
17289    );
17290
17291    check!(
17292        test_multi_cursor_autoclose_some_dont_autoclose_others,
17293        r#"
17294        <divˇ
17295        <div /ˇ
17296        <spanˇ</span>
17297        <!DOCTYPE htmlˇ
17298        </headˇ
17299        <Component<T>ˇ
17300        ˇ
17301        "#
17302        + ">" =>
17303        r#"
17304        <div>ˇ</div>
17305        <div />ˇ
17306        <span>ˇ</span>
17307        <!DOCTYPE html>ˇ
17308        </head>ˇ
17309        <Component<T>>ˇ</Component>
1731017311        "#
17312    );
17313
17314    check!(
17315        test_doesnt_mess_up_trailing_text,
17316        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17317    );
17318
17319    #[gpui::test]
17320    async fn test_multibuffer(cx: &mut TestAppContext) {
17321        init_test(cx, |settings| {
17322            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17323        });
17324
17325        let buffer_a = cx.new(|cx| {
17326            let mut buf = language::Buffer::local("<div", cx);
17327            buf.set_language(
17328                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17329                cx,
17330            );
17331            buf
17332        });
17333        let buffer_b = cx.new(|cx| {
17334            let mut buf = language::Buffer::local("<pre", cx);
17335            buf.set_language(
17336                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17337                cx,
17338            );
17339            buf
17340        });
17341        let buffer_c = cx.new(|cx| {
17342            let buf = language::Buffer::local("<span", cx);
17343            buf
17344        });
17345        let buffer = cx.new(|cx| {
17346            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17347            buf.push_excerpts(
17348                buffer_a,
17349                [ExcerptRange {
17350                    context: text::Anchor::MIN..text::Anchor::MAX,
17351                    primary: None,
17352                }],
17353                cx,
17354            );
17355            buf.push_excerpts(
17356                buffer_b,
17357                [ExcerptRange {
17358                    context: text::Anchor::MIN..text::Anchor::MAX,
17359                    primary: None,
17360                }],
17361                cx,
17362            );
17363            buf.push_excerpts(
17364                buffer_c,
17365                [ExcerptRange {
17366                    context: text::Anchor::MIN..text::Anchor::MAX,
17367                    primary: None,
17368                }],
17369                cx,
17370            );
17371            buf
17372        });
17373        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17374
17375        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17376
17377        cx.update_editor(|editor, window, cx| {
17378            editor.change_selections(None, window, cx, |selections| {
17379                selections.select(vec![
17380                    Selection::from_offset(4),
17381                    Selection::from_offset(9),
17382                    Selection::from_offset(15),
17383                ])
17384            })
17385        });
17386        cx.run_until_parked();
17387
17388        cx.update_editor(|editor, window, cx| {
17389            editor.handle_input(">", window, cx);
17390        });
17391        cx.run_until_parked();
17392
17393        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17394    }
17395}
17396
17397fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17398    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17399    point..point
17400}
17401
17402fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17403    let (text, ranges) = marked_text_ranges(marked_text, true);
17404    assert_eq!(editor.text(cx), text);
17405    assert_eq!(
17406        editor.selections.ranges(cx),
17407        ranges,
17408        "Assert selections are {}",
17409        marked_text
17410    );
17411}
17412
17413pub fn handle_signature_help_request(
17414    cx: &mut EditorLspTestContext,
17415    mocked_response: lsp::SignatureHelp,
17416) -> impl Future<Output = ()> {
17417    let mut request =
17418        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17419            let mocked_response = mocked_response.clone();
17420            async move { Ok(Some(mocked_response)) }
17421        });
17422
17423    async move {
17424        request.next().await;
17425    }
17426}
17427
17428/// Handle completion request passing a marked string specifying where the completion
17429/// should be triggered from using '|' character, what range should be replaced, and what completions
17430/// should be returned using '<' and '>' to delimit the range
17431pub fn handle_completion_request(
17432    cx: &mut EditorLspTestContext,
17433    marked_string: &str,
17434    completions: Vec<&'static str>,
17435    counter: Arc<AtomicUsize>,
17436) -> impl Future<Output = ()> {
17437    let complete_from_marker: TextRangeMarker = '|'.into();
17438    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17439    let (_, mut marked_ranges) = marked_text_ranges_by(
17440        marked_string,
17441        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17442    );
17443
17444    let complete_from_position =
17445        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17446    let replace_range =
17447        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17448
17449    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17450        let completions = completions.clone();
17451        counter.fetch_add(1, atomic::Ordering::Release);
17452        async move {
17453            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17454            assert_eq!(
17455                params.text_document_position.position,
17456                complete_from_position
17457            );
17458            Ok(Some(lsp::CompletionResponse::Array(
17459                completions
17460                    .iter()
17461                    .map(|completion_text| lsp::CompletionItem {
17462                        label: completion_text.to_string(),
17463                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17464                            range: replace_range,
17465                            new_text: completion_text.to_string(),
17466                        })),
17467                        ..Default::default()
17468                    })
17469                    .collect(),
17470            )))
17471        }
17472    });
17473
17474    async move {
17475        request.next().await;
17476    }
17477}
17478
17479fn handle_resolve_completion_request(
17480    cx: &mut EditorLspTestContext,
17481    edits: Option<Vec<(&'static str, &'static str)>>,
17482) -> impl Future<Output = ()> {
17483    let edits = edits.map(|edits| {
17484        edits
17485            .iter()
17486            .map(|(marked_string, new_text)| {
17487                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17488                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17489                lsp::TextEdit::new(replace_range, new_text.to_string())
17490            })
17491            .collect::<Vec<_>>()
17492    });
17493
17494    let mut request =
17495        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17496            let edits = edits.clone();
17497            async move {
17498                Ok(lsp::CompletionItem {
17499                    additional_text_edits: edits,
17500                    ..Default::default()
17501                })
17502            }
17503        });
17504
17505    async move {
17506        request.next().await;
17507    }
17508}
17509
17510pub(crate) fn update_test_language_settings(
17511    cx: &mut TestAppContext,
17512    f: impl Fn(&mut AllLanguageSettingsContent),
17513) {
17514    cx.update(|cx| {
17515        SettingsStore::update_global(cx, |store, cx| {
17516            store.update_user_settings::<AllLanguageSettings>(cx, f);
17517        });
17518    });
17519}
17520
17521pub(crate) fn update_test_project_settings(
17522    cx: &mut TestAppContext,
17523    f: impl Fn(&mut ProjectSettings),
17524) {
17525    cx.update(|cx| {
17526        SettingsStore::update_global(cx, |store, cx| {
17527            store.update_user_settings::<ProjectSettings>(cx, f);
17528        });
17529    });
17530}
17531
17532pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17533    cx.update(|cx| {
17534        assets::Assets.load_test_fonts(cx);
17535        let store = SettingsStore::test(cx);
17536        cx.set_global(store);
17537        theme::init(theme::LoadThemes::JustBase, cx);
17538        release_channel::init(SemanticVersion::default(), cx);
17539        client::init_settings(cx);
17540        language::init(cx);
17541        Project::init_settings(cx);
17542        workspace::init_settings(cx);
17543        crate::init(cx);
17544    });
17545
17546    update_test_language_settings(cx, f);
17547}
17548
17549#[track_caller]
17550fn assert_hunk_revert(
17551    not_reverted_text_with_selections: &str,
17552    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17553    expected_reverted_text_with_selections: &str,
17554    base_text: &str,
17555    cx: &mut EditorLspTestContext,
17556) {
17557    cx.set_state(not_reverted_text_with_selections);
17558    cx.set_head_text(base_text);
17559    cx.executor().run_until_parked();
17560
17561    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17562        let snapshot = editor.snapshot(window, cx);
17563        let reverted_hunk_statuses = snapshot
17564            .buffer_snapshot
17565            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17566            .map(|hunk| hunk.status().kind)
17567            .collect::<Vec<_>>();
17568
17569        editor.git_restore(&Default::default(), window, cx);
17570        reverted_hunk_statuses
17571    });
17572    cx.executor().run_until_parked();
17573    cx.assert_editor_state(expected_reverted_text_with_selections);
17574    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17575}