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