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