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