editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext,
    6        editor_test_context::EditorTestContext, select_ranges,
    7    },
    8    JoinLines,
    9};
   10use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   11use futures::StreamExt;
   12use gpui::{
   13    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   14    WindowBounds, WindowOptions,
   15};
   16use indoc::indoc;
   17use language::{
   18    language_settings::{
   19        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   20        LanguageSettingsContent, PrettierSettings,
   21    },
   22    BracketPairConfig,
   23    Capability::ReadWrite,
   24    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   25    Override, Point,
   26};
   27use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   28use multi_buffer::{IndentGuide, PathKey};
   29use parking_lot::Mutex;
   30use pretty_assertions::{assert_eq, assert_ne};
   31use project::project_settings::{LspSettings, ProjectSettings};
   32use project::FakeFs;
   33use serde_json::{self, json};
   34use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   35use std::{
   36    iter,
   37    sync::atomic::{self, AtomicUsize},
   38};
   39use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   40use text::ToPoint as _;
   41use unindent::Unindent;
   42use util::{
   43    assert_set_eq, path,
   44    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   45    uri,
   46};
   47use workspace::{
   48    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   49    NavigationEntry, ViewId,
   50};
   51
   52#[gpui::test]
   53fn test_edit_events(cx: &mut TestAppContext) {
   54    init_test(cx, |_| {});
   55
   56    let buffer = cx.new(|cx| {
   57        let mut buffer = language::Buffer::local("123456", cx);
   58        buffer.set_group_interval(Duration::from_secs(1));
   59        buffer
   60    });
   61
   62    let events = Rc::new(RefCell::new(Vec::new()));
   63    let editor1 = cx.add_window({
   64        let events = events.clone();
   65        |window, cx| {
   66            let entity = cx.entity().clone();
   67            cx.subscribe_in(
   68                &entity,
   69                window,
   70                move |_, _, event: &EditorEvent, _, _| match event {
   71                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   72                    EditorEvent::BufferEdited => {
   73                        events.borrow_mut().push(("editor1", "buffer edited"))
   74                    }
   75                    _ => {}
   76                },
   77            )
   78            .detach();
   79            Editor::for_buffer(buffer.clone(), None, window, cx)
   80        }
   81    });
   82
   83    let editor2 = cx.add_window({
   84        let events = events.clone();
   85        |window, cx| {
   86            cx.subscribe_in(
   87                &cx.entity().clone(),
   88                window,
   89                move |_, _, event: &EditorEvent, _, _| match event {
   90                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   91                    EditorEvent::BufferEdited => {
   92                        events.borrow_mut().push(("editor2", "buffer edited"))
   93                    }
   94                    _ => {}
   95                },
   96            )
   97            .detach();
   98            Editor::for_buffer(buffer.clone(), None, window, cx)
   99        }
  100    });
  101
  102    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  103
  104    // Mutating editor 1 will emit an `Edited` event only for that editor.
  105    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  106    assert_eq!(
  107        mem::take(&mut *events.borrow_mut()),
  108        [
  109            ("editor1", "edited"),
  110            ("editor1", "buffer edited"),
  111            ("editor2", "buffer edited"),
  112        ]
  113    );
  114
  115    // Mutating editor 2 will emit an `Edited` event only for that editor.
  116    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  117    assert_eq!(
  118        mem::take(&mut *events.borrow_mut()),
  119        [
  120            ("editor2", "edited"),
  121            ("editor1", "buffer edited"),
  122            ("editor2", "buffer edited"),
  123        ]
  124    );
  125
  126    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  127    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  128    assert_eq!(
  129        mem::take(&mut *events.borrow_mut()),
  130        [
  131            ("editor1", "edited"),
  132            ("editor1", "buffer edited"),
  133            ("editor2", "buffer edited"),
  134        ]
  135    );
  136
  137    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  138    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  139    assert_eq!(
  140        mem::take(&mut *events.borrow_mut()),
  141        [
  142            ("editor1", "edited"),
  143            ("editor1", "buffer edited"),
  144            ("editor2", "buffer edited"),
  145        ]
  146    );
  147
  148    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  149    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  150    assert_eq!(
  151        mem::take(&mut *events.borrow_mut()),
  152        [
  153            ("editor2", "edited"),
  154            ("editor1", "buffer edited"),
  155            ("editor2", "buffer edited"),
  156        ]
  157    );
  158
  159    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  160    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  161    assert_eq!(
  162        mem::take(&mut *events.borrow_mut()),
  163        [
  164            ("editor2", "edited"),
  165            ("editor1", "buffer edited"),
  166            ("editor2", "buffer edited"),
  167        ]
  168    );
  169
  170    // No event is emitted when the mutation is a no-op.
  171    _ = editor2.update(cx, |editor, window, cx| {
  172        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  173
  174        editor.backspace(&Backspace, window, cx);
  175    });
  176    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  177}
  178
  179#[gpui::test]
  180fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  181    init_test(cx, |_| {});
  182
  183    let mut now = Instant::now();
  184    let group_interval = Duration::from_millis(1);
  185    let buffer = cx.new(|cx| {
  186        let mut buf = language::Buffer::local("123456", cx);
  187        buf.set_group_interval(group_interval);
  188        buf
  189    });
  190    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  191    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  192
  193    _ = editor.update(cx, |editor, window, cx| {
  194        editor.start_transaction_at(now, window, cx);
  195        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  196
  197        editor.insert("cd", window, cx);
  198        editor.end_transaction_at(now, cx);
  199        assert_eq!(editor.text(cx), "12cd56");
  200        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  201
  202        editor.start_transaction_at(now, window, cx);
  203        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  204        editor.insert("e", window, cx);
  205        editor.end_transaction_at(now, cx);
  206        assert_eq!(editor.text(cx), "12cde6");
  207        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  208
  209        now += group_interval + Duration::from_millis(1);
  210        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  211
  212        // Simulate an edit in another editor
  213        buffer.update(cx, |buffer, cx| {
  214            buffer.start_transaction_at(now, cx);
  215            buffer.edit([(0..1, "a")], None, cx);
  216            buffer.edit([(1..1, "b")], None, cx);
  217            buffer.end_transaction_at(now, cx);
  218        });
  219
  220        assert_eq!(editor.text(cx), "ab2cde6");
  221        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  222
  223        // Last transaction happened past the group interval in a different editor.
  224        // Undo it individually and don't restore selections.
  225        editor.undo(&Undo, window, cx);
  226        assert_eq!(editor.text(cx), "12cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  228
  229        // First two transactions happened within the group interval in this editor.
  230        // Undo them together and restore selections.
  231        editor.undo(&Undo, window, cx);
  232        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  233        assert_eq!(editor.text(cx), "123456");
  234        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  235
  236        // Redo the first two transactions together.
  237        editor.redo(&Redo, window, cx);
  238        assert_eq!(editor.text(cx), "12cde6");
  239        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  240
  241        // Redo the last transaction on its own.
  242        editor.redo(&Redo, window, cx);
  243        assert_eq!(editor.text(cx), "ab2cde6");
  244        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  245
  246        // Test empty transactions.
  247        editor.start_transaction_at(now, window, cx);
  248        editor.end_transaction_at(now, cx);
  249        editor.undo(&Undo, window, cx);
  250        assert_eq!(editor.text(cx), "12cde6");
  251    });
  252}
  253
  254#[gpui::test]
  255fn test_ime_composition(cx: &mut TestAppContext) {
  256    init_test(cx, |_| {});
  257
  258    let buffer = cx.new(|cx| {
  259        let mut buffer = language::Buffer::local("abcde", cx);
  260        // Ensure automatic grouping doesn't occur.
  261        buffer.set_group_interval(Duration::ZERO);
  262        buffer
  263    });
  264
  265    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  266    cx.add_window(|window, cx| {
  267        let mut editor = build_editor(buffer.clone(), window, cx);
  268
  269        // Start a new IME composition.
  270        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  271        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  272        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  273        assert_eq!(editor.text(cx), "äbcde");
  274        assert_eq!(
  275            editor.marked_text_ranges(cx),
  276            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  277        );
  278
  279        // Finalize IME composition.
  280        editor.replace_text_in_range(None, "ā", window, cx);
  281        assert_eq!(editor.text(cx), "ābcde");
  282        assert_eq!(editor.marked_text_ranges(cx), None);
  283
  284        // IME composition edits are grouped and are undone/redone at once.
  285        editor.undo(&Default::default(), window, cx);
  286        assert_eq!(editor.text(cx), "abcde");
  287        assert_eq!(editor.marked_text_ranges(cx), None);
  288        editor.redo(&Default::default(), window, cx);
  289        assert_eq!(editor.text(cx), "ābcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291
  292        // Start a new IME composition.
  293        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  294        assert_eq!(
  295            editor.marked_text_ranges(cx),
  296            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  297        );
  298
  299        // Undoing during an IME composition cancels it.
  300        editor.undo(&Default::default(), window, cx);
  301        assert_eq!(editor.text(cx), "ābcde");
  302        assert_eq!(editor.marked_text_ranges(cx), None);
  303
  304        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  305        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  306        assert_eq!(editor.text(cx), "ābcdè");
  307        assert_eq!(
  308            editor.marked_text_ranges(cx),
  309            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  310        );
  311
  312        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  313        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  314        assert_eq!(editor.text(cx), "ābcdę");
  315        assert_eq!(editor.marked_text_ranges(cx), None);
  316
  317        // Start a new IME composition with multiple cursors.
  318        editor.change_selections(None, window, cx, |s| {
  319            s.select_ranges([
  320                OffsetUtf16(1)..OffsetUtf16(1),
  321                OffsetUtf16(3)..OffsetUtf16(3),
  322                OffsetUtf16(5)..OffsetUtf16(5),
  323            ])
  324        });
  325        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  326        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  327        assert_eq!(
  328            editor.marked_text_ranges(cx),
  329            Some(vec![
  330                OffsetUtf16(0)..OffsetUtf16(3),
  331                OffsetUtf16(4)..OffsetUtf16(7),
  332                OffsetUtf16(8)..OffsetUtf16(11)
  333            ])
  334        );
  335
  336        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  337        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  338        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  339        assert_eq!(
  340            editor.marked_text_ranges(cx),
  341            Some(vec![
  342                OffsetUtf16(1)..OffsetUtf16(2),
  343                OffsetUtf16(5)..OffsetUtf16(6),
  344                OffsetUtf16(9)..OffsetUtf16(10)
  345            ])
  346        );
  347
  348        // Finalize IME composition with multiple cursors.
  349        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  350        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  351        assert_eq!(editor.marked_text_ranges(cx), None);
  352
  353        editor
  354    });
  355}
  356
  357#[gpui::test]
  358fn test_selection_with_mouse(cx: &mut TestAppContext) {
  359    init_test(cx, |_| {});
  360
  361    let editor = cx.add_window(|window, cx| {
  362        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  363        build_editor(buffer, window, cx)
  364    });
  365
  366    _ = editor.update(cx, |editor, window, cx| {
  367        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  368    });
  369    assert_eq!(
  370        editor
  371            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  372            .unwrap(),
  373        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  374    );
  375
  376    _ = editor.update(cx, |editor, window, cx| {
  377        editor.update_selection(
  378            DisplayPoint::new(DisplayRow(3), 3),
  379            0,
  380            gpui::Point::<f32>::default(),
  381            window,
  382            cx,
  383        );
  384    });
  385
  386    assert_eq!(
  387        editor
  388            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  389            .unwrap(),
  390        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  391    );
  392
  393    _ = editor.update(cx, |editor, window, cx| {
  394        editor.update_selection(
  395            DisplayPoint::new(DisplayRow(1), 1),
  396            0,
  397            gpui::Point::<f32>::default(),
  398            window,
  399            cx,
  400        );
  401    });
  402
  403    assert_eq!(
  404        editor
  405            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  406            .unwrap(),
  407        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  408    );
  409
  410    _ = editor.update(cx, |editor, window, cx| {
  411        editor.end_selection(window, cx);
  412        editor.update_selection(
  413            DisplayPoint::new(DisplayRow(3), 3),
  414            0,
  415            gpui::Point::<f32>::default(),
  416            window,
  417            cx,
  418        );
  419    });
  420
  421    assert_eq!(
  422        editor
  423            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  424            .unwrap(),
  425        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  426    );
  427
  428    _ = editor.update(cx, |editor, window, cx| {
  429        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  430        editor.update_selection(
  431            DisplayPoint::new(DisplayRow(0), 0),
  432            0,
  433            gpui::Point::<f32>::default(),
  434            window,
  435            cx,
  436        );
  437    });
  438
  439    assert_eq!(
  440        editor
  441            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  442            .unwrap(),
  443        [
  444            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  445            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  446        ]
  447    );
  448
  449    _ = editor.update(cx, |editor, window, cx| {
  450        editor.end_selection(window, cx);
  451    });
  452
  453    assert_eq!(
  454        editor
  455            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  456            .unwrap(),
  457        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  458    );
  459}
  460
  461#[gpui::test]
  462fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  463    init_test(cx, |_| {});
  464
  465    let editor = cx.add_window(|window, cx| {
  466        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  467        build_editor(buffer, window, cx)
  468    });
  469
  470    _ = editor.update(cx, |editor, window, cx| {
  471        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  472    });
  473
  474    _ = editor.update(cx, |editor, window, cx| {
  475        editor.end_selection(window, cx);
  476    });
  477
  478    _ = editor.update(cx, |editor, window, cx| {
  479        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  480    });
  481
  482    _ = editor.update(cx, |editor, window, cx| {
  483        editor.end_selection(window, cx);
  484    });
  485
  486    assert_eq!(
  487        editor
  488            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  489            .unwrap(),
  490        [
  491            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  492            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  493        ]
  494    );
  495
  496    _ = editor.update(cx, |editor, window, cx| {
  497        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  498    });
  499
  500    _ = editor.update(cx, |editor, window, cx| {
  501        editor.end_selection(window, cx);
  502    });
  503
  504    assert_eq!(
  505        editor
  506            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  507            .unwrap(),
  508        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  509    );
  510}
  511
  512#[gpui::test]
  513fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  514    init_test(cx, |_| {});
  515
  516    let editor = cx.add_window(|window, cx| {
  517        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  518        build_editor(buffer, window, cx)
  519    });
  520
  521    _ = editor.update(cx, |editor, window, cx| {
  522        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  523        assert_eq!(
  524            editor.selections.display_ranges(cx),
  525            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  526        );
  527    });
  528
  529    _ = editor.update(cx, |editor, window, cx| {
  530        editor.update_selection(
  531            DisplayPoint::new(DisplayRow(3), 3),
  532            0,
  533            gpui::Point::<f32>::default(),
  534            window,
  535            cx,
  536        );
  537        assert_eq!(
  538            editor.selections.display_ranges(cx),
  539            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  540        );
  541    });
  542
  543    _ = editor.update(cx, |editor, window, cx| {
  544        editor.cancel(&Cancel, window, cx);
  545        editor.update_selection(
  546            DisplayPoint::new(DisplayRow(1), 1),
  547            0,
  548            gpui::Point::<f32>::default(),
  549            window,
  550            cx,
  551        );
  552        assert_eq!(
  553            editor.selections.display_ranges(cx),
  554            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  555        );
  556    });
  557}
  558
  559#[gpui::test]
  560fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  561    init_test(cx, |_| {});
  562
  563    let editor = cx.add_window(|window, cx| {
  564        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  565        build_editor(buffer, window, cx)
  566    });
  567
  568    _ = editor.update(cx, |editor, window, cx| {
  569        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  570        assert_eq!(
  571            editor.selections.display_ranges(cx),
  572            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  573        );
  574
  575        editor.move_down(&Default::default(), window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  579        );
  580
  581        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  585        );
  586
  587        editor.move_up(&Default::default(), window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  591        );
  592    });
  593}
  594
  595#[gpui::test]
  596fn test_clone(cx: &mut TestAppContext) {
  597    init_test(cx, |_| {});
  598
  599    let (text, selection_ranges) = marked_text_ranges(
  600        indoc! {"
  601            one
  602            two
  603            threeˇ
  604            four
  605            fiveˇ
  606        "},
  607        true,
  608    );
  609
  610    let editor = cx.add_window(|window, cx| {
  611        let buffer = MultiBuffer::build_simple(&text, cx);
  612        build_editor(buffer, window, cx)
  613    });
  614
  615    _ = editor.update(cx, |editor, window, cx| {
  616        editor.change_selections(None, window, cx, |s| {
  617            s.select_ranges(selection_ranges.clone())
  618        });
  619        editor.fold_creases(
  620            vec![
  621                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  622                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  623            ],
  624            true,
  625            window,
  626            cx,
  627        );
  628    });
  629
  630    let cloned_editor = editor
  631        .update(cx, |editor, _, cx| {
  632            cx.open_window(Default::default(), |window, cx| {
  633                cx.new(|cx| editor.clone(window, cx))
  634            })
  635        })
  636        .unwrap()
  637        .unwrap();
  638
  639    let snapshot = editor
  640        .update(cx, |e, window, cx| e.snapshot(window, cx))
  641        .unwrap();
  642    let cloned_snapshot = cloned_editor
  643        .update(cx, |e, window, cx| e.snapshot(window, cx))
  644        .unwrap();
  645
  646    assert_eq!(
  647        cloned_editor
  648            .update(cx, |e, _, cx| e.display_text(cx))
  649            .unwrap(),
  650        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  651    );
  652    assert_eq!(
  653        cloned_snapshot
  654            .folds_in_range(0..text.len())
  655            .collect::<Vec<_>>(),
  656        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  657    );
  658    assert_set_eq!(
  659        cloned_editor
  660            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  661            .unwrap(),
  662        editor
  663            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  664            .unwrap()
  665    );
  666    assert_set_eq!(
  667        cloned_editor
  668            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  669            .unwrap(),
  670        editor
  671            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  672            .unwrap()
  673    );
  674}
  675
  676#[gpui::test]
  677async fn test_navigation_history(cx: &mut TestAppContext) {
  678    init_test(cx, |_| {});
  679
  680    use workspace::item::Item;
  681
  682    let fs = FakeFs::new(cx.executor());
  683    let project = Project::test(fs, [], cx).await;
  684    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  685    let pane = workspace
  686        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  687        .unwrap();
  688
  689    _ = workspace.update(cx, |_v, window, cx| {
  690        cx.new(|cx| {
  691            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  692            let mut editor = build_editor(buffer.clone(), window, cx);
  693            let handle = cx.entity();
  694            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  695
  696            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  697                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  698            }
  699
  700            // Move the cursor a small distance.
  701            // Nothing is added to the navigation history.
  702            editor.change_selections(None, window, cx, |s| {
  703                s.select_display_ranges([
  704                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  705                ])
  706            });
  707            editor.change_selections(None, window, cx, |s| {
  708                s.select_display_ranges([
  709                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  710                ])
  711            });
  712            assert!(pop_history(&mut editor, cx).is_none());
  713
  714            // Move the cursor a large distance.
  715            // The history can jump back to the previous position.
  716            editor.change_selections(None, window, cx, |s| {
  717                s.select_display_ranges([
  718                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  719                ])
  720            });
  721            let nav_entry = pop_history(&mut editor, cx).unwrap();
  722            editor.navigate(nav_entry.data.unwrap(), window, cx);
  723            assert_eq!(nav_entry.item.id(), cx.entity_id());
  724            assert_eq!(
  725                editor.selections.display_ranges(cx),
  726                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  727            );
  728            assert!(pop_history(&mut editor, cx).is_none());
  729
  730            // Move the cursor a small distance via the mouse.
  731            // Nothing is added to the navigation history.
  732            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  733            editor.end_selection(window, cx);
  734            assert_eq!(
  735                editor.selections.display_ranges(cx),
  736                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  737            );
  738            assert!(pop_history(&mut editor, cx).is_none());
  739
  740            // Move the cursor a large distance via the mouse.
  741            // The history can jump back to the previous position.
  742            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  743            editor.end_selection(window, cx);
  744            assert_eq!(
  745                editor.selections.display_ranges(cx),
  746                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  747            );
  748            let nav_entry = pop_history(&mut editor, cx).unwrap();
  749            editor.navigate(nav_entry.data.unwrap(), window, cx);
  750            assert_eq!(nav_entry.item.id(), cx.entity_id());
  751            assert_eq!(
  752                editor.selections.display_ranges(cx),
  753                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  754            );
  755            assert!(pop_history(&mut editor, cx).is_none());
  756
  757            // Set scroll position to check later
  758            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  759            let original_scroll_position = editor.scroll_manager.anchor();
  760
  761            // Jump to the end of the document and adjust scroll
  762            editor.move_to_end(&MoveToEnd, window, cx);
  763            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  764            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  765
  766            let nav_entry = pop_history(&mut editor, cx).unwrap();
  767            editor.navigate(nav_entry.data.unwrap(), window, cx);
  768            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  769
  770            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  771            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  772            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  773            let invalid_point = Point::new(9999, 0);
  774            editor.navigate(
  775                Box::new(NavigationData {
  776                    cursor_anchor: invalid_anchor,
  777                    cursor_position: invalid_point,
  778                    scroll_anchor: ScrollAnchor {
  779                        anchor: invalid_anchor,
  780                        offset: Default::default(),
  781                    },
  782                    scroll_top_row: invalid_point.row,
  783                }),
  784                window,
  785                cx,
  786            );
  787            assert_eq!(
  788                editor.selections.display_ranges(cx),
  789                &[editor.max_point(cx)..editor.max_point(cx)]
  790            );
  791            assert_eq!(
  792                editor.scroll_position(cx),
  793                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  794            );
  795
  796            editor
  797        })
  798    });
  799}
  800
  801#[gpui::test]
  802fn test_cancel(cx: &mut TestAppContext) {
  803    init_test(cx, |_| {});
  804
  805    let editor = cx.add_window(|window, cx| {
  806        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  807        build_editor(buffer, window, cx)
  808    });
  809
  810    _ = editor.update(cx, |editor, window, cx| {
  811        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  812        editor.update_selection(
  813            DisplayPoint::new(DisplayRow(1), 1),
  814            0,
  815            gpui::Point::<f32>::default(),
  816            window,
  817            cx,
  818        );
  819        editor.end_selection(window, cx);
  820
  821        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  822        editor.update_selection(
  823            DisplayPoint::new(DisplayRow(0), 3),
  824            0,
  825            gpui::Point::<f32>::default(),
  826            window,
  827            cx,
  828        );
  829        editor.end_selection(window, cx);
  830        assert_eq!(
  831            editor.selections.display_ranges(cx),
  832            [
  833                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  834                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  835            ]
  836        );
  837    });
  838
  839    _ = editor.update(cx, |editor, window, cx| {
  840        editor.cancel(&Cancel, window, cx);
  841        assert_eq!(
  842            editor.selections.display_ranges(cx),
  843            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  844        );
  845    });
  846
  847    _ = editor.update(cx, |editor, window, cx| {
  848        editor.cancel(&Cancel, window, cx);
  849        assert_eq!(
  850            editor.selections.display_ranges(cx),
  851            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  852        );
  853    });
  854}
  855
  856#[gpui::test]
  857fn test_fold_action(cx: &mut TestAppContext) {
  858    init_test(cx, |_| {});
  859
  860    let editor = cx.add_window(|window, cx| {
  861        let buffer = MultiBuffer::build_simple(
  862            &"
  863                impl Foo {
  864                    // Hello!
  865
  866                    fn a() {
  867                        1
  868                    }
  869
  870                    fn b() {
  871                        2
  872                    }
  873
  874                    fn c() {
  875                        3
  876                    }
  877                }
  878            "
  879            .unindent(),
  880            cx,
  881        );
  882        build_editor(buffer.clone(), window, cx)
  883    });
  884
  885    _ = editor.update(cx, |editor, window, cx| {
  886        editor.change_selections(None, window, cx, |s| {
  887            s.select_display_ranges([
  888                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  889            ]);
  890        });
  891        editor.fold(&Fold, window, cx);
  892        assert_eq!(
  893            editor.display_text(cx),
  894            "
  895                impl Foo {
  896                    // Hello!
  897
  898                    fn a() {
  899                        1
  900                    }
  901
  902                    fn b() {⋯
  903                    }
  904
  905                    fn c() {⋯
  906                    }
  907                }
  908            "
  909            .unindent(),
  910        );
  911
  912        editor.fold(&Fold, window, cx);
  913        assert_eq!(
  914            editor.display_text(cx),
  915            "
  916                impl Foo {⋯
  917                }
  918            "
  919            .unindent(),
  920        );
  921
  922        editor.unfold_lines(&UnfoldLines, window, cx);
  923        assert_eq!(
  924            editor.display_text(cx),
  925            "
  926                impl Foo {
  927                    // Hello!
  928
  929                    fn a() {
  930                        1
  931                    }
  932
  933                    fn b() {⋯
  934                    }
  935
  936                    fn c() {⋯
  937                    }
  938                }
  939            "
  940            .unindent(),
  941        );
  942
  943        editor.unfold_lines(&UnfoldLines, window, cx);
  944        assert_eq!(
  945            editor.display_text(cx),
  946            editor.buffer.read(cx).read(cx).text()
  947        );
  948    });
  949}
  950
  951#[gpui::test]
  952fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  953    init_test(cx, |_| {});
  954
  955    let editor = cx.add_window(|window, cx| {
  956        let buffer = MultiBuffer::build_simple(
  957            &"
  958                class Foo:
  959                    # Hello!
  960
  961                    def a():
  962                        print(1)
  963
  964                    def b():
  965                        print(2)
  966
  967                    def c():
  968                        print(3)
  969            "
  970            .unindent(),
  971            cx,
  972        );
  973        build_editor(buffer.clone(), window, cx)
  974    });
  975
  976    _ = editor.update(cx, |editor, window, cx| {
  977        editor.change_selections(None, window, cx, |s| {
  978            s.select_display_ranges([
  979                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  980            ]);
  981        });
  982        editor.fold(&Fold, window, cx);
  983        assert_eq!(
  984            editor.display_text(cx),
  985            "
  986                class Foo:
  987                    # Hello!
  988
  989                    def a():
  990                        print(1)
  991
  992                    def b():⋯
  993
  994                    def c():⋯
  995            "
  996            .unindent(),
  997        );
  998
  999        editor.fold(&Fold, window, cx);
 1000        assert_eq!(
 1001            editor.display_text(cx),
 1002            "
 1003                class Foo:⋯
 1004            "
 1005            .unindent(),
 1006        );
 1007
 1008        editor.unfold_lines(&UnfoldLines, window, cx);
 1009        assert_eq!(
 1010            editor.display_text(cx),
 1011            "
 1012                class Foo:
 1013                    # Hello!
 1014
 1015                    def a():
 1016                        print(1)
 1017
 1018                    def b():⋯
 1019
 1020                    def c():⋯
 1021            "
 1022            .unindent(),
 1023        );
 1024
 1025        editor.unfold_lines(&UnfoldLines, window, cx);
 1026        assert_eq!(
 1027            editor.display_text(cx),
 1028            editor.buffer.read(cx).read(cx).text()
 1029        );
 1030    });
 1031}
 1032
 1033#[gpui::test]
 1034fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1035    init_test(cx, |_| {});
 1036
 1037    let editor = cx.add_window(|window, cx| {
 1038        let buffer = MultiBuffer::build_simple(
 1039            &"
 1040                class Foo:
 1041                    # Hello!
 1042
 1043                    def a():
 1044                        print(1)
 1045
 1046                    def b():
 1047                        print(2)
 1048
 1049
 1050                    def c():
 1051                        print(3)
 1052
 1053
 1054            "
 1055            .unindent(),
 1056            cx,
 1057        );
 1058        build_editor(buffer.clone(), window, cx)
 1059    });
 1060
 1061    _ = editor.update(cx, |editor, window, cx| {
 1062        editor.change_selections(None, window, cx, |s| {
 1063            s.select_display_ranges([
 1064                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1065            ]);
 1066        });
 1067        editor.fold(&Fold, window, cx);
 1068        assert_eq!(
 1069            editor.display_text(cx),
 1070            "
 1071                class Foo:
 1072                    # Hello!
 1073
 1074                    def a():
 1075                        print(1)
 1076
 1077                    def b():⋯
 1078
 1079
 1080                    def c():⋯
 1081
 1082
 1083            "
 1084            .unindent(),
 1085        );
 1086
 1087        editor.fold(&Fold, window, cx);
 1088        assert_eq!(
 1089            editor.display_text(cx),
 1090            "
 1091                class Foo:⋯
 1092
 1093
 1094            "
 1095            .unindent(),
 1096        );
 1097
 1098        editor.unfold_lines(&UnfoldLines, window, cx);
 1099        assert_eq!(
 1100            editor.display_text(cx),
 1101            "
 1102                class Foo:
 1103                    # Hello!
 1104
 1105                    def a():
 1106                        print(1)
 1107
 1108                    def b():⋯
 1109
 1110
 1111                    def c():⋯
 1112
 1113
 1114            "
 1115            .unindent(),
 1116        );
 1117
 1118        editor.unfold_lines(&UnfoldLines, window, cx);
 1119        assert_eq!(
 1120            editor.display_text(cx),
 1121            editor.buffer.read(cx).read(cx).text()
 1122        );
 1123    });
 1124}
 1125
 1126#[gpui::test]
 1127fn test_fold_at_level(cx: &mut TestAppContext) {
 1128    init_test(cx, |_| {});
 1129
 1130    let editor = cx.add_window(|window, cx| {
 1131        let buffer = MultiBuffer::build_simple(
 1132            &"
 1133                class Foo:
 1134                    # Hello!
 1135
 1136                    def a():
 1137                        print(1)
 1138
 1139                    def b():
 1140                        print(2)
 1141
 1142
 1143                class Bar:
 1144                    # World!
 1145
 1146                    def a():
 1147                        print(1)
 1148
 1149                    def b():
 1150                        print(2)
 1151
 1152
 1153            "
 1154            .unindent(),
 1155            cx,
 1156        );
 1157        build_editor(buffer.clone(), window, cx)
 1158    });
 1159
 1160    _ = editor.update(cx, |editor, window, cx| {
 1161        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1162        assert_eq!(
 1163            editor.display_text(cx),
 1164            "
 1165                class Foo:
 1166                    # Hello!
 1167
 1168                    def a():⋯
 1169
 1170                    def b():⋯
 1171
 1172
 1173                class Bar:
 1174                    # World!
 1175
 1176                    def a():⋯
 1177
 1178                    def b():⋯
 1179
 1180
 1181            "
 1182            .unindent(),
 1183        );
 1184
 1185        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1186        assert_eq!(
 1187            editor.display_text(cx),
 1188            "
 1189                class Foo:⋯
 1190
 1191
 1192                class Bar:⋯
 1193
 1194
 1195            "
 1196            .unindent(),
 1197        );
 1198
 1199        editor.unfold_all(&UnfoldAll, window, cx);
 1200        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1201        assert_eq!(
 1202            editor.display_text(cx),
 1203            "
 1204                class Foo:
 1205                    # Hello!
 1206
 1207                    def a():
 1208                        print(1)
 1209
 1210                    def b():
 1211                        print(2)
 1212
 1213
 1214                class Bar:
 1215                    # World!
 1216
 1217                    def a():
 1218                        print(1)
 1219
 1220                    def b():
 1221                        print(2)
 1222
 1223
 1224            "
 1225            .unindent(),
 1226        );
 1227
 1228        assert_eq!(
 1229            editor.display_text(cx),
 1230            editor.buffer.read(cx).read(cx).text()
 1231        );
 1232    });
 1233}
 1234
 1235#[gpui::test]
 1236fn test_move_cursor(cx: &mut TestAppContext) {
 1237    init_test(cx, |_| {});
 1238
 1239    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1240    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1241
 1242    buffer.update(cx, |buffer, cx| {
 1243        buffer.edit(
 1244            vec![
 1245                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1246                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1247            ],
 1248            None,
 1249            cx,
 1250        );
 1251    });
 1252    _ = editor.update(cx, |editor, window, cx| {
 1253        assert_eq!(
 1254            editor.selections.display_ranges(cx),
 1255            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1256        );
 1257
 1258        editor.move_down(&MoveDown, window, cx);
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1262        );
 1263
 1264        editor.move_right(&MoveRight, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1268        );
 1269
 1270        editor.move_left(&MoveLeft, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1274        );
 1275
 1276        editor.move_up(&MoveUp, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1280        );
 1281
 1282        editor.move_to_end(&MoveToEnd, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1286        );
 1287
 1288        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1292        );
 1293
 1294        editor.change_selections(None, window, cx, |s| {
 1295            s.select_display_ranges([
 1296                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1297            ]);
 1298        });
 1299        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1300        assert_eq!(
 1301            editor.selections.display_ranges(cx),
 1302            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1303        );
 1304
 1305        editor.select_to_end(&SelectToEnd, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1309        );
 1310    });
 1311}
 1312
 1313// TODO: Re-enable this test
 1314#[cfg(target_os = "macos")]
 1315#[gpui::test]
 1316fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1317    init_test(cx, |_| {});
 1318
 1319    let editor = cx.add_window(|window, cx| {
 1320        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1321        build_editor(buffer.clone(), window, cx)
 1322    });
 1323
 1324    assert_eq!('🟥'.len_utf8(), 4);
 1325    assert_eq!('α'.len_utf8(), 2);
 1326
 1327    _ = editor.update(cx, |editor, window, cx| {
 1328        editor.fold_creases(
 1329            vec![
 1330                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1331                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1332                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1333            ],
 1334            true,
 1335            window,
 1336            cx,
 1337        );
 1338        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1339
 1340        editor.move_right(&MoveRight, window, cx);
 1341        assert_eq!(
 1342            editor.selections.display_ranges(cx),
 1343            &[empty_range(0, "🟥".len())]
 1344        );
 1345        editor.move_right(&MoveRight, window, cx);
 1346        assert_eq!(
 1347            editor.selections.display_ranges(cx),
 1348            &[empty_range(0, "🟥🟧".len())]
 1349        );
 1350        editor.move_right(&MoveRight, window, cx);
 1351        assert_eq!(
 1352            editor.selections.display_ranges(cx),
 1353            &[empty_range(0, "🟥🟧⋯".len())]
 1354        );
 1355
 1356        editor.move_down(&MoveDown, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(1, "ab⋯e".len())]
 1360        );
 1361        editor.move_left(&MoveLeft, window, cx);
 1362        assert_eq!(
 1363            editor.selections.display_ranges(cx),
 1364            &[empty_range(1, "ab⋯".len())]
 1365        );
 1366        editor.move_left(&MoveLeft, window, cx);
 1367        assert_eq!(
 1368            editor.selections.display_ranges(cx),
 1369            &[empty_range(1, "ab".len())]
 1370        );
 1371        editor.move_left(&MoveLeft, window, cx);
 1372        assert_eq!(
 1373            editor.selections.display_ranges(cx),
 1374            &[empty_range(1, "a".len())]
 1375        );
 1376
 1377        editor.move_down(&MoveDown, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(2, "α".len())]
 1381        );
 1382        editor.move_right(&MoveRight, window, cx);
 1383        assert_eq!(
 1384            editor.selections.display_ranges(cx),
 1385            &[empty_range(2, "αβ".len())]
 1386        );
 1387        editor.move_right(&MoveRight, window, cx);
 1388        assert_eq!(
 1389            editor.selections.display_ranges(cx),
 1390            &[empty_range(2, "αβ⋯".len())]
 1391        );
 1392        editor.move_right(&MoveRight, window, cx);
 1393        assert_eq!(
 1394            editor.selections.display_ranges(cx),
 1395            &[empty_range(2, "αβ⋯ε".len())]
 1396        );
 1397
 1398        editor.move_up(&MoveUp, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(1, "ab⋯e".len())]
 1402        );
 1403        editor.move_down(&MoveDown, window, cx);
 1404        assert_eq!(
 1405            editor.selections.display_ranges(cx),
 1406            &[empty_range(2, "αβ⋯ε".len())]
 1407        );
 1408        editor.move_up(&MoveUp, window, cx);
 1409        assert_eq!(
 1410            editor.selections.display_ranges(cx),
 1411            &[empty_range(1, "ab⋯e".len())]
 1412        );
 1413
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(0, "🟥🟧".len())]
 1418        );
 1419        editor.move_left(&MoveLeft, window, cx);
 1420        assert_eq!(
 1421            editor.selections.display_ranges(cx),
 1422            &[empty_range(0, "🟥".len())]
 1423        );
 1424        editor.move_left(&MoveLeft, window, cx);
 1425        assert_eq!(
 1426            editor.selections.display_ranges(cx),
 1427            &[empty_range(0, "".len())]
 1428        );
 1429    });
 1430}
 1431
 1432#[gpui::test]
 1433fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1434    init_test(cx, |_| {});
 1435
 1436    let editor = cx.add_window(|window, cx| {
 1437        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1438        build_editor(buffer.clone(), window, cx)
 1439    });
 1440    _ = editor.update(cx, |editor, window, cx| {
 1441        editor.change_selections(None, window, cx, |s| {
 1442            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1443        });
 1444
 1445        // moving above start of document should move selection to start of document,
 1446        // but the next move down should still be at the original goal_x
 1447        editor.move_up(&MoveUp, window, cx);
 1448        assert_eq!(
 1449            editor.selections.display_ranges(cx),
 1450            &[empty_range(0, "".len())]
 1451        );
 1452
 1453        editor.move_down(&MoveDown, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(1, "abcd".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(2, "αβγ".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(3, "abcd".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1475        );
 1476
 1477        // moving past end of document should not change goal_x
 1478        editor.move_down(&MoveDown, window, cx);
 1479        assert_eq!(
 1480            editor.selections.display_ranges(cx),
 1481            &[empty_range(5, "".len())]
 1482        );
 1483
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_up(&MoveUp, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(3, "abcd".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(2, "αβγ".len())]
 1506        );
 1507    });
 1508}
 1509
 1510#[gpui::test]
 1511fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1512    init_test(cx, |_| {});
 1513    let move_to_beg = MoveToBeginningOfLine {
 1514        stop_at_soft_wraps: true,
 1515        stop_at_indent: true,
 1516    };
 1517
 1518    let delete_to_beg = DeleteToBeginningOfLine {
 1519        stop_at_indent: false,
 1520    };
 1521
 1522    let move_to_end = MoveToEndOfLine {
 1523        stop_at_soft_wraps: true,
 1524    };
 1525
 1526    let editor = cx.add_window(|window, cx| {
 1527        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1528        build_editor(buffer, window, cx)
 1529    });
 1530    _ = editor.update(cx, |editor, window, cx| {
 1531        editor.change_selections(None, window, cx, |s| {
 1532            s.select_display_ranges([
 1533                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1534                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1535            ]);
 1536        });
 1537    });
 1538
 1539    _ = editor.update(cx, |editor, window, cx| {
 1540        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1541        assert_eq!(
 1542            editor.selections.display_ranges(cx),
 1543            &[
 1544                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1545                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1546            ]
 1547        );
 1548    });
 1549
 1550    _ = editor.update(cx, |editor, window, cx| {
 1551        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1552        assert_eq!(
 1553            editor.selections.display_ranges(cx),
 1554            &[
 1555                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1556                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1557            ]
 1558        );
 1559    });
 1560
 1561    _ = editor.update(cx, |editor, window, cx| {
 1562        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1563        assert_eq!(
 1564            editor.selections.display_ranges(cx),
 1565            &[
 1566                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1567                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1568            ]
 1569        );
 1570    });
 1571
 1572    _ = editor.update(cx, |editor, window, cx| {
 1573        editor.move_to_end_of_line(&move_to_end, window, cx);
 1574        assert_eq!(
 1575            editor.selections.display_ranges(cx),
 1576            &[
 1577                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1578                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1579            ]
 1580        );
 1581    });
 1582
 1583    // Moving to the end of line again is a no-op.
 1584    _ = editor.update(cx, |editor, window, cx| {
 1585        editor.move_to_end_of_line(&move_to_end, window, cx);
 1586        assert_eq!(
 1587            editor.selections.display_ranges(cx),
 1588            &[
 1589                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1590                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1591            ]
 1592        );
 1593    });
 1594
 1595    _ = editor.update(cx, |editor, window, cx| {
 1596        editor.move_left(&MoveLeft, window, cx);
 1597        editor.select_to_beginning_of_line(
 1598            &SelectToBeginningOfLine {
 1599                stop_at_soft_wraps: true,
 1600                stop_at_indent: true,
 1601            },
 1602            window,
 1603            cx,
 1604        );
 1605        assert_eq!(
 1606            editor.selections.display_ranges(cx),
 1607            &[
 1608                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1609                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1610            ]
 1611        );
 1612    });
 1613
 1614    _ = editor.update(cx, |editor, window, cx| {
 1615        editor.select_to_beginning_of_line(
 1616            &SelectToBeginningOfLine {
 1617                stop_at_soft_wraps: true,
 1618                stop_at_indent: true,
 1619            },
 1620            window,
 1621            cx,
 1622        );
 1623        assert_eq!(
 1624            editor.selections.display_ranges(cx),
 1625            &[
 1626                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1627                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1628            ]
 1629        );
 1630    });
 1631
 1632    _ = editor.update(cx, |editor, window, cx| {
 1633        editor.select_to_beginning_of_line(
 1634            &SelectToBeginningOfLine {
 1635                stop_at_soft_wraps: true,
 1636                stop_at_indent: true,
 1637            },
 1638            window,
 1639            cx,
 1640        );
 1641        assert_eq!(
 1642            editor.selections.display_ranges(cx),
 1643            &[
 1644                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1645                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1646            ]
 1647        );
 1648    });
 1649
 1650    _ = editor.update(cx, |editor, window, cx| {
 1651        editor.select_to_end_of_line(
 1652            &SelectToEndOfLine {
 1653                stop_at_soft_wraps: true,
 1654            },
 1655            window,
 1656            cx,
 1657        );
 1658        assert_eq!(
 1659            editor.selections.display_ranges(cx),
 1660            &[
 1661                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1662                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1663            ]
 1664        );
 1665    });
 1666
 1667    _ = editor.update(cx, |editor, window, cx| {
 1668        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1669        assert_eq!(editor.display_text(cx), "ab\n  de");
 1670        assert_eq!(
 1671            editor.selections.display_ranges(cx),
 1672            &[
 1673                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1674                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1675            ]
 1676        );
 1677    });
 1678
 1679    _ = editor.update(cx, |editor, window, cx| {
 1680        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1681        assert_eq!(editor.display_text(cx), "\n");
 1682        assert_eq!(
 1683            editor.selections.display_ranges(cx),
 1684            &[
 1685                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1686                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1687            ]
 1688        );
 1689    });
 1690}
 1691
 1692#[gpui::test]
 1693fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1694    init_test(cx, |_| {});
 1695    let move_to_beg = MoveToBeginningOfLine {
 1696        stop_at_soft_wraps: false,
 1697        stop_at_indent: false,
 1698    };
 1699
 1700    let move_to_end = MoveToEndOfLine {
 1701        stop_at_soft_wraps: false,
 1702    };
 1703
 1704    let editor = cx.add_window(|window, cx| {
 1705        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1706        build_editor(buffer, window, cx)
 1707    });
 1708
 1709    _ = editor.update(cx, |editor, window, cx| {
 1710        editor.set_wrap_width(Some(140.0.into()), cx);
 1711
 1712        // We expect the following lines after wrapping
 1713        // ```
 1714        // thequickbrownfox
 1715        // jumpedoverthelazydo
 1716        // gs
 1717        // ```
 1718        // The final `gs` was soft-wrapped onto a new line.
 1719        assert_eq!(
 1720            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1721            editor.display_text(cx),
 1722        );
 1723
 1724        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1725        // Start the cursor at the `k` on the first line
 1726        editor.change_selections(None, window, cx, |s| {
 1727            s.select_display_ranges([
 1728                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1729            ]);
 1730        });
 1731
 1732        // Moving to the beginning of the line should put us at the beginning of the line.
 1733        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1734        assert_eq!(
 1735            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1736            editor.selections.display_ranges(cx)
 1737        );
 1738
 1739        // Moving to the end of the line should put us at the end of the line.
 1740        editor.move_to_end_of_line(&move_to_end, window, cx);
 1741        assert_eq!(
 1742            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1743            editor.selections.display_ranges(cx)
 1744        );
 1745
 1746        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1747        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1748        editor.change_selections(None, window, cx, |s| {
 1749            s.select_display_ranges([
 1750                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1751            ]);
 1752        });
 1753
 1754        // Moving to the beginning of the line should put us at the start of the second line of
 1755        // display text, i.e., the `j`.
 1756        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1757        assert_eq!(
 1758            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1759            editor.selections.display_ranges(cx)
 1760        );
 1761
 1762        // Moving to the beginning of the line again should be a no-op.
 1763        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1764        assert_eq!(
 1765            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1766            editor.selections.display_ranges(cx)
 1767        );
 1768
 1769        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1770        // next display line.
 1771        editor.move_to_end_of_line(&move_to_end, window, cx);
 1772        assert_eq!(
 1773            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1774            editor.selections.display_ranges(cx)
 1775        );
 1776
 1777        // Moving to the end of the line again should be a no-op.
 1778        editor.move_to_end_of_line(&move_to_end, window, cx);
 1779        assert_eq!(
 1780            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1781            editor.selections.display_ranges(cx)
 1782        );
 1783    });
 1784}
 1785
 1786#[gpui::test]
 1787fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1788    init_test(cx, |_| {});
 1789
 1790    let move_to_beg = MoveToBeginningOfLine {
 1791        stop_at_soft_wraps: true,
 1792        stop_at_indent: true,
 1793    };
 1794
 1795    let select_to_beg = SelectToBeginningOfLine {
 1796        stop_at_soft_wraps: true,
 1797        stop_at_indent: true,
 1798    };
 1799
 1800    let delete_to_beg = DeleteToBeginningOfLine {
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let move_to_end = MoveToEndOfLine {
 1805        stop_at_soft_wraps: false,
 1806    };
 1807
 1808    let editor = cx.add_window(|window, cx| {
 1809        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1810        build_editor(buffer, window, cx)
 1811    });
 1812
 1813    _ = editor.update(cx, |editor, window, cx| {
 1814        editor.change_selections(None, window, cx, |s| {
 1815            s.select_display_ranges([
 1816                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1817                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1818            ]);
 1819        });
 1820
 1821        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1822        // and the second cursor at the first non-whitespace character in the line.
 1823        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1824        assert_eq!(
 1825            editor.selections.display_ranges(cx),
 1826            &[
 1827                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1828                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1829            ]
 1830        );
 1831
 1832        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1833        // and should move the second cursor to the beginning of the line.
 1834        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1835        assert_eq!(
 1836            editor.selections.display_ranges(cx),
 1837            &[
 1838                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1839                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1840            ]
 1841        );
 1842
 1843        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1844        // and should move the second cursor back to the first non-whitespace character in the line.
 1845        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1846        assert_eq!(
 1847            editor.selections.display_ranges(cx),
 1848            &[
 1849                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1850                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1851            ]
 1852        );
 1853
 1854        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1855        // and to the first non-whitespace character in the line for the second cursor.
 1856        editor.move_to_end_of_line(&move_to_end, window, cx);
 1857        editor.move_left(&MoveLeft, window, cx);
 1858        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1859        assert_eq!(
 1860            editor.selections.display_ranges(cx),
 1861            &[
 1862                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1863                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1864            ]
 1865        );
 1866
 1867        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1868        // and should select to the beginning of the line for the second cursor.
 1869        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1870        assert_eq!(
 1871            editor.selections.display_ranges(cx),
 1872            &[
 1873                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1874                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1875            ]
 1876        );
 1877
 1878        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1879        // and should delete to the first non-whitespace character in the line for the second cursor.
 1880        editor.move_to_end_of_line(&move_to_end, window, cx);
 1881        editor.move_left(&MoveLeft, window, cx);
 1882        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1883        assert_eq!(editor.text(cx), "c\n  f");
 1884    });
 1885}
 1886
 1887#[gpui::test]
 1888fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1889    init_test(cx, |_| {});
 1890
 1891    let editor = cx.add_window(|window, cx| {
 1892        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1893        build_editor(buffer, window, cx)
 1894    });
 1895    _ = editor.update(cx, |editor, window, cx| {
 1896        editor.change_selections(None, window, cx, |s| {
 1897            s.select_display_ranges([
 1898                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1899                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1900            ])
 1901        });
 1902
 1903        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1904        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1905
 1906        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1907        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1919        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1922        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1926
 1927        editor.move_right(&MoveRight, window, cx);
 1928        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1929        assert_selection_ranges(
 1930            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1931            editor,
 1932            cx,
 1933        );
 1934
 1935        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1936        assert_selection_ranges(
 1937            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1938            editor,
 1939            cx,
 1940        );
 1941
 1942        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1943        assert_selection_ranges(
 1944            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1945            editor,
 1946            cx,
 1947        );
 1948    });
 1949}
 1950
 1951#[gpui::test]
 1952fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1953    init_test(cx, |_| {});
 1954
 1955    let editor = cx.add_window(|window, cx| {
 1956        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1957        build_editor(buffer, window, cx)
 1958    });
 1959
 1960    _ = editor.update(cx, |editor, window, cx| {
 1961        editor.set_wrap_width(Some(140.0.into()), cx);
 1962        assert_eq!(
 1963            editor.display_text(cx),
 1964            "use one::{\n    two::three::\n    four::five\n};"
 1965        );
 1966
 1967        editor.change_selections(None, window, cx, |s| {
 1968            s.select_display_ranges([
 1969                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1970            ]);
 1971        });
 1972
 1973        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1974        assert_eq!(
 1975            editor.selections.display_ranges(cx),
 1976            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1977        );
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1995        );
 1996
 1997        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2007        );
 2008    });
 2009}
 2010
 2011#[gpui::test]
 2012async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2013    init_test(cx, |_| {});
 2014    let mut cx = EditorTestContext::new(cx).await;
 2015
 2016    let line_height = cx.editor(|editor, window, _| {
 2017        editor
 2018            .style()
 2019            .unwrap()
 2020            .text
 2021            .line_height_in_pixels(window.rem_size())
 2022    });
 2023    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2024
 2025    cx.set_state(
 2026        &r#"ˇone
 2027        two
 2028
 2029        three
 2030        fourˇ
 2031        five
 2032
 2033        six"#
 2034            .unindent(),
 2035    );
 2036
 2037    cx.update_editor(|editor, window, cx| {
 2038        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2039    });
 2040    cx.assert_editor_state(
 2041        &r#"one
 2042        two
 2043        ˇ
 2044        three
 2045        four
 2046        five
 2047        ˇ
 2048        six"#
 2049            .unindent(),
 2050    );
 2051
 2052    cx.update_editor(|editor, window, cx| {
 2053        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2054    });
 2055    cx.assert_editor_state(
 2056        &r#"one
 2057        two
 2058
 2059        three
 2060        four
 2061        five
 2062        ˇ
 2063        sixˇ"#
 2064            .unindent(),
 2065    );
 2066
 2067    cx.update_editor(|editor, window, cx| {
 2068        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2069    });
 2070    cx.assert_editor_state(
 2071        &r#"one
 2072        two
 2073
 2074        three
 2075        four
 2076        five
 2077
 2078        sixˇ"#
 2079            .unindent(),
 2080    );
 2081
 2082    cx.update_editor(|editor, window, cx| {
 2083        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2084    });
 2085    cx.assert_editor_state(
 2086        &r#"one
 2087        two
 2088
 2089        three
 2090        four
 2091        five
 2092        ˇ
 2093        six"#
 2094            .unindent(),
 2095    );
 2096
 2097    cx.update_editor(|editor, window, cx| {
 2098        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2099    });
 2100    cx.assert_editor_state(
 2101        &r#"one
 2102        two
 2103        ˇ
 2104        three
 2105        four
 2106        five
 2107
 2108        six"#
 2109            .unindent(),
 2110    );
 2111
 2112    cx.update_editor(|editor, window, cx| {
 2113        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2114    });
 2115    cx.assert_editor_state(
 2116        &r#"ˇone
 2117        two
 2118
 2119        three
 2120        four
 2121        five
 2122
 2123        six"#
 2124            .unindent(),
 2125    );
 2126}
 2127
 2128#[gpui::test]
 2129async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2130    init_test(cx, |_| {});
 2131    let mut cx = EditorTestContext::new(cx).await;
 2132    let line_height = cx.editor(|editor, window, _| {
 2133        editor
 2134            .style()
 2135            .unwrap()
 2136            .text
 2137            .line_height_in_pixels(window.rem_size())
 2138    });
 2139    let window = cx.window;
 2140    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2141
 2142    cx.set_state(
 2143        r#"ˇone
 2144        two
 2145        three
 2146        four
 2147        five
 2148        six
 2149        seven
 2150        eight
 2151        nine
 2152        ten
 2153        "#,
 2154    );
 2155
 2156    cx.update_editor(|editor, window, cx| {
 2157        assert_eq!(
 2158            editor.snapshot(window, cx).scroll_position(),
 2159            gpui::Point::new(0., 0.)
 2160        );
 2161        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2162        assert_eq!(
 2163            editor.snapshot(window, cx).scroll_position(),
 2164            gpui::Point::new(0., 3.)
 2165        );
 2166        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2167        assert_eq!(
 2168            editor.snapshot(window, cx).scroll_position(),
 2169            gpui::Point::new(0., 6.)
 2170        );
 2171        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2172        assert_eq!(
 2173            editor.snapshot(window, cx).scroll_position(),
 2174            gpui::Point::new(0., 3.)
 2175        );
 2176
 2177        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 1.)
 2181        );
 2182        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2183        assert_eq!(
 2184            editor.snapshot(window, cx).scroll_position(),
 2185            gpui::Point::new(0., 3.)
 2186        );
 2187    });
 2188}
 2189
 2190#[gpui::test]
 2191async fn test_autoscroll(cx: &mut TestAppContext) {
 2192    init_test(cx, |_| {});
 2193    let mut cx = EditorTestContext::new(cx).await;
 2194
 2195    let line_height = cx.update_editor(|editor, window, cx| {
 2196        editor.set_vertical_scroll_margin(2, cx);
 2197        editor
 2198            .style()
 2199            .unwrap()
 2200            .text
 2201            .line_height_in_pixels(window.rem_size())
 2202    });
 2203    let window = cx.window;
 2204    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2205
 2206    cx.set_state(
 2207        r#"ˇone
 2208            two
 2209            three
 2210            four
 2211            five
 2212            six
 2213            seven
 2214            eight
 2215            nine
 2216            ten
 2217        "#,
 2218    );
 2219    cx.update_editor(|editor, window, cx| {
 2220        assert_eq!(
 2221            editor.snapshot(window, cx).scroll_position(),
 2222            gpui::Point::new(0., 0.0)
 2223        );
 2224    });
 2225
 2226    // Add a cursor below the visible area. Since both cursors cannot fit
 2227    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2228    // allows the vertical scroll margin below that cursor.
 2229    cx.update_editor(|editor, window, cx| {
 2230        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2231            selections.select_ranges([
 2232                Point::new(0, 0)..Point::new(0, 0),
 2233                Point::new(6, 0)..Point::new(6, 0),
 2234            ]);
 2235        })
 2236    });
 2237    cx.update_editor(|editor, window, cx| {
 2238        assert_eq!(
 2239            editor.snapshot(window, cx).scroll_position(),
 2240            gpui::Point::new(0., 3.0)
 2241        );
 2242    });
 2243
 2244    // Move down. The editor cursor scrolls down to track the newest cursor.
 2245    cx.update_editor(|editor, window, cx| {
 2246        editor.move_down(&Default::default(), window, cx);
 2247    });
 2248    cx.update_editor(|editor, window, cx| {
 2249        assert_eq!(
 2250            editor.snapshot(window, cx).scroll_position(),
 2251            gpui::Point::new(0., 4.0)
 2252        );
 2253    });
 2254
 2255    // Add a cursor above the visible area. Since both cursors fit on screen,
 2256    // the editor scrolls to show both.
 2257    cx.update_editor(|editor, window, cx| {
 2258        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2259            selections.select_ranges([
 2260                Point::new(1, 0)..Point::new(1, 0),
 2261                Point::new(6, 0)..Point::new(6, 0),
 2262            ]);
 2263        })
 2264    });
 2265    cx.update_editor(|editor, window, cx| {
 2266        assert_eq!(
 2267            editor.snapshot(window, cx).scroll_position(),
 2268            gpui::Point::new(0., 1.0)
 2269        );
 2270    });
 2271}
 2272
 2273#[gpui::test]
 2274async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2275    init_test(cx, |_| {});
 2276    let mut cx = EditorTestContext::new(cx).await;
 2277
 2278    let line_height = cx.editor(|editor, window, _cx| {
 2279        editor
 2280            .style()
 2281            .unwrap()
 2282            .text
 2283            .line_height_in_pixels(window.rem_size())
 2284    });
 2285    let window = cx.window;
 2286    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2287    cx.set_state(
 2288        &r#"
 2289        ˇone
 2290        two
 2291        threeˇ
 2292        four
 2293        five
 2294        six
 2295        seven
 2296        eight
 2297        nine
 2298        ten
 2299        "#
 2300        .unindent(),
 2301    );
 2302
 2303    cx.update_editor(|editor, window, cx| {
 2304        editor.move_page_down(&MovePageDown::default(), window, cx)
 2305    });
 2306    cx.assert_editor_state(
 2307        &r#"
 2308        one
 2309        two
 2310        three
 2311        ˇfour
 2312        five
 2313        sixˇ
 2314        seven
 2315        eight
 2316        nine
 2317        ten
 2318        "#
 2319        .unindent(),
 2320    );
 2321
 2322    cx.update_editor(|editor, window, cx| {
 2323        editor.move_page_down(&MovePageDown::default(), window, cx)
 2324    });
 2325    cx.assert_editor_state(
 2326        &r#"
 2327        one
 2328        two
 2329        three
 2330        four
 2331        five
 2332        six
 2333        ˇseven
 2334        eight
 2335        nineˇ
 2336        ten
 2337        "#
 2338        .unindent(),
 2339    );
 2340
 2341    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2342    cx.assert_editor_state(
 2343        &r#"
 2344        one
 2345        two
 2346        three
 2347        ˇfour
 2348        five
 2349        sixˇ
 2350        seven
 2351        eight
 2352        nine
 2353        ten
 2354        "#
 2355        .unindent(),
 2356    );
 2357
 2358    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2359    cx.assert_editor_state(
 2360        &r#"
 2361        ˇone
 2362        two
 2363        threeˇ
 2364        four
 2365        five
 2366        six
 2367        seven
 2368        eight
 2369        nine
 2370        ten
 2371        "#
 2372        .unindent(),
 2373    );
 2374
 2375    // Test select collapsing
 2376    cx.update_editor(|editor, window, cx| {
 2377        editor.move_page_down(&MovePageDown::default(), window, cx);
 2378        editor.move_page_down(&MovePageDown::default(), window, cx);
 2379        editor.move_page_down(&MovePageDown::default(), window, cx);
 2380    });
 2381    cx.assert_editor_state(
 2382        &r#"
 2383        one
 2384        two
 2385        three
 2386        four
 2387        five
 2388        six
 2389        seven
 2390        eight
 2391        nine
 2392        ˇten
 2393        ˇ"#
 2394        .unindent(),
 2395    );
 2396}
 2397
 2398#[gpui::test]
 2399async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2400    init_test(cx, |_| {});
 2401    let mut cx = EditorTestContext::new(cx).await;
 2402    cx.set_state("one «two threeˇ» four");
 2403    cx.update_editor(|editor, window, cx| {
 2404        editor.delete_to_beginning_of_line(
 2405            &DeleteToBeginningOfLine {
 2406                stop_at_indent: false,
 2407            },
 2408            window,
 2409            cx,
 2410        );
 2411        assert_eq!(editor.text(cx), " four");
 2412    });
 2413}
 2414
 2415#[gpui::test]
 2416fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2417    init_test(cx, |_| {});
 2418
 2419    let editor = cx.add_window(|window, cx| {
 2420        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2421        build_editor(buffer.clone(), window, cx)
 2422    });
 2423
 2424    _ = editor.update(cx, |editor, window, cx| {
 2425        editor.change_selections(None, window, cx, |s| {
 2426            s.select_display_ranges([
 2427                // an empty selection - the preceding word fragment is deleted
 2428                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2429                // characters selected - they are deleted
 2430                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2431            ])
 2432        });
 2433        editor.delete_to_previous_word_start(
 2434            &DeleteToPreviousWordStart {
 2435                ignore_newlines: false,
 2436            },
 2437            window,
 2438            cx,
 2439        );
 2440        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2441    });
 2442
 2443    _ = editor.update(cx, |editor, window, cx| {
 2444        editor.change_selections(None, window, cx, |s| {
 2445            s.select_display_ranges([
 2446                // an empty selection - the following word fragment is deleted
 2447                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2448                // characters selected - they are deleted
 2449                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2450            ])
 2451        });
 2452        editor.delete_to_next_word_end(
 2453            &DeleteToNextWordEnd {
 2454                ignore_newlines: false,
 2455            },
 2456            window,
 2457            cx,
 2458        );
 2459        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2460    });
 2461}
 2462
 2463#[gpui::test]
 2464fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2465    init_test(cx, |_| {});
 2466
 2467    let editor = cx.add_window(|window, cx| {
 2468        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2469        build_editor(buffer.clone(), window, cx)
 2470    });
 2471    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2472        ignore_newlines: false,
 2473    };
 2474    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2475        ignore_newlines: true,
 2476    };
 2477
 2478    _ = editor.update(cx, |editor, window, cx| {
 2479        editor.change_selections(None, window, cx, |s| {
 2480            s.select_display_ranges([
 2481                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2482            ])
 2483        });
 2484        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2485        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2486        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2487        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2496    });
 2497}
 2498
 2499#[gpui::test]
 2500fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2501    init_test(cx, |_| {});
 2502
 2503    let editor = cx.add_window(|window, cx| {
 2504        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2505        build_editor(buffer.clone(), window, cx)
 2506    });
 2507    let del_to_next_word_end = DeleteToNextWordEnd {
 2508        ignore_newlines: false,
 2509    };
 2510    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2511        ignore_newlines: true,
 2512    };
 2513
 2514    _ = editor.update(cx, |editor, window, cx| {
 2515        editor.change_selections(None, window, cx, |s| {
 2516            s.select_display_ranges([
 2517                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2518            ])
 2519        });
 2520        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2521        assert_eq!(
 2522            editor.buffer.read(cx).read(cx).text(),
 2523            "one\n   two\nthree\n   four"
 2524        );
 2525        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2526        assert_eq!(
 2527            editor.buffer.read(cx).read(cx).text(),
 2528            "\n   two\nthree\n   four"
 2529        );
 2530        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2531        assert_eq!(
 2532            editor.buffer.read(cx).read(cx).text(),
 2533            "two\nthree\n   four"
 2534        );
 2535        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2536        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2537        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2538        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2539        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2541    });
 2542}
 2543
 2544#[gpui::test]
 2545fn test_newline(cx: &mut TestAppContext) {
 2546    init_test(cx, |_| {});
 2547
 2548    let editor = cx.add_window(|window, cx| {
 2549        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2550        build_editor(buffer.clone(), window, cx)
 2551    });
 2552
 2553    _ = editor.update(cx, |editor, window, cx| {
 2554        editor.change_selections(None, window, cx, |s| {
 2555            s.select_display_ranges([
 2556                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2557                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2558                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2559            ])
 2560        });
 2561
 2562        editor.newline(&Newline, window, cx);
 2563        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2564    });
 2565}
 2566
 2567#[gpui::test]
 2568fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2569    init_test(cx, |_| {});
 2570
 2571    let editor = cx.add_window(|window, cx| {
 2572        let buffer = MultiBuffer::build_simple(
 2573            "
 2574                a
 2575                b(
 2576                    X
 2577                )
 2578                c(
 2579                    X
 2580                )
 2581            "
 2582            .unindent()
 2583            .as_str(),
 2584            cx,
 2585        );
 2586        let mut editor = build_editor(buffer.clone(), window, cx);
 2587        editor.change_selections(None, window, cx, |s| {
 2588            s.select_ranges([
 2589                Point::new(2, 4)..Point::new(2, 5),
 2590                Point::new(5, 4)..Point::new(5, 5),
 2591            ])
 2592        });
 2593        editor
 2594    });
 2595
 2596    _ = editor.update(cx, |editor, window, cx| {
 2597        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2598        editor.buffer.update(cx, |buffer, cx| {
 2599            buffer.edit(
 2600                [
 2601                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2602                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2603                ],
 2604                None,
 2605                cx,
 2606            );
 2607            assert_eq!(
 2608                buffer.read(cx).text(),
 2609                "
 2610                    a
 2611                    b()
 2612                    c()
 2613                "
 2614                .unindent()
 2615            );
 2616        });
 2617        assert_eq!(
 2618            editor.selections.ranges(cx),
 2619            &[
 2620                Point::new(1, 2)..Point::new(1, 2),
 2621                Point::new(2, 2)..Point::new(2, 2),
 2622            ],
 2623        );
 2624
 2625        editor.newline(&Newline, window, cx);
 2626        assert_eq!(
 2627            editor.text(cx),
 2628            "
 2629                a
 2630                b(
 2631                )
 2632                c(
 2633                )
 2634            "
 2635            .unindent()
 2636        );
 2637
 2638        // The selections are moved after the inserted newlines
 2639        assert_eq!(
 2640            editor.selections.ranges(cx),
 2641            &[
 2642                Point::new(2, 0)..Point::new(2, 0),
 2643                Point::new(4, 0)..Point::new(4, 0),
 2644            ],
 2645        );
 2646    });
 2647}
 2648
 2649#[gpui::test]
 2650async fn test_newline_above(cx: &mut TestAppContext) {
 2651    init_test(cx, |settings| {
 2652        settings.defaults.tab_size = NonZeroU32::new(4)
 2653    });
 2654
 2655    let language = Arc::new(
 2656        Language::new(
 2657            LanguageConfig::default(),
 2658            Some(tree_sitter_rust::LANGUAGE.into()),
 2659        )
 2660        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2661        .unwrap(),
 2662    );
 2663
 2664    let mut cx = EditorTestContext::new(cx).await;
 2665    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2666    cx.set_state(indoc! {"
 2667        const a: ˇA = (
 2668 2669                «const_functionˇ»(ˇ),
 2670                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2671 2672        ˇ);ˇ
 2673    "});
 2674
 2675    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2676    cx.assert_editor_state(indoc! {"
 2677        ˇ
 2678        const a: A = (
 2679            ˇ
 2680            (
 2681                ˇ
 2682                ˇ
 2683                const_function(),
 2684                ˇ
 2685                ˇ
 2686                ˇ
 2687                ˇ
 2688                something_else,
 2689                ˇ
 2690            )
 2691            ˇ
 2692            ˇ
 2693        );
 2694    "});
 2695}
 2696
 2697#[gpui::test]
 2698async fn test_newline_below(cx: &mut TestAppContext) {
 2699    init_test(cx, |settings| {
 2700        settings.defaults.tab_size = NonZeroU32::new(4)
 2701    });
 2702
 2703    let language = Arc::new(
 2704        Language::new(
 2705            LanguageConfig::default(),
 2706            Some(tree_sitter_rust::LANGUAGE.into()),
 2707        )
 2708        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2709        .unwrap(),
 2710    );
 2711
 2712    let mut cx = EditorTestContext::new(cx).await;
 2713    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2714    cx.set_state(indoc! {"
 2715        const a: ˇA = (
 2716 2717                «const_functionˇ»(ˇ),
 2718                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2719 2720        ˇ);ˇ
 2721    "});
 2722
 2723    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2724    cx.assert_editor_state(indoc! {"
 2725        const a: A = (
 2726            ˇ
 2727            (
 2728                ˇ
 2729                const_function(),
 2730                ˇ
 2731                ˇ
 2732                something_else,
 2733                ˇ
 2734                ˇ
 2735                ˇ
 2736                ˇ
 2737            )
 2738            ˇ
 2739        );
 2740        ˇ
 2741        ˇ
 2742    "});
 2743}
 2744
 2745#[gpui::test]
 2746async fn test_newline_comments(cx: &mut TestAppContext) {
 2747    init_test(cx, |settings| {
 2748        settings.defaults.tab_size = NonZeroU32::new(4)
 2749    });
 2750
 2751    let language = Arc::new(Language::new(
 2752        LanguageConfig {
 2753            line_comments: vec!["//".into()],
 2754            ..LanguageConfig::default()
 2755        },
 2756        None,
 2757    ));
 2758    {
 2759        let mut cx = EditorTestContext::new(cx).await;
 2760        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2761        cx.set_state(indoc! {"
 2762        // Fooˇ
 2763    "});
 2764
 2765        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2766        cx.assert_editor_state(indoc! {"
 2767        // Foo
 2768        //ˇ
 2769    "});
 2770        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2771        cx.set_state(indoc! {"
 2772        ˇ// Foo
 2773    "});
 2774        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2775        cx.assert_editor_state(indoc! {"
 2776
 2777        ˇ// Foo
 2778    "});
 2779    }
 2780    // Ensure that comment continuations can be disabled.
 2781    update_test_language_settings(cx, |settings| {
 2782        settings.defaults.extend_comment_on_newline = Some(false);
 2783    });
 2784    let mut cx = EditorTestContext::new(cx).await;
 2785    cx.set_state(indoc! {"
 2786        // Fooˇ
 2787    "});
 2788    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2789    cx.assert_editor_state(indoc! {"
 2790        // Foo
 2791        ˇ
 2792    "});
 2793}
 2794
 2795#[gpui::test]
 2796fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2797    init_test(cx, |_| {});
 2798
 2799    let editor = cx.add_window(|window, cx| {
 2800        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2801        let mut editor = build_editor(buffer.clone(), window, cx);
 2802        editor.change_selections(None, window, cx, |s| {
 2803            s.select_ranges([3..4, 11..12, 19..20])
 2804        });
 2805        editor
 2806    });
 2807
 2808    _ = editor.update(cx, |editor, window, cx| {
 2809        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2810        editor.buffer.update(cx, |buffer, cx| {
 2811            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2812            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2813        });
 2814        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2815
 2816        editor.insert("Z", window, cx);
 2817        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2818
 2819        // The selections are moved after the inserted characters
 2820        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2821    });
 2822}
 2823
 2824#[gpui::test]
 2825async fn test_tab(cx: &mut TestAppContext) {
 2826    init_test(cx, |settings| {
 2827        settings.defaults.tab_size = NonZeroU32::new(3)
 2828    });
 2829
 2830    let mut cx = EditorTestContext::new(cx).await;
 2831    cx.set_state(indoc! {"
 2832        ˇabˇc
 2833        ˇ🏀ˇ🏀ˇefg
 2834 2835    "});
 2836    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2837    cx.assert_editor_state(indoc! {"
 2838           ˇab ˇc
 2839           ˇ🏀  ˇ🏀  ˇefg
 2840        d  ˇ
 2841    "});
 2842
 2843    cx.set_state(indoc! {"
 2844        a
 2845        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2846    "});
 2847    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2848    cx.assert_editor_state(indoc! {"
 2849        a
 2850           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2851    "});
 2852}
 2853
 2854#[gpui::test]
 2855async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2856    init_test(cx, |_| {});
 2857
 2858    let mut cx = EditorTestContext::new(cx).await;
 2859    let language = Arc::new(
 2860        Language::new(
 2861            LanguageConfig::default(),
 2862            Some(tree_sitter_rust::LANGUAGE.into()),
 2863        )
 2864        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2865        .unwrap(),
 2866    );
 2867    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2868
 2869    // cursors that are already at the suggested indent level insert
 2870    // a soft tab. cursors that are to the left of the suggested indent
 2871    // auto-indent their line.
 2872    cx.set_state(indoc! {"
 2873        ˇ
 2874        const a: B = (
 2875            c(
 2876                d(
 2877        ˇ
 2878                )
 2879        ˇ
 2880        ˇ    )
 2881        );
 2882    "});
 2883    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2884    cx.assert_editor_state(indoc! {"
 2885            ˇ
 2886        const a: B = (
 2887            c(
 2888                d(
 2889                    ˇ
 2890                )
 2891                ˇ
 2892            ˇ)
 2893        );
 2894    "});
 2895
 2896    // handle auto-indent when there are multiple cursors on the same line
 2897    cx.set_state(indoc! {"
 2898        const a: B = (
 2899            c(
 2900        ˇ    ˇ
 2901        ˇ    )
 2902        );
 2903    "});
 2904    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        const a: B = (
 2907            c(
 2908                ˇ
 2909            ˇ)
 2910        );
 2911    "});
 2912}
 2913
 2914#[gpui::test]
 2915async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2916    init_test(cx, |settings| {
 2917        settings.defaults.tab_size = NonZeroU32::new(4)
 2918    });
 2919
 2920    let language = Arc::new(
 2921        Language::new(
 2922            LanguageConfig::default(),
 2923            Some(tree_sitter_rust::LANGUAGE.into()),
 2924        )
 2925        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2926        .unwrap(),
 2927    );
 2928
 2929    let mut cx = EditorTestContext::new(cx).await;
 2930    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2931    cx.set_state(indoc! {"
 2932        fn a() {
 2933            if b {
 2934        \t ˇc
 2935            }
 2936        }
 2937    "});
 2938
 2939    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        fn a() {
 2942            if b {
 2943                ˇc
 2944            }
 2945        }
 2946    "});
 2947}
 2948
 2949#[gpui::test]
 2950async fn test_indent_outdent(cx: &mut TestAppContext) {
 2951    init_test(cx, |settings| {
 2952        settings.defaults.tab_size = NonZeroU32::new(4);
 2953    });
 2954
 2955    let mut cx = EditorTestContext::new(cx).await;
 2956
 2957    cx.set_state(indoc! {"
 2958          «oneˇ» «twoˇ»
 2959        three
 2960         four
 2961    "});
 2962    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2963    cx.assert_editor_state(indoc! {"
 2964            «oneˇ» «twoˇ»
 2965        three
 2966         four
 2967    "});
 2968
 2969    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2970    cx.assert_editor_state(indoc! {"
 2971        «oneˇ» «twoˇ»
 2972        three
 2973         four
 2974    "});
 2975
 2976    // select across line ending
 2977    cx.set_state(indoc! {"
 2978        one two
 2979        t«hree
 2980        ˇ» four
 2981    "});
 2982    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2983    cx.assert_editor_state(indoc! {"
 2984        one two
 2985            t«hree
 2986        ˇ» four
 2987    "});
 2988
 2989    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2990    cx.assert_editor_state(indoc! {"
 2991        one two
 2992        t«hree
 2993        ˇ» four
 2994    "});
 2995
 2996    // Ensure that indenting/outdenting works when the cursor is at column 0.
 2997    cx.set_state(indoc! {"
 2998        one two
 2999        ˇthree
 3000            four
 3001    "});
 3002    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3003    cx.assert_editor_state(indoc! {"
 3004        one two
 3005            ˇthree
 3006            four
 3007    "});
 3008
 3009    cx.set_state(indoc! {"
 3010        one two
 3011        ˇ    three
 3012            four
 3013    "});
 3014    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3015    cx.assert_editor_state(indoc! {"
 3016        one two
 3017        ˇthree
 3018            four
 3019    "});
 3020}
 3021
 3022#[gpui::test]
 3023async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3024    init_test(cx, |settings| {
 3025        settings.defaults.hard_tabs = Some(true);
 3026    });
 3027
 3028    let mut cx = EditorTestContext::new(cx).await;
 3029
 3030    // select two ranges on one line
 3031    cx.set_state(indoc! {"
 3032        «oneˇ» «twoˇ»
 3033        three
 3034        four
 3035    "});
 3036    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3037    cx.assert_editor_state(indoc! {"
 3038        \t«oneˇ» «twoˇ»
 3039        three
 3040        four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        \t\t«oneˇ» «twoˇ»
 3045        three
 3046        four
 3047    "});
 3048    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        \t«oneˇ» «twoˇ»
 3051        three
 3052        four
 3053    "});
 3054    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3055    cx.assert_editor_state(indoc! {"
 3056        «oneˇ» «twoˇ»
 3057        three
 3058        four
 3059    "});
 3060
 3061    // select across a line ending
 3062    cx.set_state(indoc! {"
 3063        one two
 3064        t«hree
 3065        ˇ»four
 3066    "});
 3067    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3068    cx.assert_editor_state(indoc! {"
 3069        one two
 3070        \tt«hree
 3071        ˇ»four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        one two
 3076        \t\tt«hree
 3077        ˇ»four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        one two
 3082        \tt«hree
 3083        ˇ»four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        one two
 3088        t«hree
 3089        ˇ»four
 3090    "});
 3091
 3092    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3093    cx.set_state(indoc! {"
 3094        one two
 3095        ˇthree
 3096        four
 3097    "});
 3098    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3099    cx.assert_editor_state(indoc! {"
 3100        one two
 3101        ˇthree
 3102        four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        \tˇthree
 3108        four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        ˇthree
 3114        four
 3115    "});
 3116}
 3117
 3118#[gpui::test]
 3119fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3120    init_test(cx, |settings| {
 3121        settings.languages.extend([
 3122            (
 3123                "TOML".into(),
 3124                LanguageSettingsContent {
 3125                    tab_size: NonZeroU32::new(2),
 3126                    ..Default::default()
 3127                },
 3128            ),
 3129            (
 3130                "Rust".into(),
 3131                LanguageSettingsContent {
 3132                    tab_size: NonZeroU32::new(4),
 3133                    ..Default::default()
 3134                },
 3135            ),
 3136        ]);
 3137    });
 3138
 3139    let toml_language = Arc::new(Language::new(
 3140        LanguageConfig {
 3141            name: "TOML".into(),
 3142            ..Default::default()
 3143        },
 3144        None,
 3145    ));
 3146    let rust_language = Arc::new(Language::new(
 3147        LanguageConfig {
 3148            name: "Rust".into(),
 3149            ..Default::default()
 3150        },
 3151        None,
 3152    ));
 3153
 3154    let toml_buffer =
 3155        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3156    let rust_buffer =
 3157        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3158    let multibuffer = cx.new(|cx| {
 3159        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3160        multibuffer.push_excerpts(
 3161            toml_buffer.clone(),
 3162            [ExcerptRange {
 3163                context: Point::new(0, 0)..Point::new(2, 0),
 3164                primary: None,
 3165            }],
 3166            cx,
 3167        );
 3168        multibuffer.push_excerpts(
 3169            rust_buffer.clone(),
 3170            [ExcerptRange {
 3171                context: Point::new(0, 0)..Point::new(1, 0),
 3172                primary: None,
 3173            }],
 3174            cx,
 3175        );
 3176        multibuffer
 3177    });
 3178
 3179    cx.add_window(|window, cx| {
 3180        let mut editor = build_editor(multibuffer, window, cx);
 3181
 3182        assert_eq!(
 3183            editor.text(cx),
 3184            indoc! {"
 3185                a = 1
 3186                b = 2
 3187
 3188                const c: usize = 3;
 3189            "}
 3190        );
 3191
 3192        select_ranges(
 3193            &mut editor,
 3194            indoc! {"
 3195                «aˇ» = 1
 3196                b = 2
 3197
 3198                «const c:ˇ» usize = 3;
 3199            "},
 3200            window,
 3201            cx,
 3202        );
 3203
 3204        editor.tab(&Tab, window, cx);
 3205        assert_text_with_selections(
 3206            &mut editor,
 3207            indoc! {"
 3208                  «aˇ» = 1
 3209                b = 2
 3210
 3211                    «const c:ˇ» usize = 3;
 3212            "},
 3213            cx,
 3214        );
 3215        editor.backtab(&Backtab, window, cx);
 3216        assert_text_with_selections(
 3217            &mut editor,
 3218            indoc! {"
 3219                «aˇ» = 1
 3220                b = 2
 3221
 3222                «const c:ˇ» usize = 3;
 3223            "},
 3224            cx,
 3225        );
 3226
 3227        editor
 3228    });
 3229}
 3230
 3231#[gpui::test]
 3232async fn test_backspace(cx: &mut TestAppContext) {
 3233    init_test(cx, |_| {});
 3234
 3235    let mut cx = EditorTestContext::new(cx).await;
 3236
 3237    // Basic backspace
 3238    cx.set_state(indoc! {"
 3239        onˇe two three
 3240        fou«rˇ» five six
 3241        seven «ˇeight nine
 3242        »ten
 3243    "});
 3244    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3245    cx.assert_editor_state(indoc! {"
 3246        oˇe two three
 3247        fouˇ five six
 3248        seven ˇten
 3249    "});
 3250
 3251    // Test backspace inside and around indents
 3252    cx.set_state(indoc! {"
 3253        zero
 3254            ˇone
 3255                ˇtwo
 3256            ˇ ˇ ˇ  three
 3257        ˇ  ˇ  four
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        zero
 3262        ˇone
 3263            ˇtwo
 3264        ˇ  threeˇ  four
 3265    "});
 3266
 3267    // Test backspace with line_mode set to true
 3268    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3269    cx.set_state(indoc! {"
 3270        The ˇquick ˇbrown
 3271        fox jumps over
 3272        the lazy dog
 3273        ˇThe qu«ick bˇ»rown"});
 3274    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3275    cx.assert_editor_state(indoc! {"
 3276        ˇfox jumps over
 3277        the lazy dogˇ"});
 3278}
 3279
 3280#[gpui::test]
 3281async fn test_delete(cx: &mut TestAppContext) {
 3282    init_test(cx, |_| {});
 3283
 3284    let mut cx = EditorTestContext::new(cx).await;
 3285    cx.set_state(indoc! {"
 3286        onˇe two three
 3287        fou«rˇ» five six
 3288        seven «ˇeight nine
 3289        »ten
 3290    "});
 3291    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3292    cx.assert_editor_state(indoc! {"
 3293        onˇ two three
 3294        fouˇ five six
 3295        seven ˇten
 3296    "});
 3297
 3298    // Test backspace with line_mode set to true
 3299    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3300    cx.set_state(indoc! {"
 3301        The ˇquick ˇbrown
 3302        fox «ˇjum»ps over
 3303        the lazy dog
 3304        ˇThe qu«ick bˇ»rown"});
 3305    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3306    cx.assert_editor_state("ˇthe lazy dogˇ");
 3307}
 3308
 3309#[gpui::test]
 3310fn test_delete_line(cx: &mut TestAppContext) {
 3311    init_test(cx, |_| {});
 3312
 3313    let editor = cx.add_window(|window, cx| {
 3314        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3315        build_editor(buffer, window, cx)
 3316    });
 3317    _ = editor.update(cx, |editor, window, cx| {
 3318        editor.change_selections(None, window, cx, |s| {
 3319            s.select_display_ranges([
 3320                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3321                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3322                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3323            ])
 3324        });
 3325        editor.delete_line(&DeleteLine, window, cx);
 3326        assert_eq!(editor.display_text(cx), "ghi");
 3327        assert_eq!(
 3328            editor.selections.display_ranges(cx),
 3329            vec![
 3330                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3331                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3332            ]
 3333        );
 3334    });
 3335
 3336    let editor = cx.add_window(|window, cx| {
 3337        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3338        build_editor(buffer, window, cx)
 3339    });
 3340    _ = editor.update(cx, |editor, window, cx| {
 3341        editor.change_selections(None, window, cx, |s| {
 3342            s.select_display_ranges([
 3343                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3344            ])
 3345        });
 3346        editor.delete_line(&DeleteLine, window, cx);
 3347        assert_eq!(editor.display_text(cx), "ghi\n");
 3348        assert_eq!(
 3349            editor.selections.display_ranges(cx),
 3350            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3351        );
 3352    });
 3353}
 3354
 3355#[gpui::test]
 3356fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3357    init_test(cx, |_| {});
 3358
 3359    cx.add_window(|window, cx| {
 3360        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3361        let mut editor = build_editor(buffer.clone(), window, cx);
 3362        let buffer = buffer.read(cx).as_singleton().unwrap();
 3363
 3364        assert_eq!(
 3365            editor.selections.ranges::<Point>(cx),
 3366            &[Point::new(0, 0)..Point::new(0, 0)]
 3367        );
 3368
 3369        // When on single line, replace newline at end by space
 3370        editor.join_lines(&JoinLines, window, cx);
 3371        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3372        assert_eq!(
 3373            editor.selections.ranges::<Point>(cx),
 3374            &[Point::new(0, 3)..Point::new(0, 3)]
 3375        );
 3376
 3377        // When multiple lines are selected, remove newlines that are spanned by the selection
 3378        editor.change_selections(None, window, cx, |s| {
 3379            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3380        });
 3381        editor.join_lines(&JoinLines, window, cx);
 3382        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3383        assert_eq!(
 3384            editor.selections.ranges::<Point>(cx),
 3385            &[Point::new(0, 11)..Point::new(0, 11)]
 3386        );
 3387
 3388        // Undo should be transactional
 3389        editor.undo(&Undo, window, cx);
 3390        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3391        assert_eq!(
 3392            editor.selections.ranges::<Point>(cx),
 3393            &[Point::new(0, 5)..Point::new(2, 2)]
 3394        );
 3395
 3396        // When joining an empty line don't insert a space
 3397        editor.change_selections(None, window, cx, |s| {
 3398            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3399        });
 3400        editor.join_lines(&JoinLines, window, cx);
 3401        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3402        assert_eq!(
 3403            editor.selections.ranges::<Point>(cx),
 3404            [Point::new(2, 3)..Point::new(2, 3)]
 3405        );
 3406
 3407        // We can remove trailing newlines
 3408        editor.join_lines(&JoinLines, window, cx);
 3409        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3410        assert_eq!(
 3411            editor.selections.ranges::<Point>(cx),
 3412            [Point::new(2, 3)..Point::new(2, 3)]
 3413        );
 3414
 3415        // We don't blow up on the last line
 3416        editor.join_lines(&JoinLines, window, cx);
 3417        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3418        assert_eq!(
 3419            editor.selections.ranges::<Point>(cx),
 3420            [Point::new(2, 3)..Point::new(2, 3)]
 3421        );
 3422
 3423        // reset to test indentation
 3424        editor.buffer.update(cx, |buffer, cx| {
 3425            buffer.edit(
 3426                [
 3427                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3428                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3429                ],
 3430                None,
 3431                cx,
 3432            )
 3433        });
 3434
 3435        // We remove any leading spaces
 3436        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3437        editor.change_selections(None, window, cx, |s| {
 3438            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3439        });
 3440        editor.join_lines(&JoinLines, window, cx);
 3441        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3442
 3443        // We don't insert a space for a line containing only spaces
 3444        editor.join_lines(&JoinLines, window, cx);
 3445        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3446
 3447        // We ignore any leading tabs
 3448        editor.join_lines(&JoinLines, window, cx);
 3449        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3450
 3451        editor
 3452    });
 3453}
 3454
 3455#[gpui::test]
 3456fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3457    init_test(cx, |_| {});
 3458
 3459    cx.add_window(|window, cx| {
 3460        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3461        let mut editor = build_editor(buffer.clone(), window, cx);
 3462        let buffer = buffer.read(cx).as_singleton().unwrap();
 3463
 3464        editor.change_selections(None, window, cx, |s| {
 3465            s.select_ranges([
 3466                Point::new(0, 2)..Point::new(1, 1),
 3467                Point::new(1, 2)..Point::new(1, 2),
 3468                Point::new(3, 1)..Point::new(3, 2),
 3469            ])
 3470        });
 3471
 3472        editor.join_lines(&JoinLines, window, cx);
 3473        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3474
 3475        assert_eq!(
 3476            editor.selections.ranges::<Point>(cx),
 3477            [
 3478                Point::new(0, 7)..Point::new(0, 7),
 3479                Point::new(1, 3)..Point::new(1, 3)
 3480            ]
 3481        );
 3482        editor
 3483    });
 3484}
 3485
 3486#[gpui::test]
 3487async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3488    init_test(cx, |_| {});
 3489
 3490    let mut cx = EditorTestContext::new(cx).await;
 3491
 3492    let diff_base = r#"
 3493        Line 0
 3494        Line 1
 3495        Line 2
 3496        Line 3
 3497        "#
 3498    .unindent();
 3499
 3500    cx.set_state(
 3501        &r#"
 3502        ˇLine 0
 3503        Line 1
 3504        Line 2
 3505        Line 3
 3506        "#
 3507        .unindent(),
 3508    );
 3509
 3510    cx.set_head_text(&diff_base);
 3511    executor.run_until_parked();
 3512
 3513    // Join lines
 3514    cx.update_editor(|editor, window, cx| {
 3515        editor.join_lines(&JoinLines, window, cx);
 3516    });
 3517    executor.run_until_parked();
 3518
 3519    cx.assert_editor_state(
 3520        &r#"
 3521        Line 0ˇ Line 1
 3522        Line 2
 3523        Line 3
 3524        "#
 3525        .unindent(),
 3526    );
 3527    // Join again
 3528    cx.update_editor(|editor, window, cx| {
 3529        editor.join_lines(&JoinLines, window, cx);
 3530    });
 3531    executor.run_until_parked();
 3532
 3533    cx.assert_editor_state(
 3534        &r#"
 3535        Line 0 Line 1ˇ Line 2
 3536        Line 3
 3537        "#
 3538        .unindent(),
 3539    );
 3540}
 3541
 3542#[gpui::test]
 3543async fn test_custom_newlines_cause_no_false_positive_diffs(
 3544    executor: BackgroundExecutor,
 3545    cx: &mut TestAppContext,
 3546) {
 3547    init_test(cx, |_| {});
 3548    let mut cx = EditorTestContext::new(cx).await;
 3549    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3550    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3551    executor.run_until_parked();
 3552
 3553    cx.update_editor(|editor, window, cx| {
 3554        let snapshot = editor.snapshot(window, cx);
 3555        assert_eq!(
 3556            snapshot
 3557                .buffer_snapshot
 3558                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3559                .collect::<Vec<_>>(),
 3560            Vec::new(),
 3561            "Should not have any diffs for files with custom newlines"
 3562        );
 3563    });
 3564}
 3565
 3566#[gpui::test]
 3567async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3568    init_test(cx, |_| {});
 3569
 3570    let mut cx = EditorTestContext::new(cx).await;
 3571
 3572    // Test sort_lines_case_insensitive()
 3573    cx.set_state(indoc! {"
 3574        «z
 3575        y
 3576        x
 3577        Z
 3578        Y
 3579        Xˇ»
 3580    "});
 3581    cx.update_editor(|e, window, cx| {
 3582        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3583    });
 3584    cx.assert_editor_state(indoc! {"
 3585        «x
 3586        X
 3587        y
 3588        Y
 3589        z
 3590        Zˇ»
 3591    "});
 3592
 3593    // Test reverse_lines()
 3594    cx.set_state(indoc! {"
 3595        «5
 3596        4
 3597        3
 3598        2
 3599        1ˇ»
 3600    "});
 3601    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3602    cx.assert_editor_state(indoc! {"
 3603        «1
 3604        2
 3605        3
 3606        4
 3607        5ˇ»
 3608    "});
 3609
 3610    // Skip testing shuffle_line()
 3611
 3612    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3613    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3614
 3615    // Don't manipulate when cursor is on single line, but expand the selection
 3616    cx.set_state(indoc! {"
 3617        ddˇdd
 3618        ccc
 3619        bb
 3620        a
 3621    "});
 3622    cx.update_editor(|e, window, cx| {
 3623        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3624    });
 3625    cx.assert_editor_state(indoc! {"
 3626        «ddddˇ»
 3627        ccc
 3628        bb
 3629        a
 3630    "});
 3631
 3632    // Basic manipulate case
 3633    // Start selection moves to column 0
 3634    // End of selection shrinks to fit shorter line
 3635    cx.set_state(indoc! {"
 3636        dd«d
 3637        ccc
 3638        bb
 3639        aaaaaˇ»
 3640    "});
 3641    cx.update_editor(|e, window, cx| {
 3642        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3643    });
 3644    cx.assert_editor_state(indoc! {"
 3645        «aaaaa
 3646        bb
 3647        ccc
 3648        dddˇ»
 3649    "});
 3650
 3651    // Manipulate case with newlines
 3652    cx.set_state(indoc! {"
 3653        dd«d
 3654        ccc
 3655
 3656        bb
 3657        aaaaa
 3658
 3659        ˇ»
 3660    "});
 3661    cx.update_editor(|e, window, cx| {
 3662        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3663    });
 3664    cx.assert_editor_state(indoc! {"
 3665        «
 3666
 3667        aaaaa
 3668        bb
 3669        ccc
 3670        dddˇ»
 3671
 3672    "});
 3673
 3674    // Adding new line
 3675    cx.set_state(indoc! {"
 3676        aa«a
 3677        bbˇ»b
 3678    "});
 3679    cx.update_editor(|e, window, cx| {
 3680        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3681    });
 3682    cx.assert_editor_state(indoc! {"
 3683        «aaa
 3684        bbb
 3685        added_lineˇ»
 3686    "});
 3687
 3688    // Removing line
 3689    cx.set_state(indoc! {"
 3690        aa«a
 3691        bbbˇ»
 3692    "});
 3693    cx.update_editor(|e, window, cx| {
 3694        e.manipulate_lines(window, cx, |lines| {
 3695            lines.pop();
 3696        })
 3697    });
 3698    cx.assert_editor_state(indoc! {"
 3699        «aaaˇ»
 3700    "});
 3701
 3702    // Removing all lines
 3703    cx.set_state(indoc! {"
 3704        aa«a
 3705        bbbˇ»
 3706    "});
 3707    cx.update_editor(|e, window, cx| {
 3708        e.manipulate_lines(window, cx, |lines| {
 3709            lines.drain(..);
 3710        })
 3711    });
 3712    cx.assert_editor_state(indoc! {"
 3713        ˇ
 3714    "});
 3715}
 3716
 3717#[gpui::test]
 3718async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3719    init_test(cx, |_| {});
 3720
 3721    let mut cx = EditorTestContext::new(cx).await;
 3722
 3723    // Consider continuous selection as single selection
 3724    cx.set_state(indoc! {"
 3725        Aaa«aa
 3726        cˇ»c«c
 3727        bb
 3728        aaaˇ»aa
 3729    "});
 3730    cx.update_editor(|e, window, cx| {
 3731        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3732    });
 3733    cx.assert_editor_state(indoc! {"
 3734        «Aaaaa
 3735        ccc
 3736        bb
 3737        aaaaaˇ»
 3738    "});
 3739
 3740    cx.set_state(indoc! {"
 3741        Aaa«aa
 3742        cˇ»c«c
 3743        bb
 3744        aaaˇ»aa
 3745    "});
 3746    cx.update_editor(|e, window, cx| {
 3747        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3748    });
 3749    cx.assert_editor_state(indoc! {"
 3750        «Aaaaa
 3751        ccc
 3752        bbˇ»
 3753    "});
 3754
 3755    // Consider non continuous selection as distinct dedup operations
 3756    cx.set_state(indoc! {"
 3757        «aaaaa
 3758        bb
 3759        aaaaa
 3760        aaaaaˇ»
 3761
 3762        aaa«aaˇ»
 3763    "});
 3764    cx.update_editor(|e, window, cx| {
 3765        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3766    });
 3767    cx.assert_editor_state(indoc! {"
 3768        «aaaaa
 3769        bbˇ»
 3770
 3771        «aaaaaˇ»
 3772    "});
 3773}
 3774
 3775#[gpui::test]
 3776async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3777    init_test(cx, |_| {});
 3778
 3779    let mut cx = EditorTestContext::new(cx).await;
 3780
 3781    cx.set_state(indoc! {"
 3782        «Aaa
 3783        aAa
 3784        Aaaˇ»
 3785    "});
 3786    cx.update_editor(|e, window, cx| {
 3787        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3788    });
 3789    cx.assert_editor_state(indoc! {"
 3790        «Aaa
 3791        aAaˇ»
 3792    "});
 3793
 3794    cx.set_state(indoc! {"
 3795        «Aaa
 3796        aAa
 3797        aaAˇ»
 3798    "});
 3799    cx.update_editor(|e, window, cx| {
 3800        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3801    });
 3802    cx.assert_editor_state(indoc! {"
 3803        «Aaaˇ»
 3804    "});
 3805}
 3806
 3807#[gpui::test]
 3808async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3809    init_test(cx, |_| {});
 3810
 3811    let mut cx = EditorTestContext::new(cx).await;
 3812
 3813    // Manipulate with multiple selections on a single line
 3814    cx.set_state(indoc! {"
 3815        dd«dd
 3816        cˇ»c«c
 3817        bb
 3818        aaaˇ»aa
 3819    "});
 3820    cx.update_editor(|e, window, cx| {
 3821        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3822    });
 3823    cx.assert_editor_state(indoc! {"
 3824        «aaaaa
 3825        bb
 3826        ccc
 3827        ddddˇ»
 3828    "});
 3829
 3830    // Manipulate with multiple disjoin selections
 3831    cx.set_state(indoc! {"
 3832 3833        4
 3834        3
 3835        2
 3836        1ˇ»
 3837
 3838        dd«dd
 3839        ccc
 3840        bb
 3841        aaaˇ»aa
 3842    "});
 3843    cx.update_editor(|e, window, cx| {
 3844        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3845    });
 3846    cx.assert_editor_state(indoc! {"
 3847        «1
 3848        2
 3849        3
 3850        4
 3851        5ˇ»
 3852
 3853        «aaaaa
 3854        bb
 3855        ccc
 3856        ddddˇ»
 3857    "});
 3858
 3859    // Adding lines on each selection
 3860    cx.set_state(indoc! {"
 3861 3862        1ˇ»
 3863
 3864        bb«bb
 3865        aaaˇ»aa
 3866    "});
 3867    cx.update_editor(|e, window, cx| {
 3868        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3869    });
 3870    cx.assert_editor_state(indoc! {"
 3871        «2
 3872        1
 3873        added lineˇ»
 3874
 3875        «bbbb
 3876        aaaaa
 3877        added lineˇ»
 3878    "});
 3879
 3880    // Removing lines on each selection
 3881    cx.set_state(indoc! {"
 3882 3883        1ˇ»
 3884
 3885        bb«bb
 3886        aaaˇ»aa
 3887    "});
 3888    cx.update_editor(|e, window, cx| {
 3889        e.manipulate_lines(window, cx, |lines| {
 3890            lines.pop();
 3891        })
 3892    });
 3893    cx.assert_editor_state(indoc! {"
 3894        «2ˇ»
 3895
 3896        «bbbbˇ»
 3897    "});
 3898}
 3899
 3900#[gpui::test]
 3901async fn test_manipulate_text(cx: &mut TestAppContext) {
 3902    init_test(cx, |_| {});
 3903
 3904    let mut cx = EditorTestContext::new(cx).await;
 3905
 3906    // Test convert_to_upper_case()
 3907    cx.set_state(indoc! {"
 3908        «hello worldˇ»
 3909    "});
 3910    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3911    cx.assert_editor_state(indoc! {"
 3912        «HELLO WORLDˇ»
 3913    "});
 3914
 3915    // Test convert_to_lower_case()
 3916    cx.set_state(indoc! {"
 3917        «HELLO WORLDˇ»
 3918    "});
 3919    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3920    cx.assert_editor_state(indoc! {"
 3921        «hello worldˇ»
 3922    "});
 3923
 3924    // Test multiple line, single selection case
 3925    cx.set_state(indoc! {"
 3926        «The quick brown
 3927        fox jumps over
 3928        the lazy dogˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «The Quick Brown
 3933        Fox Jumps Over
 3934        The Lazy Dogˇ»
 3935    "});
 3936
 3937    // Test multiple line, single selection case
 3938    cx.set_state(indoc! {"
 3939        «The quick brown
 3940        fox jumps over
 3941        the lazy dogˇ»
 3942    "});
 3943    cx.update_editor(|e, window, cx| {
 3944        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3945    });
 3946    cx.assert_editor_state(indoc! {"
 3947        «TheQuickBrown
 3948        FoxJumpsOver
 3949        TheLazyDogˇ»
 3950    "});
 3951
 3952    // From here on out, test more complex cases of manipulate_text()
 3953
 3954    // Test no selection case - should affect words cursors are in
 3955    // Cursor at beginning, middle, and end of word
 3956    cx.set_state(indoc! {"
 3957        ˇhello big beauˇtiful worldˇ
 3958    "});
 3959    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3960    cx.assert_editor_state(indoc! {"
 3961        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3962    "});
 3963
 3964    // Test multiple selections on a single line and across multiple lines
 3965    cx.set_state(indoc! {"
 3966        «Theˇ» quick «brown
 3967        foxˇ» jumps «overˇ»
 3968        the «lazyˇ» dog
 3969    "});
 3970    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3971    cx.assert_editor_state(indoc! {"
 3972        «THEˇ» quick «BROWN
 3973        FOXˇ» jumps «OVERˇ»
 3974        the «LAZYˇ» dog
 3975    "});
 3976
 3977    // Test case where text length grows
 3978    cx.set_state(indoc! {"
 3979        «tschüߡ»
 3980    "});
 3981    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3982    cx.assert_editor_state(indoc! {"
 3983        «TSCHÜSSˇ»
 3984    "});
 3985
 3986    // Test to make sure we don't crash when text shrinks
 3987    cx.set_state(indoc! {"
 3988        aaa_bbbˇ
 3989    "});
 3990    cx.update_editor(|e, window, cx| {
 3991        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3992    });
 3993    cx.assert_editor_state(indoc! {"
 3994        «aaaBbbˇ»
 3995    "});
 3996
 3997    // Test to make sure we all aware of the fact that each word can grow and shrink
 3998    // Final selections should be aware of this fact
 3999    cx.set_state(indoc! {"
 4000        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4001    "});
 4002    cx.update_editor(|e, window, cx| {
 4003        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4004    });
 4005    cx.assert_editor_state(indoc! {"
 4006        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4007    "});
 4008
 4009    cx.set_state(indoc! {"
 4010        «hElLo, WoRld!ˇ»
 4011    "});
 4012    cx.update_editor(|e, window, cx| {
 4013        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4014    });
 4015    cx.assert_editor_state(indoc! {"
 4016        «HeLlO, wOrLD!ˇ»
 4017    "});
 4018}
 4019
 4020#[gpui::test]
 4021fn test_duplicate_line(cx: &mut TestAppContext) {
 4022    init_test(cx, |_| {});
 4023
 4024    let editor = cx.add_window(|window, cx| {
 4025        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4026        build_editor(buffer, window, cx)
 4027    });
 4028    _ = editor.update(cx, |editor, window, cx| {
 4029        editor.change_selections(None, window, cx, |s| {
 4030            s.select_display_ranges([
 4031                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4032                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4033                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4034                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4035            ])
 4036        });
 4037        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4038        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4039        assert_eq!(
 4040            editor.selections.display_ranges(cx),
 4041            vec![
 4042                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4043                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4044                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4045                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4046            ]
 4047        );
 4048    });
 4049
 4050    let editor = cx.add_window(|window, cx| {
 4051        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4052        build_editor(buffer, window, cx)
 4053    });
 4054    _ = editor.update(cx, |editor, window, cx| {
 4055        editor.change_selections(None, window, cx, |s| {
 4056            s.select_display_ranges([
 4057                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4058                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4059            ])
 4060        });
 4061        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4062        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4063        assert_eq!(
 4064            editor.selections.display_ranges(cx),
 4065            vec![
 4066                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4067                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4068            ]
 4069        );
 4070    });
 4071
 4072    // With `move_upwards` the selections stay in place, except for
 4073    // the lines inserted above them
 4074    let editor = cx.add_window(|window, cx| {
 4075        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4076        build_editor(buffer, window, cx)
 4077    });
 4078    _ = editor.update(cx, |editor, window, cx| {
 4079        editor.change_selections(None, window, cx, |s| {
 4080            s.select_display_ranges([
 4081                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4082                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4083                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4084                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4085            ])
 4086        });
 4087        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4088        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4089        assert_eq!(
 4090            editor.selections.display_ranges(cx),
 4091            vec![
 4092                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4093                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4094                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4095                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4096            ]
 4097        );
 4098    });
 4099
 4100    let editor = cx.add_window(|window, cx| {
 4101        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4102        build_editor(buffer, window, cx)
 4103    });
 4104    _ = editor.update(cx, |editor, window, cx| {
 4105        editor.change_selections(None, window, cx, |s| {
 4106            s.select_display_ranges([
 4107                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4108                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4109            ])
 4110        });
 4111        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4112        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4113        assert_eq!(
 4114            editor.selections.display_ranges(cx),
 4115            vec![
 4116                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4117                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4118            ]
 4119        );
 4120    });
 4121
 4122    let editor = cx.add_window(|window, cx| {
 4123        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4124        build_editor(buffer, window, cx)
 4125    });
 4126    _ = editor.update(cx, |editor, window, cx| {
 4127        editor.change_selections(None, window, cx, |s| {
 4128            s.select_display_ranges([
 4129                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4130                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4131            ])
 4132        });
 4133        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4134        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4135        assert_eq!(
 4136            editor.selections.display_ranges(cx),
 4137            vec![
 4138                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4139                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4140            ]
 4141        );
 4142    });
 4143}
 4144
 4145#[gpui::test]
 4146fn test_move_line_up_down(cx: &mut TestAppContext) {
 4147    init_test(cx, |_| {});
 4148
 4149    let editor = cx.add_window(|window, cx| {
 4150        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4151        build_editor(buffer, window, cx)
 4152    });
 4153    _ = editor.update(cx, |editor, window, cx| {
 4154        editor.fold_creases(
 4155            vec![
 4156                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4157                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4158                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4159            ],
 4160            true,
 4161            window,
 4162            cx,
 4163        );
 4164        editor.change_selections(None, window, cx, |s| {
 4165            s.select_display_ranges([
 4166                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4167                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4168                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4169                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4170            ])
 4171        });
 4172        assert_eq!(
 4173            editor.display_text(cx),
 4174            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4175        );
 4176
 4177        editor.move_line_up(&MoveLineUp, window, cx);
 4178        assert_eq!(
 4179            editor.display_text(cx),
 4180            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4181        );
 4182        assert_eq!(
 4183            editor.selections.display_ranges(cx),
 4184            vec![
 4185                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4186                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4187                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4188                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4189            ]
 4190        );
 4191    });
 4192
 4193    _ = editor.update(cx, |editor, window, cx| {
 4194        editor.move_line_down(&MoveLineDown, window, cx);
 4195        assert_eq!(
 4196            editor.display_text(cx),
 4197            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4198        );
 4199        assert_eq!(
 4200            editor.selections.display_ranges(cx),
 4201            vec![
 4202                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4206            ]
 4207        );
 4208    });
 4209
 4210    _ = editor.update(cx, |editor, window, cx| {
 4211        editor.move_line_down(&MoveLineDown, window, cx);
 4212        assert_eq!(
 4213            editor.display_text(cx),
 4214            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4215        );
 4216        assert_eq!(
 4217            editor.selections.display_ranges(cx),
 4218            vec![
 4219                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4220                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4221                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4222                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4223            ]
 4224        );
 4225    });
 4226
 4227    _ = editor.update(cx, |editor, window, cx| {
 4228        editor.move_line_up(&MoveLineUp, window, cx);
 4229        assert_eq!(
 4230            editor.display_text(cx),
 4231            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4232        );
 4233        assert_eq!(
 4234            editor.selections.display_ranges(cx),
 4235            vec![
 4236                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4237                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4238                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4239                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4240            ]
 4241        );
 4242    });
 4243}
 4244
 4245#[gpui::test]
 4246fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4247    init_test(cx, |_| {});
 4248
 4249    let editor = cx.add_window(|window, cx| {
 4250        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4251        build_editor(buffer, window, cx)
 4252    });
 4253    _ = editor.update(cx, |editor, window, cx| {
 4254        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4255        editor.insert_blocks(
 4256            [BlockProperties {
 4257                style: BlockStyle::Fixed,
 4258                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4259                height: 1,
 4260                render: Arc::new(|_| div().into_any()),
 4261                priority: 0,
 4262            }],
 4263            Some(Autoscroll::fit()),
 4264            cx,
 4265        );
 4266        editor.change_selections(None, window, cx, |s| {
 4267            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4268        });
 4269        editor.move_line_down(&MoveLineDown, window, cx);
 4270    });
 4271}
 4272
 4273#[gpui::test]
 4274async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4275    init_test(cx, |_| {});
 4276
 4277    let mut cx = EditorTestContext::new(cx).await;
 4278    cx.set_state(
 4279        &"
 4280            ˇzero
 4281            one
 4282            two
 4283            three
 4284            four
 4285            five
 4286        "
 4287        .unindent(),
 4288    );
 4289
 4290    // Create a four-line block that replaces three lines of text.
 4291    cx.update_editor(|editor, window, cx| {
 4292        let snapshot = editor.snapshot(window, cx);
 4293        let snapshot = &snapshot.buffer_snapshot;
 4294        let placement = BlockPlacement::Replace(
 4295            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4296        );
 4297        editor.insert_blocks(
 4298            [BlockProperties {
 4299                placement,
 4300                height: 4,
 4301                style: BlockStyle::Sticky,
 4302                render: Arc::new(|_| gpui::div().into_any_element()),
 4303                priority: 0,
 4304            }],
 4305            None,
 4306            cx,
 4307        );
 4308    });
 4309
 4310    // Move down so that the cursor touches the block.
 4311    cx.update_editor(|editor, window, cx| {
 4312        editor.move_down(&Default::default(), window, cx);
 4313    });
 4314    cx.assert_editor_state(
 4315        &"
 4316            zero
 4317            «one
 4318            two
 4319            threeˇ»
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Move down past the block.
 4327    cx.update_editor(|editor, window, cx| {
 4328        editor.move_down(&Default::default(), window, cx);
 4329    });
 4330    cx.assert_editor_state(
 4331        &"
 4332            zero
 4333            one
 4334            two
 4335            three
 4336            ˇfour
 4337            five
 4338        "
 4339        .unindent(),
 4340    );
 4341}
 4342
 4343#[gpui::test]
 4344fn test_transpose(cx: &mut TestAppContext) {
 4345    init_test(cx, |_| {});
 4346
 4347    _ = cx.add_window(|window, cx| {
 4348        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4349        editor.set_style(EditorStyle::default(), window, cx);
 4350        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4351        editor.transpose(&Default::default(), window, cx);
 4352        assert_eq!(editor.text(cx), "bac");
 4353        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4354
 4355        editor.transpose(&Default::default(), window, cx);
 4356        assert_eq!(editor.text(cx), "bca");
 4357        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4358
 4359        editor.transpose(&Default::default(), window, cx);
 4360        assert_eq!(editor.text(cx), "bac");
 4361        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4362
 4363        editor
 4364    });
 4365
 4366    _ = cx.add_window(|window, cx| {
 4367        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4368        editor.set_style(EditorStyle::default(), window, cx);
 4369        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4370        editor.transpose(&Default::default(), window, cx);
 4371        assert_eq!(editor.text(cx), "acb\nde");
 4372        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4373
 4374        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4375        editor.transpose(&Default::default(), window, cx);
 4376        assert_eq!(editor.text(cx), "acbd\ne");
 4377        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4378
 4379        editor.transpose(&Default::default(), window, cx);
 4380        assert_eq!(editor.text(cx), "acbde\n");
 4381        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4382
 4383        editor.transpose(&Default::default(), window, cx);
 4384        assert_eq!(editor.text(cx), "acbd\ne");
 4385        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4386
 4387        editor
 4388    });
 4389
 4390    _ = cx.add_window(|window, cx| {
 4391        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4392        editor.set_style(EditorStyle::default(), window, cx);
 4393        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4394        editor.transpose(&Default::default(), window, cx);
 4395        assert_eq!(editor.text(cx), "bacd\ne");
 4396        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4397
 4398        editor.transpose(&Default::default(), window, cx);
 4399        assert_eq!(editor.text(cx), "bcade\n");
 4400        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4401
 4402        editor.transpose(&Default::default(), window, cx);
 4403        assert_eq!(editor.text(cx), "bcda\ne");
 4404        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4405
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "bcade\n");
 4408        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4409
 4410        editor.transpose(&Default::default(), window, cx);
 4411        assert_eq!(editor.text(cx), "bcaed\n");
 4412        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4413
 4414        editor
 4415    });
 4416
 4417    _ = cx.add_window(|window, cx| {
 4418        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4419        editor.set_style(EditorStyle::default(), window, cx);
 4420        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4421        editor.transpose(&Default::default(), window, cx);
 4422        assert_eq!(editor.text(cx), "🏀🍐✋");
 4423        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4424
 4425        editor.transpose(&Default::default(), window, cx);
 4426        assert_eq!(editor.text(cx), "🏀✋🍐");
 4427        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4428
 4429        editor.transpose(&Default::default(), window, cx);
 4430        assert_eq!(editor.text(cx), "🏀🍐✋");
 4431        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4432
 4433        editor
 4434    });
 4435}
 4436
 4437#[gpui::test]
 4438async fn test_rewrap(cx: &mut TestAppContext) {
 4439    init_test(cx, |settings| {
 4440        settings.languages.extend([
 4441            (
 4442                "Markdown".into(),
 4443                LanguageSettingsContent {
 4444                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4445                    ..Default::default()
 4446                },
 4447            ),
 4448            (
 4449                "Plain Text".into(),
 4450                LanguageSettingsContent {
 4451                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4452                    ..Default::default()
 4453                },
 4454            ),
 4455        ])
 4456    });
 4457
 4458    let mut cx = EditorTestContext::new(cx).await;
 4459
 4460    let language_with_c_comments = Arc::new(Language::new(
 4461        LanguageConfig {
 4462            line_comments: vec!["// ".into()],
 4463            ..LanguageConfig::default()
 4464        },
 4465        None,
 4466    ));
 4467    let language_with_pound_comments = Arc::new(Language::new(
 4468        LanguageConfig {
 4469            line_comments: vec!["# ".into()],
 4470            ..LanguageConfig::default()
 4471        },
 4472        None,
 4473    ));
 4474    let markdown_language = Arc::new(Language::new(
 4475        LanguageConfig {
 4476            name: "Markdown".into(),
 4477            ..LanguageConfig::default()
 4478        },
 4479        None,
 4480    ));
 4481    let language_with_doc_comments = Arc::new(Language::new(
 4482        LanguageConfig {
 4483            line_comments: vec!["// ".into(), "/// ".into()],
 4484            ..LanguageConfig::default()
 4485        },
 4486        Some(tree_sitter_rust::LANGUAGE.into()),
 4487    ));
 4488
 4489    let plaintext_language = Arc::new(Language::new(
 4490        LanguageConfig {
 4491            name: "Plain Text".into(),
 4492            ..LanguageConfig::default()
 4493        },
 4494        None,
 4495    ));
 4496
 4497    assert_rewrap(
 4498        indoc! {"
 4499            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4500        "},
 4501        indoc! {"
 4502            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4503            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4504            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4505            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4506            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4507            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4508            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4509            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4510            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4511            // porttitor id. Aliquam id accumsan eros.
 4512        "},
 4513        language_with_c_comments.clone(),
 4514        &mut cx,
 4515    );
 4516
 4517    // Test that rewrapping works inside of a selection
 4518    assert_rewrap(
 4519        indoc! {"
 4520            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4521        "},
 4522        indoc! {"
 4523            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4524            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4525            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4526            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4527            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4528            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4529            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4530            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4531            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4532            // porttitor id. Aliquam id accumsan eros.ˇ»
 4533        "},
 4534        language_with_c_comments.clone(),
 4535        &mut cx,
 4536    );
 4537
 4538    // Test that cursors that expand to the same region are collapsed.
 4539    assert_rewrap(
 4540        indoc! {"
 4541            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4542            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4543            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4544            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4545        "},
 4546        indoc! {"
 4547            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4548            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4549            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4550            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4551            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4552            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4553            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4554            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4555            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4556            // porttitor id. Aliquam id accumsan eros.
 4557        "},
 4558        language_with_c_comments.clone(),
 4559        &mut cx,
 4560    );
 4561
 4562    // Test that non-contiguous selections are treated separately.
 4563    assert_rewrap(
 4564        indoc! {"
 4565            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4566            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4567            //
 4568            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4569            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4570        "},
 4571        indoc! {"
 4572            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4573            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4574            // auctor, eu lacinia sapien scelerisque.
 4575            //
 4576            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4577            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4578            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4579            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4580            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4581            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4582            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4583        "},
 4584        language_with_c_comments.clone(),
 4585        &mut cx,
 4586    );
 4587
 4588    // Test that different comment prefixes are supported.
 4589    assert_rewrap(
 4590        indoc! {"
 4591            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4592        "},
 4593        indoc! {"
 4594            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4595            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4596            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4597            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4598            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4599            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4600            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4601            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4602            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4603            # accumsan eros.
 4604        "},
 4605        language_with_pound_comments.clone(),
 4606        &mut cx,
 4607    );
 4608
 4609    // Test that rewrapping is ignored outside of comments in most languages.
 4610    assert_rewrap(
 4611        indoc! {"
 4612            /// Adds two numbers.
 4613            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4614            fn add(a: u32, b: u32) -> u32 {
 4615                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4616            }
 4617        "},
 4618        indoc! {"
 4619            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4620            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4621            fn add(a: u32, b: u32) -> u32 {
 4622                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4623            }
 4624        "},
 4625        language_with_doc_comments.clone(),
 4626        &mut cx,
 4627    );
 4628
 4629    // Test that rewrapping works in Markdown and Plain Text languages.
 4630    assert_rewrap(
 4631        indoc! {"
 4632            # Hello
 4633
 4634            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4635        "},
 4636        indoc! {"
 4637            # Hello
 4638
 4639            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4640            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4641            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4642            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4643            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4644            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4645            Integer sit amet scelerisque nisi.
 4646        "},
 4647        markdown_language,
 4648        &mut cx,
 4649    );
 4650
 4651    assert_rewrap(
 4652        indoc! {"
 4653            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4654        "},
 4655        indoc! {"
 4656            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4657            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4658            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4659            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4660            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4661            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4662            Integer sit amet scelerisque nisi.
 4663        "},
 4664        plaintext_language,
 4665        &mut cx,
 4666    );
 4667
 4668    // Test rewrapping unaligned comments in a selection.
 4669    assert_rewrap(
 4670        indoc! {"
 4671            fn foo() {
 4672                if true {
 4673            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4674            // Praesent semper egestas tellus id dignissim.ˇ»
 4675                    do_something();
 4676                } else {
 4677                    //
 4678                }
 4679            }
 4680        "},
 4681        indoc! {"
 4682            fn foo() {
 4683                if true {
 4684            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4685                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4686                    // egestas tellus id dignissim.ˇ»
 4687                    do_something();
 4688                } else {
 4689                    //
 4690                }
 4691            }
 4692        "},
 4693        language_with_doc_comments.clone(),
 4694        &mut cx,
 4695    );
 4696
 4697    assert_rewrap(
 4698        indoc! {"
 4699            fn foo() {
 4700                if true {
 4701            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4702            // Praesent semper egestas tellus id dignissim.»
 4703                    do_something();
 4704                } else {
 4705                    //
 4706                }
 4707
 4708            }
 4709        "},
 4710        indoc! {"
 4711            fn foo() {
 4712                if true {
 4713            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4714                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4715                    // egestas tellus id dignissim.»
 4716                    do_something();
 4717                } else {
 4718                    //
 4719                }
 4720
 4721            }
 4722        "},
 4723        language_with_doc_comments.clone(),
 4724        &mut cx,
 4725    );
 4726
 4727    #[track_caller]
 4728    fn assert_rewrap(
 4729        unwrapped_text: &str,
 4730        wrapped_text: &str,
 4731        language: Arc<Language>,
 4732        cx: &mut EditorTestContext,
 4733    ) {
 4734        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4735        cx.set_state(unwrapped_text);
 4736        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4737        cx.assert_editor_state(wrapped_text);
 4738    }
 4739}
 4740
 4741#[gpui::test]
 4742async fn test_hard_wrap(cx: &mut TestAppContext) {
 4743    init_test(cx, |_| {});
 4744    let mut cx = EditorTestContext::new(cx).await;
 4745
 4746    cx.update_editor(|editor, _, cx| {
 4747        editor.set_hard_wrap(Some(14), cx);
 4748    });
 4749
 4750    cx.set_state(indoc!(
 4751        "
 4752        one two three ˇ
 4753        "
 4754    ));
 4755    cx.simulate_input("four");
 4756    cx.run_until_parked();
 4757
 4758    cx.assert_editor_state(indoc!(
 4759        "
 4760        one two three
 4761        fourˇ
 4762        "
 4763    ));
 4764}
 4765
 4766#[gpui::test]
 4767async fn test_clipboard(cx: &mut TestAppContext) {
 4768    init_test(cx, |_| {});
 4769
 4770    let mut cx = EditorTestContext::new(cx).await;
 4771
 4772    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4773    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4774    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4775
 4776    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4777    cx.set_state("two ˇfour ˇsix ˇ");
 4778    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4779    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4780
 4781    // Paste again but with only two cursors. Since the number of cursors doesn't
 4782    // match the number of slices in the clipboard, the entire clipboard text
 4783    // is pasted at each cursor.
 4784    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4785    cx.update_editor(|e, window, cx| {
 4786        e.handle_input("( ", window, cx);
 4787        e.paste(&Paste, window, cx);
 4788        e.handle_input(") ", window, cx);
 4789    });
 4790    cx.assert_editor_state(
 4791        &([
 4792            "( one✅ ",
 4793            "three ",
 4794            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4795            "three ",
 4796            "five ) ˇ",
 4797        ]
 4798        .join("\n")),
 4799    );
 4800
 4801    // Cut with three selections, one of which is full-line.
 4802    cx.set_state(indoc! {"
 4803        1«2ˇ»3
 4804        4ˇ567
 4805        «8ˇ»9"});
 4806    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4807    cx.assert_editor_state(indoc! {"
 4808        1ˇ3
 4809        ˇ9"});
 4810
 4811    // Paste with three selections, noticing how the copied selection that was full-line
 4812    // gets inserted before the second cursor.
 4813    cx.set_state(indoc! {"
 4814        1ˇ3
 4815 4816        «oˇ»ne"});
 4817    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4818    cx.assert_editor_state(indoc! {"
 4819        12ˇ3
 4820        4567
 4821 4822        8ˇne"});
 4823
 4824    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4825    cx.set_state(indoc! {"
 4826        The quick brown
 4827        fox juˇmps over
 4828        the lazy dog"});
 4829    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4830    assert_eq!(
 4831        cx.read_from_clipboard()
 4832            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4833        Some("fox jumps over\n".to_string())
 4834    );
 4835
 4836    // Paste with three selections, noticing how the copied full-line selection is inserted
 4837    // before the empty selections but replaces the selection that is non-empty.
 4838    cx.set_state(indoc! {"
 4839        Tˇhe quick brown
 4840        «foˇ»x jumps over
 4841        tˇhe lazy dog"});
 4842    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4843    cx.assert_editor_state(indoc! {"
 4844        fox jumps over
 4845        Tˇhe quick brown
 4846        fox jumps over
 4847        ˇx jumps over
 4848        fox jumps over
 4849        tˇhe lazy dog"});
 4850}
 4851
 4852#[gpui::test]
 4853async fn test_paste_multiline(cx: &mut TestAppContext) {
 4854    init_test(cx, |_| {});
 4855
 4856    let mut cx = EditorTestContext::new(cx).await;
 4857    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4858
 4859    // Cut an indented block, without the leading whitespace.
 4860    cx.set_state(indoc! {"
 4861        const a: B = (
 4862            c(),
 4863            «d(
 4864                e,
 4865                f
 4866            )ˇ»
 4867        );
 4868    "});
 4869    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4870    cx.assert_editor_state(indoc! {"
 4871        const a: B = (
 4872            c(),
 4873            ˇ
 4874        );
 4875    "});
 4876
 4877    // Paste it at the same position.
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state(indoc! {"
 4880        const a: B = (
 4881            c(),
 4882            d(
 4883                e,
 4884                f
 4885 4886        );
 4887    "});
 4888
 4889    // Paste it at a line with a lower indent level.
 4890    cx.set_state(indoc! {"
 4891        ˇ
 4892        const a: B = (
 4893            c(),
 4894        );
 4895    "});
 4896    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4897    cx.assert_editor_state(indoc! {"
 4898        d(
 4899            e,
 4900            f
 4901 4902        const a: B = (
 4903            c(),
 4904        );
 4905    "});
 4906
 4907    // Cut an indented block, with the leading whitespace.
 4908    cx.set_state(indoc! {"
 4909        const a: B = (
 4910            c(),
 4911        «    d(
 4912                e,
 4913                f
 4914            )
 4915        ˇ»);
 4916    "});
 4917    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        const a: B = (
 4920            c(),
 4921        ˇ);
 4922    "});
 4923
 4924    // Paste it at the same position.
 4925    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4926    cx.assert_editor_state(indoc! {"
 4927        const a: B = (
 4928            c(),
 4929            d(
 4930                e,
 4931                f
 4932            )
 4933        ˇ);
 4934    "});
 4935
 4936    // Paste it at a line with a higher indent level.
 4937    cx.set_state(indoc! {"
 4938        const a: B = (
 4939            c(),
 4940            d(
 4941                e,
 4942 4943            )
 4944        );
 4945    "});
 4946    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4947    cx.assert_editor_state(indoc! {"
 4948        const a: B = (
 4949            c(),
 4950            d(
 4951                e,
 4952                f    d(
 4953                    e,
 4954                    f
 4955                )
 4956        ˇ
 4957            )
 4958        );
 4959    "});
 4960
 4961    // Copy an indented block, starting mid-line
 4962    cx.set_state(indoc! {"
 4963        const a: B = (
 4964            c(),
 4965            somethin«g(
 4966                e,
 4967                f
 4968            )ˇ»
 4969        );
 4970    "});
 4971    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4972
 4973    // Paste it on a line with a lower indent level
 4974    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 4975    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4976    cx.assert_editor_state(indoc! {"
 4977        const a: B = (
 4978            c(),
 4979            something(
 4980                e,
 4981                f
 4982            )
 4983        );
 4984        g(
 4985            e,
 4986            f
 4987"});
 4988}
 4989
 4990#[gpui::test]
 4991async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4992    init_test(cx, |_| {});
 4993
 4994    cx.write_to_clipboard(ClipboardItem::new_string(
 4995        "    d(\n        e\n    );\n".into(),
 4996    ));
 4997
 4998    let mut cx = EditorTestContext::new(cx).await;
 4999    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5000
 5001    cx.set_state(indoc! {"
 5002        fn a() {
 5003            b();
 5004            if c() {
 5005                ˇ
 5006            }
 5007        }
 5008    "});
 5009
 5010    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5011    cx.assert_editor_state(indoc! {"
 5012        fn a() {
 5013            b();
 5014            if c() {
 5015                d(
 5016                    e
 5017                );
 5018        ˇ
 5019            }
 5020        }
 5021    "});
 5022
 5023    cx.set_state(indoc! {"
 5024        fn a() {
 5025            b();
 5026            ˇ
 5027        }
 5028    "});
 5029
 5030    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5031    cx.assert_editor_state(indoc! {"
 5032        fn a() {
 5033            b();
 5034            d(
 5035                e
 5036            );
 5037        ˇ
 5038        }
 5039    "});
 5040}
 5041
 5042#[gpui::test]
 5043fn test_select_all(cx: &mut TestAppContext) {
 5044    init_test(cx, |_| {});
 5045
 5046    let editor = cx.add_window(|window, cx| {
 5047        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5048        build_editor(buffer, window, cx)
 5049    });
 5050    _ = editor.update(cx, |editor, window, cx| {
 5051        editor.select_all(&SelectAll, window, cx);
 5052        assert_eq!(
 5053            editor.selections.display_ranges(cx),
 5054            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5055        );
 5056    });
 5057}
 5058
 5059#[gpui::test]
 5060fn test_select_line(cx: &mut TestAppContext) {
 5061    init_test(cx, |_| {});
 5062
 5063    let editor = cx.add_window(|window, cx| {
 5064        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5065        build_editor(buffer, window, cx)
 5066    });
 5067    _ = editor.update(cx, |editor, window, cx| {
 5068        editor.change_selections(None, window, cx, |s| {
 5069            s.select_display_ranges([
 5070                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5071                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5072                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5073                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5074            ])
 5075        });
 5076        editor.select_line(&SelectLine, window, cx);
 5077        assert_eq!(
 5078            editor.selections.display_ranges(cx),
 5079            vec![
 5080                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5081                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5082            ]
 5083        );
 5084    });
 5085
 5086    _ = editor.update(cx, |editor, window, cx| {
 5087        editor.select_line(&SelectLine, window, cx);
 5088        assert_eq!(
 5089            editor.selections.display_ranges(cx),
 5090            vec![
 5091                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5092                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5093            ]
 5094        );
 5095    });
 5096
 5097    _ = editor.update(cx, |editor, window, cx| {
 5098        editor.select_line(&SelectLine, window, cx);
 5099        assert_eq!(
 5100            editor.selections.display_ranges(cx),
 5101            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5102        );
 5103    });
 5104}
 5105
 5106#[gpui::test]
 5107async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5108    init_test(cx, |_| {});
 5109    let mut cx = EditorTestContext::new(cx).await;
 5110
 5111    #[track_caller]
 5112    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5113        cx.set_state(initial_state);
 5114        cx.update_editor(|e, window, cx| {
 5115            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5116        });
 5117        cx.assert_editor_state(expected_state);
 5118    }
 5119
 5120    // Selection starts and ends at the middle of lines, left-to-right
 5121    test(
 5122        &mut cx,
 5123        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5124        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5125    );
 5126    // Same thing, right-to-left
 5127    test(
 5128        &mut cx,
 5129        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5130        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5131    );
 5132
 5133    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5134    test(
 5135        &mut cx,
 5136        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5137        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5138    );
 5139    // Same thing, right-to-left
 5140    test(
 5141        &mut cx,
 5142        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5143        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5144    );
 5145
 5146    // Whole buffer, left-to-right, last line ends with newline
 5147    test(
 5148        &mut cx,
 5149        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5150        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5151    );
 5152    // Same thing, right-to-left
 5153    test(
 5154        &mut cx,
 5155        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5156        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5157    );
 5158
 5159    // Starts at the end of a line, ends at the start of another
 5160    test(
 5161        &mut cx,
 5162        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5163        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5164    );
 5165}
 5166
 5167#[gpui::test]
 5168async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5169    init_test(cx, |_| {});
 5170
 5171    let editor = cx.add_window(|window, cx| {
 5172        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5173        build_editor(buffer, window, cx)
 5174    });
 5175
 5176    // setup
 5177    _ = editor.update(cx, |editor, window, cx| {
 5178        editor.fold_creases(
 5179            vec![
 5180                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5181                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5182                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5183            ],
 5184            true,
 5185            window,
 5186            cx,
 5187        );
 5188        assert_eq!(
 5189            editor.display_text(cx),
 5190            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5191        );
 5192    });
 5193
 5194    _ = editor.update(cx, |editor, window, cx| {
 5195        editor.change_selections(None, window, cx, |s| {
 5196            s.select_display_ranges([
 5197                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5198                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5199                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5200                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5201            ])
 5202        });
 5203        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5204        assert_eq!(
 5205            editor.display_text(cx),
 5206            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5207        );
 5208    });
 5209    EditorTestContext::for_editor(editor, cx)
 5210        .await
 5211        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5212
 5213    _ = editor.update(cx, |editor, window, cx| {
 5214        editor.change_selections(None, window, cx, |s| {
 5215            s.select_display_ranges([
 5216                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5217            ])
 5218        });
 5219        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5220        assert_eq!(
 5221            editor.display_text(cx),
 5222            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5223        );
 5224        assert_eq!(
 5225            editor.selections.display_ranges(cx),
 5226            [
 5227                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5228                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5229                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5230                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5231                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5232                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5233                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5234            ]
 5235        );
 5236    });
 5237    EditorTestContext::for_editor(editor, cx)
 5238        .await
 5239        .assert_editor_state(
 5240            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5241        );
 5242}
 5243
 5244#[gpui::test]
 5245async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5246    init_test(cx, |_| {});
 5247
 5248    let mut cx = EditorTestContext::new(cx).await;
 5249
 5250    cx.set_state(indoc!(
 5251        r#"abc
 5252           defˇghi
 5253
 5254           jk
 5255           nlmo
 5256           "#
 5257    ));
 5258
 5259    cx.update_editor(|editor, window, cx| {
 5260        editor.add_selection_above(&Default::default(), window, cx);
 5261    });
 5262
 5263    cx.assert_editor_state(indoc!(
 5264        r#"abcˇ
 5265           defˇghi
 5266
 5267           jk
 5268           nlmo
 5269           "#
 5270    ));
 5271
 5272    cx.update_editor(|editor, window, cx| {
 5273        editor.add_selection_above(&Default::default(), window, cx);
 5274    });
 5275
 5276    cx.assert_editor_state(indoc!(
 5277        r#"abcˇ
 5278            defˇghi
 5279
 5280            jk
 5281            nlmo
 5282            "#
 5283    ));
 5284
 5285    cx.update_editor(|editor, window, cx| {
 5286        editor.add_selection_below(&Default::default(), window, cx);
 5287    });
 5288
 5289    cx.assert_editor_state(indoc!(
 5290        r#"abc
 5291           defˇghi
 5292
 5293           jk
 5294           nlmo
 5295           "#
 5296    ));
 5297
 5298    cx.update_editor(|editor, window, cx| {
 5299        editor.undo_selection(&Default::default(), window, cx);
 5300    });
 5301
 5302    cx.assert_editor_state(indoc!(
 5303        r#"abcˇ
 5304           defˇghi
 5305
 5306           jk
 5307           nlmo
 5308           "#
 5309    ));
 5310
 5311    cx.update_editor(|editor, window, cx| {
 5312        editor.redo_selection(&Default::default(), window, cx);
 5313    });
 5314
 5315    cx.assert_editor_state(indoc!(
 5316        r#"abc
 5317           defˇghi
 5318
 5319           jk
 5320           nlmo
 5321           "#
 5322    ));
 5323
 5324    cx.update_editor(|editor, window, cx| {
 5325        editor.add_selection_below(&Default::default(), window, cx);
 5326    });
 5327
 5328    cx.assert_editor_state(indoc!(
 5329        r#"abc
 5330           defˇghi
 5331
 5332           jk
 5333           nlmˇo
 5334           "#
 5335    ));
 5336
 5337    cx.update_editor(|editor, window, cx| {
 5338        editor.add_selection_below(&Default::default(), window, cx);
 5339    });
 5340
 5341    cx.assert_editor_state(indoc!(
 5342        r#"abc
 5343           defˇghi
 5344
 5345           jk
 5346           nlmˇo
 5347           "#
 5348    ));
 5349
 5350    // change selections
 5351    cx.set_state(indoc!(
 5352        r#"abc
 5353           def«ˇg»hi
 5354
 5355           jk
 5356           nlmo
 5357           "#
 5358    ));
 5359
 5360    cx.update_editor(|editor, window, cx| {
 5361        editor.add_selection_below(&Default::default(), window, cx);
 5362    });
 5363
 5364    cx.assert_editor_state(indoc!(
 5365        r#"abc
 5366           def«ˇg»hi
 5367
 5368           jk
 5369           nlm«ˇo»
 5370           "#
 5371    ));
 5372
 5373    cx.update_editor(|editor, window, cx| {
 5374        editor.add_selection_below(&Default::default(), window, cx);
 5375    });
 5376
 5377    cx.assert_editor_state(indoc!(
 5378        r#"abc
 5379           def«ˇg»hi
 5380
 5381           jk
 5382           nlm«ˇo»
 5383           "#
 5384    ));
 5385
 5386    cx.update_editor(|editor, window, cx| {
 5387        editor.add_selection_above(&Default::default(), window, cx);
 5388    });
 5389
 5390    cx.assert_editor_state(indoc!(
 5391        r#"abc
 5392           def«ˇg»hi
 5393
 5394           jk
 5395           nlmo
 5396           "#
 5397    ));
 5398
 5399    cx.update_editor(|editor, window, cx| {
 5400        editor.add_selection_above(&Default::default(), window, cx);
 5401    });
 5402
 5403    cx.assert_editor_state(indoc!(
 5404        r#"abc
 5405           def«ˇg»hi
 5406
 5407           jk
 5408           nlmo
 5409           "#
 5410    ));
 5411
 5412    // Change selections again
 5413    cx.set_state(indoc!(
 5414        r#"a«bc
 5415           defgˇ»hi
 5416
 5417           jk
 5418           nlmo
 5419           "#
 5420    ));
 5421
 5422    cx.update_editor(|editor, window, cx| {
 5423        editor.add_selection_below(&Default::default(), window, cx);
 5424    });
 5425
 5426    cx.assert_editor_state(indoc!(
 5427        r#"a«bcˇ»
 5428           d«efgˇ»hi
 5429
 5430           j«kˇ»
 5431           nlmo
 5432           "#
 5433    ));
 5434
 5435    cx.update_editor(|editor, window, cx| {
 5436        editor.add_selection_below(&Default::default(), window, cx);
 5437    });
 5438    cx.assert_editor_state(indoc!(
 5439        r#"a«bcˇ»
 5440           d«efgˇ»hi
 5441
 5442           j«kˇ»
 5443           n«lmoˇ»
 5444           "#
 5445    ));
 5446    cx.update_editor(|editor, window, cx| {
 5447        editor.add_selection_above(&Default::default(), window, cx);
 5448    });
 5449
 5450    cx.assert_editor_state(indoc!(
 5451        r#"a«bcˇ»
 5452           d«efgˇ»hi
 5453
 5454           j«kˇ»
 5455           nlmo
 5456           "#
 5457    ));
 5458
 5459    // Change selections again
 5460    cx.set_state(indoc!(
 5461        r#"abc
 5462           d«ˇefghi
 5463
 5464           jk
 5465           nlm»o
 5466           "#
 5467    ));
 5468
 5469    cx.update_editor(|editor, window, cx| {
 5470        editor.add_selection_above(&Default::default(), window, cx);
 5471    });
 5472
 5473    cx.assert_editor_state(indoc!(
 5474        r#"a«ˇbc»
 5475           d«ˇef»ghi
 5476
 5477           j«ˇk»
 5478           n«ˇlm»o
 5479           "#
 5480    ));
 5481
 5482    cx.update_editor(|editor, window, cx| {
 5483        editor.add_selection_below(&Default::default(), window, cx);
 5484    });
 5485
 5486    cx.assert_editor_state(indoc!(
 5487        r#"abc
 5488           d«ˇef»ghi
 5489
 5490           j«ˇk»
 5491           n«ˇlm»o
 5492           "#
 5493    ));
 5494}
 5495
 5496#[gpui::test]
 5497async fn test_select_next(cx: &mut TestAppContext) {
 5498    init_test(cx, |_| {});
 5499
 5500    let mut cx = EditorTestContext::new(cx).await;
 5501    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5502
 5503    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5504        .unwrap();
 5505    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5506
 5507    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5508        .unwrap();
 5509    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5510
 5511    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5512    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5513
 5514    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5515    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5516
 5517    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5518        .unwrap();
 5519    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5520
 5521    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5522        .unwrap();
 5523    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5524}
 5525
 5526#[gpui::test]
 5527async fn test_select_all_matches(cx: &mut TestAppContext) {
 5528    init_test(cx, |_| {});
 5529
 5530    let mut cx = EditorTestContext::new(cx).await;
 5531
 5532    // Test caret-only selections
 5533    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5534    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5535        .unwrap();
 5536    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5537
 5538    // Test left-to-right selections
 5539    cx.set_state("abc\n«abcˇ»\nabc");
 5540    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5541        .unwrap();
 5542    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5543
 5544    // Test right-to-left selections
 5545    cx.set_state("abc\n«ˇabc»\nabc");
 5546    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5547        .unwrap();
 5548    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5549
 5550    // Test selecting whitespace with caret selection
 5551    cx.set_state("abc\nˇ   abc\nabc");
 5552    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5553        .unwrap();
 5554    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5555
 5556    // Test selecting whitespace with left-to-right selection
 5557    cx.set_state("abc\n«ˇ  »abc\nabc");
 5558    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5559        .unwrap();
 5560    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5561
 5562    // Test no matches with right-to-left selection
 5563    cx.set_state("abc\n«  ˇ»abc\nabc");
 5564    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5565        .unwrap();
 5566    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5567}
 5568
 5569#[gpui::test]
 5570async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5571    init_test(cx, |_| {});
 5572
 5573    let mut cx = EditorTestContext::new(cx).await;
 5574    cx.set_state(
 5575        r#"let foo = 2;
 5576lˇet foo = 2;
 5577let fooˇ = 2;
 5578let foo = 2;
 5579let foo = ˇ2;"#,
 5580    );
 5581
 5582    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5583        .unwrap();
 5584    cx.assert_editor_state(
 5585        r#"let foo = 2;
 5586«letˇ» foo = 2;
 5587let «fooˇ» = 2;
 5588let foo = 2;
 5589let foo = «2ˇ»;"#,
 5590    );
 5591
 5592    // noop for multiple selections with different contents
 5593    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5594        .unwrap();
 5595    cx.assert_editor_state(
 5596        r#"let foo = 2;
 5597«letˇ» foo = 2;
 5598let «fooˇ» = 2;
 5599let foo = 2;
 5600let foo = «2ˇ»;"#,
 5601    );
 5602}
 5603
 5604#[gpui::test]
 5605async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5606    init_test(cx, |_| {});
 5607
 5608    let mut cx =
 5609        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5610
 5611    cx.assert_editor_state(indoc! {"
 5612        ˇbbb
 5613        ccc
 5614
 5615        bbb
 5616        ccc
 5617        "});
 5618    cx.dispatch_action(SelectPrevious::default());
 5619    cx.assert_editor_state(indoc! {"
 5620                «bbbˇ»
 5621                ccc
 5622
 5623                bbb
 5624                ccc
 5625                "});
 5626    cx.dispatch_action(SelectPrevious::default());
 5627    cx.assert_editor_state(indoc! {"
 5628                «bbbˇ»
 5629                ccc
 5630
 5631                «bbbˇ»
 5632                ccc
 5633                "});
 5634}
 5635
 5636#[gpui::test]
 5637async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5638    init_test(cx, |_| {});
 5639
 5640    let mut cx = EditorTestContext::new(cx).await;
 5641    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5642
 5643    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5644        .unwrap();
 5645    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5646
 5647    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5648        .unwrap();
 5649    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5650
 5651    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5652    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5653
 5654    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5655    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5656
 5657    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5658        .unwrap();
 5659    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5660
 5661    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5662        .unwrap();
 5663    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5664
 5665    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5666        .unwrap();
 5667    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5668}
 5669
 5670#[gpui::test]
 5671async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5672    init_test(cx, |_| {});
 5673
 5674    let mut cx = EditorTestContext::new(cx).await;
 5675    cx.set_state("");
 5676
 5677    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5678        .unwrap();
 5679    cx.assert_editor_state("«aˇ»");
 5680    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5681        .unwrap();
 5682    cx.assert_editor_state("«aˇ»");
 5683}
 5684
 5685#[gpui::test]
 5686async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5687    init_test(cx, |_| {});
 5688
 5689    let mut cx = EditorTestContext::new(cx).await;
 5690    cx.set_state(
 5691        r#"let foo = 2;
 5692lˇet foo = 2;
 5693let fooˇ = 2;
 5694let foo = 2;
 5695let foo = ˇ2;"#,
 5696    );
 5697
 5698    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5699        .unwrap();
 5700    cx.assert_editor_state(
 5701        r#"let foo = 2;
 5702«letˇ» foo = 2;
 5703let «fooˇ» = 2;
 5704let foo = 2;
 5705let foo = «2ˇ»;"#,
 5706    );
 5707
 5708    // noop for multiple selections with different contents
 5709    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5710        .unwrap();
 5711    cx.assert_editor_state(
 5712        r#"let foo = 2;
 5713«letˇ» foo = 2;
 5714let «fooˇ» = 2;
 5715let foo = 2;
 5716let foo = «2ˇ»;"#,
 5717    );
 5718}
 5719
 5720#[gpui::test]
 5721async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5722    init_test(cx, |_| {});
 5723
 5724    let mut cx = EditorTestContext::new(cx).await;
 5725    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5726
 5727    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5728        .unwrap();
 5729    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5730
 5731    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5732        .unwrap();
 5733    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5734
 5735    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5736    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5737
 5738    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5739    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5740
 5741    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5742        .unwrap();
 5743    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5744
 5745    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5746        .unwrap();
 5747    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5748}
 5749
 5750#[gpui::test]
 5751async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5752    init_test(cx, |_| {});
 5753
 5754    let language = Arc::new(Language::new(
 5755        LanguageConfig::default(),
 5756        Some(tree_sitter_rust::LANGUAGE.into()),
 5757    ));
 5758
 5759    let text = r#"
 5760        use mod1::mod2::{mod3, mod4};
 5761
 5762        fn fn_1(param1: bool, param2: &str) {
 5763            let var1 = "text";
 5764        }
 5765    "#
 5766    .unindent();
 5767
 5768    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5769    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5770    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5771
 5772    editor
 5773        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5774        .await;
 5775
 5776    editor.update_in(cx, |editor, window, cx| {
 5777        editor.change_selections(None, window, cx, |s| {
 5778            s.select_display_ranges([
 5779                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5780                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5781                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5782            ]);
 5783        });
 5784        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5785    });
 5786    editor.update(cx, |editor, cx| {
 5787        assert_text_with_selections(
 5788            editor,
 5789            indoc! {r#"
 5790                use mod1::mod2::{mod3, «mod4ˇ»};
 5791
 5792                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5793                    let var1 = "«textˇ»";
 5794                }
 5795            "#},
 5796            cx,
 5797        );
 5798    });
 5799
 5800    editor.update_in(cx, |editor, window, cx| {
 5801        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5802    });
 5803    editor.update(cx, |editor, cx| {
 5804        assert_text_with_selections(
 5805            editor,
 5806            indoc! {r#"
 5807                use mod1::mod2::«{mod3, mod4}ˇ»;
 5808
 5809                «ˇfn fn_1(param1: bool, param2: &str) {
 5810                    let var1 = "text";
 5811 5812            "#},
 5813            cx,
 5814        );
 5815    });
 5816
 5817    editor.update_in(cx, |editor, window, cx| {
 5818        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5819    });
 5820    assert_eq!(
 5821        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5822        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5823    );
 5824
 5825    // Trying to expand the selected syntax node one more time has no effect.
 5826    editor.update_in(cx, |editor, window, cx| {
 5827        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5828    });
 5829    assert_eq!(
 5830        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5831        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5832    );
 5833
 5834    editor.update_in(cx, |editor, window, cx| {
 5835        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5836    });
 5837    editor.update(cx, |editor, cx| {
 5838        assert_text_with_selections(
 5839            editor,
 5840            indoc! {r#"
 5841                use mod1::mod2::«{mod3, mod4}ˇ»;
 5842
 5843                «ˇfn fn_1(param1: bool, param2: &str) {
 5844                    let var1 = "text";
 5845 5846            "#},
 5847            cx,
 5848        );
 5849    });
 5850
 5851    editor.update_in(cx, |editor, window, cx| {
 5852        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5853    });
 5854    editor.update(cx, |editor, cx| {
 5855        assert_text_with_selections(
 5856            editor,
 5857            indoc! {r#"
 5858                use mod1::mod2::{mod3, «mod4ˇ»};
 5859
 5860                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5861                    let var1 = "«textˇ»";
 5862                }
 5863            "#},
 5864            cx,
 5865        );
 5866    });
 5867
 5868    editor.update_in(cx, |editor, window, cx| {
 5869        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5870    });
 5871    editor.update(cx, |editor, cx| {
 5872        assert_text_with_selections(
 5873            editor,
 5874            indoc! {r#"
 5875                use mod1::mod2::{mod3, mo«ˇ»d4};
 5876
 5877                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5878                    let var1 = "te«ˇ»xt";
 5879                }
 5880            "#},
 5881            cx,
 5882        );
 5883    });
 5884
 5885    // Trying to shrink the selected syntax node one more time has no effect.
 5886    editor.update_in(cx, |editor, window, cx| {
 5887        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5888    });
 5889    editor.update_in(cx, |editor, _, cx| {
 5890        assert_text_with_selections(
 5891            editor,
 5892            indoc! {r#"
 5893                use mod1::mod2::{mod3, mo«ˇ»d4};
 5894
 5895                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5896                    let var1 = "te«ˇ»xt";
 5897                }
 5898            "#},
 5899            cx,
 5900        );
 5901    });
 5902
 5903    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5904    // a fold.
 5905    editor.update_in(cx, |editor, window, cx| {
 5906        editor.fold_creases(
 5907            vec![
 5908                Crease::simple(
 5909                    Point::new(0, 21)..Point::new(0, 24),
 5910                    FoldPlaceholder::test(),
 5911                ),
 5912                Crease::simple(
 5913                    Point::new(3, 20)..Point::new(3, 22),
 5914                    FoldPlaceholder::test(),
 5915                ),
 5916            ],
 5917            true,
 5918            window,
 5919            cx,
 5920        );
 5921        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5922    });
 5923    editor.update(cx, |editor, cx| {
 5924        assert_text_with_selections(
 5925            editor,
 5926            indoc! {r#"
 5927                use mod1::mod2::«{mod3, mod4}ˇ»;
 5928
 5929                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5930                    «let var1 = "text";ˇ»
 5931                }
 5932            "#},
 5933            cx,
 5934        );
 5935    });
 5936}
 5937
 5938#[gpui::test]
 5939async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5940    init_test(cx, |_| {});
 5941
 5942    let base_text = r#"
 5943        impl A {
 5944            // this is an uncommitted comment
 5945
 5946            fn b() {
 5947                c();
 5948            }
 5949
 5950            // this is another uncommitted comment
 5951
 5952            fn d() {
 5953                // e
 5954                // f
 5955            }
 5956        }
 5957
 5958        fn g() {
 5959            // h
 5960        }
 5961    "#
 5962    .unindent();
 5963
 5964    let text = r#"
 5965        ˇimpl A {
 5966
 5967            fn b() {
 5968                c();
 5969            }
 5970
 5971            fn d() {
 5972                // e
 5973                // f
 5974            }
 5975        }
 5976
 5977        fn g() {
 5978            // h
 5979        }
 5980    "#
 5981    .unindent();
 5982
 5983    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5984    cx.set_state(&text);
 5985    cx.set_head_text(&base_text);
 5986    cx.update_editor(|editor, window, cx| {
 5987        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5988    });
 5989
 5990    cx.assert_state_with_diff(
 5991        "
 5992        ˇimpl A {
 5993      -     // this is an uncommitted comment
 5994
 5995            fn b() {
 5996                c();
 5997            }
 5998
 5999      -     // this is another uncommitted comment
 6000      -
 6001            fn d() {
 6002                // e
 6003                // f
 6004            }
 6005        }
 6006
 6007        fn g() {
 6008            // h
 6009        }
 6010    "
 6011        .unindent(),
 6012    );
 6013
 6014    let expected_display_text = "
 6015        impl A {
 6016            // this is an uncommitted comment
 6017
 6018            fn b() {
 6019 6020            }
 6021
 6022            // this is another uncommitted comment
 6023
 6024            fn d() {
 6025 6026            }
 6027        }
 6028
 6029        fn g() {
 6030 6031        }
 6032        "
 6033    .unindent();
 6034
 6035    cx.update_editor(|editor, window, cx| {
 6036        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6037        assert_eq!(editor.display_text(cx), expected_display_text);
 6038    });
 6039}
 6040
 6041#[gpui::test]
 6042async fn test_autoindent(cx: &mut TestAppContext) {
 6043    init_test(cx, |_| {});
 6044
 6045    let language = Arc::new(
 6046        Language::new(
 6047            LanguageConfig {
 6048                brackets: BracketPairConfig {
 6049                    pairs: vec![
 6050                        BracketPair {
 6051                            start: "{".to_string(),
 6052                            end: "}".to_string(),
 6053                            close: false,
 6054                            surround: false,
 6055                            newline: true,
 6056                        },
 6057                        BracketPair {
 6058                            start: "(".to_string(),
 6059                            end: ")".to_string(),
 6060                            close: false,
 6061                            surround: false,
 6062                            newline: true,
 6063                        },
 6064                    ],
 6065                    ..Default::default()
 6066                },
 6067                ..Default::default()
 6068            },
 6069            Some(tree_sitter_rust::LANGUAGE.into()),
 6070        )
 6071        .with_indents_query(
 6072            r#"
 6073                (_ "(" ")" @end) @indent
 6074                (_ "{" "}" @end) @indent
 6075            "#,
 6076        )
 6077        .unwrap(),
 6078    );
 6079
 6080    let text = "fn a() {}";
 6081
 6082    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6083    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6084    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6085    editor
 6086        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6087        .await;
 6088
 6089    editor.update_in(cx, |editor, window, cx| {
 6090        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6091        editor.newline(&Newline, window, cx);
 6092        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6093        assert_eq!(
 6094            editor.selections.ranges(cx),
 6095            &[
 6096                Point::new(1, 4)..Point::new(1, 4),
 6097                Point::new(3, 4)..Point::new(3, 4),
 6098                Point::new(5, 0)..Point::new(5, 0)
 6099            ]
 6100        );
 6101    });
 6102}
 6103
 6104#[gpui::test]
 6105async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6106    init_test(cx, |_| {});
 6107
 6108    {
 6109        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6110        cx.set_state(indoc! {"
 6111            impl A {
 6112
 6113                fn b() {}
 6114
 6115            «fn c() {
 6116
 6117            }ˇ»
 6118            }
 6119        "});
 6120
 6121        cx.update_editor(|editor, window, cx| {
 6122            editor.autoindent(&Default::default(), window, cx);
 6123        });
 6124
 6125        cx.assert_editor_state(indoc! {"
 6126            impl A {
 6127
 6128                fn b() {}
 6129
 6130                «fn c() {
 6131
 6132                }ˇ»
 6133            }
 6134        "});
 6135    }
 6136
 6137    {
 6138        let mut cx = EditorTestContext::new_multibuffer(
 6139            cx,
 6140            [indoc! { "
 6141                impl A {
 6142                «
 6143                // a
 6144                fn b(){}
 6145                »
 6146                «
 6147                    }
 6148                    fn c(){}
 6149                »
 6150            "}],
 6151        );
 6152
 6153        let buffer = cx.update_editor(|editor, _, cx| {
 6154            let buffer = editor.buffer().update(cx, |buffer, _| {
 6155                buffer.all_buffers().iter().next().unwrap().clone()
 6156            });
 6157            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6158            buffer
 6159        });
 6160
 6161        cx.run_until_parked();
 6162        cx.update_editor(|editor, window, cx| {
 6163            editor.select_all(&Default::default(), window, cx);
 6164            editor.autoindent(&Default::default(), window, cx)
 6165        });
 6166        cx.run_until_parked();
 6167
 6168        cx.update(|_, cx| {
 6169            pretty_assertions::assert_eq!(
 6170                buffer.read(cx).text(),
 6171                indoc! { "
 6172                    impl A {
 6173
 6174                        // a
 6175                        fn b(){}
 6176
 6177
 6178                    }
 6179                    fn c(){}
 6180
 6181                " }
 6182            )
 6183        });
 6184    }
 6185}
 6186
 6187#[gpui::test]
 6188async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6189    init_test(cx, |_| {});
 6190
 6191    let mut cx = EditorTestContext::new(cx).await;
 6192
 6193    let language = Arc::new(Language::new(
 6194        LanguageConfig {
 6195            brackets: BracketPairConfig {
 6196                pairs: vec![
 6197                    BracketPair {
 6198                        start: "{".to_string(),
 6199                        end: "}".to_string(),
 6200                        close: true,
 6201                        surround: true,
 6202                        newline: true,
 6203                    },
 6204                    BracketPair {
 6205                        start: "(".to_string(),
 6206                        end: ")".to_string(),
 6207                        close: true,
 6208                        surround: true,
 6209                        newline: true,
 6210                    },
 6211                    BracketPair {
 6212                        start: "/*".to_string(),
 6213                        end: " */".to_string(),
 6214                        close: true,
 6215                        surround: true,
 6216                        newline: true,
 6217                    },
 6218                    BracketPair {
 6219                        start: "[".to_string(),
 6220                        end: "]".to_string(),
 6221                        close: false,
 6222                        surround: false,
 6223                        newline: true,
 6224                    },
 6225                    BracketPair {
 6226                        start: "\"".to_string(),
 6227                        end: "\"".to_string(),
 6228                        close: true,
 6229                        surround: true,
 6230                        newline: false,
 6231                    },
 6232                    BracketPair {
 6233                        start: "<".to_string(),
 6234                        end: ">".to_string(),
 6235                        close: false,
 6236                        surround: true,
 6237                        newline: true,
 6238                    },
 6239                ],
 6240                ..Default::default()
 6241            },
 6242            autoclose_before: "})]".to_string(),
 6243            ..Default::default()
 6244        },
 6245        Some(tree_sitter_rust::LANGUAGE.into()),
 6246    ));
 6247
 6248    cx.language_registry().add(language.clone());
 6249    cx.update_buffer(|buffer, cx| {
 6250        buffer.set_language(Some(language), cx);
 6251    });
 6252
 6253    cx.set_state(
 6254        &r#"
 6255            🏀ˇ
 6256            εˇ
 6257            ❤️ˇ
 6258        "#
 6259        .unindent(),
 6260    );
 6261
 6262    // autoclose multiple nested brackets at multiple cursors
 6263    cx.update_editor(|editor, window, cx| {
 6264        editor.handle_input("{", window, cx);
 6265        editor.handle_input("{", window, cx);
 6266        editor.handle_input("{", window, cx);
 6267    });
 6268    cx.assert_editor_state(
 6269        &"
 6270            🏀{{{ˇ}}}
 6271            ε{{{ˇ}}}
 6272            ❤️{{{ˇ}}}
 6273        "
 6274        .unindent(),
 6275    );
 6276
 6277    // insert a different closing bracket
 6278    cx.update_editor(|editor, window, cx| {
 6279        editor.handle_input(")", window, cx);
 6280    });
 6281    cx.assert_editor_state(
 6282        &"
 6283            🏀{{{)ˇ}}}
 6284            ε{{{)ˇ}}}
 6285            ❤️{{{)ˇ}}}
 6286        "
 6287        .unindent(),
 6288    );
 6289
 6290    // skip over the auto-closed brackets when typing a closing bracket
 6291    cx.update_editor(|editor, window, cx| {
 6292        editor.move_right(&MoveRight, window, cx);
 6293        editor.handle_input("}", window, cx);
 6294        editor.handle_input("}", window, cx);
 6295        editor.handle_input("}", window, cx);
 6296    });
 6297    cx.assert_editor_state(
 6298        &"
 6299            🏀{{{)}}}}ˇ
 6300            ε{{{)}}}}ˇ
 6301            ❤️{{{)}}}}ˇ
 6302        "
 6303        .unindent(),
 6304    );
 6305
 6306    // autoclose multi-character pairs
 6307    cx.set_state(
 6308        &"
 6309            ˇ
 6310            ˇ
 6311        "
 6312        .unindent(),
 6313    );
 6314    cx.update_editor(|editor, window, cx| {
 6315        editor.handle_input("/", window, cx);
 6316        editor.handle_input("*", window, cx);
 6317    });
 6318    cx.assert_editor_state(
 6319        &"
 6320            /*ˇ */
 6321            /*ˇ */
 6322        "
 6323        .unindent(),
 6324    );
 6325
 6326    // one cursor autocloses a multi-character pair, one cursor
 6327    // does not autoclose.
 6328    cx.set_state(
 6329        &"
 6330 6331            ˇ
 6332        "
 6333        .unindent(),
 6334    );
 6335    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6336    cx.assert_editor_state(
 6337        &"
 6338            /*ˇ */
 6339 6340        "
 6341        .unindent(),
 6342    );
 6343
 6344    // Don't autoclose if the next character isn't whitespace and isn't
 6345    // listed in the language's "autoclose_before" section.
 6346    cx.set_state("ˇa b");
 6347    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6348    cx.assert_editor_state("{ˇa b");
 6349
 6350    // Don't autoclose if `close` is false for the bracket pair
 6351    cx.set_state("ˇ");
 6352    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6353    cx.assert_editor_state("");
 6354
 6355    // Surround with brackets if text is selected
 6356    cx.set_state("«aˇ» b");
 6357    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6358    cx.assert_editor_state("{«aˇ»} b");
 6359
 6360    // Autoclose when not immediately after a word character
 6361    cx.set_state("a ˇ");
 6362    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6363    cx.assert_editor_state("a \"ˇ\"");
 6364
 6365    // Autoclose pair where the start and end characters are the same
 6366    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6367    cx.assert_editor_state("a \"\"ˇ");
 6368
 6369    // Don't autoclose when immediately after a word character
 6370    cx.set_state("");
 6371    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6372    cx.assert_editor_state("a\"ˇ");
 6373
 6374    // Do autoclose when after a non-word character
 6375    cx.set_state("");
 6376    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6377    cx.assert_editor_state("{\"ˇ\"");
 6378
 6379    // Non identical pairs autoclose regardless of preceding character
 6380    cx.set_state("");
 6381    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6382    cx.assert_editor_state("a{ˇ}");
 6383
 6384    // Don't autoclose pair if autoclose is disabled
 6385    cx.set_state("ˇ");
 6386    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6387    cx.assert_editor_state("");
 6388
 6389    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6390    cx.set_state("«aˇ» b");
 6391    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6392    cx.assert_editor_state("<«aˇ»> b");
 6393}
 6394
 6395#[gpui::test]
 6396async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6397    init_test(cx, |settings| {
 6398        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6399    });
 6400
 6401    let mut cx = EditorTestContext::new(cx).await;
 6402
 6403    let language = Arc::new(Language::new(
 6404        LanguageConfig {
 6405            brackets: BracketPairConfig {
 6406                pairs: vec![
 6407                    BracketPair {
 6408                        start: "{".to_string(),
 6409                        end: "}".to_string(),
 6410                        close: true,
 6411                        surround: true,
 6412                        newline: true,
 6413                    },
 6414                    BracketPair {
 6415                        start: "(".to_string(),
 6416                        end: ")".to_string(),
 6417                        close: true,
 6418                        surround: true,
 6419                        newline: true,
 6420                    },
 6421                    BracketPair {
 6422                        start: "[".to_string(),
 6423                        end: "]".to_string(),
 6424                        close: false,
 6425                        surround: false,
 6426                        newline: true,
 6427                    },
 6428                ],
 6429                ..Default::default()
 6430            },
 6431            autoclose_before: "})]".to_string(),
 6432            ..Default::default()
 6433        },
 6434        Some(tree_sitter_rust::LANGUAGE.into()),
 6435    ));
 6436
 6437    cx.language_registry().add(language.clone());
 6438    cx.update_buffer(|buffer, cx| {
 6439        buffer.set_language(Some(language), cx);
 6440    });
 6441
 6442    cx.set_state(
 6443        &"
 6444            ˇ
 6445            ˇ
 6446            ˇ
 6447        "
 6448        .unindent(),
 6449    );
 6450
 6451    // ensure only matching closing brackets are skipped over
 6452    cx.update_editor(|editor, window, cx| {
 6453        editor.handle_input("}", window, cx);
 6454        editor.move_left(&MoveLeft, window, cx);
 6455        editor.handle_input(")", window, cx);
 6456        editor.move_left(&MoveLeft, window, cx);
 6457    });
 6458    cx.assert_editor_state(
 6459        &"
 6460            ˇ)}
 6461            ˇ)}
 6462            ˇ)}
 6463        "
 6464        .unindent(),
 6465    );
 6466
 6467    // skip-over closing brackets at multiple cursors
 6468    cx.update_editor(|editor, window, cx| {
 6469        editor.handle_input(")", window, cx);
 6470        editor.handle_input("}", window, cx);
 6471    });
 6472    cx.assert_editor_state(
 6473        &"
 6474            )}ˇ
 6475            )}ˇ
 6476            )}ˇ
 6477        "
 6478        .unindent(),
 6479    );
 6480
 6481    // ignore non-close brackets
 6482    cx.update_editor(|editor, window, cx| {
 6483        editor.handle_input("]", window, cx);
 6484        editor.move_left(&MoveLeft, window, cx);
 6485        editor.handle_input("]", window, cx);
 6486    });
 6487    cx.assert_editor_state(
 6488        &"
 6489            )}]ˇ]
 6490            )}]ˇ]
 6491            )}]ˇ]
 6492        "
 6493        .unindent(),
 6494    );
 6495}
 6496
 6497#[gpui::test]
 6498async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6499    init_test(cx, |_| {});
 6500
 6501    let mut cx = EditorTestContext::new(cx).await;
 6502
 6503    let html_language = Arc::new(
 6504        Language::new(
 6505            LanguageConfig {
 6506                name: "HTML".into(),
 6507                brackets: BracketPairConfig {
 6508                    pairs: vec![
 6509                        BracketPair {
 6510                            start: "<".into(),
 6511                            end: ">".into(),
 6512                            close: true,
 6513                            ..Default::default()
 6514                        },
 6515                        BracketPair {
 6516                            start: "{".into(),
 6517                            end: "}".into(),
 6518                            close: true,
 6519                            ..Default::default()
 6520                        },
 6521                        BracketPair {
 6522                            start: "(".into(),
 6523                            end: ")".into(),
 6524                            close: true,
 6525                            ..Default::default()
 6526                        },
 6527                    ],
 6528                    ..Default::default()
 6529                },
 6530                autoclose_before: "})]>".into(),
 6531                ..Default::default()
 6532            },
 6533            Some(tree_sitter_html::LANGUAGE.into()),
 6534        )
 6535        .with_injection_query(
 6536            r#"
 6537            (script_element
 6538                (raw_text) @injection.content
 6539                (#set! injection.language "javascript"))
 6540            "#,
 6541        )
 6542        .unwrap(),
 6543    );
 6544
 6545    let javascript_language = Arc::new(Language::new(
 6546        LanguageConfig {
 6547            name: "JavaScript".into(),
 6548            brackets: BracketPairConfig {
 6549                pairs: vec![
 6550                    BracketPair {
 6551                        start: "/*".into(),
 6552                        end: " */".into(),
 6553                        close: true,
 6554                        ..Default::default()
 6555                    },
 6556                    BracketPair {
 6557                        start: "{".into(),
 6558                        end: "}".into(),
 6559                        close: true,
 6560                        ..Default::default()
 6561                    },
 6562                    BracketPair {
 6563                        start: "(".into(),
 6564                        end: ")".into(),
 6565                        close: true,
 6566                        ..Default::default()
 6567                    },
 6568                ],
 6569                ..Default::default()
 6570            },
 6571            autoclose_before: "})]>".into(),
 6572            ..Default::default()
 6573        },
 6574        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6575    ));
 6576
 6577    cx.language_registry().add(html_language.clone());
 6578    cx.language_registry().add(javascript_language.clone());
 6579
 6580    cx.update_buffer(|buffer, cx| {
 6581        buffer.set_language(Some(html_language), cx);
 6582    });
 6583
 6584    cx.set_state(
 6585        &r#"
 6586            <body>ˇ
 6587                <script>
 6588                    var x = 1;ˇ
 6589                </script>
 6590            </body>ˇ
 6591        "#
 6592        .unindent(),
 6593    );
 6594
 6595    // Precondition: different languages are active at different locations.
 6596    cx.update_editor(|editor, window, cx| {
 6597        let snapshot = editor.snapshot(window, cx);
 6598        let cursors = editor.selections.ranges::<usize>(cx);
 6599        let languages = cursors
 6600            .iter()
 6601            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6602            .collect::<Vec<_>>();
 6603        assert_eq!(
 6604            languages,
 6605            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6606        );
 6607    });
 6608
 6609    // Angle brackets autoclose in HTML, but not JavaScript.
 6610    cx.update_editor(|editor, window, cx| {
 6611        editor.handle_input("<", window, cx);
 6612        editor.handle_input("a", window, cx);
 6613    });
 6614    cx.assert_editor_state(
 6615        &r#"
 6616            <body><aˇ>
 6617                <script>
 6618                    var x = 1;<aˇ
 6619                </script>
 6620            </body><aˇ>
 6621        "#
 6622        .unindent(),
 6623    );
 6624
 6625    // Curly braces and parens autoclose in both HTML and JavaScript.
 6626    cx.update_editor(|editor, window, cx| {
 6627        editor.handle_input(" b=", window, cx);
 6628        editor.handle_input("{", window, cx);
 6629        editor.handle_input("c", window, cx);
 6630        editor.handle_input("(", window, cx);
 6631    });
 6632    cx.assert_editor_state(
 6633        &r#"
 6634            <body><a b={c(ˇ)}>
 6635                <script>
 6636                    var x = 1;<a b={c(ˇ)}
 6637                </script>
 6638            </body><a b={c(ˇ)}>
 6639        "#
 6640        .unindent(),
 6641    );
 6642
 6643    // Brackets that were already autoclosed are skipped.
 6644    cx.update_editor(|editor, window, cx| {
 6645        editor.handle_input(")", window, cx);
 6646        editor.handle_input("d", window, cx);
 6647        editor.handle_input("}", window, cx);
 6648    });
 6649    cx.assert_editor_state(
 6650        &r#"
 6651            <body><a b={c()d}ˇ>
 6652                <script>
 6653                    var x = 1;<a b={c()d}ˇ
 6654                </script>
 6655            </body><a b={c()d}ˇ>
 6656        "#
 6657        .unindent(),
 6658    );
 6659    cx.update_editor(|editor, window, cx| {
 6660        editor.handle_input(">", window, cx);
 6661    });
 6662    cx.assert_editor_state(
 6663        &r#"
 6664            <body><a b={c()d}>ˇ
 6665                <script>
 6666                    var x = 1;<a b={c()d}>ˇ
 6667                </script>
 6668            </body><a b={c()d}>ˇ
 6669        "#
 6670        .unindent(),
 6671    );
 6672
 6673    // Reset
 6674    cx.set_state(
 6675        &r#"
 6676            <body>ˇ
 6677                <script>
 6678                    var x = 1;ˇ
 6679                </script>
 6680            </body>ˇ
 6681        "#
 6682        .unindent(),
 6683    );
 6684
 6685    cx.update_editor(|editor, window, cx| {
 6686        editor.handle_input("<", window, cx);
 6687    });
 6688    cx.assert_editor_state(
 6689        &r#"
 6690            <body><ˇ>
 6691                <script>
 6692                    var x = 1;<ˇ
 6693                </script>
 6694            </body><ˇ>
 6695        "#
 6696        .unindent(),
 6697    );
 6698
 6699    // When backspacing, the closing angle brackets are removed.
 6700    cx.update_editor(|editor, window, cx| {
 6701        editor.backspace(&Backspace, window, cx);
 6702    });
 6703    cx.assert_editor_state(
 6704        &r#"
 6705            <body>ˇ
 6706                <script>
 6707                    var x = 1;ˇ
 6708                </script>
 6709            </body>ˇ
 6710        "#
 6711        .unindent(),
 6712    );
 6713
 6714    // Block comments autoclose in JavaScript, but not HTML.
 6715    cx.update_editor(|editor, window, cx| {
 6716        editor.handle_input("/", window, cx);
 6717        editor.handle_input("*", window, cx);
 6718    });
 6719    cx.assert_editor_state(
 6720        &r#"
 6721            <body>/*ˇ
 6722                <script>
 6723                    var x = 1;/*ˇ */
 6724                </script>
 6725            </body>/*ˇ
 6726        "#
 6727        .unindent(),
 6728    );
 6729}
 6730
 6731#[gpui::test]
 6732async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6733    init_test(cx, |_| {});
 6734
 6735    let mut cx = EditorTestContext::new(cx).await;
 6736
 6737    let rust_language = Arc::new(
 6738        Language::new(
 6739            LanguageConfig {
 6740                name: "Rust".into(),
 6741                brackets: serde_json::from_value(json!([
 6742                    { "start": "{", "end": "}", "close": true, "newline": true },
 6743                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6744                ]))
 6745                .unwrap(),
 6746                autoclose_before: "})]>".into(),
 6747                ..Default::default()
 6748            },
 6749            Some(tree_sitter_rust::LANGUAGE.into()),
 6750        )
 6751        .with_override_query("(string_literal) @string")
 6752        .unwrap(),
 6753    );
 6754
 6755    cx.language_registry().add(rust_language.clone());
 6756    cx.update_buffer(|buffer, cx| {
 6757        buffer.set_language(Some(rust_language), cx);
 6758    });
 6759
 6760    cx.set_state(
 6761        &r#"
 6762            let x = ˇ
 6763        "#
 6764        .unindent(),
 6765    );
 6766
 6767    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6768    cx.update_editor(|editor, window, cx| {
 6769        editor.handle_input("\"", window, cx);
 6770    });
 6771    cx.assert_editor_state(
 6772        &r#"
 6773            let x = "ˇ"
 6774        "#
 6775        .unindent(),
 6776    );
 6777
 6778    // Inserting another quotation mark. The cursor moves across the existing
 6779    // automatically-inserted quotation mark.
 6780    cx.update_editor(|editor, window, cx| {
 6781        editor.handle_input("\"", window, cx);
 6782    });
 6783    cx.assert_editor_state(
 6784        &r#"
 6785            let x = ""ˇ
 6786        "#
 6787        .unindent(),
 6788    );
 6789
 6790    // Reset
 6791    cx.set_state(
 6792        &r#"
 6793            let x = ˇ
 6794        "#
 6795        .unindent(),
 6796    );
 6797
 6798    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6799    cx.update_editor(|editor, window, cx| {
 6800        editor.handle_input("\"", window, cx);
 6801        editor.handle_input(" ", window, cx);
 6802        editor.move_left(&Default::default(), window, cx);
 6803        editor.handle_input("\\", window, cx);
 6804        editor.handle_input("\"", window, cx);
 6805    });
 6806    cx.assert_editor_state(
 6807        &r#"
 6808            let x = "\"ˇ "
 6809        "#
 6810        .unindent(),
 6811    );
 6812
 6813    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6814    // mark. Nothing is inserted.
 6815    cx.update_editor(|editor, window, cx| {
 6816        editor.move_right(&Default::default(), window, cx);
 6817        editor.handle_input("\"", window, cx);
 6818    });
 6819    cx.assert_editor_state(
 6820        &r#"
 6821            let x = "\" "ˇ
 6822        "#
 6823        .unindent(),
 6824    );
 6825}
 6826
 6827#[gpui::test]
 6828async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6829    init_test(cx, |_| {});
 6830
 6831    let language = Arc::new(Language::new(
 6832        LanguageConfig {
 6833            brackets: BracketPairConfig {
 6834                pairs: vec![
 6835                    BracketPair {
 6836                        start: "{".to_string(),
 6837                        end: "}".to_string(),
 6838                        close: true,
 6839                        surround: true,
 6840                        newline: true,
 6841                    },
 6842                    BracketPair {
 6843                        start: "/* ".to_string(),
 6844                        end: "*/".to_string(),
 6845                        close: true,
 6846                        surround: true,
 6847                        ..Default::default()
 6848                    },
 6849                ],
 6850                ..Default::default()
 6851            },
 6852            ..Default::default()
 6853        },
 6854        Some(tree_sitter_rust::LANGUAGE.into()),
 6855    ));
 6856
 6857    let text = r#"
 6858        a
 6859        b
 6860        c
 6861    "#
 6862    .unindent();
 6863
 6864    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6865    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6866    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6867    editor
 6868        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6869        .await;
 6870
 6871    editor.update_in(cx, |editor, window, cx| {
 6872        editor.change_selections(None, window, cx, |s| {
 6873            s.select_display_ranges([
 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        editor.handle_input("{", window, cx);
 6881        editor.handle_input("{", window, cx);
 6882        editor.handle_input("{", window, cx);
 6883        assert_eq!(
 6884            editor.text(cx),
 6885            "
 6886                {{{a}}}
 6887                {{{b}}}
 6888                {{{c}}}
 6889            "
 6890            .unindent()
 6891        );
 6892        assert_eq!(
 6893            editor.selections.display_ranges(cx),
 6894            [
 6895                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6896                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6897                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6898            ]
 6899        );
 6900
 6901        editor.undo(&Undo, window, cx);
 6902        editor.undo(&Undo, window, cx);
 6903        editor.undo(&Undo, window, cx);
 6904        assert_eq!(
 6905            editor.text(cx),
 6906            "
 6907                a
 6908                b
 6909                c
 6910            "
 6911            .unindent()
 6912        );
 6913        assert_eq!(
 6914            editor.selections.display_ranges(cx),
 6915            [
 6916                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6917                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6918                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6919            ]
 6920        );
 6921
 6922        // Ensure inserting the first character of a multi-byte bracket pair
 6923        // doesn't surround the selections with the bracket.
 6924        editor.handle_input("/", window, cx);
 6925        assert_eq!(
 6926            editor.text(cx),
 6927            "
 6928                /
 6929                /
 6930                /
 6931            "
 6932            .unindent()
 6933        );
 6934        assert_eq!(
 6935            editor.selections.display_ranges(cx),
 6936            [
 6937                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6938                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6939                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6940            ]
 6941        );
 6942
 6943        editor.undo(&Undo, window, cx);
 6944        assert_eq!(
 6945            editor.text(cx),
 6946            "
 6947                a
 6948                b
 6949                c
 6950            "
 6951            .unindent()
 6952        );
 6953        assert_eq!(
 6954            editor.selections.display_ranges(cx),
 6955            [
 6956                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6957                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6958                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6959            ]
 6960        );
 6961
 6962        // Ensure inserting the last character of a multi-byte bracket pair
 6963        // doesn't surround the selections with the bracket.
 6964        editor.handle_input("*", window, cx);
 6965        assert_eq!(
 6966            editor.text(cx),
 6967            "
 6968                *
 6969                *
 6970                *
 6971            "
 6972            .unindent()
 6973        );
 6974        assert_eq!(
 6975            editor.selections.display_ranges(cx),
 6976            [
 6977                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6978                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6979                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6980            ]
 6981        );
 6982    });
 6983}
 6984
 6985#[gpui::test]
 6986async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6987    init_test(cx, |_| {});
 6988
 6989    let language = Arc::new(Language::new(
 6990        LanguageConfig {
 6991            brackets: BracketPairConfig {
 6992                pairs: vec![BracketPair {
 6993                    start: "{".to_string(),
 6994                    end: "}".to_string(),
 6995                    close: true,
 6996                    surround: true,
 6997                    newline: true,
 6998                }],
 6999                ..Default::default()
 7000            },
 7001            autoclose_before: "}".to_string(),
 7002            ..Default::default()
 7003        },
 7004        Some(tree_sitter_rust::LANGUAGE.into()),
 7005    ));
 7006
 7007    let text = r#"
 7008        a
 7009        b
 7010        c
 7011    "#
 7012    .unindent();
 7013
 7014    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7015    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7016    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7017    editor
 7018        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7019        .await;
 7020
 7021    editor.update_in(cx, |editor, window, cx| {
 7022        editor.change_selections(None, window, cx, |s| {
 7023            s.select_ranges([
 7024                Point::new(0, 1)..Point::new(0, 1),
 7025                Point::new(1, 1)..Point::new(1, 1),
 7026                Point::new(2, 1)..Point::new(2, 1),
 7027            ])
 7028        });
 7029
 7030        editor.handle_input("{", window, cx);
 7031        editor.handle_input("{", window, cx);
 7032        editor.handle_input("_", window, cx);
 7033        assert_eq!(
 7034            editor.text(cx),
 7035            "
 7036                a{{_}}
 7037                b{{_}}
 7038                c{{_}}
 7039            "
 7040            .unindent()
 7041        );
 7042        assert_eq!(
 7043            editor.selections.ranges::<Point>(cx),
 7044            [
 7045                Point::new(0, 4)..Point::new(0, 4),
 7046                Point::new(1, 4)..Point::new(1, 4),
 7047                Point::new(2, 4)..Point::new(2, 4)
 7048            ]
 7049        );
 7050
 7051        editor.backspace(&Default::default(), window, cx);
 7052        editor.backspace(&Default::default(), window, cx);
 7053        assert_eq!(
 7054            editor.text(cx),
 7055            "
 7056                a{}
 7057                b{}
 7058                c{}
 7059            "
 7060            .unindent()
 7061        );
 7062        assert_eq!(
 7063            editor.selections.ranges::<Point>(cx),
 7064            [
 7065                Point::new(0, 2)..Point::new(0, 2),
 7066                Point::new(1, 2)..Point::new(1, 2),
 7067                Point::new(2, 2)..Point::new(2, 2)
 7068            ]
 7069        );
 7070
 7071        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7072        assert_eq!(
 7073            editor.text(cx),
 7074            "
 7075                a
 7076                b
 7077                c
 7078            "
 7079            .unindent()
 7080        );
 7081        assert_eq!(
 7082            editor.selections.ranges::<Point>(cx),
 7083            [
 7084                Point::new(0, 1)..Point::new(0, 1),
 7085                Point::new(1, 1)..Point::new(1, 1),
 7086                Point::new(2, 1)..Point::new(2, 1)
 7087            ]
 7088        );
 7089    });
 7090}
 7091
 7092#[gpui::test]
 7093async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7094    init_test(cx, |settings| {
 7095        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7096    });
 7097
 7098    let mut cx = EditorTestContext::new(cx).await;
 7099
 7100    let language = Arc::new(Language::new(
 7101        LanguageConfig {
 7102            brackets: BracketPairConfig {
 7103                pairs: vec![
 7104                    BracketPair {
 7105                        start: "{".to_string(),
 7106                        end: "}".to_string(),
 7107                        close: true,
 7108                        surround: true,
 7109                        newline: true,
 7110                    },
 7111                    BracketPair {
 7112                        start: "(".to_string(),
 7113                        end: ")".to_string(),
 7114                        close: true,
 7115                        surround: true,
 7116                        newline: true,
 7117                    },
 7118                    BracketPair {
 7119                        start: "[".to_string(),
 7120                        end: "]".to_string(),
 7121                        close: false,
 7122                        surround: true,
 7123                        newline: true,
 7124                    },
 7125                ],
 7126                ..Default::default()
 7127            },
 7128            autoclose_before: "})]".to_string(),
 7129            ..Default::default()
 7130        },
 7131        Some(tree_sitter_rust::LANGUAGE.into()),
 7132    ));
 7133
 7134    cx.language_registry().add(language.clone());
 7135    cx.update_buffer(|buffer, cx| {
 7136        buffer.set_language(Some(language), cx);
 7137    });
 7138
 7139    cx.set_state(
 7140        &"
 7141            {(ˇ)}
 7142            [[ˇ]]
 7143            {(ˇ)}
 7144        "
 7145        .unindent(),
 7146    );
 7147
 7148    cx.update_editor(|editor, window, cx| {
 7149        editor.backspace(&Default::default(), window, cx);
 7150        editor.backspace(&Default::default(), window, cx);
 7151    });
 7152
 7153    cx.assert_editor_state(
 7154        &"
 7155            ˇ
 7156            ˇ]]
 7157            ˇ
 7158        "
 7159        .unindent(),
 7160    );
 7161
 7162    cx.update_editor(|editor, window, cx| {
 7163        editor.handle_input("{", window, cx);
 7164        editor.handle_input("{", window, cx);
 7165        editor.move_right(&MoveRight, window, cx);
 7166        editor.move_right(&MoveRight, window, cx);
 7167        editor.move_left(&MoveLeft, window, cx);
 7168        editor.move_left(&MoveLeft, window, cx);
 7169        editor.backspace(&Default::default(), window, cx);
 7170    });
 7171
 7172    cx.assert_editor_state(
 7173        &"
 7174            {ˇ}
 7175            {ˇ}]]
 7176            {ˇ}
 7177        "
 7178        .unindent(),
 7179    );
 7180
 7181    cx.update_editor(|editor, window, cx| {
 7182        editor.backspace(&Default::default(), window, cx);
 7183    });
 7184
 7185    cx.assert_editor_state(
 7186        &"
 7187            ˇ
 7188            ˇ]]
 7189            ˇ
 7190        "
 7191        .unindent(),
 7192    );
 7193}
 7194
 7195#[gpui::test]
 7196async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7197    init_test(cx, |_| {});
 7198
 7199    let language = Arc::new(Language::new(
 7200        LanguageConfig::default(),
 7201        Some(tree_sitter_rust::LANGUAGE.into()),
 7202    ));
 7203
 7204    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7205    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7206    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7207    editor
 7208        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7209        .await;
 7210
 7211    editor.update_in(cx, |editor, window, cx| {
 7212        editor.set_auto_replace_emoji_shortcode(true);
 7213
 7214        editor.handle_input("Hello ", window, cx);
 7215        editor.handle_input(":wave", window, cx);
 7216        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7217
 7218        editor.handle_input(":", window, cx);
 7219        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7220
 7221        editor.handle_input(" :smile", window, cx);
 7222        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7223
 7224        editor.handle_input(":", window, cx);
 7225        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7226
 7227        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7228        editor.handle_input(":wave", window, cx);
 7229        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7230
 7231        editor.handle_input(":", window, cx);
 7232        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7233
 7234        editor.handle_input(":1", window, cx);
 7235        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7236
 7237        editor.handle_input(":", window, cx);
 7238        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7239
 7240        // Ensure shortcode does not get replaced when it is part of a word
 7241        editor.handle_input(" Test:wave", window, cx);
 7242        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7243
 7244        editor.handle_input(":", window, cx);
 7245        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7246
 7247        editor.set_auto_replace_emoji_shortcode(false);
 7248
 7249        // Ensure shortcode does not get replaced when auto replace is off
 7250        editor.handle_input(" :wave", window, cx);
 7251        assert_eq!(
 7252            editor.text(cx),
 7253            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7254        );
 7255
 7256        editor.handle_input(":", window, cx);
 7257        assert_eq!(
 7258            editor.text(cx),
 7259            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7260        );
 7261    });
 7262}
 7263
 7264#[gpui::test]
 7265async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7266    init_test(cx, |_| {});
 7267
 7268    let (text, insertion_ranges) = marked_text_ranges(
 7269        indoc! {"
 7270            ˇ
 7271        "},
 7272        false,
 7273    );
 7274
 7275    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7276    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7277
 7278    _ = editor.update_in(cx, |editor, window, cx| {
 7279        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7280
 7281        editor
 7282            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7283            .unwrap();
 7284
 7285        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7286            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7287            assert_eq!(editor.text(cx), expected_text);
 7288            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7289        }
 7290
 7291        assert(
 7292            editor,
 7293            cx,
 7294            indoc! {"
 7295            type «» =•
 7296            "},
 7297        );
 7298
 7299        assert!(editor.context_menu_visible(), "There should be a matches");
 7300    });
 7301}
 7302
 7303#[gpui::test]
 7304async fn test_snippets(cx: &mut TestAppContext) {
 7305    init_test(cx, |_| {});
 7306
 7307    let (text, insertion_ranges) = marked_text_ranges(
 7308        indoc! {"
 7309            a.ˇ b
 7310            a.ˇ b
 7311            a.ˇ b
 7312        "},
 7313        false,
 7314    );
 7315
 7316    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7317    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7318
 7319    editor.update_in(cx, |editor, window, cx| {
 7320        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7321
 7322        editor
 7323            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7324            .unwrap();
 7325
 7326        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7327            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7328            assert_eq!(editor.text(cx), expected_text);
 7329            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7330        }
 7331
 7332        assert(
 7333            editor,
 7334            cx,
 7335            indoc! {"
 7336                a.f(«one», two, «three») b
 7337                a.f(«one», two, «three») b
 7338                a.f(«one», two, «three») b
 7339            "},
 7340        );
 7341
 7342        // Can't move earlier than the first tab stop
 7343        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7344        assert(
 7345            editor,
 7346            cx,
 7347            indoc! {"
 7348                a.f(«one», two, «three») b
 7349                a.f(«one», two, «three») b
 7350                a.f(«one», two, «three») b
 7351            "},
 7352        );
 7353
 7354        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7355        assert(
 7356            editor,
 7357            cx,
 7358            indoc! {"
 7359                a.f(one, «two», three) b
 7360                a.f(one, «two», three) b
 7361                a.f(one, «two», three) b
 7362            "},
 7363        );
 7364
 7365        editor.move_to_prev_snippet_tabstop(window, cx);
 7366        assert(
 7367            editor,
 7368            cx,
 7369            indoc! {"
 7370                a.f(«one», two, «three») b
 7371                a.f(«one», two, «three») b
 7372                a.f(«one», two, «three») b
 7373            "},
 7374        );
 7375
 7376        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7377        assert(
 7378            editor,
 7379            cx,
 7380            indoc! {"
 7381                a.f(one, «two», three) b
 7382                a.f(one, «two», three) b
 7383                a.f(one, «two», three) b
 7384            "},
 7385        );
 7386        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7387        assert(
 7388            editor,
 7389            cx,
 7390            indoc! {"
 7391                a.f(one, two, three)ˇ b
 7392                a.f(one, two, three)ˇ b
 7393                a.f(one, two, three)ˇ b
 7394            "},
 7395        );
 7396
 7397        // As soon as the last tab stop is reached, snippet state is gone
 7398        editor.move_to_prev_snippet_tabstop(window, cx);
 7399        assert(
 7400            editor,
 7401            cx,
 7402            indoc! {"
 7403                a.f(one, two, three)ˇ b
 7404                a.f(one, two, three)ˇ b
 7405                a.f(one, two, three)ˇ b
 7406            "},
 7407        );
 7408    });
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7413    init_test(cx, |_| {});
 7414
 7415    let fs = FakeFs::new(cx.executor());
 7416    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7417
 7418    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7419
 7420    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7421    language_registry.add(rust_lang());
 7422    let mut fake_servers = language_registry.register_fake_lsp(
 7423        "Rust",
 7424        FakeLspAdapter {
 7425            capabilities: lsp::ServerCapabilities {
 7426                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7427                ..Default::default()
 7428            },
 7429            ..Default::default()
 7430        },
 7431    );
 7432
 7433    let buffer = project
 7434        .update(cx, |project, cx| {
 7435            project.open_local_buffer(path!("/file.rs"), cx)
 7436        })
 7437        .await
 7438        .unwrap();
 7439
 7440    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7441    let (editor, cx) = cx.add_window_view(|window, cx| {
 7442        build_editor_with_project(project.clone(), buffer, window, cx)
 7443    });
 7444    editor.update_in(cx, |editor, window, cx| {
 7445        editor.set_text("one\ntwo\nthree\n", window, cx)
 7446    });
 7447    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7448
 7449    cx.executor().start_waiting();
 7450    let fake_server = fake_servers.next().await.unwrap();
 7451
 7452    let save = editor
 7453        .update_in(cx, |editor, window, cx| {
 7454            editor.save(true, project.clone(), window, cx)
 7455        })
 7456        .unwrap();
 7457    fake_server
 7458        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7459            assert_eq!(
 7460                params.text_document.uri,
 7461                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7462            );
 7463            assert_eq!(params.options.tab_size, 4);
 7464            Ok(Some(vec![lsp::TextEdit::new(
 7465                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7466                ", ".to_string(),
 7467            )]))
 7468        })
 7469        .next()
 7470        .await;
 7471    cx.executor().start_waiting();
 7472    save.await;
 7473
 7474    assert_eq!(
 7475        editor.update(cx, |editor, cx| editor.text(cx)),
 7476        "one, two\nthree\n"
 7477    );
 7478    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7479
 7480    editor.update_in(cx, |editor, window, cx| {
 7481        editor.set_text("one\ntwo\nthree\n", window, cx)
 7482    });
 7483    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7484
 7485    // Ensure we can still save even if formatting hangs.
 7486    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7487        assert_eq!(
 7488            params.text_document.uri,
 7489            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7490        );
 7491        futures::future::pending::<()>().await;
 7492        unreachable!()
 7493    });
 7494    let save = editor
 7495        .update_in(cx, |editor, window, cx| {
 7496            editor.save(true, project.clone(), window, cx)
 7497        })
 7498        .unwrap();
 7499    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7500    cx.executor().start_waiting();
 7501    save.await;
 7502    assert_eq!(
 7503        editor.update(cx, |editor, cx| editor.text(cx)),
 7504        "one\ntwo\nthree\n"
 7505    );
 7506    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7507
 7508    // For non-dirty buffer, no formatting request should be sent
 7509    let save = editor
 7510        .update_in(cx, |editor, window, cx| {
 7511            editor.save(true, project.clone(), window, cx)
 7512        })
 7513        .unwrap();
 7514    let _pending_format_request = fake_server
 7515        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7516            panic!("Should not be invoked on non-dirty buffer");
 7517        })
 7518        .next();
 7519    cx.executor().start_waiting();
 7520    save.await;
 7521
 7522    // Set rust language override and assert overridden tabsize is sent to language server
 7523    update_test_language_settings(cx, |settings| {
 7524        settings.languages.insert(
 7525            "Rust".into(),
 7526            LanguageSettingsContent {
 7527                tab_size: NonZeroU32::new(8),
 7528                ..Default::default()
 7529            },
 7530        );
 7531    });
 7532
 7533    editor.update_in(cx, |editor, window, cx| {
 7534        editor.set_text("somehting_new\n", window, cx)
 7535    });
 7536    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7537    let save = editor
 7538        .update_in(cx, |editor, window, cx| {
 7539            editor.save(true, project.clone(), window, cx)
 7540        })
 7541        .unwrap();
 7542    fake_server
 7543        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7544            assert_eq!(
 7545                params.text_document.uri,
 7546                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7547            );
 7548            assert_eq!(params.options.tab_size, 8);
 7549            Ok(Some(vec![]))
 7550        })
 7551        .next()
 7552        .await;
 7553    cx.executor().start_waiting();
 7554    save.await;
 7555}
 7556
 7557#[gpui::test]
 7558async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7559    init_test(cx, |_| {});
 7560
 7561    let cols = 4;
 7562    let rows = 10;
 7563    let sample_text_1 = sample_text(rows, cols, 'a');
 7564    assert_eq!(
 7565        sample_text_1,
 7566        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7567    );
 7568    let sample_text_2 = sample_text(rows, cols, 'l');
 7569    assert_eq!(
 7570        sample_text_2,
 7571        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7572    );
 7573    let sample_text_3 = sample_text(rows, cols, 'v');
 7574    assert_eq!(
 7575        sample_text_3,
 7576        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7577    );
 7578
 7579    let fs = FakeFs::new(cx.executor());
 7580    fs.insert_tree(
 7581        path!("/a"),
 7582        json!({
 7583            "main.rs": sample_text_1,
 7584            "other.rs": sample_text_2,
 7585            "lib.rs": sample_text_3,
 7586        }),
 7587    )
 7588    .await;
 7589
 7590    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7591    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7592    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7593
 7594    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7595    language_registry.add(rust_lang());
 7596    let mut fake_servers = language_registry.register_fake_lsp(
 7597        "Rust",
 7598        FakeLspAdapter {
 7599            capabilities: lsp::ServerCapabilities {
 7600                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7601                ..Default::default()
 7602            },
 7603            ..Default::default()
 7604        },
 7605    );
 7606
 7607    let worktree = project.update(cx, |project, cx| {
 7608        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7609        assert_eq!(worktrees.len(), 1);
 7610        worktrees.pop().unwrap()
 7611    });
 7612    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7613
 7614    let buffer_1 = project
 7615        .update(cx, |project, cx| {
 7616            project.open_buffer((worktree_id, "main.rs"), cx)
 7617        })
 7618        .await
 7619        .unwrap();
 7620    let buffer_2 = project
 7621        .update(cx, |project, cx| {
 7622            project.open_buffer((worktree_id, "other.rs"), cx)
 7623        })
 7624        .await
 7625        .unwrap();
 7626    let buffer_3 = project
 7627        .update(cx, |project, cx| {
 7628            project.open_buffer((worktree_id, "lib.rs"), cx)
 7629        })
 7630        .await
 7631        .unwrap();
 7632
 7633    let multi_buffer = cx.new(|cx| {
 7634        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7635        multi_buffer.push_excerpts(
 7636            buffer_1.clone(),
 7637            [
 7638                ExcerptRange {
 7639                    context: Point::new(0, 0)..Point::new(3, 0),
 7640                    primary: None,
 7641                },
 7642                ExcerptRange {
 7643                    context: Point::new(5, 0)..Point::new(7, 0),
 7644                    primary: None,
 7645                },
 7646                ExcerptRange {
 7647                    context: Point::new(9, 0)..Point::new(10, 4),
 7648                    primary: None,
 7649                },
 7650            ],
 7651            cx,
 7652        );
 7653        multi_buffer.push_excerpts(
 7654            buffer_2.clone(),
 7655            [
 7656                ExcerptRange {
 7657                    context: Point::new(0, 0)..Point::new(3, 0),
 7658                    primary: None,
 7659                },
 7660                ExcerptRange {
 7661                    context: Point::new(5, 0)..Point::new(7, 0),
 7662                    primary: None,
 7663                },
 7664                ExcerptRange {
 7665                    context: Point::new(9, 0)..Point::new(10, 4),
 7666                    primary: None,
 7667                },
 7668            ],
 7669            cx,
 7670        );
 7671        multi_buffer.push_excerpts(
 7672            buffer_3.clone(),
 7673            [
 7674                ExcerptRange {
 7675                    context: Point::new(0, 0)..Point::new(3, 0),
 7676                    primary: None,
 7677                },
 7678                ExcerptRange {
 7679                    context: Point::new(5, 0)..Point::new(7, 0),
 7680                    primary: None,
 7681                },
 7682                ExcerptRange {
 7683                    context: Point::new(9, 0)..Point::new(10, 4),
 7684                    primary: None,
 7685                },
 7686            ],
 7687            cx,
 7688        );
 7689        multi_buffer
 7690    });
 7691    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7692        Editor::new(
 7693            EditorMode::Full,
 7694            multi_buffer,
 7695            Some(project.clone()),
 7696            window,
 7697            cx,
 7698        )
 7699    });
 7700
 7701    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7702        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7703            s.select_ranges(Some(1..2))
 7704        });
 7705        editor.insert("|one|two|three|", window, cx);
 7706    });
 7707    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7708    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7709        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7710            s.select_ranges(Some(60..70))
 7711        });
 7712        editor.insert("|four|five|six|", window, cx);
 7713    });
 7714    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7715
 7716    // First two buffers should be edited, but not the third one.
 7717    assert_eq!(
 7718        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7719        "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}",
 7720    );
 7721    buffer_1.update(cx, |buffer, _| {
 7722        assert!(buffer.is_dirty());
 7723        assert_eq!(
 7724            buffer.text(),
 7725            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7726        )
 7727    });
 7728    buffer_2.update(cx, |buffer, _| {
 7729        assert!(buffer.is_dirty());
 7730        assert_eq!(
 7731            buffer.text(),
 7732            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7733        )
 7734    });
 7735    buffer_3.update(cx, |buffer, _| {
 7736        assert!(!buffer.is_dirty());
 7737        assert_eq!(buffer.text(), sample_text_3,)
 7738    });
 7739    cx.executor().run_until_parked();
 7740
 7741    cx.executor().start_waiting();
 7742    let save = multi_buffer_editor
 7743        .update_in(cx, |editor, window, cx| {
 7744            editor.save(true, project.clone(), window, cx)
 7745        })
 7746        .unwrap();
 7747
 7748    let fake_server = fake_servers.next().await.unwrap();
 7749    fake_server
 7750        .server
 7751        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7752            Ok(Some(vec![lsp::TextEdit::new(
 7753                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7754                format!("[{} formatted]", params.text_document.uri),
 7755            )]))
 7756        })
 7757        .detach();
 7758    save.await;
 7759
 7760    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7761    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7762    assert_eq!(
 7763        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7764        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}"),
 7765    );
 7766    buffer_1.update(cx, |buffer, _| {
 7767        assert!(!buffer.is_dirty());
 7768        assert_eq!(
 7769            buffer.text(),
 7770            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7771        )
 7772    });
 7773    buffer_2.update(cx, |buffer, _| {
 7774        assert!(!buffer.is_dirty());
 7775        assert_eq!(
 7776            buffer.text(),
 7777            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7778        )
 7779    });
 7780    buffer_3.update(cx, |buffer, _| {
 7781        assert!(!buffer.is_dirty());
 7782        assert_eq!(buffer.text(), sample_text_3,)
 7783    });
 7784}
 7785
 7786#[gpui::test]
 7787async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7788    init_test(cx, |_| {});
 7789
 7790    let fs = FakeFs::new(cx.executor());
 7791    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7792
 7793    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7794
 7795    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7796    language_registry.add(rust_lang());
 7797    let mut fake_servers = language_registry.register_fake_lsp(
 7798        "Rust",
 7799        FakeLspAdapter {
 7800            capabilities: lsp::ServerCapabilities {
 7801                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7802                ..Default::default()
 7803            },
 7804            ..Default::default()
 7805        },
 7806    );
 7807
 7808    let buffer = project
 7809        .update(cx, |project, cx| {
 7810            project.open_local_buffer(path!("/file.rs"), cx)
 7811        })
 7812        .await
 7813        .unwrap();
 7814
 7815    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7816    let (editor, cx) = cx.add_window_view(|window, cx| {
 7817        build_editor_with_project(project.clone(), buffer, window, cx)
 7818    });
 7819    editor.update_in(cx, |editor, window, cx| {
 7820        editor.set_text("one\ntwo\nthree\n", window, cx)
 7821    });
 7822    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7823
 7824    cx.executor().start_waiting();
 7825    let fake_server = fake_servers.next().await.unwrap();
 7826
 7827    let save = editor
 7828        .update_in(cx, |editor, window, cx| {
 7829            editor.save(true, project.clone(), window, cx)
 7830        })
 7831        .unwrap();
 7832    fake_server
 7833        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7834            assert_eq!(
 7835                params.text_document.uri,
 7836                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7837            );
 7838            assert_eq!(params.options.tab_size, 4);
 7839            Ok(Some(vec![lsp::TextEdit::new(
 7840                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7841                ", ".to_string(),
 7842            )]))
 7843        })
 7844        .next()
 7845        .await;
 7846    cx.executor().start_waiting();
 7847    save.await;
 7848    assert_eq!(
 7849        editor.update(cx, |editor, cx| editor.text(cx)),
 7850        "one, two\nthree\n"
 7851    );
 7852    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7853
 7854    editor.update_in(cx, |editor, window, cx| {
 7855        editor.set_text("one\ntwo\nthree\n", window, cx)
 7856    });
 7857    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7858
 7859    // Ensure we can still save even if formatting hangs.
 7860    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7861        move |params, _| async move {
 7862            assert_eq!(
 7863                params.text_document.uri,
 7864                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7865            );
 7866            futures::future::pending::<()>().await;
 7867            unreachable!()
 7868        },
 7869    );
 7870    let save = editor
 7871        .update_in(cx, |editor, window, cx| {
 7872            editor.save(true, project.clone(), window, cx)
 7873        })
 7874        .unwrap();
 7875    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7876    cx.executor().start_waiting();
 7877    save.await;
 7878    assert_eq!(
 7879        editor.update(cx, |editor, cx| editor.text(cx)),
 7880        "one\ntwo\nthree\n"
 7881    );
 7882    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7883
 7884    // For non-dirty buffer, no formatting request should be sent
 7885    let save = editor
 7886        .update_in(cx, |editor, window, cx| {
 7887            editor.save(true, project.clone(), window, cx)
 7888        })
 7889        .unwrap();
 7890    let _pending_format_request = fake_server
 7891        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7892            panic!("Should not be invoked on non-dirty buffer");
 7893        })
 7894        .next();
 7895    cx.executor().start_waiting();
 7896    save.await;
 7897
 7898    // Set Rust language override and assert overridden tabsize is sent to language server
 7899    update_test_language_settings(cx, |settings| {
 7900        settings.languages.insert(
 7901            "Rust".into(),
 7902            LanguageSettingsContent {
 7903                tab_size: NonZeroU32::new(8),
 7904                ..Default::default()
 7905            },
 7906        );
 7907    });
 7908
 7909    editor.update_in(cx, |editor, window, cx| {
 7910        editor.set_text("somehting_new\n", window, cx)
 7911    });
 7912    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7913    let save = editor
 7914        .update_in(cx, |editor, window, cx| {
 7915            editor.save(true, project.clone(), window, cx)
 7916        })
 7917        .unwrap();
 7918    fake_server
 7919        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7920            assert_eq!(
 7921                params.text_document.uri,
 7922                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7923            );
 7924            assert_eq!(params.options.tab_size, 8);
 7925            Ok(Some(vec![]))
 7926        })
 7927        .next()
 7928        .await;
 7929    cx.executor().start_waiting();
 7930    save.await;
 7931}
 7932
 7933#[gpui::test]
 7934async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7935    init_test(cx, |settings| {
 7936        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7937            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7938        ))
 7939    });
 7940
 7941    let fs = FakeFs::new(cx.executor());
 7942    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7943
 7944    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7945
 7946    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7947    language_registry.add(Arc::new(Language::new(
 7948        LanguageConfig {
 7949            name: "Rust".into(),
 7950            matcher: LanguageMatcher {
 7951                path_suffixes: vec!["rs".to_string()],
 7952                ..Default::default()
 7953            },
 7954            ..LanguageConfig::default()
 7955        },
 7956        Some(tree_sitter_rust::LANGUAGE.into()),
 7957    )));
 7958    update_test_language_settings(cx, |settings| {
 7959        // Enable Prettier formatting for the same buffer, and ensure
 7960        // LSP is called instead of Prettier.
 7961        settings.defaults.prettier = Some(PrettierSettings {
 7962            allowed: true,
 7963            ..PrettierSettings::default()
 7964        });
 7965    });
 7966    let mut fake_servers = language_registry.register_fake_lsp(
 7967        "Rust",
 7968        FakeLspAdapter {
 7969            capabilities: lsp::ServerCapabilities {
 7970                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7971                ..Default::default()
 7972            },
 7973            ..Default::default()
 7974        },
 7975    );
 7976
 7977    let buffer = project
 7978        .update(cx, |project, cx| {
 7979            project.open_local_buffer(path!("/file.rs"), cx)
 7980        })
 7981        .await
 7982        .unwrap();
 7983
 7984    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7985    let (editor, cx) = cx.add_window_view(|window, cx| {
 7986        build_editor_with_project(project.clone(), buffer, window, cx)
 7987    });
 7988    editor.update_in(cx, |editor, window, cx| {
 7989        editor.set_text("one\ntwo\nthree\n", window, cx)
 7990    });
 7991
 7992    cx.executor().start_waiting();
 7993    let fake_server = fake_servers.next().await.unwrap();
 7994
 7995    let format = editor
 7996        .update_in(cx, |editor, window, cx| {
 7997            editor.perform_format(
 7998                project.clone(),
 7999                FormatTrigger::Manual,
 8000                FormatTarget::Buffers,
 8001                window,
 8002                cx,
 8003            )
 8004        })
 8005        .unwrap();
 8006    fake_server
 8007        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8008            assert_eq!(
 8009                params.text_document.uri,
 8010                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8011            );
 8012            assert_eq!(params.options.tab_size, 4);
 8013            Ok(Some(vec![lsp::TextEdit::new(
 8014                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8015                ", ".to_string(),
 8016            )]))
 8017        })
 8018        .next()
 8019        .await;
 8020    cx.executor().start_waiting();
 8021    format.await;
 8022    assert_eq!(
 8023        editor.update(cx, |editor, cx| editor.text(cx)),
 8024        "one, two\nthree\n"
 8025    );
 8026
 8027    editor.update_in(cx, |editor, window, cx| {
 8028        editor.set_text("one\ntwo\nthree\n", window, cx)
 8029    });
 8030    // Ensure we don't lock if formatting hangs.
 8031    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8032        assert_eq!(
 8033            params.text_document.uri,
 8034            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8035        );
 8036        futures::future::pending::<()>().await;
 8037        unreachable!()
 8038    });
 8039    let format = editor
 8040        .update_in(cx, |editor, window, cx| {
 8041            editor.perform_format(
 8042                project,
 8043                FormatTrigger::Manual,
 8044                FormatTarget::Buffers,
 8045                window,
 8046                cx,
 8047            )
 8048        })
 8049        .unwrap();
 8050    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8051    cx.executor().start_waiting();
 8052    format.await;
 8053    assert_eq!(
 8054        editor.update(cx, |editor, cx| editor.text(cx)),
 8055        "one\ntwo\nthree\n"
 8056    );
 8057}
 8058
 8059#[gpui::test]
 8060async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8061    init_test(cx, |settings| {
 8062        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8063            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8064        ))
 8065    });
 8066
 8067    let fs = FakeFs::new(cx.executor());
 8068    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8069
 8070    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8071
 8072    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8073    language_registry.add(Arc::new(Language::new(
 8074        LanguageConfig {
 8075            name: "TypeScript".into(),
 8076            matcher: LanguageMatcher {
 8077                path_suffixes: vec!["ts".to_string()],
 8078                ..Default::default()
 8079            },
 8080            ..LanguageConfig::default()
 8081        },
 8082        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8083    )));
 8084    update_test_language_settings(cx, |settings| {
 8085        settings.defaults.prettier = Some(PrettierSettings {
 8086            allowed: true,
 8087            ..PrettierSettings::default()
 8088        });
 8089    });
 8090    let mut fake_servers = language_registry.register_fake_lsp(
 8091        "TypeScript",
 8092        FakeLspAdapter {
 8093            capabilities: lsp::ServerCapabilities {
 8094                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8095                ..Default::default()
 8096            },
 8097            ..Default::default()
 8098        },
 8099    );
 8100
 8101    let buffer = project
 8102        .update(cx, |project, cx| {
 8103            project.open_local_buffer(path!("/file.ts"), cx)
 8104        })
 8105        .await
 8106        .unwrap();
 8107
 8108    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8109    let (editor, cx) = cx.add_window_view(|window, cx| {
 8110        build_editor_with_project(project.clone(), buffer, window, cx)
 8111    });
 8112    editor.update_in(cx, |editor, window, cx| {
 8113        editor.set_text(
 8114            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8115            window,
 8116            cx,
 8117        )
 8118    });
 8119
 8120    cx.executor().start_waiting();
 8121    let fake_server = fake_servers.next().await.unwrap();
 8122
 8123    let format = editor
 8124        .update_in(cx, |editor, window, cx| {
 8125            editor.perform_code_action_kind(
 8126                project.clone(),
 8127                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8128                window,
 8129                cx,
 8130            )
 8131        })
 8132        .unwrap();
 8133    fake_server
 8134        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8135            assert_eq!(
 8136                params.text_document.uri,
 8137                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8138            );
 8139            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8140                lsp::CodeAction {
 8141                    title: "Organize Imports".to_string(),
 8142                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8143                    edit: Some(lsp::WorkspaceEdit {
 8144                        changes: Some(
 8145                            [(
 8146                                params.text_document.uri.clone(),
 8147                                vec![lsp::TextEdit::new(
 8148                                    lsp::Range::new(
 8149                                        lsp::Position::new(1, 0),
 8150                                        lsp::Position::new(2, 0),
 8151                                    ),
 8152                                    "".to_string(),
 8153                                )],
 8154                            )]
 8155                            .into_iter()
 8156                            .collect(),
 8157                        ),
 8158                        ..Default::default()
 8159                    }),
 8160                    ..Default::default()
 8161                },
 8162            )]))
 8163        })
 8164        .next()
 8165        .await;
 8166    cx.executor().start_waiting();
 8167    format.await;
 8168    assert_eq!(
 8169        editor.update(cx, |editor, cx| editor.text(cx)),
 8170        "import { a } from 'module';\n\nconst x = a;\n"
 8171    );
 8172
 8173    editor.update_in(cx, |editor, window, cx| {
 8174        editor.set_text(
 8175            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8176            window,
 8177            cx,
 8178        )
 8179    });
 8180    // Ensure we don't lock if code action hangs.
 8181    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8182        move |params, _| async move {
 8183            assert_eq!(
 8184                params.text_document.uri,
 8185                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8186            );
 8187            futures::future::pending::<()>().await;
 8188            unreachable!()
 8189        },
 8190    );
 8191    let format = editor
 8192        .update_in(cx, |editor, window, cx| {
 8193            editor.perform_code_action_kind(
 8194                project,
 8195                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8196                window,
 8197                cx,
 8198            )
 8199        })
 8200        .unwrap();
 8201    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8202    cx.executor().start_waiting();
 8203    format.await;
 8204    assert_eq!(
 8205        editor.update(cx, |editor, cx| editor.text(cx)),
 8206        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8207    );
 8208}
 8209
 8210#[gpui::test]
 8211async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8212    init_test(cx, |_| {});
 8213
 8214    let mut cx = EditorLspTestContext::new_rust(
 8215        lsp::ServerCapabilities {
 8216            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8217            ..Default::default()
 8218        },
 8219        cx,
 8220    )
 8221    .await;
 8222
 8223    cx.set_state(indoc! {"
 8224        one.twoˇ
 8225    "});
 8226
 8227    // The format request takes a long time. When it completes, it inserts
 8228    // a newline and an indent before the `.`
 8229    cx.lsp
 8230        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8231            let executor = cx.background_executor().clone();
 8232            async move {
 8233                executor.timer(Duration::from_millis(100)).await;
 8234                Ok(Some(vec![lsp::TextEdit {
 8235                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8236                    new_text: "\n    ".into(),
 8237                }]))
 8238            }
 8239        });
 8240
 8241    // Submit a format request.
 8242    let format_1 = cx
 8243        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8244        .unwrap();
 8245    cx.executor().run_until_parked();
 8246
 8247    // Submit a second format request.
 8248    let format_2 = cx
 8249        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8250        .unwrap();
 8251    cx.executor().run_until_parked();
 8252
 8253    // Wait for both format requests to complete
 8254    cx.executor().advance_clock(Duration::from_millis(200));
 8255    cx.executor().start_waiting();
 8256    format_1.await.unwrap();
 8257    cx.executor().start_waiting();
 8258    format_2.await.unwrap();
 8259
 8260    // The formatting edits only happens once.
 8261    cx.assert_editor_state(indoc! {"
 8262        one
 8263            .twoˇ
 8264    "});
 8265}
 8266
 8267#[gpui::test]
 8268async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8269    init_test(cx, |settings| {
 8270        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8271    });
 8272
 8273    let mut cx = EditorLspTestContext::new_rust(
 8274        lsp::ServerCapabilities {
 8275            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8276            ..Default::default()
 8277        },
 8278        cx,
 8279    )
 8280    .await;
 8281
 8282    // Set up a buffer white some trailing whitespace and no trailing newline.
 8283    cx.set_state(
 8284        &[
 8285            "one ",   //
 8286            "twoˇ",   //
 8287            "three ", //
 8288            "four",   //
 8289        ]
 8290        .join("\n"),
 8291    );
 8292
 8293    // Submit a format request.
 8294    let format = cx
 8295        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8296        .unwrap();
 8297
 8298    // Record which buffer changes have been sent to the language server
 8299    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8300    cx.lsp
 8301        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8302            let buffer_changes = buffer_changes.clone();
 8303            move |params, _| {
 8304                buffer_changes.lock().extend(
 8305                    params
 8306                        .content_changes
 8307                        .into_iter()
 8308                        .map(|e| (e.range.unwrap(), e.text)),
 8309                );
 8310            }
 8311        });
 8312
 8313    // Handle formatting requests to the language server.
 8314    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8315        let buffer_changes = buffer_changes.clone();
 8316        move |_, _| {
 8317            // When formatting is requested, trailing whitespace has already been stripped,
 8318            // and the trailing newline has already been added.
 8319            assert_eq!(
 8320                &buffer_changes.lock()[1..],
 8321                &[
 8322                    (
 8323                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8324                        "".into()
 8325                    ),
 8326                    (
 8327                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8328                        "".into()
 8329                    ),
 8330                    (
 8331                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8332                        "\n".into()
 8333                    ),
 8334                ]
 8335            );
 8336
 8337            // Insert blank lines between each line of the buffer.
 8338            async move {
 8339                Ok(Some(vec![
 8340                    lsp::TextEdit {
 8341                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8342                        new_text: "\n".into(),
 8343                    },
 8344                    lsp::TextEdit {
 8345                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8346                        new_text: "\n".into(),
 8347                    },
 8348                ]))
 8349            }
 8350        }
 8351    });
 8352
 8353    // After formatting the buffer, the trailing whitespace is stripped,
 8354    // a newline is appended, and the edits provided by the language server
 8355    // have been applied.
 8356    format.await.unwrap();
 8357    cx.assert_editor_state(
 8358        &[
 8359            "one",   //
 8360            "",      //
 8361            "twoˇ",  //
 8362            "",      //
 8363            "three", //
 8364            "four",  //
 8365            "",      //
 8366        ]
 8367        .join("\n"),
 8368    );
 8369
 8370    // Undoing the formatting undoes the trailing whitespace removal, the
 8371    // trailing newline, and the LSP edits.
 8372    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8373    cx.assert_editor_state(
 8374        &[
 8375            "one ",   //
 8376            "twoˇ",   //
 8377            "three ", //
 8378            "four",   //
 8379        ]
 8380        .join("\n"),
 8381    );
 8382}
 8383
 8384#[gpui::test]
 8385async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8386    cx: &mut TestAppContext,
 8387) {
 8388    init_test(cx, |_| {});
 8389
 8390    cx.update(|cx| {
 8391        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8392            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8393                settings.auto_signature_help = Some(true);
 8394            });
 8395        });
 8396    });
 8397
 8398    let mut cx = EditorLspTestContext::new_rust(
 8399        lsp::ServerCapabilities {
 8400            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8401                ..Default::default()
 8402            }),
 8403            ..Default::default()
 8404        },
 8405        cx,
 8406    )
 8407    .await;
 8408
 8409    let language = Language::new(
 8410        LanguageConfig {
 8411            name: "Rust".into(),
 8412            brackets: BracketPairConfig {
 8413                pairs: vec![
 8414                    BracketPair {
 8415                        start: "{".to_string(),
 8416                        end: "}".to_string(),
 8417                        close: true,
 8418                        surround: true,
 8419                        newline: true,
 8420                    },
 8421                    BracketPair {
 8422                        start: "(".to_string(),
 8423                        end: ")".to_string(),
 8424                        close: true,
 8425                        surround: true,
 8426                        newline: true,
 8427                    },
 8428                    BracketPair {
 8429                        start: "/*".to_string(),
 8430                        end: " */".to_string(),
 8431                        close: true,
 8432                        surround: true,
 8433                        newline: true,
 8434                    },
 8435                    BracketPair {
 8436                        start: "[".to_string(),
 8437                        end: "]".to_string(),
 8438                        close: false,
 8439                        surround: false,
 8440                        newline: true,
 8441                    },
 8442                    BracketPair {
 8443                        start: "\"".to_string(),
 8444                        end: "\"".to_string(),
 8445                        close: true,
 8446                        surround: true,
 8447                        newline: false,
 8448                    },
 8449                    BracketPair {
 8450                        start: "<".to_string(),
 8451                        end: ">".to_string(),
 8452                        close: false,
 8453                        surround: true,
 8454                        newline: true,
 8455                    },
 8456                ],
 8457                ..Default::default()
 8458            },
 8459            autoclose_before: "})]".to_string(),
 8460            ..Default::default()
 8461        },
 8462        Some(tree_sitter_rust::LANGUAGE.into()),
 8463    );
 8464    let language = Arc::new(language);
 8465
 8466    cx.language_registry().add(language.clone());
 8467    cx.update_buffer(|buffer, cx| {
 8468        buffer.set_language(Some(language), cx);
 8469    });
 8470
 8471    cx.set_state(
 8472        &r#"
 8473            fn main() {
 8474                sampleˇ
 8475            }
 8476        "#
 8477        .unindent(),
 8478    );
 8479
 8480    cx.update_editor(|editor, window, cx| {
 8481        editor.handle_input("(", window, cx);
 8482    });
 8483    cx.assert_editor_state(
 8484        &"
 8485            fn main() {
 8486                sample(ˇ)
 8487            }
 8488        "
 8489        .unindent(),
 8490    );
 8491
 8492    let mocked_response = lsp::SignatureHelp {
 8493        signatures: vec![lsp::SignatureInformation {
 8494            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8495            documentation: None,
 8496            parameters: Some(vec![
 8497                lsp::ParameterInformation {
 8498                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8499                    documentation: None,
 8500                },
 8501                lsp::ParameterInformation {
 8502                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8503                    documentation: None,
 8504                },
 8505            ]),
 8506            active_parameter: None,
 8507        }],
 8508        active_signature: Some(0),
 8509        active_parameter: Some(0),
 8510    };
 8511    handle_signature_help_request(&mut cx, mocked_response).await;
 8512
 8513    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8514        .await;
 8515
 8516    cx.editor(|editor, _, _| {
 8517        let signature_help_state = editor.signature_help_state.popover().cloned();
 8518        assert_eq!(
 8519            signature_help_state.unwrap().label,
 8520            "param1: u8, param2: u8"
 8521        );
 8522    });
 8523}
 8524
 8525#[gpui::test]
 8526async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8527    init_test(cx, |_| {});
 8528
 8529    cx.update(|cx| {
 8530        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8531            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8532                settings.auto_signature_help = Some(false);
 8533                settings.show_signature_help_after_edits = Some(false);
 8534            });
 8535        });
 8536    });
 8537
 8538    let mut cx = EditorLspTestContext::new_rust(
 8539        lsp::ServerCapabilities {
 8540            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8541                ..Default::default()
 8542            }),
 8543            ..Default::default()
 8544        },
 8545        cx,
 8546    )
 8547    .await;
 8548
 8549    let language = Language::new(
 8550        LanguageConfig {
 8551            name: "Rust".into(),
 8552            brackets: BracketPairConfig {
 8553                pairs: vec![
 8554                    BracketPair {
 8555                        start: "{".to_string(),
 8556                        end: "}".to_string(),
 8557                        close: true,
 8558                        surround: true,
 8559                        newline: true,
 8560                    },
 8561                    BracketPair {
 8562                        start: "(".to_string(),
 8563                        end: ")".to_string(),
 8564                        close: true,
 8565                        surround: true,
 8566                        newline: true,
 8567                    },
 8568                    BracketPair {
 8569                        start: "/*".to_string(),
 8570                        end: " */".to_string(),
 8571                        close: true,
 8572                        surround: true,
 8573                        newline: true,
 8574                    },
 8575                    BracketPair {
 8576                        start: "[".to_string(),
 8577                        end: "]".to_string(),
 8578                        close: false,
 8579                        surround: false,
 8580                        newline: true,
 8581                    },
 8582                    BracketPair {
 8583                        start: "\"".to_string(),
 8584                        end: "\"".to_string(),
 8585                        close: true,
 8586                        surround: true,
 8587                        newline: false,
 8588                    },
 8589                    BracketPair {
 8590                        start: "<".to_string(),
 8591                        end: ">".to_string(),
 8592                        close: false,
 8593                        surround: true,
 8594                        newline: true,
 8595                    },
 8596                ],
 8597                ..Default::default()
 8598            },
 8599            autoclose_before: "})]".to_string(),
 8600            ..Default::default()
 8601        },
 8602        Some(tree_sitter_rust::LANGUAGE.into()),
 8603    );
 8604    let language = Arc::new(language);
 8605
 8606    cx.language_registry().add(language.clone());
 8607    cx.update_buffer(|buffer, cx| {
 8608        buffer.set_language(Some(language), cx);
 8609    });
 8610
 8611    // Ensure that signature_help is not called when no signature help is enabled.
 8612    cx.set_state(
 8613        &r#"
 8614            fn main() {
 8615                sampleˇ
 8616            }
 8617        "#
 8618        .unindent(),
 8619    );
 8620    cx.update_editor(|editor, window, cx| {
 8621        editor.handle_input("(", window, cx);
 8622    });
 8623    cx.assert_editor_state(
 8624        &"
 8625            fn main() {
 8626                sample(ˇ)
 8627            }
 8628        "
 8629        .unindent(),
 8630    );
 8631    cx.editor(|editor, _, _| {
 8632        assert!(editor.signature_help_state.task().is_none());
 8633    });
 8634
 8635    let mocked_response = lsp::SignatureHelp {
 8636        signatures: vec![lsp::SignatureInformation {
 8637            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8638            documentation: None,
 8639            parameters: Some(vec![
 8640                lsp::ParameterInformation {
 8641                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8642                    documentation: None,
 8643                },
 8644                lsp::ParameterInformation {
 8645                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8646                    documentation: None,
 8647                },
 8648            ]),
 8649            active_parameter: None,
 8650        }],
 8651        active_signature: Some(0),
 8652        active_parameter: Some(0),
 8653    };
 8654
 8655    // Ensure that signature_help is called when enabled afte edits
 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(false);
 8660                settings.show_signature_help_after_edits = Some(true);
 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.clone()).await;
 8684    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8685        .await;
 8686    cx.update_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        editor.signature_help_state = SignatureHelpState::default();
 8694    });
 8695
 8696    // Ensure that signature_help is called when auto signature help override is enabled
 8697    cx.update(|_, cx| {
 8698        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8699            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8700                settings.auto_signature_help = Some(true);
 8701                settings.show_signature_help_after_edits = Some(false);
 8702            });
 8703        });
 8704    });
 8705    cx.set_state(
 8706        &r#"
 8707            fn main() {
 8708                sampleˇ
 8709            }
 8710        "#
 8711        .unindent(),
 8712    );
 8713    cx.update_editor(|editor, window, cx| {
 8714        editor.handle_input("(", window, cx);
 8715    });
 8716    cx.assert_editor_state(
 8717        &"
 8718            fn main() {
 8719                sample(ˇ)
 8720            }
 8721        "
 8722        .unindent(),
 8723    );
 8724    handle_signature_help_request(&mut cx, mocked_response).await;
 8725    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8726        .await;
 8727    cx.editor(|editor, _, _| {
 8728        let signature_help_state = editor.signature_help_state.popover().cloned();
 8729        assert!(signature_help_state.is_some());
 8730        assert_eq!(
 8731            signature_help_state.unwrap().label,
 8732            "param1: u8, param2: u8"
 8733        );
 8734    });
 8735}
 8736
 8737#[gpui::test]
 8738async fn test_signature_help(cx: &mut TestAppContext) {
 8739    init_test(cx, |_| {});
 8740    cx.update(|cx| {
 8741        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8742            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8743                settings.auto_signature_help = Some(true);
 8744            });
 8745        });
 8746    });
 8747
 8748    let mut cx = EditorLspTestContext::new_rust(
 8749        lsp::ServerCapabilities {
 8750            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8751                ..Default::default()
 8752            }),
 8753            ..Default::default()
 8754        },
 8755        cx,
 8756    )
 8757    .await;
 8758
 8759    // A test that directly calls `show_signature_help`
 8760    cx.update_editor(|editor, window, cx| {
 8761        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8762    });
 8763
 8764    let mocked_response = lsp::SignatureHelp {
 8765        signatures: vec![lsp::SignatureInformation {
 8766            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8767            documentation: None,
 8768            parameters: Some(vec![
 8769                lsp::ParameterInformation {
 8770                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8771                    documentation: None,
 8772                },
 8773                lsp::ParameterInformation {
 8774                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8775                    documentation: None,
 8776                },
 8777            ]),
 8778            active_parameter: None,
 8779        }],
 8780        active_signature: Some(0),
 8781        active_parameter: Some(0),
 8782    };
 8783    handle_signature_help_request(&mut cx, mocked_response).await;
 8784
 8785    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8786        .await;
 8787
 8788    cx.editor(|editor, _, _| {
 8789        let signature_help_state = editor.signature_help_state.popover().cloned();
 8790        assert!(signature_help_state.is_some());
 8791        assert_eq!(
 8792            signature_help_state.unwrap().label,
 8793            "param1: u8, param2: u8"
 8794        );
 8795    });
 8796
 8797    // When exiting outside from inside the brackets, `signature_help` is closed.
 8798    cx.set_state(indoc! {"
 8799        fn main() {
 8800            sample(ˇ);
 8801        }
 8802
 8803        fn sample(param1: u8, param2: u8) {}
 8804    "});
 8805
 8806    cx.update_editor(|editor, window, cx| {
 8807        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8808    });
 8809
 8810    let mocked_response = lsp::SignatureHelp {
 8811        signatures: Vec::new(),
 8812        active_signature: None,
 8813        active_parameter: None,
 8814    };
 8815    handle_signature_help_request(&mut cx, mocked_response).await;
 8816
 8817    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8818        .await;
 8819
 8820    cx.editor(|editor, _, _| {
 8821        assert!(!editor.signature_help_state.is_shown());
 8822    });
 8823
 8824    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8825    cx.set_state(indoc! {"
 8826        fn main() {
 8827            sample(ˇ);
 8828        }
 8829
 8830        fn sample(param1: u8, param2: u8) {}
 8831    "});
 8832
 8833    let mocked_response = lsp::SignatureHelp {
 8834        signatures: vec![lsp::SignatureInformation {
 8835            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8836            documentation: None,
 8837            parameters: Some(vec![
 8838                lsp::ParameterInformation {
 8839                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8840                    documentation: None,
 8841                },
 8842                lsp::ParameterInformation {
 8843                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8844                    documentation: None,
 8845                },
 8846            ]),
 8847            active_parameter: None,
 8848        }],
 8849        active_signature: Some(0),
 8850        active_parameter: Some(0),
 8851    };
 8852    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8853    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8854        .await;
 8855    cx.editor(|editor, _, _| {
 8856        assert!(editor.signature_help_state.is_shown());
 8857    });
 8858
 8859    // Restore the popover with more parameter input
 8860    cx.set_state(indoc! {"
 8861        fn main() {
 8862            sample(param1, param2ˇ);
 8863        }
 8864
 8865        fn sample(param1: u8, param2: u8) {}
 8866    "});
 8867
 8868    let mocked_response = lsp::SignatureHelp {
 8869        signatures: vec![lsp::SignatureInformation {
 8870            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8871            documentation: None,
 8872            parameters: Some(vec![
 8873                lsp::ParameterInformation {
 8874                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8875                    documentation: None,
 8876                },
 8877                lsp::ParameterInformation {
 8878                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8879                    documentation: None,
 8880                },
 8881            ]),
 8882            active_parameter: None,
 8883        }],
 8884        active_signature: Some(0),
 8885        active_parameter: Some(1),
 8886    };
 8887    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8888    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8889        .await;
 8890
 8891    // When selecting a range, the popover is gone.
 8892    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8893    cx.update_editor(|editor, window, cx| {
 8894        editor.change_selections(None, window, cx, |s| {
 8895            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8896        })
 8897    });
 8898    cx.assert_editor_state(indoc! {"
 8899        fn main() {
 8900            sample(param1, «ˇparam2»);
 8901        }
 8902
 8903        fn sample(param1: u8, param2: u8) {}
 8904    "});
 8905    cx.editor(|editor, _, _| {
 8906        assert!(!editor.signature_help_state.is_shown());
 8907    });
 8908
 8909    // When unselecting again, the popover is back if within the brackets.
 8910    cx.update_editor(|editor, window, cx| {
 8911        editor.change_selections(None, window, cx, |s| {
 8912            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8913        })
 8914    });
 8915    cx.assert_editor_state(indoc! {"
 8916        fn main() {
 8917            sample(param1, ˇparam2);
 8918        }
 8919
 8920        fn sample(param1: u8, param2: u8) {}
 8921    "});
 8922    handle_signature_help_request(&mut cx, mocked_response).await;
 8923    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8924        .await;
 8925    cx.editor(|editor, _, _| {
 8926        assert!(editor.signature_help_state.is_shown());
 8927    });
 8928
 8929    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8930    cx.update_editor(|editor, window, cx| {
 8931        editor.change_selections(None, window, cx, |s| {
 8932            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8933            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8934        })
 8935    });
 8936    cx.assert_editor_state(indoc! {"
 8937        fn main() {
 8938            sample(param1, ˇparam2);
 8939        }
 8940
 8941        fn sample(param1: u8, param2: u8) {}
 8942    "});
 8943
 8944    let mocked_response = lsp::SignatureHelp {
 8945        signatures: vec![lsp::SignatureInformation {
 8946            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8947            documentation: None,
 8948            parameters: Some(vec![
 8949                lsp::ParameterInformation {
 8950                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8951                    documentation: None,
 8952                },
 8953                lsp::ParameterInformation {
 8954                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8955                    documentation: None,
 8956                },
 8957            ]),
 8958            active_parameter: None,
 8959        }],
 8960        active_signature: Some(0),
 8961        active_parameter: Some(1),
 8962    };
 8963    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8964    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8965        .await;
 8966    cx.update_editor(|editor, _, cx| {
 8967        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8968    });
 8969    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8970        .await;
 8971    cx.update_editor(|editor, window, cx| {
 8972        editor.change_selections(None, window, cx, |s| {
 8973            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8974        })
 8975    });
 8976    cx.assert_editor_state(indoc! {"
 8977        fn main() {
 8978            sample(param1, «ˇparam2»);
 8979        }
 8980
 8981        fn sample(param1: u8, param2: u8) {}
 8982    "});
 8983    cx.update_editor(|editor, window, cx| {
 8984        editor.change_selections(None, window, cx, |s| {
 8985            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8986        })
 8987    });
 8988    cx.assert_editor_state(indoc! {"
 8989        fn main() {
 8990            sample(param1, ˇparam2);
 8991        }
 8992
 8993        fn sample(param1: u8, param2: u8) {}
 8994    "});
 8995    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8996        .await;
 8997}
 8998
 8999#[gpui::test]
 9000async fn test_completion(cx: &mut TestAppContext) {
 9001    init_test(cx, |_| {});
 9002
 9003    let mut cx = EditorLspTestContext::new_rust(
 9004        lsp::ServerCapabilities {
 9005            completion_provider: Some(lsp::CompletionOptions {
 9006                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9007                resolve_provider: Some(true),
 9008                ..Default::default()
 9009            }),
 9010            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9011            ..Default::default()
 9012        },
 9013        cx,
 9014    )
 9015    .await;
 9016    let counter = Arc::new(AtomicUsize::new(0));
 9017
 9018    cx.set_state(indoc! {"
 9019        oneˇ
 9020        two
 9021        three
 9022    "});
 9023    cx.simulate_keystroke(".");
 9024    handle_completion_request(
 9025        &mut cx,
 9026        indoc! {"
 9027            one.|<>
 9028            two
 9029            three
 9030        "},
 9031        vec!["first_completion", "second_completion"],
 9032        counter.clone(),
 9033    )
 9034    .await;
 9035    cx.condition(|editor, _| editor.context_menu_visible())
 9036        .await;
 9037    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9038
 9039    let _handler = handle_signature_help_request(
 9040        &mut cx,
 9041        lsp::SignatureHelp {
 9042            signatures: vec![lsp::SignatureInformation {
 9043                label: "test signature".to_string(),
 9044                documentation: None,
 9045                parameters: Some(vec![lsp::ParameterInformation {
 9046                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9047                    documentation: None,
 9048                }]),
 9049                active_parameter: None,
 9050            }],
 9051            active_signature: None,
 9052            active_parameter: None,
 9053        },
 9054    );
 9055    cx.update_editor(|editor, window, cx| {
 9056        assert!(
 9057            !editor.signature_help_state.is_shown(),
 9058            "No signature help was called for"
 9059        );
 9060        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9061    });
 9062    cx.run_until_parked();
 9063    cx.update_editor(|editor, _, _| {
 9064        assert!(
 9065            !editor.signature_help_state.is_shown(),
 9066            "No signature help should be shown when completions menu is open"
 9067        );
 9068    });
 9069
 9070    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9071        editor.context_menu_next(&Default::default(), window, cx);
 9072        editor
 9073            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9074            .unwrap()
 9075    });
 9076    cx.assert_editor_state(indoc! {"
 9077        one.second_completionˇ
 9078        two
 9079        three
 9080    "});
 9081
 9082    handle_resolve_completion_request(
 9083        &mut cx,
 9084        Some(vec![
 9085            (
 9086                //This overlaps with the primary completion edit which is
 9087                //misbehavior from the LSP spec, test that we filter it out
 9088                indoc! {"
 9089                    one.second_ˇcompletion
 9090                    two
 9091                    threeˇ
 9092                "},
 9093                "overlapping additional edit",
 9094            ),
 9095            (
 9096                indoc! {"
 9097                    one.second_completion
 9098                    two
 9099                    threeˇ
 9100                "},
 9101                "\nadditional edit",
 9102            ),
 9103        ]),
 9104    )
 9105    .await;
 9106    apply_additional_edits.await.unwrap();
 9107    cx.assert_editor_state(indoc! {"
 9108        one.second_completionˇ
 9109        two
 9110        three
 9111        additional edit
 9112    "});
 9113
 9114    cx.set_state(indoc! {"
 9115        one.second_completion
 9116        twoˇ
 9117        threeˇ
 9118        additional edit
 9119    "});
 9120    cx.simulate_keystroke(" ");
 9121    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9122    cx.simulate_keystroke("s");
 9123    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9124
 9125    cx.assert_editor_state(indoc! {"
 9126        one.second_completion
 9127        two sˇ
 9128        three sˇ
 9129        additional edit
 9130    "});
 9131    handle_completion_request(
 9132        &mut cx,
 9133        indoc! {"
 9134            one.second_completion
 9135            two s
 9136            three <s|>
 9137            additional edit
 9138        "},
 9139        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9140        counter.clone(),
 9141    )
 9142    .await;
 9143    cx.condition(|editor, _| editor.context_menu_visible())
 9144        .await;
 9145    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9146
 9147    cx.simulate_keystroke("i");
 9148
 9149    handle_completion_request(
 9150        &mut cx,
 9151        indoc! {"
 9152            one.second_completion
 9153            two si
 9154            three <si|>
 9155            additional edit
 9156        "},
 9157        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9158        counter.clone(),
 9159    )
 9160    .await;
 9161    cx.condition(|editor, _| editor.context_menu_visible())
 9162        .await;
 9163    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9164
 9165    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9166        editor
 9167            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9168            .unwrap()
 9169    });
 9170    cx.assert_editor_state(indoc! {"
 9171        one.second_completion
 9172        two sixth_completionˇ
 9173        three sixth_completionˇ
 9174        additional edit
 9175    "});
 9176
 9177    apply_additional_edits.await.unwrap();
 9178
 9179    update_test_language_settings(&mut cx, |settings| {
 9180        settings.defaults.show_completions_on_input = Some(false);
 9181    });
 9182    cx.set_state("editorˇ");
 9183    cx.simulate_keystroke(".");
 9184    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9185    cx.simulate_keystroke("c");
 9186    cx.simulate_keystroke("l");
 9187    cx.simulate_keystroke("o");
 9188    cx.assert_editor_state("editor.cloˇ");
 9189    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9190    cx.update_editor(|editor, window, cx| {
 9191        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9192    });
 9193    handle_completion_request(
 9194        &mut cx,
 9195        "editor.<clo|>",
 9196        vec!["close", "clobber"],
 9197        counter.clone(),
 9198    )
 9199    .await;
 9200    cx.condition(|editor, _| editor.context_menu_visible())
 9201        .await;
 9202    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9203
 9204    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9205        editor
 9206            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9207            .unwrap()
 9208    });
 9209    cx.assert_editor_state("editor.closeˇ");
 9210    handle_resolve_completion_request(&mut cx, None).await;
 9211    apply_additional_edits.await.unwrap();
 9212}
 9213
 9214#[gpui::test]
 9215async fn test_word_completion(cx: &mut TestAppContext) {
 9216    let lsp_fetch_timeout_ms = 10;
 9217    init_test(cx, |language_settings| {
 9218        language_settings.defaults.completions = Some(CompletionSettings {
 9219            words: WordsCompletionMode::Fallback,
 9220            lsp: true,
 9221            lsp_fetch_timeout_ms: 10,
 9222        });
 9223    });
 9224
 9225    let mut cx = EditorLspTestContext::new_rust(
 9226        lsp::ServerCapabilities {
 9227            completion_provider: Some(lsp::CompletionOptions {
 9228                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9229                ..lsp::CompletionOptions::default()
 9230            }),
 9231            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9232            ..lsp::ServerCapabilities::default()
 9233        },
 9234        cx,
 9235    )
 9236    .await;
 9237
 9238    let throttle_completions = Arc::new(AtomicBool::new(false));
 9239
 9240    let lsp_throttle_completions = throttle_completions.clone();
 9241    let _completion_requests_handler =
 9242        cx.lsp
 9243            .server
 9244            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9245                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9246                async move {
 9247                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9248                        cx.background_executor()
 9249                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9250                            .await;
 9251                    }
 9252                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9253                        lsp::CompletionItem {
 9254                            label: "first".into(),
 9255                            ..lsp::CompletionItem::default()
 9256                        },
 9257                        lsp::CompletionItem {
 9258                            label: "last".into(),
 9259                            ..lsp::CompletionItem::default()
 9260                        },
 9261                    ])))
 9262                }
 9263            });
 9264
 9265    cx.set_state(indoc! {"
 9266        oneˇ
 9267        two
 9268        three
 9269    "});
 9270    cx.simulate_keystroke(".");
 9271    cx.executor().run_until_parked();
 9272    cx.condition(|editor, _| editor.context_menu_visible())
 9273        .await;
 9274    cx.update_editor(|editor, window, cx| {
 9275        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9276        {
 9277            assert_eq!(
 9278                completion_menu_entries(&menu),
 9279                &["first", "last"],
 9280                "When LSP server is fast to reply, no fallback word completions are used"
 9281            );
 9282        } else {
 9283            panic!("expected completion menu to be open");
 9284        }
 9285        editor.cancel(&Cancel, window, cx);
 9286    });
 9287    cx.executor().run_until_parked();
 9288    cx.condition(|editor, _| !editor.context_menu_visible())
 9289        .await;
 9290
 9291    throttle_completions.store(true, atomic::Ordering::Release);
 9292    cx.simulate_keystroke(".");
 9293    cx.executor()
 9294        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9295    cx.executor().run_until_parked();
 9296    cx.condition(|editor, _| editor.context_menu_visible())
 9297        .await;
 9298    cx.update_editor(|editor, _, _| {
 9299        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9300        {
 9301            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9302                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9303        } else {
 9304            panic!("expected completion menu to be open");
 9305        }
 9306    });
 9307}
 9308
 9309#[gpui::test]
 9310async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9311    init_test(cx, |language_settings| {
 9312        language_settings.defaults.completions = Some(CompletionSettings {
 9313            words: WordsCompletionMode::Enabled,
 9314            lsp: true,
 9315            lsp_fetch_timeout_ms: 0,
 9316        });
 9317    });
 9318
 9319    let mut cx = EditorLspTestContext::new_rust(
 9320        lsp::ServerCapabilities {
 9321            completion_provider: Some(lsp::CompletionOptions {
 9322                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9323                ..lsp::CompletionOptions::default()
 9324            }),
 9325            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9326            ..lsp::ServerCapabilities::default()
 9327        },
 9328        cx,
 9329    )
 9330    .await;
 9331
 9332    let _completion_requests_handler =
 9333        cx.lsp
 9334            .server
 9335            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9336                Ok(Some(lsp::CompletionResponse::Array(vec![
 9337                    lsp::CompletionItem {
 9338                        label: "first".into(),
 9339                        ..lsp::CompletionItem::default()
 9340                    },
 9341                    lsp::CompletionItem {
 9342                        label: "last".into(),
 9343                        ..lsp::CompletionItem::default()
 9344                    },
 9345                ])))
 9346            });
 9347
 9348    cx.set_state(indoc! {"ˇ
 9349        first
 9350        last
 9351        second
 9352    "});
 9353    cx.simulate_keystroke(".");
 9354    cx.executor().run_until_parked();
 9355    cx.condition(|editor, _| editor.context_menu_visible())
 9356        .await;
 9357    cx.update_editor(|editor, _, _| {
 9358        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9359        {
 9360            assert_eq!(
 9361                completion_menu_entries(&menu),
 9362                &["first", "last", "second"],
 9363                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9364            );
 9365        } else {
 9366            panic!("expected completion menu to be open");
 9367        }
 9368    });
 9369}
 9370
 9371#[gpui::test]
 9372async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9373    init_test(cx, |language_settings| {
 9374        language_settings.defaults.completions = Some(CompletionSettings {
 9375            words: WordsCompletionMode::Disabled,
 9376            lsp: true,
 9377            lsp_fetch_timeout_ms: 0,
 9378        });
 9379    });
 9380
 9381    let mut cx = EditorLspTestContext::new_rust(
 9382        lsp::ServerCapabilities {
 9383            completion_provider: Some(lsp::CompletionOptions {
 9384                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9385                ..lsp::CompletionOptions::default()
 9386            }),
 9387            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9388            ..lsp::ServerCapabilities::default()
 9389        },
 9390        cx,
 9391    )
 9392    .await;
 9393
 9394    let _completion_requests_handler =
 9395        cx.lsp
 9396            .server
 9397            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9398                panic!("LSP completions should not be queried when dealing with word completions")
 9399            });
 9400
 9401    cx.set_state(indoc! {"ˇ
 9402        first
 9403        last
 9404        second
 9405    "});
 9406    cx.update_editor(|editor, window, cx| {
 9407        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9408    });
 9409    cx.executor().run_until_parked();
 9410    cx.condition(|editor, _| editor.context_menu_visible())
 9411        .await;
 9412    cx.update_editor(|editor, _, _| {
 9413        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9414        {
 9415            assert_eq!(
 9416                completion_menu_entries(&menu),
 9417                &["first", "last", "second"],
 9418                "`ShowWordCompletions` action should show word completions"
 9419            );
 9420        } else {
 9421            panic!("expected completion menu to be open");
 9422        }
 9423    });
 9424
 9425    cx.simulate_keystroke("s");
 9426    cx.executor().run_until_parked();
 9427    cx.condition(|editor, _| editor.context_menu_visible())
 9428        .await;
 9429    cx.update_editor(|editor, _, _| {
 9430        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9431        {
 9432            assert_eq!(
 9433                completion_menu_entries(&menu),
 9434                &["second"],
 9435                "After showing word completions, further editing should filter them and not query the LSP"
 9436            );
 9437        } else {
 9438            panic!("expected completion menu to be open");
 9439        }
 9440    });
 9441}
 9442
 9443#[gpui::test]
 9444async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9445    init_test(cx, |language_settings| {
 9446        language_settings.defaults.completions = Some(CompletionSettings {
 9447            words: WordsCompletionMode::Fallback,
 9448            lsp: false,
 9449            lsp_fetch_timeout_ms: 0,
 9450        });
 9451    });
 9452
 9453    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9454
 9455    cx.set_state(indoc! {"ˇ
 9456        0_usize
 9457        let
 9458        33
 9459        4.5f32
 9460    "});
 9461    cx.update_editor(|editor, window, cx| {
 9462        editor.show_completions(&ShowCompletions::default(), window, cx);
 9463    });
 9464    cx.executor().run_until_parked();
 9465    cx.condition(|editor, _| editor.context_menu_visible())
 9466        .await;
 9467    cx.update_editor(|editor, window, cx| {
 9468        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9469        {
 9470            assert_eq!(
 9471                completion_menu_entries(&menu),
 9472                &["let"],
 9473                "With no digits in the completion query, no digits should be in the word completions"
 9474            );
 9475        } else {
 9476            panic!("expected completion menu to be open");
 9477        }
 9478        editor.cancel(&Cancel, window, cx);
 9479    });
 9480
 9481    cx.set_state(indoc! {" 9482        0_usize
 9483        let
 9484        3
 9485        33.35f32
 9486    "});
 9487    cx.update_editor(|editor, window, cx| {
 9488        editor.show_completions(&ShowCompletions::default(), window, cx);
 9489    });
 9490    cx.executor().run_until_parked();
 9491    cx.condition(|editor, _| editor.context_menu_visible())
 9492        .await;
 9493    cx.update_editor(|editor, _, _| {
 9494        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9495        {
 9496            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9497                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9498        } else {
 9499            panic!("expected completion menu to be open");
 9500        }
 9501    });
 9502}
 9503
 9504#[gpui::test]
 9505async fn test_multiline_completion(cx: &mut TestAppContext) {
 9506    init_test(cx, |_| {});
 9507
 9508    let fs = FakeFs::new(cx.executor());
 9509    fs.insert_tree(
 9510        path!("/a"),
 9511        json!({
 9512            "main.ts": "a",
 9513        }),
 9514    )
 9515    .await;
 9516
 9517    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9518    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9519    let typescript_language = Arc::new(Language::new(
 9520        LanguageConfig {
 9521            name: "TypeScript".into(),
 9522            matcher: LanguageMatcher {
 9523                path_suffixes: vec!["ts".to_string()],
 9524                ..LanguageMatcher::default()
 9525            },
 9526            line_comments: vec!["// ".into()],
 9527            ..LanguageConfig::default()
 9528        },
 9529        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9530    ));
 9531    language_registry.add(typescript_language.clone());
 9532    let mut fake_servers = language_registry.register_fake_lsp(
 9533        "TypeScript",
 9534        FakeLspAdapter {
 9535            capabilities: lsp::ServerCapabilities {
 9536                completion_provider: Some(lsp::CompletionOptions {
 9537                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9538                    ..lsp::CompletionOptions::default()
 9539                }),
 9540                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9541                ..lsp::ServerCapabilities::default()
 9542            },
 9543            // Emulate vtsls label generation
 9544            label_for_completion: Some(Box::new(|item, _| {
 9545                let text = if let Some(description) = item
 9546                    .label_details
 9547                    .as_ref()
 9548                    .and_then(|label_details| label_details.description.as_ref())
 9549                {
 9550                    format!("{} {}", item.label, description)
 9551                } else if let Some(detail) = &item.detail {
 9552                    format!("{} {}", item.label, detail)
 9553                } else {
 9554                    item.label.clone()
 9555                };
 9556                let len = text.len();
 9557                Some(language::CodeLabel {
 9558                    text,
 9559                    runs: Vec::new(),
 9560                    filter_range: 0..len,
 9561                })
 9562            })),
 9563            ..FakeLspAdapter::default()
 9564        },
 9565    );
 9566    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9567    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9568    let worktree_id = workspace
 9569        .update(cx, |workspace, _window, cx| {
 9570            workspace.project().update(cx, |project, cx| {
 9571                project.worktrees(cx).next().unwrap().read(cx).id()
 9572            })
 9573        })
 9574        .unwrap();
 9575    let _buffer = project
 9576        .update(cx, |project, cx| {
 9577            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9578        })
 9579        .await
 9580        .unwrap();
 9581    let editor = workspace
 9582        .update(cx, |workspace, window, cx| {
 9583            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9584        })
 9585        .unwrap()
 9586        .await
 9587        .unwrap()
 9588        .downcast::<Editor>()
 9589        .unwrap();
 9590    let fake_server = fake_servers.next().await.unwrap();
 9591
 9592    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9593    let multiline_label_2 = "a\nb\nc\n";
 9594    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9595    let multiline_description = "d\ne\nf\n";
 9596    let multiline_detail_2 = "g\nh\ni\n";
 9597
 9598    let mut completion_handle =
 9599        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9600            Ok(Some(lsp::CompletionResponse::Array(vec![
 9601                lsp::CompletionItem {
 9602                    label: multiline_label.to_string(),
 9603                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9604                        range: lsp::Range {
 9605                            start: lsp::Position {
 9606                                line: params.text_document_position.position.line,
 9607                                character: params.text_document_position.position.character,
 9608                            },
 9609                            end: lsp::Position {
 9610                                line: params.text_document_position.position.line,
 9611                                character: params.text_document_position.position.character,
 9612                            },
 9613                        },
 9614                        new_text: "new_text_1".to_string(),
 9615                    })),
 9616                    ..lsp::CompletionItem::default()
 9617                },
 9618                lsp::CompletionItem {
 9619                    label: "single line label 1".to_string(),
 9620                    detail: Some(multiline_detail.to_string()),
 9621                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9622                        range: lsp::Range {
 9623                            start: lsp::Position {
 9624                                line: params.text_document_position.position.line,
 9625                                character: params.text_document_position.position.character,
 9626                            },
 9627                            end: lsp::Position {
 9628                                line: params.text_document_position.position.line,
 9629                                character: params.text_document_position.position.character,
 9630                            },
 9631                        },
 9632                        new_text: "new_text_2".to_string(),
 9633                    })),
 9634                    ..lsp::CompletionItem::default()
 9635                },
 9636                lsp::CompletionItem {
 9637                    label: "single line label 2".to_string(),
 9638                    label_details: Some(lsp::CompletionItemLabelDetails {
 9639                        description: Some(multiline_description.to_string()),
 9640                        detail: None,
 9641                    }),
 9642                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9643                        range: lsp::Range {
 9644                            start: lsp::Position {
 9645                                line: params.text_document_position.position.line,
 9646                                character: params.text_document_position.position.character,
 9647                            },
 9648                            end: lsp::Position {
 9649                                line: params.text_document_position.position.line,
 9650                                character: params.text_document_position.position.character,
 9651                            },
 9652                        },
 9653                        new_text: "new_text_2".to_string(),
 9654                    })),
 9655                    ..lsp::CompletionItem::default()
 9656                },
 9657                lsp::CompletionItem {
 9658                    label: multiline_label_2.to_string(),
 9659                    detail: Some(multiline_detail_2.to_string()),
 9660                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9661                        range: lsp::Range {
 9662                            start: lsp::Position {
 9663                                line: params.text_document_position.position.line,
 9664                                character: params.text_document_position.position.character,
 9665                            },
 9666                            end: lsp::Position {
 9667                                line: params.text_document_position.position.line,
 9668                                character: params.text_document_position.position.character,
 9669                            },
 9670                        },
 9671                        new_text: "new_text_3".to_string(),
 9672                    })),
 9673                    ..lsp::CompletionItem::default()
 9674                },
 9675                lsp::CompletionItem {
 9676                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9677                    detail: Some(
 9678                        "Details with many     spaces and \t but without newlines".to_string(),
 9679                    ),
 9680                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9681                        range: lsp::Range {
 9682                            start: lsp::Position {
 9683                                line: params.text_document_position.position.line,
 9684                                character: params.text_document_position.position.character,
 9685                            },
 9686                            end: lsp::Position {
 9687                                line: params.text_document_position.position.line,
 9688                                character: params.text_document_position.position.character,
 9689                            },
 9690                        },
 9691                        new_text: "new_text_4".to_string(),
 9692                    })),
 9693                    ..lsp::CompletionItem::default()
 9694                },
 9695            ])))
 9696        });
 9697
 9698    editor.update_in(cx, |editor, window, cx| {
 9699        cx.focus_self(window);
 9700        editor.move_to_end(&MoveToEnd, window, cx);
 9701        editor.handle_input(".", window, cx);
 9702    });
 9703    cx.run_until_parked();
 9704    completion_handle.next().await.unwrap();
 9705
 9706    editor.update(cx, |editor, _| {
 9707        assert!(editor.context_menu_visible());
 9708        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9709        {
 9710            let completion_labels = menu
 9711                .completions
 9712                .borrow()
 9713                .iter()
 9714                .map(|c| c.label.text.clone())
 9715                .collect::<Vec<_>>();
 9716            assert_eq!(
 9717                completion_labels,
 9718                &[
 9719                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9720                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9721                    "single line label 2 d e f ",
 9722                    "a b c g h i ",
 9723                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9724                ],
 9725                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9726            );
 9727
 9728            for completion in menu
 9729                .completions
 9730                .borrow()
 9731                .iter() {
 9732                    assert_eq!(
 9733                        completion.label.filter_range,
 9734                        0..completion.label.text.len(),
 9735                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9736                    );
 9737                }
 9738
 9739        } else {
 9740            panic!("expected completion menu to be open");
 9741        }
 9742    });
 9743}
 9744
 9745#[gpui::test]
 9746async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9747    init_test(cx, |_| {});
 9748    let mut cx = EditorLspTestContext::new_rust(
 9749        lsp::ServerCapabilities {
 9750            completion_provider: Some(lsp::CompletionOptions {
 9751                trigger_characters: Some(vec![".".to_string()]),
 9752                ..Default::default()
 9753            }),
 9754            ..Default::default()
 9755        },
 9756        cx,
 9757    )
 9758    .await;
 9759    cx.lsp
 9760        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9761            Ok(Some(lsp::CompletionResponse::Array(vec![
 9762                lsp::CompletionItem {
 9763                    label: "first".into(),
 9764                    ..Default::default()
 9765                },
 9766                lsp::CompletionItem {
 9767                    label: "last".into(),
 9768                    ..Default::default()
 9769                },
 9770            ])))
 9771        });
 9772    cx.set_state("variableˇ");
 9773    cx.simulate_keystroke(".");
 9774    cx.executor().run_until_parked();
 9775
 9776    cx.update_editor(|editor, _, _| {
 9777        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9778        {
 9779            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9780        } else {
 9781            panic!("expected completion menu to be open");
 9782        }
 9783    });
 9784
 9785    cx.update_editor(|editor, window, cx| {
 9786        editor.move_page_down(&MovePageDown::default(), window, cx);
 9787        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9788        {
 9789            assert!(
 9790                menu.selected_item == 1,
 9791                "expected PageDown to select the last item from the context menu"
 9792            );
 9793        } else {
 9794            panic!("expected completion menu to stay open after PageDown");
 9795        }
 9796    });
 9797
 9798    cx.update_editor(|editor, window, cx| {
 9799        editor.move_page_up(&MovePageUp::default(), window, cx);
 9800        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9801        {
 9802            assert!(
 9803                menu.selected_item == 0,
 9804                "expected PageUp to select the first item from the context menu"
 9805            );
 9806        } else {
 9807            panic!("expected completion menu to stay open after PageUp");
 9808        }
 9809    });
 9810}
 9811
 9812#[gpui::test]
 9813async fn test_completion_sort(cx: &mut TestAppContext) {
 9814    init_test(cx, |_| {});
 9815    let mut cx = EditorLspTestContext::new_rust(
 9816        lsp::ServerCapabilities {
 9817            completion_provider: Some(lsp::CompletionOptions {
 9818                trigger_characters: Some(vec![".".to_string()]),
 9819                ..Default::default()
 9820            }),
 9821            ..Default::default()
 9822        },
 9823        cx,
 9824    )
 9825    .await;
 9826    cx.lsp
 9827        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9828            Ok(Some(lsp::CompletionResponse::Array(vec![
 9829                lsp::CompletionItem {
 9830                    label: "Range".into(),
 9831                    sort_text: Some("a".into()),
 9832                    ..Default::default()
 9833                },
 9834                lsp::CompletionItem {
 9835                    label: "r".into(),
 9836                    sort_text: Some("b".into()),
 9837                    ..Default::default()
 9838                },
 9839                lsp::CompletionItem {
 9840                    label: "ret".into(),
 9841                    sort_text: Some("c".into()),
 9842                    ..Default::default()
 9843                },
 9844                lsp::CompletionItem {
 9845                    label: "return".into(),
 9846                    sort_text: Some("d".into()),
 9847                    ..Default::default()
 9848                },
 9849                lsp::CompletionItem {
 9850                    label: "slice".into(),
 9851                    sort_text: Some("d".into()),
 9852                    ..Default::default()
 9853                },
 9854            ])))
 9855        });
 9856    cx.set_state("");
 9857    cx.executor().run_until_parked();
 9858    cx.update_editor(|editor, window, cx| {
 9859        editor.show_completions(
 9860            &ShowCompletions {
 9861                trigger: Some("r".into()),
 9862            },
 9863            window,
 9864            cx,
 9865        );
 9866    });
 9867    cx.executor().run_until_parked();
 9868
 9869    cx.update_editor(|editor, _, _| {
 9870        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9871        {
 9872            assert_eq!(
 9873                completion_menu_entries(&menu),
 9874                &["r", "ret", "Range", "return"]
 9875            );
 9876        } else {
 9877            panic!("expected completion menu to be open");
 9878        }
 9879    });
 9880}
 9881
 9882#[gpui::test]
 9883async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9884    init_test(cx, |_| {});
 9885
 9886    let mut cx = EditorLspTestContext::new_rust(
 9887        lsp::ServerCapabilities {
 9888            completion_provider: Some(lsp::CompletionOptions {
 9889                trigger_characters: Some(vec![".".to_string()]),
 9890                resolve_provider: Some(true),
 9891                ..Default::default()
 9892            }),
 9893            ..Default::default()
 9894        },
 9895        cx,
 9896    )
 9897    .await;
 9898
 9899    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9900    cx.simulate_keystroke(".");
 9901    let completion_item = lsp::CompletionItem {
 9902        label: "Some".into(),
 9903        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9904        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9905        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9906            kind: lsp::MarkupKind::Markdown,
 9907            value: "```rust\nSome(2)\n```".to_string(),
 9908        })),
 9909        deprecated: Some(false),
 9910        sort_text: Some("Some".to_string()),
 9911        filter_text: Some("Some".to_string()),
 9912        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9913        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9914            range: lsp::Range {
 9915                start: lsp::Position {
 9916                    line: 0,
 9917                    character: 22,
 9918                },
 9919                end: lsp::Position {
 9920                    line: 0,
 9921                    character: 22,
 9922                },
 9923            },
 9924            new_text: "Some(2)".to_string(),
 9925        })),
 9926        additional_text_edits: Some(vec![lsp::TextEdit {
 9927            range: lsp::Range {
 9928                start: lsp::Position {
 9929                    line: 0,
 9930                    character: 20,
 9931                },
 9932                end: lsp::Position {
 9933                    line: 0,
 9934                    character: 22,
 9935                },
 9936            },
 9937            new_text: "".to_string(),
 9938        }]),
 9939        ..Default::default()
 9940    };
 9941
 9942    let closure_completion_item = completion_item.clone();
 9943    let counter = Arc::new(AtomicUsize::new(0));
 9944    let counter_clone = counter.clone();
 9945    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9946        let task_completion_item = closure_completion_item.clone();
 9947        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9948        async move {
 9949            Ok(Some(lsp::CompletionResponse::Array(vec![
 9950                task_completion_item,
 9951            ])))
 9952        }
 9953    });
 9954
 9955    cx.condition(|editor, _| editor.context_menu_visible())
 9956        .await;
 9957    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9958    assert!(request.next().await.is_some());
 9959    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9960
 9961    cx.simulate_keystroke("S");
 9962    cx.simulate_keystroke("o");
 9963    cx.simulate_keystroke("m");
 9964    cx.condition(|editor, _| editor.context_menu_visible())
 9965        .await;
 9966    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9967    assert!(request.next().await.is_some());
 9968    assert!(request.next().await.is_some());
 9969    assert!(request.next().await.is_some());
 9970    request.close();
 9971    assert!(request.next().await.is_none());
 9972    assert_eq!(
 9973        counter.load(atomic::Ordering::Acquire),
 9974        4,
 9975        "With the completions menu open, only one LSP request should happen per input"
 9976    );
 9977}
 9978
 9979#[gpui::test]
 9980async fn test_toggle_comment(cx: &mut TestAppContext) {
 9981    init_test(cx, |_| {});
 9982    let mut cx = EditorTestContext::new(cx).await;
 9983    let language = Arc::new(Language::new(
 9984        LanguageConfig {
 9985            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9986            ..Default::default()
 9987        },
 9988        Some(tree_sitter_rust::LANGUAGE.into()),
 9989    ));
 9990    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9991
 9992    // If multiple selections intersect a line, the line is only toggled once.
 9993    cx.set_state(indoc! {"
 9994        fn a() {
 9995            «//b();
 9996            ˇ»// «c();
 9997            //ˇ»  d();
 9998        }
 9999    "});
10000
10001    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10002
10003    cx.assert_editor_state(indoc! {"
10004        fn a() {
10005            «b();
10006            c();
10007            ˇ» d();
10008        }
10009    "});
10010
10011    // The comment prefix is inserted at the same column for every line in a
10012    // selection.
10013    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10014
10015    cx.assert_editor_state(indoc! {"
10016        fn a() {
10017            // «b();
10018            // c();
10019            ˇ»//  d();
10020        }
10021    "});
10022
10023    // If a selection ends at the beginning of a line, that line is not toggled.
10024    cx.set_selections_state(indoc! {"
10025        fn a() {
10026            // b();
10027            «// c();
10028        ˇ»    //  d();
10029        }
10030    "});
10031
10032    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10033
10034    cx.assert_editor_state(indoc! {"
10035        fn a() {
10036            // b();
10037            «c();
10038        ˇ»    //  d();
10039        }
10040    "});
10041
10042    // If a selection span a single line and is empty, the line is toggled.
10043    cx.set_state(indoc! {"
10044        fn a() {
10045            a();
10046            b();
10047        ˇ
10048        }
10049    "});
10050
10051    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10052
10053    cx.assert_editor_state(indoc! {"
10054        fn a() {
10055            a();
10056            b();
10057        //•ˇ
10058        }
10059    "});
10060
10061    // If a selection span multiple lines, empty lines are not toggled.
10062    cx.set_state(indoc! {"
10063        fn a() {
10064            «a();
10065
10066            c();ˇ»
10067        }
10068    "});
10069
10070    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10071
10072    cx.assert_editor_state(indoc! {"
10073        fn a() {
10074            // «a();
10075
10076            // c();ˇ»
10077        }
10078    "});
10079
10080    // If a selection includes multiple comment prefixes, all lines are uncommented.
10081    cx.set_state(indoc! {"
10082        fn a() {
10083            «// a();
10084            /// b();
10085            //! c();ˇ»
10086        }
10087    "});
10088
10089    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10090
10091    cx.assert_editor_state(indoc! {"
10092        fn a() {
10093            «a();
10094            b();
10095            c();ˇ»
10096        }
10097    "});
10098}
10099
10100#[gpui::test]
10101async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10102    init_test(cx, |_| {});
10103    let mut cx = EditorTestContext::new(cx).await;
10104    let language = Arc::new(Language::new(
10105        LanguageConfig {
10106            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10107            ..Default::default()
10108        },
10109        Some(tree_sitter_rust::LANGUAGE.into()),
10110    ));
10111    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10112
10113    let toggle_comments = &ToggleComments {
10114        advance_downwards: false,
10115        ignore_indent: true,
10116    };
10117
10118    // If multiple selections intersect a line, the line is only toggled once.
10119    cx.set_state(indoc! {"
10120        fn a() {
10121        //    «b();
10122        //    c();
10123        //    ˇ» d();
10124        }
10125    "});
10126
10127    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10128
10129    cx.assert_editor_state(indoc! {"
10130        fn a() {
10131            «b();
10132            c();
10133            ˇ» d();
10134        }
10135    "});
10136
10137    // The comment prefix is inserted at the beginning of each line
10138    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10139
10140    cx.assert_editor_state(indoc! {"
10141        fn a() {
10142        //    «b();
10143        //    c();
10144        //    ˇ» d();
10145        }
10146    "});
10147
10148    // If a selection ends at the beginning of a line, that line is not toggled.
10149    cx.set_selections_state(indoc! {"
10150        fn a() {
10151        //    b();
10152        //    «c();
10153        ˇ»//     d();
10154        }
10155    "});
10156
10157    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10158
10159    cx.assert_editor_state(indoc! {"
10160        fn a() {
10161        //    b();
10162            «c();
10163        ˇ»//     d();
10164        }
10165    "});
10166
10167    // If a selection span a single line and is empty, the line is toggled.
10168    cx.set_state(indoc! {"
10169        fn a() {
10170            a();
10171            b();
10172        ˇ
10173        }
10174    "});
10175
10176    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10177
10178    cx.assert_editor_state(indoc! {"
10179        fn a() {
10180            a();
10181            b();
10182        //ˇ
10183        }
10184    "});
10185
10186    // If a selection span multiple lines, empty lines are not toggled.
10187    cx.set_state(indoc! {"
10188        fn a() {
10189            «a();
10190
10191            c();ˇ»
10192        }
10193    "});
10194
10195    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10196
10197    cx.assert_editor_state(indoc! {"
10198        fn a() {
10199        //    «a();
10200
10201        //    c();ˇ»
10202        }
10203    "});
10204
10205    // If a selection includes multiple comment prefixes, all lines are uncommented.
10206    cx.set_state(indoc! {"
10207        fn a() {
10208        //    «a();
10209        ///    b();
10210        //!    c();ˇ»
10211        }
10212    "});
10213
10214    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10215
10216    cx.assert_editor_state(indoc! {"
10217        fn a() {
10218            «a();
10219            b();
10220            c();ˇ»
10221        }
10222    "});
10223}
10224
10225#[gpui::test]
10226async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10227    init_test(cx, |_| {});
10228
10229    let language = Arc::new(Language::new(
10230        LanguageConfig {
10231            line_comments: vec!["// ".into()],
10232            ..Default::default()
10233        },
10234        Some(tree_sitter_rust::LANGUAGE.into()),
10235    ));
10236
10237    let mut cx = EditorTestContext::new(cx).await;
10238
10239    cx.language_registry().add(language.clone());
10240    cx.update_buffer(|buffer, cx| {
10241        buffer.set_language(Some(language), cx);
10242    });
10243
10244    let toggle_comments = &ToggleComments {
10245        advance_downwards: true,
10246        ignore_indent: false,
10247    };
10248
10249    // Single cursor on one line -> advance
10250    // Cursor moves horizontally 3 characters as well on non-blank line
10251    cx.set_state(indoc!(
10252        "fn a() {
10253             ˇdog();
10254             cat();
10255        }"
10256    ));
10257    cx.update_editor(|editor, window, cx| {
10258        editor.toggle_comments(toggle_comments, window, cx);
10259    });
10260    cx.assert_editor_state(indoc!(
10261        "fn a() {
10262             // dog();
10263             catˇ();
10264        }"
10265    ));
10266
10267    // Single selection on one line -> don't advance
10268    cx.set_state(indoc!(
10269        "fn a() {
10270             «dog()ˇ»;
10271             cat();
10272        }"
10273    ));
10274    cx.update_editor(|editor, window, cx| {
10275        editor.toggle_comments(toggle_comments, window, cx);
10276    });
10277    cx.assert_editor_state(indoc!(
10278        "fn a() {
10279             // «dog()ˇ»;
10280             cat();
10281        }"
10282    ));
10283
10284    // Multiple cursors on one line -> advance
10285    cx.set_state(indoc!(
10286        "fn a() {
10287             ˇdˇog();
10288             cat();
10289        }"
10290    ));
10291    cx.update_editor(|editor, window, cx| {
10292        editor.toggle_comments(toggle_comments, window, cx);
10293    });
10294    cx.assert_editor_state(indoc!(
10295        "fn a() {
10296             // dog();
10297             catˇ(ˇ);
10298        }"
10299    ));
10300
10301    // Multiple cursors on one line, with selection -> don't advance
10302    cx.set_state(indoc!(
10303        "fn a() {
10304             ˇdˇog«()ˇ»;
10305             cat();
10306        }"
10307    ));
10308    cx.update_editor(|editor, window, cx| {
10309        editor.toggle_comments(toggle_comments, window, cx);
10310    });
10311    cx.assert_editor_state(indoc!(
10312        "fn a() {
10313             // ˇdˇog«()ˇ»;
10314             cat();
10315        }"
10316    ));
10317
10318    // Single cursor on one line -> advance
10319    // Cursor moves to column 0 on blank line
10320    cx.set_state(indoc!(
10321        "fn a() {
10322             ˇdog();
10323
10324             cat();
10325        }"
10326    ));
10327    cx.update_editor(|editor, window, cx| {
10328        editor.toggle_comments(toggle_comments, window, cx);
10329    });
10330    cx.assert_editor_state(indoc!(
10331        "fn a() {
10332             // dog();
10333        ˇ
10334             cat();
10335        }"
10336    ));
10337
10338    // Single cursor on one line -> advance
10339    // Cursor starts and ends at column 0
10340    cx.set_state(indoc!(
10341        "fn a() {
10342         ˇ    dog();
10343             cat();
10344        }"
10345    ));
10346    cx.update_editor(|editor, window, cx| {
10347        editor.toggle_comments(toggle_comments, window, cx);
10348    });
10349    cx.assert_editor_state(indoc!(
10350        "fn a() {
10351             // dog();
10352         ˇ    cat();
10353        }"
10354    ));
10355}
10356
10357#[gpui::test]
10358async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10359    init_test(cx, |_| {});
10360
10361    let mut cx = EditorTestContext::new(cx).await;
10362
10363    let html_language = Arc::new(
10364        Language::new(
10365            LanguageConfig {
10366                name: "HTML".into(),
10367                block_comment: Some(("<!-- ".into(), " -->".into())),
10368                ..Default::default()
10369            },
10370            Some(tree_sitter_html::LANGUAGE.into()),
10371        )
10372        .with_injection_query(
10373            r#"
10374            (script_element
10375                (raw_text) @injection.content
10376                (#set! injection.language "javascript"))
10377            "#,
10378        )
10379        .unwrap(),
10380    );
10381
10382    let javascript_language = Arc::new(Language::new(
10383        LanguageConfig {
10384            name: "JavaScript".into(),
10385            line_comments: vec!["// ".into()],
10386            ..Default::default()
10387        },
10388        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10389    ));
10390
10391    cx.language_registry().add(html_language.clone());
10392    cx.language_registry().add(javascript_language.clone());
10393    cx.update_buffer(|buffer, cx| {
10394        buffer.set_language(Some(html_language), cx);
10395    });
10396
10397    // Toggle comments for empty selections
10398    cx.set_state(
10399        &r#"
10400            <p>A</p>ˇ
10401            <p>B</p>ˇ
10402            <p>C</p>ˇ
10403        "#
10404        .unindent(),
10405    );
10406    cx.update_editor(|editor, window, cx| {
10407        editor.toggle_comments(&ToggleComments::default(), window, cx)
10408    });
10409    cx.assert_editor_state(
10410        &r#"
10411            <!-- <p>A</p>ˇ -->
10412            <!-- <p>B</p>ˇ -->
10413            <!-- <p>C</p>ˇ -->
10414        "#
10415        .unindent(),
10416    );
10417    cx.update_editor(|editor, window, cx| {
10418        editor.toggle_comments(&ToggleComments::default(), window, cx)
10419    });
10420    cx.assert_editor_state(
10421        &r#"
10422            <p>A</p>ˇ
10423            <p>B</p>ˇ
10424            <p>C</p>ˇ
10425        "#
10426        .unindent(),
10427    );
10428
10429    // Toggle comments for mixture of empty and non-empty selections, where
10430    // multiple selections occupy a given line.
10431    cx.set_state(
10432        &r#"
10433            <p>A«</p>
10434            <p>ˇ»B</p>ˇ
10435            <p>C«</p>
10436            <p>ˇ»D</p>ˇ
10437        "#
10438        .unindent(),
10439    );
10440
10441    cx.update_editor(|editor, window, cx| {
10442        editor.toggle_comments(&ToggleComments::default(), window, cx)
10443    });
10444    cx.assert_editor_state(
10445        &r#"
10446            <!-- <p>A«</p>
10447            <p>ˇ»B</p>ˇ -->
10448            <!-- <p>C«</p>
10449            <p>ˇ»D</p>ˇ -->
10450        "#
10451        .unindent(),
10452    );
10453    cx.update_editor(|editor, window, cx| {
10454        editor.toggle_comments(&ToggleComments::default(), window, cx)
10455    });
10456    cx.assert_editor_state(
10457        &r#"
10458            <p>A«</p>
10459            <p>ˇ»B</p>ˇ
10460            <p>C«</p>
10461            <p>ˇ»D</p>ˇ
10462        "#
10463        .unindent(),
10464    );
10465
10466    // Toggle comments when different languages are active for different
10467    // selections.
10468    cx.set_state(
10469        &r#"
10470            ˇ<script>
10471                ˇvar x = new Y();
10472            ˇ</script>
10473        "#
10474        .unindent(),
10475    );
10476    cx.executor().run_until_parked();
10477    cx.update_editor(|editor, window, cx| {
10478        editor.toggle_comments(&ToggleComments::default(), window, cx)
10479    });
10480    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10481    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10482    cx.assert_editor_state(
10483        &r#"
10484            <!-- ˇ<script> -->
10485                // ˇvar x = new Y();
10486            <!-- ˇ</script> -->
10487        "#
10488        .unindent(),
10489    );
10490}
10491
10492#[gpui::test]
10493fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10494    init_test(cx, |_| {});
10495
10496    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10497    let multibuffer = cx.new(|cx| {
10498        let mut multibuffer = MultiBuffer::new(ReadWrite);
10499        multibuffer.push_excerpts(
10500            buffer.clone(),
10501            [
10502                ExcerptRange {
10503                    context: Point::new(0, 0)..Point::new(0, 4),
10504                    primary: None,
10505                },
10506                ExcerptRange {
10507                    context: Point::new(1, 0)..Point::new(1, 4),
10508                    primary: None,
10509                },
10510            ],
10511            cx,
10512        );
10513        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10514        multibuffer
10515    });
10516
10517    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10518    editor.update_in(cx, |editor, window, cx| {
10519        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10520        editor.change_selections(None, window, cx, |s| {
10521            s.select_ranges([
10522                Point::new(0, 0)..Point::new(0, 0),
10523                Point::new(1, 0)..Point::new(1, 0),
10524            ])
10525        });
10526
10527        editor.handle_input("X", window, cx);
10528        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10529        assert_eq!(
10530            editor.selections.ranges(cx),
10531            [
10532                Point::new(0, 1)..Point::new(0, 1),
10533                Point::new(1, 1)..Point::new(1, 1),
10534            ]
10535        );
10536
10537        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10538        editor.change_selections(None, window, cx, |s| {
10539            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10540        });
10541        editor.backspace(&Default::default(), window, cx);
10542        assert_eq!(editor.text(cx), "Xa\nbbb");
10543        assert_eq!(
10544            editor.selections.ranges(cx),
10545            [Point::new(1, 0)..Point::new(1, 0)]
10546        );
10547
10548        editor.change_selections(None, window, cx, |s| {
10549            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10550        });
10551        editor.backspace(&Default::default(), window, cx);
10552        assert_eq!(editor.text(cx), "X\nbb");
10553        assert_eq!(
10554            editor.selections.ranges(cx),
10555            [Point::new(0, 1)..Point::new(0, 1)]
10556        );
10557    });
10558}
10559
10560#[gpui::test]
10561fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10562    init_test(cx, |_| {});
10563
10564    let markers = vec![('[', ']').into(), ('(', ')').into()];
10565    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10566        indoc! {"
10567            [aaaa
10568            (bbbb]
10569            cccc)",
10570        },
10571        markers.clone(),
10572    );
10573    let excerpt_ranges = markers.into_iter().map(|marker| {
10574        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10575        ExcerptRange {
10576            context,
10577            primary: None,
10578        }
10579    });
10580    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10581    let multibuffer = cx.new(|cx| {
10582        let mut multibuffer = MultiBuffer::new(ReadWrite);
10583        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10584        multibuffer
10585    });
10586
10587    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10588    editor.update_in(cx, |editor, window, cx| {
10589        let (expected_text, selection_ranges) = marked_text_ranges(
10590            indoc! {"
10591                aaaa
10592                bˇbbb
10593                bˇbbˇb
10594                cccc"
10595            },
10596            true,
10597        );
10598        assert_eq!(editor.text(cx), expected_text);
10599        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10600
10601        editor.handle_input("X", window, cx);
10602
10603        let (expected_text, expected_selections) = marked_text_ranges(
10604            indoc! {"
10605                aaaa
10606                bXˇbbXb
10607                bXˇbbXˇb
10608                cccc"
10609            },
10610            false,
10611        );
10612        assert_eq!(editor.text(cx), expected_text);
10613        assert_eq!(editor.selections.ranges(cx), expected_selections);
10614
10615        editor.newline(&Newline, window, cx);
10616        let (expected_text, expected_selections) = marked_text_ranges(
10617            indoc! {"
10618                aaaa
10619                bX
10620                ˇbbX
10621                b
10622                bX
10623                ˇbbX
10624                ˇb
10625                cccc"
10626            },
10627            false,
10628        );
10629        assert_eq!(editor.text(cx), expected_text);
10630        assert_eq!(editor.selections.ranges(cx), expected_selections);
10631    });
10632}
10633
10634#[gpui::test]
10635fn test_refresh_selections(cx: &mut TestAppContext) {
10636    init_test(cx, |_| {});
10637
10638    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10639    let mut excerpt1_id = None;
10640    let multibuffer = cx.new(|cx| {
10641        let mut multibuffer = MultiBuffer::new(ReadWrite);
10642        excerpt1_id = multibuffer
10643            .push_excerpts(
10644                buffer.clone(),
10645                [
10646                    ExcerptRange {
10647                        context: Point::new(0, 0)..Point::new(1, 4),
10648                        primary: None,
10649                    },
10650                    ExcerptRange {
10651                        context: Point::new(1, 0)..Point::new(2, 4),
10652                        primary: None,
10653                    },
10654                ],
10655                cx,
10656            )
10657            .into_iter()
10658            .next();
10659        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10660        multibuffer
10661    });
10662
10663    let editor = cx.add_window(|window, cx| {
10664        let mut editor = build_editor(multibuffer.clone(), window, cx);
10665        let snapshot = editor.snapshot(window, cx);
10666        editor.change_selections(None, window, cx, |s| {
10667            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10668        });
10669        editor.begin_selection(
10670            Point::new(2, 1).to_display_point(&snapshot),
10671            true,
10672            1,
10673            window,
10674            cx,
10675        );
10676        assert_eq!(
10677            editor.selections.ranges(cx),
10678            [
10679                Point::new(1, 3)..Point::new(1, 3),
10680                Point::new(2, 1)..Point::new(2, 1),
10681            ]
10682        );
10683        editor
10684    });
10685
10686    // Refreshing selections is a no-op when excerpts haven't changed.
10687    _ = editor.update(cx, |editor, window, cx| {
10688        editor.change_selections(None, window, cx, |s| s.refresh());
10689        assert_eq!(
10690            editor.selections.ranges(cx),
10691            [
10692                Point::new(1, 3)..Point::new(1, 3),
10693                Point::new(2, 1)..Point::new(2, 1),
10694            ]
10695        );
10696    });
10697
10698    multibuffer.update(cx, |multibuffer, cx| {
10699        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10700    });
10701    _ = editor.update(cx, |editor, window, cx| {
10702        // Removing an excerpt causes the first selection to become degenerate.
10703        assert_eq!(
10704            editor.selections.ranges(cx),
10705            [
10706                Point::new(0, 0)..Point::new(0, 0),
10707                Point::new(0, 1)..Point::new(0, 1)
10708            ]
10709        );
10710
10711        // Refreshing selections will relocate the first selection to the original buffer
10712        // location.
10713        editor.change_selections(None, window, cx, |s| s.refresh());
10714        assert_eq!(
10715            editor.selections.ranges(cx),
10716            [
10717                Point::new(0, 1)..Point::new(0, 1),
10718                Point::new(0, 3)..Point::new(0, 3)
10719            ]
10720        );
10721        assert!(editor.selections.pending_anchor().is_some());
10722    });
10723}
10724
10725#[gpui::test]
10726fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10727    init_test(cx, |_| {});
10728
10729    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10730    let mut excerpt1_id = None;
10731    let multibuffer = cx.new(|cx| {
10732        let mut multibuffer = MultiBuffer::new(ReadWrite);
10733        excerpt1_id = multibuffer
10734            .push_excerpts(
10735                buffer.clone(),
10736                [
10737                    ExcerptRange {
10738                        context: Point::new(0, 0)..Point::new(1, 4),
10739                        primary: None,
10740                    },
10741                    ExcerptRange {
10742                        context: Point::new(1, 0)..Point::new(2, 4),
10743                        primary: None,
10744                    },
10745                ],
10746                cx,
10747            )
10748            .into_iter()
10749            .next();
10750        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10751        multibuffer
10752    });
10753
10754    let editor = cx.add_window(|window, cx| {
10755        let mut editor = build_editor(multibuffer.clone(), window, cx);
10756        let snapshot = editor.snapshot(window, cx);
10757        editor.begin_selection(
10758            Point::new(1, 3).to_display_point(&snapshot),
10759            false,
10760            1,
10761            window,
10762            cx,
10763        );
10764        assert_eq!(
10765            editor.selections.ranges(cx),
10766            [Point::new(1, 3)..Point::new(1, 3)]
10767        );
10768        editor
10769    });
10770
10771    multibuffer.update(cx, |multibuffer, cx| {
10772        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10773    });
10774    _ = editor.update(cx, |editor, window, cx| {
10775        assert_eq!(
10776            editor.selections.ranges(cx),
10777            [Point::new(0, 0)..Point::new(0, 0)]
10778        );
10779
10780        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10781        editor.change_selections(None, window, cx, |s| s.refresh());
10782        assert_eq!(
10783            editor.selections.ranges(cx),
10784            [Point::new(0, 3)..Point::new(0, 3)]
10785        );
10786        assert!(editor.selections.pending_anchor().is_some());
10787    });
10788}
10789
10790#[gpui::test]
10791async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10792    init_test(cx, |_| {});
10793
10794    let language = Arc::new(
10795        Language::new(
10796            LanguageConfig {
10797                brackets: BracketPairConfig {
10798                    pairs: vec![
10799                        BracketPair {
10800                            start: "{".to_string(),
10801                            end: "}".to_string(),
10802                            close: true,
10803                            surround: true,
10804                            newline: true,
10805                        },
10806                        BracketPair {
10807                            start: "/* ".to_string(),
10808                            end: " */".to_string(),
10809                            close: true,
10810                            surround: true,
10811                            newline: true,
10812                        },
10813                    ],
10814                    ..Default::default()
10815                },
10816                ..Default::default()
10817            },
10818            Some(tree_sitter_rust::LANGUAGE.into()),
10819        )
10820        .with_indents_query("")
10821        .unwrap(),
10822    );
10823
10824    let text = concat!(
10825        "{   }\n",     //
10826        "  x\n",       //
10827        "  /*   */\n", //
10828        "x\n",         //
10829        "{{} }\n",     //
10830    );
10831
10832    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10833    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10834    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10835    editor
10836        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10837        .await;
10838
10839    editor.update_in(cx, |editor, window, cx| {
10840        editor.change_selections(None, window, cx, |s| {
10841            s.select_display_ranges([
10842                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10843                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10844                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10845            ])
10846        });
10847        editor.newline(&Newline, window, cx);
10848
10849        assert_eq!(
10850            editor.buffer().read(cx).read(cx).text(),
10851            concat!(
10852                "{ \n",    // Suppress rustfmt
10853                "\n",      //
10854                "}\n",     //
10855                "  x\n",   //
10856                "  /* \n", //
10857                "  \n",    //
10858                "  */\n",  //
10859                "x\n",     //
10860                "{{} \n",  //
10861                "}\n",     //
10862            )
10863        );
10864    });
10865}
10866
10867#[gpui::test]
10868fn test_highlighted_ranges(cx: &mut TestAppContext) {
10869    init_test(cx, |_| {});
10870
10871    let editor = cx.add_window(|window, cx| {
10872        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10873        build_editor(buffer.clone(), window, cx)
10874    });
10875
10876    _ = editor.update(cx, |editor, window, cx| {
10877        struct Type1;
10878        struct Type2;
10879
10880        let buffer = editor.buffer.read(cx).snapshot(cx);
10881
10882        let anchor_range =
10883            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10884
10885        editor.highlight_background::<Type1>(
10886            &[
10887                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10888                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10889                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10890                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10891            ],
10892            |_| Hsla::red(),
10893            cx,
10894        );
10895        editor.highlight_background::<Type2>(
10896            &[
10897                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10898                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10899                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10900                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10901            ],
10902            |_| Hsla::green(),
10903            cx,
10904        );
10905
10906        let snapshot = editor.snapshot(window, cx);
10907        let mut highlighted_ranges = editor.background_highlights_in_range(
10908            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10909            &snapshot,
10910            cx.theme().colors(),
10911        );
10912        // Enforce a consistent ordering based on color without relying on the ordering of the
10913        // highlight's `TypeId` which is non-executor.
10914        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10915        assert_eq!(
10916            highlighted_ranges,
10917            &[
10918                (
10919                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10920                    Hsla::red(),
10921                ),
10922                (
10923                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10924                    Hsla::red(),
10925                ),
10926                (
10927                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10928                    Hsla::green(),
10929                ),
10930                (
10931                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10932                    Hsla::green(),
10933                ),
10934            ]
10935        );
10936        assert_eq!(
10937            editor.background_highlights_in_range(
10938                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10939                &snapshot,
10940                cx.theme().colors(),
10941            ),
10942            &[(
10943                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10944                Hsla::red(),
10945            )]
10946        );
10947    });
10948}
10949
10950#[gpui::test]
10951async fn test_following(cx: &mut TestAppContext) {
10952    init_test(cx, |_| {});
10953
10954    let fs = FakeFs::new(cx.executor());
10955    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10956
10957    let buffer = project.update(cx, |project, cx| {
10958        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10959        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10960    });
10961    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10962    let follower = cx.update(|cx| {
10963        cx.open_window(
10964            WindowOptions {
10965                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10966                    gpui::Point::new(px(0.), px(0.)),
10967                    gpui::Point::new(px(10.), px(80.)),
10968                ))),
10969                ..Default::default()
10970            },
10971            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10972        )
10973        .unwrap()
10974    });
10975
10976    let is_still_following = Rc::new(RefCell::new(true));
10977    let follower_edit_event_count = Rc::new(RefCell::new(0));
10978    let pending_update = Rc::new(RefCell::new(None));
10979    let leader_entity = leader.root(cx).unwrap();
10980    let follower_entity = follower.root(cx).unwrap();
10981    _ = follower.update(cx, {
10982        let update = pending_update.clone();
10983        let is_still_following = is_still_following.clone();
10984        let follower_edit_event_count = follower_edit_event_count.clone();
10985        |_, window, cx| {
10986            cx.subscribe_in(
10987                &leader_entity,
10988                window,
10989                move |_, leader, event, window, cx| {
10990                    leader.read(cx).add_event_to_update_proto(
10991                        event,
10992                        &mut update.borrow_mut(),
10993                        window,
10994                        cx,
10995                    );
10996                },
10997            )
10998            .detach();
10999
11000            cx.subscribe_in(
11001                &follower_entity,
11002                window,
11003                move |_, _, event: &EditorEvent, _window, _cx| {
11004                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11005                        *is_still_following.borrow_mut() = false;
11006                    }
11007
11008                    if let EditorEvent::BufferEdited = event {
11009                        *follower_edit_event_count.borrow_mut() += 1;
11010                    }
11011                },
11012            )
11013            .detach();
11014        }
11015    });
11016
11017    // Update the selections only
11018    _ = leader.update(cx, |leader, window, cx| {
11019        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11020    });
11021    follower
11022        .update(cx, |follower, window, cx| {
11023            follower.apply_update_proto(
11024                &project,
11025                pending_update.borrow_mut().take().unwrap(),
11026                window,
11027                cx,
11028            )
11029        })
11030        .unwrap()
11031        .await
11032        .unwrap();
11033    _ = follower.update(cx, |follower, _, cx| {
11034        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11035    });
11036    assert!(*is_still_following.borrow());
11037    assert_eq!(*follower_edit_event_count.borrow(), 0);
11038
11039    // Update the scroll position only
11040    _ = leader.update(cx, |leader, window, cx| {
11041        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11042    });
11043    follower
11044        .update(cx, |follower, window, cx| {
11045            follower.apply_update_proto(
11046                &project,
11047                pending_update.borrow_mut().take().unwrap(),
11048                window,
11049                cx,
11050            )
11051        })
11052        .unwrap()
11053        .await
11054        .unwrap();
11055    assert_eq!(
11056        follower
11057            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11058            .unwrap(),
11059        gpui::Point::new(1.5, 3.5)
11060    );
11061    assert!(*is_still_following.borrow());
11062    assert_eq!(*follower_edit_event_count.borrow(), 0);
11063
11064    // Update the selections and scroll position. The follower's scroll position is updated
11065    // via autoscroll, not via the leader's exact scroll position.
11066    _ = leader.update(cx, |leader, window, cx| {
11067        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11068        leader.request_autoscroll(Autoscroll::newest(), cx);
11069        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11070    });
11071    follower
11072        .update(cx, |follower, window, cx| {
11073            follower.apply_update_proto(
11074                &project,
11075                pending_update.borrow_mut().take().unwrap(),
11076                window,
11077                cx,
11078            )
11079        })
11080        .unwrap()
11081        .await
11082        .unwrap();
11083    _ = follower.update(cx, |follower, _, cx| {
11084        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11085        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11086    });
11087    assert!(*is_still_following.borrow());
11088
11089    // Creating a pending selection that precedes another selection
11090    _ = leader.update(cx, |leader, window, cx| {
11091        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11092        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11093    });
11094    follower
11095        .update(cx, |follower, window, cx| {
11096            follower.apply_update_proto(
11097                &project,
11098                pending_update.borrow_mut().take().unwrap(),
11099                window,
11100                cx,
11101            )
11102        })
11103        .unwrap()
11104        .await
11105        .unwrap();
11106    _ = follower.update(cx, |follower, _, cx| {
11107        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11108    });
11109    assert!(*is_still_following.borrow());
11110
11111    // Extend the pending selection so that it surrounds another selection
11112    _ = leader.update(cx, |leader, window, cx| {
11113        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11114    });
11115    follower
11116        .update(cx, |follower, window, cx| {
11117            follower.apply_update_proto(
11118                &project,
11119                pending_update.borrow_mut().take().unwrap(),
11120                window,
11121                cx,
11122            )
11123        })
11124        .unwrap()
11125        .await
11126        .unwrap();
11127    _ = follower.update(cx, |follower, _, cx| {
11128        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11129    });
11130
11131    // Scrolling locally breaks the follow
11132    _ = follower.update(cx, |follower, window, cx| {
11133        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11134        follower.set_scroll_anchor(
11135            ScrollAnchor {
11136                anchor: top_anchor,
11137                offset: gpui::Point::new(0.0, 0.5),
11138            },
11139            window,
11140            cx,
11141        );
11142    });
11143    assert!(!(*is_still_following.borrow()));
11144}
11145
11146#[gpui::test]
11147async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11148    init_test(cx, |_| {});
11149
11150    let fs = FakeFs::new(cx.executor());
11151    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11152    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11153    let pane = workspace
11154        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11155        .unwrap();
11156
11157    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11158
11159    let leader = pane.update_in(cx, |_, window, cx| {
11160        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11161        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11162    });
11163
11164    // Start following the editor when it has no excerpts.
11165    let mut state_message =
11166        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11167    let workspace_entity = workspace.root(cx).unwrap();
11168    let follower_1 = cx
11169        .update_window(*workspace.deref(), |_, window, cx| {
11170            Editor::from_state_proto(
11171                workspace_entity,
11172                ViewId {
11173                    creator: Default::default(),
11174                    id: 0,
11175                },
11176                &mut state_message,
11177                window,
11178                cx,
11179            )
11180        })
11181        .unwrap()
11182        .unwrap()
11183        .await
11184        .unwrap();
11185
11186    let update_message = Rc::new(RefCell::new(None));
11187    follower_1.update_in(cx, {
11188        let update = update_message.clone();
11189        |_, window, cx| {
11190            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11191                leader.read(cx).add_event_to_update_proto(
11192                    event,
11193                    &mut update.borrow_mut(),
11194                    window,
11195                    cx,
11196                );
11197            })
11198            .detach();
11199        }
11200    });
11201
11202    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11203        (
11204            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11205            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11206        )
11207    });
11208
11209    // Insert some excerpts.
11210    leader.update(cx, |leader, cx| {
11211        leader.buffer.update(cx, |multibuffer, cx| {
11212            let excerpt_ids = multibuffer.push_excerpts(
11213                buffer_1.clone(),
11214                [
11215                    ExcerptRange {
11216                        context: 1..6,
11217                        primary: None,
11218                    },
11219                    ExcerptRange {
11220                        context: 12..15,
11221                        primary: None,
11222                    },
11223                    ExcerptRange {
11224                        context: 0..3,
11225                        primary: None,
11226                    },
11227                ],
11228                cx,
11229            );
11230            multibuffer.insert_excerpts_after(
11231                excerpt_ids[0],
11232                buffer_2.clone(),
11233                [
11234                    ExcerptRange {
11235                        context: 8..12,
11236                        primary: None,
11237                    },
11238                    ExcerptRange {
11239                        context: 0..6,
11240                        primary: None,
11241                    },
11242                ],
11243                cx,
11244            );
11245        });
11246    });
11247
11248    // Apply the update of adding the excerpts.
11249    follower_1
11250        .update_in(cx, |follower, window, cx| {
11251            follower.apply_update_proto(
11252                &project,
11253                update_message.borrow().clone().unwrap(),
11254                window,
11255                cx,
11256            )
11257        })
11258        .await
11259        .unwrap();
11260    assert_eq!(
11261        follower_1.update(cx, |editor, cx| editor.text(cx)),
11262        leader.update(cx, |editor, cx| editor.text(cx))
11263    );
11264    update_message.borrow_mut().take();
11265
11266    // Start following separately after it already has excerpts.
11267    let mut state_message =
11268        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11269    let workspace_entity = workspace.root(cx).unwrap();
11270    let follower_2 = cx
11271        .update_window(*workspace.deref(), |_, window, cx| {
11272            Editor::from_state_proto(
11273                workspace_entity,
11274                ViewId {
11275                    creator: Default::default(),
11276                    id: 0,
11277                },
11278                &mut state_message,
11279                window,
11280                cx,
11281            )
11282        })
11283        .unwrap()
11284        .unwrap()
11285        .await
11286        .unwrap();
11287    assert_eq!(
11288        follower_2.update(cx, |editor, cx| editor.text(cx)),
11289        leader.update(cx, |editor, cx| editor.text(cx))
11290    );
11291
11292    // Remove some excerpts.
11293    leader.update(cx, |leader, cx| {
11294        leader.buffer.update(cx, |multibuffer, cx| {
11295            let excerpt_ids = multibuffer.excerpt_ids();
11296            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11297            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11298        });
11299    });
11300
11301    // Apply the update of removing the excerpts.
11302    follower_1
11303        .update_in(cx, |follower, window, cx| {
11304            follower.apply_update_proto(
11305                &project,
11306                update_message.borrow().clone().unwrap(),
11307                window,
11308                cx,
11309            )
11310        })
11311        .await
11312        .unwrap();
11313    follower_2
11314        .update_in(cx, |follower, window, cx| {
11315            follower.apply_update_proto(
11316                &project,
11317                update_message.borrow().clone().unwrap(),
11318                window,
11319                cx,
11320            )
11321        })
11322        .await
11323        .unwrap();
11324    update_message.borrow_mut().take();
11325    assert_eq!(
11326        follower_1.update(cx, |editor, cx| editor.text(cx)),
11327        leader.update(cx, |editor, cx| editor.text(cx))
11328    );
11329}
11330
11331#[gpui::test]
11332async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11333    init_test(cx, |_| {});
11334
11335    let mut cx = EditorTestContext::new(cx).await;
11336    let lsp_store =
11337        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11338
11339    cx.set_state(indoc! {"
11340        ˇfn func(abc def: i32) -> u32 {
11341        }
11342    "});
11343
11344    cx.update(|_, cx| {
11345        lsp_store.update(cx, |lsp_store, cx| {
11346            lsp_store
11347                .update_diagnostics(
11348                    LanguageServerId(0),
11349                    lsp::PublishDiagnosticsParams {
11350                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11351                        version: None,
11352                        diagnostics: vec![
11353                            lsp::Diagnostic {
11354                                range: lsp::Range::new(
11355                                    lsp::Position::new(0, 11),
11356                                    lsp::Position::new(0, 12),
11357                                ),
11358                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11359                                ..Default::default()
11360                            },
11361                            lsp::Diagnostic {
11362                                range: lsp::Range::new(
11363                                    lsp::Position::new(0, 12),
11364                                    lsp::Position::new(0, 15),
11365                                ),
11366                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11367                                ..Default::default()
11368                            },
11369                            lsp::Diagnostic {
11370                                range: lsp::Range::new(
11371                                    lsp::Position::new(0, 25),
11372                                    lsp::Position::new(0, 28),
11373                                ),
11374                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11375                                ..Default::default()
11376                            },
11377                        ],
11378                    },
11379                    &[],
11380                    cx,
11381                )
11382                .unwrap()
11383        });
11384    });
11385
11386    executor.run_until_parked();
11387
11388    cx.update_editor(|editor, window, cx| {
11389        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11390    });
11391
11392    cx.assert_editor_state(indoc! {"
11393        fn func(abc def: i32) -> ˇu32 {
11394        }
11395    "});
11396
11397    cx.update_editor(|editor, window, cx| {
11398        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11399    });
11400
11401    cx.assert_editor_state(indoc! {"
11402        fn func(abc ˇdef: i32) -> u32 {
11403        }
11404    "});
11405
11406    cx.update_editor(|editor, window, cx| {
11407        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11408    });
11409
11410    cx.assert_editor_state(indoc! {"
11411        fn func(abcˇ def: i32) -> u32 {
11412        }
11413    "});
11414
11415    cx.update_editor(|editor, window, cx| {
11416        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11417    });
11418
11419    cx.assert_editor_state(indoc! {"
11420        fn func(abc def: i32) -> ˇu32 {
11421        }
11422    "});
11423}
11424
11425#[gpui::test]
11426async fn cycle_through_same_place_diagnostics(
11427    executor: BackgroundExecutor,
11428    cx: &mut TestAppContext,
11429) {
11430    init_test(cx, |_| {});
11431
11432    let mut cx = EditorTestContext::new(cx).await;
11433    let lsp_store =
11434        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11435
11436    cx.set_state(indoc! {"
11437        ˇfn func(abc def: i32) -> u32 {
11438        }
11439    "});
11440
11441    cx.update(|_, cx| {
11442        lsp_store.update(cx, |lsp_store, cx| {
11443            lsp_store
11444                .update_diagnostics(
11445                    LanguageServerId(0),
11446                    lsp::PublishDiagnosticsParams {
11447                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11448                        version: None,
11449                        diagnostics: vec![
11450                            lsp::Diagnostic {
11451                                range: lsp::Range::new(
11452                                    lsp::Position::new(0, 11),
11453                                    lsp::Position::new(0, 12),
11454                                ),
11455                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11456                                ..Default::default()
11457                            },
11458                            lsp::Diagnostic {
11459                                range: lsp::Range::new(
11460                                    lsp::Position::new(0, 12),
11461                                    lsp::Position::new(0, 15),
11462                                ),
11463                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11464                                ..Default::default()
11465                            },
11466                            lsp::Diagnostic {
11467                                range: lsp::Range::new(
11468                                    lsp::Position::new(0, 12),
11469                                    lsp::Position::new(0, 15),
11470                                ),
11471                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11472                                ..Default::default()
11473                            },
11474                            lsp::Diagnostic {
11475                                range: lsp::Range::new(
11476                                    lsp::Position::new(0, 25),
11477                                    lsp::Position::new(0, 28),
11478                                ),
11479                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11480                                ..Default::default()
11481                            },
11482                        ],
11483                    },
11484                    &[],
11485                    cx,
11486                )
11487                .unwrap()
11488        });
11489    });
11490    executor.run_until_parked();
11491
11492    //// Backward
11493
11494    // Fourth diagnostic
11495    cx.update_editor(|editor, window, cx| {
11496        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11497    });
11498    cx.assert_editor_state(indoc! {"
11499        fn func(abc def: i32) -> ˇu32 {
11500        }
11501    "});
11502
11503    // Third diagnostic
11504    cx.update_editor(|editor, window, cx| {
11505        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11506    });
11507    cx.assert_editor_state(indoc! {"
11508        fn func(abc ˇdef: i32) -> u32 {
11509        }
11510    "});
11511
11512    // Second diagnostic, same place
11513    cx.update_editor(|editor, window, cx| {
11514        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11515    });
11516    cx.assert_editor_state(indoc! {"
11517        fn func(abc ˇdef: i32) -> u32 {
11518        }
11519    "});
11520
11521    // First diagnostic
11522    cx.update_editor(|editor, window, cx| {
11523        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11524    });
11525    cx.assert_editor_state(indoc! {"
11526        fn func(abcˇ def: i32) -> u32 {
11527        }
11528    "});
11529
11530    // Wrapped over, fourth diagnostic
11531    cx.update_editor(|editor, window, cx| {
11532        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11533    });
11534    cx.assert_editor_state(indoc! {"
11535        fn func(abc def: i32) -> ˇu32 {
11536        }
11537    "});
11538
11539    cx.update_editor(|editor, window, cx| {
11540        editor.move_to_beginning(&MoveToBeginning, window, cx);
11541    });
11542    cx.assert_editor_state(indoc! {"
11543        ˇfn func(abc def: i32) -> u32 {
11544        }
11545    "});
11546
11547    //// Forward
11548
11549    // First diagnostic
11550    cx.update_editor(|editor, window, cx| {
11551        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11552    });
11553    cx.assert_editor_state(indoc! {"
11554        fn func(abcˇ def: i32) -> u32 {
11555        }
11556    "});
11557
11558    // Second diagnostic
11559    cx.update_editor(|editor, window, cx| {
11560        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11561    });
11562    cx.assert_editor_state(indoc! {"
11563        fn func(abc ˇdef: i32) -> u32 {
11564        }
11565    "});
11566
11567    // Third diagnostic, same place
11568    cx.update_editor(|editor, window, cx| {
11569        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11570    });
11571    cx.assert_editor_state(indoc! {"
11572        fn func(abc ˇdef: i32) -> u32 {
11573        }
11574    "});
11575
11576    // Fourth diagnostic
11577    cx.update_editor(|editor, window, cx| {
11578        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11579    });
11580    cx.assert_editor_state(indoc! {"
11581        fn func(abc def: i32) -> ˇu32 {
11582        }
11583    "});
11584
11585    // Wrapped around, first diagnostic
11586    cx.update_editor(|editor, window, cx| {
11587        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11588    });
11589    cx.assert_editor_state(indoc! {"
11590        fn func(abcˇ def: i32) -> u32 {
11591        }
11592    "});
11593}
11594
11595#[gpui::test]
11596async fn active_diagnostics_dismiss_after_invalidation(
11597    executor: BackgroundExecutor,
11598    cx: &mut TestAppContext,
11599) {
11600    init_test(cx, |_| {});
11601
11602    let mut cx = EditorTestContext::new(cx).await;
11603    let lsp_store =
11604        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11605
11606    cx.set_state(indoc! {"
11607        ˇfn func(abc def: i32) -> u32 {
11608        }
11609    "});
11610
11611    let message = "Something's wrong!";
11612    cx.update(|_, cx| {
11613        lsp_store.update(cx, |lsp_store, cx| {
11614            lsp_store
11615                .update_diagnostics(
11616                    LanguageServerId(0),
11617                    lsp::PublishDiagnosticsParams {
11618                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11619                        version: None,
11620                        diagnostics: vec![lsp::Diagnostic {
11621                            range: lsp::Range::new(
11622                                lsp::Position::new(0, 11),
11623                                lsp::Position::new(0, 12),
11624                            ),
11625                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11626                            message: message.to_string(),
11627                            ..Default::default()
11628                        }],
11629                    },
11630                    &[],
11631                    cx,
11632                )
11633                .unwrap()
11634        });
11635    });
11636    executor.run_until_parked();
11637
11638    cx.update_editor(|editor, window, cx| {
11639        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11640        assert_eq!(
11641            editor
11642                .active_diagnostics
11643                .as_ref()
11644                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11645            Some(message),
11646            "Should have a diagnostics group activated"
11647        );
11648    });
11649    cx.assert_editor_state(indoc! {"
11650        fn func(abcˇ def: i32) -> u32 {
11651        }
11652    "});
11653
11654    cx.update(|_, cx| {
11655        lsp_store.update(cx, |lsp_store, cx| {
11656            lsp_store
11657                .update_diagnostics(
11658                    LanguageServerId(0),
11659                    lsp::PublishDiagnosticsParams {
11660                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11661                        version: None,
11662                        diagnostics: Vec::new(),
11663                    },
11664                    &[],
11665                    cx,
11666                )
11667                .unwrap()
11668        });
11669    });
11670    executor.run_until_parked();
11671    cx.update_editor(|editor, _, _| {
11672        assert_eq!(
11673            editor.active_diagnostics, None,
11674            "After no diagnostics set to the editor, no diagnostics should be active"
11675        );
11676    });
11677    cx.assert_editor_state(indoc! {"
11678        fn func(abcˇ def: i32) -> u32 {
11679        }
11680    "});
11681
11682    cx.update_editor(|editor, window, cx| {
11683        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11684        assert_eq!(
11685            editor.active_diagnostics, None,
11686            "Should be no diagnostics to go to and activate"
11687        );
11688    });
11689    cx.assert_editor_state(indoc! {"
11690        fn func(abcˇ def: i32) -> u32 {
11691        }
11692    "});
11693}
11694
11695#[gpui::test]
11696async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11697    init_test(cx, |_| {});
11698
11699    let mut cx = EditorTestContext::new(cx).await;
11700
11701    cx.set_state(indoc! {"
11702        fn func(abˇc def: i32) -> u32 {
11703        }
11704    "});
11705    let lsp_store =
11706        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11707
11708    cx.update(|_, cx| {
11709        lsp_store.update(cx, |lsp_store, cx| {
11710            lsp_store.update_diagnostics(
11711                LanguageServerId(0),
11712                lsp::PublishDiagnosticsParams {
11713                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11714                    version: None,
11715                    diagnostics: vec![lsp::Diagnostic {
11716                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11717                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11718                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11719                        ..Default::default()
11720                    }],
11721                },
11722                &[],
11723                cx,
11724            )
11725        })
11726    }).unwrap();
11727    cx.run_until_parked();
11728    cx.update_editor(|editor, window, cx| {
11729        hover_popover::hover(editor, &Default::default(), window, cx)
11730    });
11731    cx.run_until_parked();
11732    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11733}
11734
11735#[gpui::test]
11736async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11737    init_test(cx, |_| {});
11738
11739    let mut cx = EditorTestContext::new(cx).await;
11740
11741    let diff_base = r#"
11742        use some::mod;
11743
11744        const A: u32 = 42;
11745
11746        fn main() {
11747            println!("hello");
11748
11749            println!("world");
11750        }
11751        "#
11752    .unindent();
11753
11754    // Edits are modified, removed, modified, added
11755    cx.set_state(
11756        &r#"
11757        use some::modified;
11758
11759        ˇ
11760        fn main() {
11761            println!("hello there");
11762
11763            println!("around the");
11764            println!("world");
11765        }
11766        "#
11767        .unindent(),
11768    );
11769
11770    cx.set_head_text(&diff_base);
11771    executor.run_until_parked();
11772
11773    cx.update_editor(|editor, window, cx| {
11774        //Wrap around the bottom of the buffer
11775        for _ in 0..3 {
11776            editor.go_to_next_hunk(&GoToHunk, window, cx);
11777        }
11778    });
11779
11780    cx.assert_editor_state(
11781        &r#"
11782        ˇuse some::modified;
11783
11784
11785        fn main() {
11786            println!("hello there");
11787
11788            println!("around the");
11789            println!("world");
11790        }
11791        "#
11792        .unindent(),
11793    );
11794
11795    cx.update_editor(|editor, window, cx| {
11796        //Wrap around the top of the buffer
11797        for _ in 0..2 {
11798            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11799        }
11800    });
11801
11802    cx.assert_editor_state(
11803        &r#"
11804        use some::modified;
11805
11806
11807        fn main() {
11808        ˇ    println!("hello there");
11809
11810            println!("around the");
11811            println!("world");
11812        }
11813        "#
11814        .unindent(),
11815    );
11816
11817    cx.update_editor(|editor, window, cx| {
11818        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11819    });
11820
11821    cx.assert_editor_state(
11822        &r#"
11823        use some::modified;
11824
11825        ˇ
11826        fn main() {
11827            println!("hello there");
11828
11829            println!("around the");
11830            println!("world");
11831        }
11832        "#
11833        .unindent(),
11834    );
11835
11836    cx.update_editor(|editor, window, cx| {
11837        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11838    });
11839
11840    cx.assert_editor_state(
11841        &r#"
11842        ˇuse some::modified;
11843
11844
11845        fn main() {
11846            println!("hello there");
11847
11848            println!("around the");
11849            println!("world");
11850        }
11851        "#
11852        .unindent(),
11853    );
11854
11855    cx.update_editor(|editor, window, cx| {
11856        for _ in 0..2 {
11857            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11858        }
11859    });
11860
11861    cx.assert_editor_state(
11862        &r#"
11863        use some::modified;
11864
11865
11866        fn main() {
11867        ˇ    println!("hello there");
11868
11869            println!("around the");
11870            println!("world");
11871        }
11872        "#
11873        .unindent(),
11874    );
11875
11876    cx.update_editor(|editor, window, cx| {
11877        editor.fold(&Fold, window, cx);
11878    });
11879
11880    cx.update_editor(|editor, window, cx| {
11881        editor.go_to_next_hunk(&GoToHunk, window, cx);
11882    });
11883
11884    cx.assert_editor_state(
11885        &r#"
11886        ˇuse some::modified;
11887
11888
11889        fn main() {
11890            println!("hello there");
11891
11892            println!("around the");
11893            println!("world");
11894        }
11895        "#
11896        .unindent(),
11897    );
11898}
11899
11900#[test]
11901fn test_split_words() {
11902    fn split(text: &str) -> Vec<&str> {
11903        split_words(text).collect()
11904    }
11905
11906    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11907    assert_eq!(split("hello_world"), &["hello_", "world"]);
11908    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11909    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11910    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11911    assert_eq!(split("helloworld"), &["helloworld"]);
11912
11913    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11914}
11915
11916#[gpui::test]
11917async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11918    init_test(cx, |_| {});
11919
11920    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11921    let mut assert = |before, after| {
11922        let _state_context = cx.set_state(before);
11923        cx.run_until_parked();
11924        cx.update_editor(|editor, window, cx| {
11925            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11926        });
11927        cx.assert_editor_state(after);
11928    };
11929
11930    // Outside bracket jumps to outside of matching bracket
11931    assert("console.logˇ(var);", "console.log(var)ˇ;");
11932    assert("console.log(var)ˇ;", "console.logˇ(var);");
11933
11934    // Inside bracket jumps to inside of matching bracket
11935    assert("console.log(ˇvar);", "console.log(varˇ);");
11936    assert("console.log(varˇ);", "console.log(ˇvar);");
11937
11938    // When outside a bracket and inside, favor jumping to the inside bracket
11939    assert(
11940        "console.log('foo', [1, 2, 3]ˇ);",
11941        "console.log(ˇ'foo', [1, 2, 3]);",
11942    );
11943    assert(
11944        "console.log(ˇ'foo', [1, 2, 3]);",
11945        "console.log('foo', [1, 2, 3]ˇ);",
11946    );
11947
11948    // Bias forward if two options are equally likely
11949    assert(
11950        "let result = curried_fun()ˇ();",
11951        "let result = curried_fun()()ˇ;",
11952    );
11953
11954    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11955    assert(
11956        indoc! {"
11957            function test() {
11958                console.log('test')ˇ
11959            }"},
11960        indoc! {"
11961            function test() {
11962                console.logˇ('test')
11963            }"},
11964    );
11965}
11966
11967#[gpui::test]
11968async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11969    init_test(cx, |_| {});
11970
11971    let fs = FakeFs::new(cx.executor());
11972    fs.insert_tree(
11973        path!("/a"),
11974        json!({
11975            "main.rs": "fn main() { let a = 5; }",
11976            "other.rs": "// Test file",
11977        }),
11978    )
11979    .await;
11980    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11981
11982    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11983    language_registry.add(Arc::new(Language::new(
11984        LanguageConfig {
11985            name: "Rust".into(),
11986            matcher: LanguageMatcher {
11987                path_suffixes: vec!["rs".to_string()],
11988                ..Default::default()
11989            },
11990            brackets: BracketPairConfig {
11991                pairs: vec![BracketPair {
11992                    start: "{".to_string(),
11993                    end: "}".to_string(),
11994                    close: true,
11995                    surround: true,
11996                    newline: true,
11997                }],
11998                disabled_scopes_by_bracket_ix: Vec::new(),
11999            },
12000            ..Default::default()
12001        },
12002        Some(tree_sitter_rust::LANGUAGE.into()),
12003    )));
12004    let mut fake_servers = language_registry.register_fake_lsp(
12005        "Rust",
12006        FakeLspAdapter {
12007            capabilities: lsp::ServerCapabilities {
12008                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12009                    first_trigger_character: "{".to_string(),
12010                    more_trigger_character: None,
12011                }),
12012                ..Default::default()
12013            },
12014            ..Default::default()
12015        },
12016    );
12017
12018    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12019
12020    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12021
12022    let worktree_id = workspace
12023        .update(cx, |workspace, _, cx| {
12024            workspace.project().update(cx, |project, cx| {
12025                project.worktrees(cx).next().unwrap().read(cx).id()
12026            })
12027        })
12028        .unwrap();
12029
12030    let buffer = project
12031        .update(cx, |project, cx| {
12032            project.open_local_buffer(path!("/a/main.rs"), cx)
12033        })
12034        .await
12035        .unwrap();
12036    let editor_handle = workspace
12037        .update(cx, |workspace, window, cx| {
12038            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12039        })
12040        .unwrap()
12041        .await
12042        .unwrap()
12043        .downcast::<Editor>()
12044        .unwrap();
12045
12046    cx.executor().start_waiting();
12047    let fake_server = fake_servers.next().await.unwrap();
12048
12049    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
12050        assert_eq!(
12051            params.text_document_position.text_document.uri,
12052            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12053        );
12054        assert_eq!(
12055            params.text_document_position.position,
12056            lsp::Position::new(0, 21),
12057        );
12058
12059        Ok(Some(vec![lsp::TextEdit {
12060            new_text: "]".to_string(),
12061            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12062        }]))
12063    });
12064
12065    editor_handle.update_in(cx, |editor, window, cx| {
12066        window.focus(&editor.focus_handle(cx));
12067        editor.change_selections(None, window, cx, |s| {
12068            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12069        });
12070        editor.handle_input("{", window, cx);
12071    });
12072
12073    cx.executor().run_until_parked();
12074
12075    buffer.update(cx, |buffer, _| {
12076        assert_eq!(
12077            buffer.text(),
12078            "fn main() { let a = {5}; }",
12079            "No extra braces from on type formatting should appear in the buffer"
12080        )
12081    });
12082}
12083
12084#[gpui::test]
12085async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12086    init_test(cx, |_| {});
12087
12088    let fs = FakeFs::new(cx.executor());
12089    fs.insert_tree(
12090        path!("/a"),
12091        json!({
12092            "main.rs": "fn main() { let a = 5; }",
12093            "other.rs": "// Test file",
12094        }),
12095    )
12096    .await;
12097
12098    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12099
12100    let server_restarts = Arc::new(AtomicUsize::new(0));
12101    let closure_restarts = Arc::clone(&server_restarts);
12102    let language_server_name = "test language server";
12103    let language_name: LanguageName = "Rust".into();
12104
12105    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12106    language_registry.add(Arc::new(Language::new(
12107        LanguageConfig {
12108            name: language_name.clone(),
12109            matcher: LanguageMatcher {
12110                path_suffixes: vec!["rs".to_string()],
12111                ..Default::default()
12112            },
12113            ..Default::default()
12114        },
12115        Some(tree_sitter_rust::LANGUAGE.into()),
12116    )));
12117    let mut fake_servers = language_registry.register_fake_lsp(
12118        "Rust",
12119        FakeLspAdapter {
12120            name: language_server_name,
12121            initialization_options: Some(json!({
12122                "testOptionValue": true
12123            })),
12124            initializer: Some(Box::new(move |fake_server| {
12125                let task_restarts = Arc::clone(&closure_restarts);
12126                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12127                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12128                    futures::future::ready(Ok(()))
12129                });
12130            })),
12131            ..Default::default()
12132        },
12133    );
12134
12135    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12136    let _buffer = project
12137        .update(cx, |project, cx| {
12138            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12139        })
12140        .await
12141        .unwrap();
12142    let _fake_server = fake_servers.next().await.unwrap();
12143    update_test_language_settings(cx, |language_settings| {
12144        language_settings.languages.insert(
12145            language_name.clone(),
12146            LanguageSettingsContent {
12147                tab_size: NonZeroU32::new(8),
12148                ..Default::default()
12149            },
12150        );
12151    });
12152    cx.executor().run_until_parked();
12153    assert_eq!(
12154        server_restarts.load(atomic::Ordering::Acquire),
12155        0,
12156        "Should not restart LSP server on an unrelated change"
12157    );
12158
12159    update_test_project_settings(cx, |project_settings| {
12160        project_settings.lsp.insert(
12161            "Some other server name".into(),
12162            LspSettings {
12163                binary: None,
12164                settings: None,
12165                initialization_options: Some(json!({
12166                    "some other init value": false
12167                })),
12168            },
12169        );
12170    });
12171    cx.executor().run_until_parked();
12172    assert_eq!(
12173        server_restarts.load(atomic::Ordering::Acquire),
12174        0,
12175        "Should not restart LSP server on an unrelated LSP settings change"
12176    );
12177
12178    update_test_project_settings(cx, |project_settings| {
12179        project_settings.lsp.insert(
12180            language_server_name.into(),
12181            LspSettings {
12182                binary: None,
12183                settings: None,
12184                initialization_options: Some(json!({
12185                    "anotherInitValue": false
12186                })),
12187            },
12188        );
12189    });
12190    cx.executor().run_until_parked();
12191    assert_eq!(
12192        server_restarts.load(atomic::Ordering::Acquire),
12193        1,
12194        "Should restart LSP server on a related LSP settings change"
12195    );
12196
12197    update_test_project_settings(cx, |project_settings| {
12198        project_settings.lsp.insert(
12199            language_server_name.into(),
12200            LspSettings {
12201                binary: None,
12202                settings: None,
12203                initialization_options: Some(json!({
12204                    "anotherInitValue": false
12205                })),
12206            },
12207        );
12208    });
12209    cx.executor().run_until_parked();
12210    assert_eq!(
12211        server_restarts.load(atomic::Ordering::Acquire),
12212        1,
12213        "Should not restart LSP server on a related LSP settings change that is the same"
12214    );
12215
12216    update_test_project_settings(cx, |project_settings| {
12217        project_settings.lsp.insert(
12218            language_server_name.into(),
12219            LspSettings {
12220                binary: None,
12221                settings: None,
12222                initialization_options: None,
12223            },
12224        );
12225    });
12226    cx.executor().run_until_parked();
12227    assert_eq!(
12228        server_restarts.load(atomic::Ordering::Acquire),
12229        2,
12230        "Should restart LSP server on another related LSP settings change"
12231    );
12232}
12233
12234#[gpui::test]
12235async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12236    init_test(cx, |_| {});
12237
12238    let mut cx = EditorLspTestContext::new_rust(
12239        lsp::ServerCapabilities {
12240            completion_provider: Some(lsp::CompletionOptions {
12241                trigger_characters: Some(vec![".".to_string()]),
12242                resolve_provider: Some(true),
12243                ..Default::default()
12244            }),
12245            ..Default::default()
12246        },
12247        cx,
12248    )
12249    .await;
12250
12251    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12252    cx.simulate_keystroke(".");
12253    let completion_item = lsp::CompletionItem {
12254        label: "some".into(),
12255        kind: Some(lsp::CompletionItemKind::SNIPPET),
12256        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12257        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12258            kind: lsp::MarkupKind::Markdown,
12259            value: "```rust\nSome(2)\n```".to_string(),
12260        })),
12261        deprecated: Some(false),
12262        sort_text: Some("fffffff2".to_string()),
12263        filter_text: Some("some".to_string()),
12264        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12265        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12266            range: lsp::Range {
12267                start: lsp::Position {
12268                    line: 0,
12269                    character: 22,
12270                },
12271                end: lsp::Position {
12272                    line: 0,
12273                    character: 22,
12274                },
12275            },
12276            new_text: "Some(2)".to_string(),
12277        })),
12278        additional_text_edits: Some(vec![lsp::TextEdit {
12279            range: lsp::Range {
12280                start: lsp::Position {
12281                    line: 0,
12282                    character: 20,
12283                },
12284                end: lsp::Position {
12285                    line: 0,
12286                    character: 22,
12287                },
12288            },
12289            new_text: "".to_string(),
12290        }]),
12291        ..Default::default()
12292    };
12293
12294    let closure_completion_item = completion_item.clone();
12295    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12296        let task_completion_item = closure_completion_item.clone();
12297        async move {
12298            Ok(Some(lsp::CompletionResponse::Array(vec![
12299                task_completion_item,
12300            ])))
12301        }
12302    });
12303
12304    request.next().await;
12305
12306    cx.condition(|editor, _| editor.context_menu_visible())
12307        .await;
12308    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12309        editor
12310            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12311            .unwrap()
12312    });
12313    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12314
12315    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12316        let task_completion_item = completion_item.clone();
12317        async move { Ok(task_completion_item) }
12318    })
12319    .next()
12320    .await
12321    .unwrap();
12322    apply_additional_edits.await.unwrap();
12323    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12324}
12325
12326#[gpui::test]
12327async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12328    init_test(cx, |_| {});
12329
12330    let mut cx = EditorLspTestContext::new_rust(
12331        lsp::ServerCapabilities {
12332            completion_provider: Some(lsp::CompletionOptions {
12333                trigger_characters: Some(vec![".".to_string()]),
12334                resolve_provider: Some(true),
12335                ..Default::default()
12336            }),
12337            ..Default::default()
12338        },
12339        cx,
12340    )
12341    .await;
12342
12343    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12344    cx.simulate_keystroke(".");
12345
12346    let item1 = lsp::CompletionItem {
12347        label: "method id()".to_string(),
12348        filter_text: Some("id".to_string()),
12349        detail: None,
12350        documentation: None,
12351        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12352            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12353            new_text: ".id".to_string(),
12354        })),
12355        ..lsp::CompletionItem::default()
12356    };
12357
12358    let item2 = lsp::CompletionItem {
12359        label: "other".to_string(),
12360        filter_text: Some("other".to_string()),
12361        detail: None,
12362        documentation: None,
12363        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12364            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12365            new_text: ".other".to_string(),
12366        })),
12367        ..lsp::CompletionItem::default()
12368    };
12369
12370    let item1 = item1.clone();
12371    cx.handle_request::<lsp::request::Completion, _, _>({
12372        let item1 = item1.clone();
12373        move |_, _, _| {
12374            let item1 = item1.clone();
12375            let item2 = item2.clone();
12376            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12377        }
12378    })
12379    .next()
12380    .await;
12381
12382    cx.condition(|editor, _| editor.context_menu_visible())
12383        .await;
12384    cx.update_editor(|editor, _, _| {
12385        let context_menu = editor.context_menu.borrow_mut();
12386        let context_menu = context_menu
12387            .as_ref()
12388            .expect("Should have the context menu deployed");
12389        match context_menu {
12390            CodeContextMenu::Completions(completions_menu) => {
12391                let completions = completions_menu.completions.borrow_mut();
12392                assert_eq!(
12393                    completions
12394                        .iter()
12395                        .map(|completion| &completion.label.text)
12396                        .collect::<Vec<_>>(),
12397                    vec!["method id()", "other"]
12398                )
12399            }
12400            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12401        }
12402    });
12403
12404    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12405        let item1 = item1.clone();
12406        move |_, item_to_resolve, _| {
12407            let item1 = item1.clone();
12408            async move {
12409                if item1 == item_to_resolve {
12410                    Ok(lsp::CompletionItem {
12411                        label: "method id()".to_string(),
12412                        filter_text: Some("id".to_string()),
12413                        detail: Some("Now resolved!".to_string()),
12414                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12415                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12416                            range: lsp::Range::new(
12417                                lsp::Position::new(0, 22),
12418                                lsp::Position::new(0, 22),
12419                            ),
12420                            new_text: ".id".to_string(),
12421                        })),
12422                        ..lsp::CompletionItem::default()
12423                    })
12424                } else {
12425                    Ok(item_to_resolve)
12426                }
12427            }
12428        }
12429    })
12430    .next()
12431    .await
12432    .unwrap();
12433    cx.run_until_parked();
12434
12435    cx.update_editor(|editor, window, cx| {
12436        editor.context_menu_next(&Default::default(), window, cx);
12437    });
12438
12439    cx.update_editor(|editor, _, _| {
12440        let context_menu = editor.context_menu.borrow_mut();
12441        let context_menu = context_menu
12442            .as_ref()
12443            .expect("Should have the context menu deployed");
12444        match context_menu {
12445            CodeContextMenu::Completions(completions_menu) => {
12446                let completions = completions_menu.completions.borrow_mut();
12447                assert_eq!(
12448                    completions
12449                        .iter()
12450                        .map(|completion| &completion.label.text)
12451                        .collect::<Vec<_>>(),
12452                    vec!["method id() Now resolved!", "other"],
12453                    "Should update first completion label, but not second as the filter text did not match."
12454                );
12455            }
12456            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12457        }
12458    });
12459}
12460
12461#[gpui::test]
12462async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12463    init_test(cx, |_| {});
12464
12465    let mut cx = EditorLspTestContext::new_rust(
12466        lsp::ServerCapabilities {
12467            completion_provider: Some(lsp::CompletionOptions {
12468                trigger_characters: Some(vec![".".to_string()]),
12469                resolve_provider: Some(true),
12470                ..Default::default()
12471            }),
12472            ..Default::default()
12473        },
12474        cx,
12475    )
12476    .await;
12477
12478    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12479    cx.simulate_keystroke(".");
12480
12481    let unresolved_item_1 = lsp::CompletionItem {
12482        label: "id".to_string(),
12483        filter_text: Some("id".to_string()),
12484        detail: None,
12485        documentation: None,
12486        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12487            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12488            new_text: ".id".to_string(),
12489        })),
12490        ..lsp::CompletionItem::default()
12491    };
12492    let resolved_item_1 = lsp::CompletionItem {
12493        additional_text_edits: Some(vec![lsp::TextEdit {
12494            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12495            new_text: "!!".to_string(),
12496        }]),
12497        ..unresolved_item_1.clone()
12498    };
12499    let unresolved_item_2 = lsp::CompletionItem {
12500        label: "other".to_string(),
12501        filter_text: Some("other".to_string()),
12502        detail: None,
12503        documentation: None,
12504        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12505            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12506            new_text: ".other".to_string(),
12507        })),
12508        ..lsp::CompletionItem::default()
12509    };
12510    let resolved_item_2 = lsp::CompletionItem {
12511        additional_text_edits: Some(vec![lsp::TextEdit {
12512            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12513            new_text: "??".to_string(),
12514        }]),
12515        ..unresolved_item_2.clone()
12516    };
12517
12518    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12519    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12520    cx.lsp
12521        .server
12522        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12523            let unresolved_item_1 = unresolved_item_1.clone();
12524            let resolved_item_1 = resolved_item_1.clone();
12525            let unresolved_item_2 = unresolved_item_2.clone();
12526            let resolved_item_2 = resolved_item_2.clone();
12527            let resolve_requests_1 = resolve_requests_1.clone();
12528            let resolve_requests_2 = resolve_requests_2.clone();
12529            move |unresolved_request, _| {
12530                let unresolved_item_1 = unresolved_item_1.clone();
12531                let resolved_item_1 = resolved_item_1.clone();
12532                let unresolved_item_2 = unresolved_item_2.clone();
12533                let resolved_item_2 = resolved_item_2.clone();
12534                let resolve_requests_1 = resolve_requests_1.clone();
12535                let resolve_requests_2 = resolve_requests_2.clone();
12536                async move {
12537                    if unresolved_request == unresolved_item_1 {
12538                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12539                        Ok(resolved_item_1.clone())
12540                    } else if unresolved_request == unresolved_item_2 {
12541                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12542                        Ok(resolved_item_2.clone())
12543                    } else {
12544                        panic!("Unexpected completion item {unresolved_request:?}")
12545                    }
12546                }
12547            }
12548        })
12549        .detach();
12550
12551    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12552        let unresolved_item_1 = unresolved_item_1.clone();
12553        let unresolved_item_2 = unresolved_item_2.clone();
12554        async move {
12555            Ok(Some(lsp::CompletionResponse::Array(vec![
12556                unresolved_item_1,
12557                unresolved_item_2,
12558            ])))
12559        }
12560    })
12561    .next()
12562    .await;
12563
12564    cx.condition(|editor, _| editor.context_menu_visible())
12565        .await;
12566    cx.update_editor(|editor, _, _| {
12567        let context_menu = editor.context_menu.borrow_mut();
12568        let context_menu = context_menu
12569            .as_ref()
12570            .expect("Should have the context menu deployed");
12571        match context_menu {
12572            CodeContextMenu::Completions(completions_menu) => {
12573                let completions = completions_menu.completions.borrow_mut();
12574                assert_eq!(
12575                    completions
12576                        .iter()
12577                        .map(|completion| &completion.label.text)
12578                        .collect::<Vec<_>>(),
12579                    vec!["id", "other"]
12580                )
12581            }
12582            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12583        }
12584    });
12585    cx.run_until_parked();
12586
12587    cx.update_editor(|editor, window, cx| {
12588        editor.context_menu_next(&ContextMenuNext, window, cx);
12589    });
12590    cx.run_until_parked();
12591    cx.update_editor(|editor, window, cx| {
12592        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12593    });
12594    cx.run_until_parked();
12595    cx.update_editor(|editor, window, cx| {
12596        editor.context_menu_next(&ContextMenuNext, window, cx);
12597    });
12598    cx.run_until_parked();
12599    cx.update_editor(|editor, window, cx| {
12600        editor
12601            .compose_completion(&ComposeCompletion::default(), window, cx)
12602            .expect("No task returned")
12603    })
12604    .await
12605    .expect("Completion failed");
12606    cx.run_until_parked();
12607
12608    cx.update_editor(|editor, _, cx| {
12609        assert_eq!(
12610            resolve_requests_1.load(atomic::Ordering::Acquire),
12611            1,
12612            "Should always resolve once despite multiple selections"
12613        );
12614        assert_eq!(
12615            resolve_requests_2.load(atomic::Ordering::Acquire),
12616            1,
12617            "Should always resolve once after multiple selections and applying the completion"
12618        );
12619        assert_eq!(
12620            editor.text(cx),
12621            "fn main() { let a = ??.other; }",
12622            "Should use resolved data when applying the completion"
12623        );
12624    });
12625}
12626
12627#[gpui::test]
12628async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12629    init_test(cx, |_| {});
12630
12631    let item_0 = lsp::CompletionItem {
12632        label: "abs".into(),
12633        insert_text: Some("abs".into()),
12634        data: Some(json!({ "very": "special"})),
12635        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12636        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12637            lsp::InsertReplaceEdit {
12638                new_text: "abs".to_string(),
12639                insert: lsp::Range::default(),
12640                replace: lsp::Range::default(),
12641            },
12642        )),
12643        ..lsp::CompletionItem::default()
12644    };
12645    let items = iter::once(item_0.clone())
12646        .chain((11..51).map(|i| lsp::CompletionItem {
12647            label: format!("item_{}", i),
12648            insert_text: Some(format!("item_{}", i)),
12649            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12650            ..lsp::CompletionItem::default()
12651        }))
12652        .collect::<Vec<_>>();
12653
12654    let default_commit_characters = vec!["?".to_string()];
12655    let default_data = json!({ "default": "data"});
12656    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12657    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12658    let default_edit_range = lsp::Range {
12659        start: lsp::Position {
12660            line: 0,
12661            character: 5,
12662        },
12663        end: lsp::Position {
12664            line: 0,
12665            character: 5,
12666        },
12667    };
12668
12669    let mut cx = EditorLspTestContext::new_rust(
12670        lsp::ServerCapabilities {
12671            completion_provider: Some(lsp::CompletionOptions {
12672                trigger_characters: Some(vec![".".to_string()]),
12673                resolve_provider: Some(true),
12674                ..Default::default()
12675            }),
12676            ..Default::default()
12677        },
12678        cx,
12679    )
12680    .await;
12681
12682    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12683    cx.simulate_keystroke(".");
12684
12685    let completion_data = default_data.clone();
12686    let completion_characters = default_commit_characters.clone();
12687    let completion_items = items.clone();
12688    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12689        let default_data = completion_data.clone();
12690        let default_commit_characters = completion_characters.clone();
12691        let items = completion_items.clone();
12692        async move {
12693            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12694                items,
12695                item_defaults: Some(lsp::CompletionListItemDefaults {
12696                    data: Some(default_data.clone()),
12697                    commit_characters: Some(default_commit_characters.clone()),
12698                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12699                        default_edit_range,
12700                    )),
12701                    insert_text_format: Some(default_insert_text_format),
12702                    insert_text_mode: Some(default_insert_text_mode),
12703                }),
12704                ..lsp::CompletionList::default()
12705            })))
12706        }
12707    })
12708    .next()
12709    .await;
12710
12711    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12712    cx.lsp
12713        .server
12714        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12715            let closure_resolved_items = resolved_items.clone();
12716            move |item_to_resolve, _| {
12717                let closure_resolved_items = closure_resolved_items.clone();
12718                async move {
12719                    closure_resolved_items.lock().push(item_to_resolve.clone());
12720                    Ok(item_to_resolve)
12721                }
12722            }
12723        })
12724        .detach();
12725
12726    cx.condition(|editor, _| editor.context_menu_visible())
12727        .await;
12728    cx.run_until_parked();
12729    cx.update_editor(|editor, _, _| {
12730        let menu = editor.context_menu.borrow_mut();
12731        match menu.as_ref().expect("should have the completions menu") {
12732            CodeContextMenu::Completions(completions_menu) => {
12733                assert_eq!(
12734                    completions_menu
12735                        .entries
12736                        .borrow()
12737                        .iter()
12738                        .map(|mat| mat.string.clone())
12739                        .collect::<Vec<String>>(),
12740                    items
12741                        .iter()
12742                        .map(|completion| completion.label.clone())
12743                        .collect::<Vec<String>>()
12744                );
12745            }
12746            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12747        }
12748    });
12749    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12750    // with 4 from the end.
12751    assert_eq!(
12752        *resolved_items.lock(),
12753        [&items[0..16], &items[items.len() - 4..items.len()]]
12754            .concat()
12755            .iter()
12756            .cloned()
12757            .map(|mut item| {
12758                if item.data.is_none() {
12759                    item.data = Some(default_data.clone());
12760                }
12761                item
12762            })
12763            .collect::<Vec<lsp::CompletionItem>>(),
12764        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12765    );
12766    resolved_items.lock().clear();
12767
12768    cx.update_editor(|editor, window, cx| {
12769        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12770    });
12771    cx.run_until_parked();
12772    // Completions that have already been resolved are skipped.
12773    assert_eq!(
12774        *resolved_items.lock(),
12775        items[items.len() - 16..items.len() - 4]
12776            .iter()
12777            .cloned()
12778            .map(|mut item| {
12779                if item.data.is_none() {
12780                    item.data = Some(default_data.clone());
12781                }
12782                item
12783            })
12784            .collect::<Vec<lsp::CompletionItem>>()
12785    );
12786    resolved_items.lock().clear();
12787}
12788
12789#[gpui::test]
12790async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12791    init_test(cx, |_| {});
12792
12793    let mut cx = EditorLspTestContext::new(
12794        Language::new(
12795            LanguageConfig {
12796                matcher: LanguageMatcher {
12797                    path_suffixes: vec!["jsx".into()],
12798                    ..Default::default()
12799                },
12800                overrides: [(
12801                    "element".into(),
12802                    LanguageConfigOverride {
12803                        word_characters: Override::Set(['-'].into_iter().collect()),
12804                        ..Default::default()
12805                    },
12806                )]
12807                .into_iter()
12808                .collect(),
12809                ..Default::default()
12810            },
12811            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12812        )
12813        .with_override_query("(jsx_self_closing_element) @element")
12814        .unwrap(),
12815        lsp::ServerCapabilities {
12816            completion_provider: Some(lsp::CompletionOptions {
12817                trigger_characters: Some(vec![":".to_string()]),
12818                ..Default::default()
12819            }),
12820            ..Default::default()
12821        },
12822        cx,
12823    )
12824    .await;
12825
12826    cx.lsp
12827        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12828            Ok(Some(lsp::CompletionResponse::Array(vec![
12829                lsp::CompletionItem {
12830                    label: "bg-blue".into(),
12831                    ..Default::default()
12832                },
12833                lsp::CompletionItem {
12834                    label: "bg-red".into(),
12835                    ..Default::default()
12836                },
12837                lsp::CompletionItem {
12838                    label: "bg-yellow".into(),
12839                    ..Default::default()
12840                },
12841            ])))
12842        });
12843
12844    cx.set_state(r#"<p class="bgˇ" />"#);
12845
12846    // Trigger completion when typing a dash, because the dash is an extra
12847    // word character in the 'element' scope, which contains the cursor.
12848    cx.simulate_keystroke("-");
12849    cx.executor().run_until_parked();
12850    cx.update_editor(|editor, _, _| {
12851        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12852        {
12853            assert_eq!(
12854                completion_menu_entries(&menu),
12855                &["bg-red", "bg-blue", "bg-yellow"]
12856            );
12857        } else {
12858            panic!("expected completion menu to be open");
12859        }
12860    });
12861
12862    cx.simulate_keystroke("l");
12863    cx.executor().run_until_parked();
12864    cx.update_editor(|editor, _, _| {
12865        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12866        {
12867            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12868        } else {
12869            panic!("expected completion menu to be open");
12870        }
12871    });
12872
12873    // When filtering completions, consider the character after the '-' to
12874    // be the start of a subword.
12875    cx.set_state(r#"<p class="yelˇ" />"#);
12876    cx.simulate_keystroke("l");
12877    cx.executor().run_until_parked();
12878    cx.update_editor(|editor, _, _| {
12879        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12880        {
12881            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12882        } else {
12883            panic!("expected completion menu to be open");
12884        }
12885    });
12886}
12887
12888fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12889    let entries = menu.entries.borrow();
12890    entries.iter().map(|mat| mat.string.clone()).collect()
12891}
12892
12893#[gpui::test]
12894async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12895    init_test(cx, |settings| {
12896        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12897            FormatterList(vec![Formatter::Prettier].into()),
12898        ))
12899    });
12900
12901    let fs = FakeFs::new(cx.executor());
12902    fs.insert_file(path!("/file.ts"), Default::default()).await;
12903
12904    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12905    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12906
12907    language_registry.add(Arc::new(Language::new(
12908        LanguageConfig {
12909            name: "TypeScript".into(),
12910            matcher: LanguageMatcher {
12911                path_suffixes: vec!["ts".to_string()],
12912                ..Default::default()
12913            },
12914            ..Default::default()
12915        },
12916        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12917    )));
12918    update_test_language_settings(cx, |settings| {
12919        settings.defaults.prettier = Some(PrettierSettings {
12920            allowed: true,
12921            ..PrettierSettings::default()
12922        });
12923    });
12924
12925    let test_plugin = "test_plugin";
12926    let _ = language_registry.register_fake_lsp(
12927        "TypeScript",
12928        FakeLspAdapter {
12929            prettier_plugins: vec![test_plugin],
12930            ..Default::default()
12931        },
12932    );
12933
12934    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12935    let buffer = project
12936        .update(cx, |project, cx| {
12937            project.open_local_buffer(path!("/file.ts"), cx)
12938        })
12939        .await
12940        .unwrap();
12941
12942    let buffer_text = "one\ntwo\nthree\n";
12943    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12944    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12945    editor.update_in(cx, |editor, window, cx| {
12946        editor.set_text(buffer_text, window, cx)
12947    });
12948
12949    editor
12950        .update_in(cx, |editor, window, cx| {
12951            editor.perform_format(
12952                project.clone(),
12953                FormatTrigger::Manual,
12954                FormatTarget::Buffers,
12955                window,
12956                cx,
12957            )
12958        })
12959        .unwrap()
12960        .await;
12961    assert_eq!(
12962        editor.update(cx, |editor, cx| editor.text(cx)),
12963        buffer_text.to_string() + prettier_format_suffix,
12964        "Test prettier formatting was not applied to the original buffer text",
12965    );
12966
12967    update_test_language_settings(cx, |settings| {
12968        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12969    });
12970    let format = editor.update_in(cx, |editor, window, cx| {
12971        editor.perform_format(
12972            project.clone(),
12973            FormatTrigger::Manual,
12974            FormatTarget::Buffers,
12975            window,
12976            cx,
12977        )
12978    });
12979    format.await.unwrap();
12980    assert_eq!(
12981        editor.update(cx, |editor, cx| editor.text(cx)),
12982        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12983        "Autoformatting (via test prettier) was not applied to the original buffer text",
12984    );
12985}
12986
12987#[gpui::test]
12988async fn test_addition_reverts(cx: &mut TestAppContext) {
12989    init_test(cx, |_| {});
12990    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12991    let base_text = indoc! {r#"
12992        struct Row;
12993        struct Row1;
12994        struct Row2;
12995
12996        struct Row4;
12997        struct Row5;
12998        struct Row6;
12999
13000        struct Row8;
13001        struct Row9;
13002        struct Row10;"#};
13003
13004    // When addition hunks are not adjacent to carets, no hunk revert is performed
13005    assert_hunk_revert(
13006        indoc! {r#"struct Row;
13007                   struct Row1;
13008                   struct Row1.1;
13009                   struct Row1.2;
13010                   struct Row2;ˇ
13011
13012                   struct Row4;
13013                   struct Row5;
13014                   struct Row6;
13015
13016                   struct Row8;
13017                   ˇstruct Row9;
13018                   struct Row9.1;
13019                   struct Row9.2;
13020                   struct Row9.3;
13021                   struct Row10;"#},
13022        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13023        indoc! {r#"struct Row;
13024                   struct Row1;
13025                   struct Row1.1;
13026                   struct Row1.2;
13027                   struct Row2;ˇ
13028
13029                   struct Row4;
13030                   struct Row5;
13031                   struct Row6;
13032
13033                   struct Row8;
13034                   ˇstruct Row9;
13035                   struct Row9.1;
13036                   struct Row9.2;
13037                   struct Row9.3;
13038                   struct Row10;"#},
13039        base_text,
13040        &mut cx,
13041    );
13042    // Same for selections
13043    assert_hunk_revert(
13044        indoc! {r#"struct Row;
13045                   struct Row1;
13046                   struct Row2;
13047                   struct Row2.1;
13048                   struct Row2.2;
13049                   «ˇ
13050                   struct Row4;
13051                   struct» Row5;
13052                   «struct Row6;
13053                   ˇ»
13054                   struct Row9.1;
13055                   struct Row9.2;
13056                   struct Row9.3;
13057                   struct Row8;
13058                   struct Row9;
13059                   struct Row10;"#},
13060        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13061        indoc! {r#"struct Row;
13062                   struct Row1;
13063                   struct Row2;
13064                   struct Row2.1;
13065                   struct Row2.2;
13066                   «ˇ
13067                   struct Row4;
13068                   struct» Row5;
13069                   «struct Row6;
13070                   ˇ»
13071                   struct Row9.1;
13072                   struct Row9.2;
13073                   struct Row9.3;
13074                   struct Row8;
13075                   struct Row9;
13076                   struct Row10;"#},
13077        base_text,
13078        &mut cx,
13079    );
13080
13081    // When carets and selections intersect the addition hunks, those are reverted.
13082    // Adjacent carets got merged.
13083    assert_hunk_revert(
13084        indoc! {r#"struct Row;
13085                   ˇ// something on the top
13086                   struct Row1;
13087                   struct Row2;
13088                   struct Roˇw3.1;
13089                   struct Row2.2;
13090                   struct Row2.3;ˇ
13091
13092                   struct Row4;
13093                   struct ˇRow5.1;
13094                   struct Row5.2;
13095                   struct «Rowˇ»5.3;
13096                   struct Row5;
13097                   struct Row6;
13098                   ˇ
13099                   struct Row9.1;
13100                   struct «Rowˇ»9.2;
13101                   struct «ˇRow»9.3;
13102                   struct Row8;
13103                   struct Row9;
13104                   «ˇ// something on bottom»
13105                   struct Row10;"#},
13106        vec![
13107            DiffHunkStatusKind::Added,
13108            DiffHunkStatusKind::Added,
13109            DiffHunkStatusKind::Added,
13110            DiffHunkStatusKind::Added,
13111            DiffHunkStatusKind::Added,
13112        ],
13113        indoc! {r#"struct Row;
13114                   ˇstruct Row1;
13115                   struct Row2;
13116                   ˇ
13117                   struct Row4;
13118                   ˇstruct Row5;
13119                   struct Row6;
13120                   ˇ
13121                   ˇstruct Row8;
13122                   struct Row9;
13123                   ˇstruct Row10;"#},
13124        base_text,
13125        &mut cx,
13126    );
13127}
13128
13129#[gpui::test]
13130async fn test_modification_reverts(cx: &mut TestAppContext) {
13131    init_test(cx, |_| {});
13132    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13133    let base_text = indoc! {r#"
13134        struct Row;
13135        struct Row1;
13136        struct Row2;
13137
13138        struct Row4;
13139        struct Row5;
13140        struct Row6;
13141
13142        struct Row8;
13143        struct Row9;
13144        struct Row10;"#};
13145
13146    // Modification hunks behave the same as the addition ones.
13147    assert_hunk_revert(
13148        indoc! {r#"struct Row;
13149                   struct Row1;
13150                   struct Row33;
13151                   ˇ
13152                   struct Row4;
13153                   struct Row5;
13154                   struct Row6;
13155                   ˇ
13156                   struct Row99;
13157                   struct Row9;
13158                   struct Row10;"#},
13159        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13160        indoc! {r#"struct Row;
13161                   struct Row1;
13162                   struct Row33;
13163                   ˇ
13164                   struct Row4;
13165                   struct Row5;
13166                   struct Row6;
13167                   ˇ
13168                   struct Row99;
13169                   struct Row9;
13170                   struct Row10;"#},
13171        base_text,
13172        &mut cx,
13173    );
13174    assert_hunk_revert(
13175        indoc! {r#"struct Row;
13176                   struct Row1;
13177                   struct Row33;
13178                   «ˇ
13179                   struct Row4;
13180                   struct» Row5;
13181                   «struct Row6;
13182                   ˇ»
13183                   struct Row99;
13184                   struct Row9;
13185                   struct Row10;"#},
13186        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13187        indoc! {r#"struct Row;
13188                   struct Row1;
13189                   struct Row33;
13190                   «ˇ
13191                   struct Row4;
13192                   struct» Row5;
13193                   «struct Row6;
13194                   ˇ»
13195                   struct Row99;
13196                   struct Row9;
13197                   struct Row10;"#},
13198        base_text,
13199        &mut cx,
13200    );
13201
13202    assert_hunk_revert(
13203        indoc! {r#"ˇstruct Row1.1;
13204                   struct Row1;
13205                   «ˇstr»uct Row22;
13206
13207                   struct ˇRow44;
13208                   struct Row5;
13209                   struct «Rˇ»ow66;ˇ
13210
13211                   «struˇ»ct Row88;
13212                   struct Row9;
13213                   struct Row1011;ˇ"#},
13214        vec![
13215            DiffHunkStatusKind::Modified,
13216            DiffHunkStatusKind::Modified,
13217            DiffHunkStatusKind::Modified,
13218            DiffHunkStatusKind::Modified,
13219            DiffHunkStatusKind::Modified,
13220            DiffHunkStatusKind::Modified,
13221        ],
13222        indoc! {r#"struct Row;
13223                   ˇstruct Row1;
13224                   struct Row2;
13225                   ˇ
13226                   struct Row4;
13227                   ˇstruct Row5;
13228                   struct Row6;
13229                   ˇ
13230                   struct Row8;
13231                   ˇstruct Row9;
13232                   struct Row10;ˇ"#},
13233        base_text,
13234        &mut cx,
13235    );
13236}
13237
13238#[gpui::test]
13239async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13240    init_test(cx, |_| {});
13241    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13242    let base_text = indoc! {r#"
13243        one
13244
13245        two
13246        three
13247        "#};
13248
13249    cx.set_head_text(base_text);
13250    cx.set_state("\nˇ\n");
13251    cx.executor().run_until_parked();
13252    cx.update_editor(|editor, _window, cx| {
13253        editor.expand_selected_diff_hunks(cx);
13254    });
13255    cx.executor().run_until_parked();
13256    cx.update_editor(|editor, window, cx| {
13257        editor.backspace(&Default::default(), window, cx);
13258    });
13259    cx.run_until_parked();
13260    cx.assert_state_with_diff(
13261        indoc! {r#"
13262
13263        - two
13264        - threeˇ
13265        +
13266        "#}
13267        .to_string(),
13268    );
13269}
13270
13271#[gpui::test]
13272async fn test_deletion_reverts(cx: &mut TestAppContext) {
13273    init_test(cx, |_| {});
13274    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13275    let base_text = indoc! {r#"struct Row;
13276struct Row1;
13277struct Row2;
13278
13279struct Row4;
13280struct Row5;
13281struct Row6;
13282
13283struct Row8;
13284struct Row9;
13285struct Row10;"#};
13286
13287    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13288    assert_hunk_revert(
13289        indoc! {r#"struct Row;
13290                   struct Row2;
13291
13292                   ˇstruct Row4;
13293                   struct Row5;
13294                   struct Row6;
13295                   ˇ
13296                   struct Row8;
13297                   struct Row10;"#},
13298        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13299        indoc! {r#"struct Row;
13300                   struct Row2;
13301
13302                   ˇstruct Row4;
13303                   struct Row5;
13304                   struct Row6;
13305                   ˇ
13306                   struct Row8;
13307                   struct Row10;"#},
13308        base_text,
13309        &mut cx,
13310    );
13311    assert_hunk_revert(
13312        indoc! {r#"struct Row;
13313                   struct Row2;
13314
13315                   «ˇstruct Row4;
13316                   struct» Row5;
13317                   «struct Row6;
13318                   ˇ»
13319                   struct Row8;
13320                   struct Row10;"#},
13321        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13322        indoc! {r#"struct Row;
13323                   struct Row2;
13324
13325                   «ˇstruct Row4;
13326                   struct» Row5;
13327                   «struct Row6;
13328                   ˇ»
13329                   struct Row8;
13330                   struct Row10;"#},
13331        base_text,
13332        &mut cx,
13333    );
13334
13335    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13336    assert_hunk_revert(
13337        indoc! {r#"struct Row;
13338                   ˇstruct Row2;
13339
13340                   struct Row4;
13341                   struct Row5;
13342                   struct Row6;
13343
13344                   struct Row8;ˇ
13345                   struct Row10;"#},
13346        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13347        indoc! {r#"struct Row;
13348                   struct Row1;
13349                   ˇstruct Row2;
13350
13351                   struct Row4;
13352                   struct Row5;
13353                   struct Row6;
13354
13355                   struct Row8;ˇ
13356                   struct Row9;
13357                   struct Row10;"#},
13358        base_text,
13359        &mut cx,
13360    );
13361    assert_hunk_revert(
13362        indoc! {r#"struct Row;
13363                   struct Row2«ˇ;
13364                   struct Row4;
13365                   struct» Row5;
13366                   «struct Row6;
13367
13368                   struct Row8;ˇ»
13369                   struct Row10;"#},
13370        vec![
13371            DiffHunkStatusKind::Deleted,
13372            DiffHunkStatusKind::Deleted,
13373            DiffHunkStatusKind::Deleted,
13374        ],
13375        indoc! {r#"struct Row;
13376                   struct Row1;
13377                   struct Row2«ˇ;
13378
13379                   struct Row4;
13380                   struct» Row5;
13381                   «struct Row6;
13382
13383                   struct Row8;ˇ»
13384                   struct Row9;
13385                   struct Row10;"#},
13386        base_text,
13387        &mut cx,
13388    );
13389}
13390
13391#[gpui::test]
13392async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13393    init_test(cx, |_| {});
13394
13395    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13396    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13397    let base_text_3 =
13398        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13399
13400    let text_1 = edit_first_char_of_every_line(base_text_1);
13401    let text_2 = edit_first_char_of_every_line(base_text_2);
13402    let text_3 = edit_first_char_of_every_line(base_text_3);
13403
13404    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13405    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13406    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13407
13408    let multibuffer = cx.new(|cx| {
13409        let mut multibuffer = MultiBuffer::new(ReadWrite);
13410        multibuffer.push_excerpts(
13411            buffer_1.clone(),
13412            [
13413                ExcerptRange {
13414                    context: Point::new(0, 0)..Point::new(3, 0),
13415                    primary: None,
13416                },
13417                ExcerptRange {
13418                    context: Point::new(5, 0)..Point::new(7, 0),
13419                    primary: None,
13420                },
13421                ExcerptRange {
13422                    context: Point::new(9, 0)..Point::new(10, 4),
13423                    primary: None,
13424                },
13425            ],
13426            cx,
13427        );
13428        multibuffer.push_excerpts(
13429            buffer_2.clone(),
13430            [
13431                ExcerptRange {
13432                    context: Point::new(0, 0)..Point::new(3, 0),
13433                    primary: None,
13434                },
13435                ExcerptRange {
13436                    context: Point::new(5, 0)..Point::new(7, 0),
13437                    primary: None,
13438                },
13439                ExcerptRange {
13440                    context: Point::new(9, 0)..Point::new(10, 4),
13441                    primary: None,
13442                },
13443            ],
13444            cx,
13445        );
13446        multibuffer.push_excerpts(
13447            buffer_3.clone(),
13448            [
13449                ExcerptRange {
13450                    context: Point::new(0, 0)..Point::new(3, 0),
13451                    primary: None,
13452                },
13453                ExcerptRange {
13454                    context: Point::new(5, 0)..Point::new(7, 0),
13455                    primary: None,
13456                },
13457                ExcerptRange {
13458                    context: Point::new(9, 0)..Point::new(10, 4),
13459                    primary: None,
13460                },
13461            ],
13462            cx,
13463        );
13464        multibuffer
13465    });
13466
13467    let fs = FakeFs::new(cx.executor());
13468    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13469    let (editor, cx) = cx
13470        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13471    editor.update_in(cx, |editor, _window, cx| {
13472        for (buffer, diff_base) in [
13473            (buffer_1.clone(), base_text_1),
13474            (buffer_2.clone(), base_text_2),
13475            (buffer_3.clone(), base_text_3),
13476        ] {
13477            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13478            editor
13479                .buffer
13480                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13481        }
13482    });
13483    cx.executor().run_until_parked();
13484
13485    editor.update_in(cx, |editor, window, cx| {
13486        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}");
13487        editor.select_all(&SelectAll, window, cx);
13488        editor.git_restore(&Default::default(), window, cx);
13489    });
13490    cx.executor().run_until_parked();
13491
13492    // When all ranges are selected, all buffer hunks are reverted.
13493    editor.update(cx, |editor, cx| {
13494        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");
13495    });
13496    buffer_1.update(cx, |buffer, _| {
13497        assert_eq!(buffer.text(), base_text_1);
13498    });
13499    buffer_2.update(cx, |buffer, _| {
13500        assert_eq!(buffer.text(), base_text_2);
13501    });
13502    buffer_3.update(cx, |buffer, _| {
13503        assert_eq!(buffer.text(), base_text_3);
13504    });
13505
13506    editor.update_in(cx, |editor, window, cx| {
13507        editor.undo(&Default::default(), window, cx);
13508    });
13509
13510    editor.update_in(cx, |editor, window, cx| {
13511        editor.change_selections(None, window, cx, |s| {
13512            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13513        });
13514        editor.git_restore(&Default::default(), window, cx);
13515    });
13516
13517    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13518    // but not affect buffer_2 and its related excerpts.
13519    editor.update(cx, |editor, cx| {
13520        assert_eq!(
13521            editor.text(cx),
13522            "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}"
13523        );
13524    });
13525    buffer_1.update(cx, |buffer, _| {
13526        assert_eq!(buffer.text(), base_text_1);
13527    });
13528    buffer_2.update(cx, |buffer, _| {
13529        assert_eq!(
13530            buffer.text(),
13531            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13532        );
13533    });
13534    buffer_3.update(cx, |buffer, _| {
13535        assert_eq!(
13536            buffer.text(),
13537            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13538        );
13539    });
13540
13541    fn edit_first_char_of_every_line(text: &str) -> String {
13542        text.split('\n')
13543            .map(|line| format!("X{}", &line[1..]))
13544            .collect::<Vec<_>>()
13545            .join("\n")
13546    }
13547}
13548
13549#[gpui::test]
13550async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13551    init_test(cx, |_| {});
13552
13553    let cols = 4;
13554    let rows = 10;
13555    let sample_text_1 = sample_text(rows, cols, 'a');
13556    assert_eq!(
13557        sample_text_1,
13558        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13559    );
13560    let sample_text_2 = sample_text(rows, cols, 'l');
13561    assert_eq!(
13562        sample_text_2,
13563        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13564    );
13565    let sample_text_3 = sample_text(rows, cols, 'v');
13566    assert_eq!(
13567        sample_text_3,
13568        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13569    );
13570
13571    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13572    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13573    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13574
13575    let multi_buffer = cx.new(|cx| {
13576        let mut multibuffer = MultiBuffer::new(ReadWrite);
13577        multibuffer.push_excerpts(
13578            buffer_1.clone(),
13579            [
13580                ExcerptRange {
13581                    context: Point::new(0, 0)..Point::new(3, 0),
13582                    primary: None,
13583                },
13584                ExcerptRange {
13585                    context: Point::new(5, 0)..Point::new(7, 0),
13586                    primary: None,
13587                },
13588                ExcerptRange {
13589                    context: Point::new(9, 0)..Point::new(10, 4),
13590                    primary: None,
13591                },
13592            ],
13593            cx,
13594        );
13595        multibuffer.push_excerpts(
13596            buffer_2.clone(),
13597            [
13598                ExcerptRange {
13599                    context: Point::new(0, 0)..Point::new(3, 0),
13600                    primary: None,
13601                },
13602                ExcerptRange {
13603                    context: Point::new(5, 0)..Point::new(7, 0),
13604                    primary: None,
13605                },
13606                ExcerptRange {
13607                    context: Point::new(9, 0)..Point::new(10, 4),
13608                    primary: None,
13609                },
13610            ],
13611            cx,
13612        );
13613        multibuffer.push_excerpts(
13614            buffer_3.clone(),
13615            [
13616                ExcerptRange {
13617                    context: Point::new(0, 0)..Point::new(3, 0),
13618                    primary: None,
13619                },
13620                ExcerptRange {
13621                    context: Point::new(5, 0)..Point::new(7, 0),
13622                    primary: None,
13623                },
13624                ExcerptRange {
13625                    context: Point::new(9, 0)..Point::new(10, 4),
13626                    primary: None,
13627                },
13628            ],
13629            cx,
13630        );
13631        multibuffer
13632    });
13633
13634    let fs = FakeFs::new(cx.executor());
13635    fs.insert_tree(
13636        "/a",
13637        json!({
13638            "main.rs": sample_text_1,
13639            "other.rs": sample_text_2,
13640            "lib.rs": sample_text_3,
13641        }),
13642    )
13643    .await;
13644    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13645    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13646    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13647    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13648        Editor::new(
13649            EditorMode::Full,
13650            multi_buffer,
13651            Some(project.clone()),
13652            window,
13653            cx,
13654        )
13655    });
13656    let multibuffer_item_id = workspace
13657        .update(cx, |workspace, window, cx| {
13658            assert!(
13659                workspace.active_item(cx).is_none(),
13660                "active item should be None before the first item is added"
13661            );
13662            workspace.add_item_to_active_pane(
13663                Box::new(multi_buffer_editor.clone()),
13664                None,
13665                true,
13666                window,
13667                cx,
13668            );
13669            let active_item = workspace
13670                .active_item(cx)
13671                .expect("should have an active item after adding the multi buffer");
13672            assert!(
13673                !active_item.is_singleton(cx),
13674                "A multi buffer was expected to active after adding"
13675            );
13676            active_item.item_id()
13677        })
13678        .unwrap();
13679    cx.executor().run_until_parked();
13680
13681    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13682        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13683            s.select_ranges(Some(1..2))
13684        });
13685        editor.open_excerpts(&OpenExcerpts, window, cx);
13686    });
13687    cx.executor().run_until_parked();
13688    let first_item_id = workspace
13689        .update(cx, |workspace, window, cx| {
13690            let active_item = workspace
13691                .active_item(cx)
13692                .expect("should have an active item after navigating into the 1st buffer");
13693            let first_item_id = active_item.item_id();
13694            assert_ne!(
13695                first_item_id, multibuffer_item_id,
13696                "Should navigate into the 1st buffer and activate it"
13697            );
13698            assert!(
13699                active_item.is_singleton(cx),
13700                "New active item should be a singleton buffer"
13701            );
13702            assert_eq!(
13703                active_item
13704                    .act_as::<Editor>(cx)
13705                    .expect("should have navigated into an editor for the 1st buffer")
13706                    .read(cx)
13707                    .text(cx),
13708                sample_text_1
13709            );
13710
13711            workspace
13712                .go_back(workspace.active_pane().downgrade(), window, cx)
13713                .detach_and_log_err(cx);
13714
13715            first_item_id
13716        })
13717        .unwrap();
13718    cx.executor().run_until_parked();
13719    workspace
13720        .update(cx, |workspace, _, cx| {
13721            let active_item = workspace
13722                .active_item(cx)
13723                .expect("should have an active item after navigating back");
13724            assert_eq!(
13725                active_item.item_id(),
13726                multibuffer_item_id,
13727                "Should navigate back to the multi buffer"
13728            );
13729            assert!(!active_item.is_singleton(cx));
13730        })
13731        .unwrap();
13732
13733    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13734        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13735            s.select_ranges(Some(39..40))
13736        });
13737        editor.open_excerpts(&OpenExcerpts, window, cx);
13738    });
13739    cx.executor().run_until_parked();
13740    let second_item_id = workspace
13741        .update(cx, |workspace, window, cx| {
13742            let active_item = workspace
13743                .active_item(cx)
13744                .expect("should have an active item after navigating into the 2nd buffer");
13745            let second_item_id = active_item.item_id();
13746            assert_ne!(
13747                second_item_id, multibuffer_item_id,
13748                "Should navigate away from the multibuffer"
13749            );
13750            assert_ne!(
13751                second_item_id, first_item_id,
13752                "Should navigate into the 2nd buffer and activate it"
13753            );
13754            assert!(
13755                active_item.is_singleton(cx),
13756                "New active item should be a singleton buffer"
13757            );
13758            assert_eq!(
13759                active_item
13760                    .act_as::<Editor>(cx)
13761                    .expect("should have navigated into an editor")
13762                    .read(cx)
13763                    .text(cx),
13764                sample_text_2
13765            );
13766
13767            workspace
13768                .go_back(workspace.active_pane().downgrade(), window, cx)
13769                .detach_and_log_err(cx);
13770
13771            second_item_id
13772        })
13773        .unwrap();
13774    cx.executor().run_until_parked();
13775    workspace
13776        .update(cx, |workspace, _, cx| {
13777            let active_item = workspace
13778                .active_item(cx)
13779                .expect("should have an active item after navigating back from the 2nd buffer");
13780            assert_eq!(
13781                active_item.item_id(),
13782                multibuffer_item_id,
13783                "Should navigate back from the 2nd buffer to the multi buffer"
13784            );
13785            assert!(!active_item.is_singleton(cx));
13786        })
13787        .unwrap();
13788
13789    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13790        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13791            s.select_ranges(Some(70..70))
13792        });
13793        editor.open_excerpts(&OpenExcerpts, window, cx);
13794    });
13795    cx.executor().run_until_parked();
13796    workspace
13797        .update(cx, |workspace, window, cx| {
13798            let active_item = workspace
13799                .active_item(cx)
13800                .expect("should have an active item after navigating into the 3rd buffer");
13801            let third_item_id = active_item.item_id();
13802            assert_ne!(
13803                third_item_id, multibuffer_item_id,
13804                "Should navigate into the 3rd buffer and activate it"
13805            );
13806            assert_ne!(third_item_id, first_item_id);
13807            assert_ne!(third_item_id, second_item_id);
13808            assert!(
13809                active_item.is_singleton(cx),
13810                "New active item should be a singleton buffer"
13811            );
13812            assert_eq!(
13813                active_item
13814                    .act_as::<Editor>(cx)
13815                    .expect("should have navigated into an editor")
13816                    .read(cx)
13817                    .text(cx),
13818                sample_text_3
13819            );
13820
13821            workspace
13822                .go_back(workspace.active_pane().downgrade(), window, cx)
13823                .detach_and_log_err(cx);
13824        })
13825        .unwrap();
13826    cx.executor().run_until_parked();
13827    workspace
13828        .update(cx, |workspace, _, cx| {
13829            let active_item = workspace
13830                .active_item(cx)
13831                .expect("should have an active item after navigating back from the 3rd buffer");
13832            assert_eq!(
13833                active_item.item_id(),
13834                multibuffer_item_id,
13835                "Should navigate back from the 3rd buffer to the multi buffer"
13836            );
13837            assert!(!active_item.is_singleton(cx));
13838        })
13839        .unwrap();
13840}
13841
13842#[gpui::test]
13843async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13844    init_test(cx, |_| {});
13845
13846    let mut cx = EditorTestContext::new(cx).await;
13847
13848    let diff_base = r#"
13849        use some::mod;
13850
13851        const A: u32 = 42;
13852
13853        fn main() {
13854            println!("hello");
13855
13856            println!("world");
13857        }
13858        "#
13859    .unindent();
13860
13861    cx.set_state(
13862        &r#"
13863        use some::modified;
13864
13865        ˇ
13866        fn main() {
13867            println!("hello there");
13868
13869            println!("around the");
13870            println!("world");
13871        }
13872        "#
13873        .unindent(),
13874    );
13875
13876    cx.set_head_text(&diff_base);
13877    executor.run_until_parked();
13878
13879    cx.update_editor(|editor, window, cx| {
13880        editor.go_to_next_hunk(&GoToHunk, window, cx);
13881        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13882    });
13883    executor.run_until_parked();
13884    cx.assert_state_with_diff(
13885        r#"
13886          use some::modified;
13887
13888
13889          fn main() {
13890        -     println!("hello");
13891        + ˇ    println!("hello there");
13892
13893              println!("around the");
13894              println!("world");
13895          }
13896        "#
13897        .unindent(),
13898    );
13899
13900    cx.update_editor(|editor, window, cx| {
13901        for _ in 0..2 {
13902            editor.go_to_next_hunk(&GoToHunk, window, cx);
13903            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13904        }
13905    });
13906    executor.run_until_parked();
13907    cx.assert_state_with_diff(
13908        r#"
13909        - use some::mod;
13910        + ˇuse some::modified;
13911
13912
13913          fn main() {
13914        -     println!("hello");
13915        +     println!("hello there");
13916
13917        +     println!("around the");
13918              println!("world");
13919          }
13920        "#
13921        .unindent(),
13922    );
13923
13924    cx.update_editor(|editor, window, cx| {
13925        editor.go_to_next_hunk(&GoToHunk, window, cx);
13926        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13927    });
13928    executor.run_until_parked();
13929    cx.assert_state_with_diff(
13930        r#"
13931        - use some::mod;
13932        + use some::modified;
13933
13934        - const A: u32 = 42;
13935          ˇ
13936          fn main() {
13937        -     println!("hello");
13938        +     println!("hello there");
13939
13940        +     println!("around the");
13941              println!("world");
13942          }
13943        "#
13944        .unindent(),
13945    );
13946
13947    cx.update_editor(|editor, window, cx| {
13948        editor.cancel(&Cancel, window, cx);
13949    });
13950
13951    cx.assert_state_with_diff(
13952        r#"
13953          use some::modified;
13954
13955          ˇ
13956          fn main() {
13957              println!("hello there");
13958
13959              println!("around the");
13960              println!("world");
13961          }
13962        "#
13963        .unindent(),
13964    );
13965}
13966
13967#[gpui::test]
13968async fn test_diff_base_change_with_expanded_diff_hunks(
13969    executor: BackgroundExecutor,
13970    cx: &mut TestAppContext,
13971) {
13972    init_test(cx, |_| {});
13973
13974    let mut cx = EditorTestContext::new(cx).await;
13975
13976    let diff_base = r#"
13977        use some::mod1;
13978        use some::mod2;
13979
13980        const A: u32 = 42;
13981        const B: u32 = 42;
13982        const C: u32 = 42;
13983
13984        fn main() {
13985            println!("hello");
13986
13987            println!("world");
13988        }
13989        "#
13990    .unindent();
13991
13992    cx.set_state(
13993        &r#"
13994        use some::mod2;
13995
13996        const A: u32 = 42;
13997        const C: u32 = 42;
13998
13999        fn main(ˇ) {
14000            //println!("hello");
14001
14002            println!("world");
14003            //
14004            //
14005        }
14006        "#
14007        .unindent(),
14008    );
14009
14010    cx.set_head_text(&diff_base);
14011    executor.run_until_parked();
14012
14013    cx.update_editor(|editor, window, cx| {
14014        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14015    });
14016    executor.run_until_parked();
14017    cx.assert_state_with_diff(
14018        r#"
14019        - use some::mod1;
14020          use some::mod2;
14021
14022          const A: u32 = 42;
14023        - const B: u32 = 42;
14024          const C: u32 = 42;
14025
14026          fn main(ˇ) {
14027        -     println!("hello");
14028        +     //println!("hello");
14029
14030              println!("world");
14031        +     //
14032        +     //
14033          }
14034        "#
14035        .unindent(),
14036    );
14037
14038    cx.set_head_text("new diff base!");
14039    executor.run_until_parked();
14040    cx.assert_state_with_diff(
14041        r#"
14042        - new diff base!
14043        + use some::mod2;
14044        +
14045        + const A: u32 = 42;
14046        + const C: u32 = 42;
14047        +
14048        + fn main(ˇ) {
14049        +     //println!("hello");
14050        +
14051        +     println!("world");
14052        +     //
14053        +     //
14054        + }
14055        "#
14056        .unindent(),
14057    );
14058}
14059
14060#[gpui::test]
14061async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14062    init_test(cx, |_| {});
14063
14064    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14065    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14066    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14067    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14068    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14069    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14070
14071    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14072    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14073    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14074
14075    let multi_buffer = cx.new(|cx| {
14076        let mut multibuffer = MultiBuffer::new(ReadWrite);
14077        multibuffer.push_excerpts(
14078            buffer_1.clone(),
14079            [
14080                ExcerptRange {
14081                    context: Point::new(0, 0)..Point::new(3, 0),
14082                    primary: None,
14083                },
14084                ExcerptRange {
14085                    context: Point::new(5, 0)..Point::new(7, 0),
14086                    primary: None,
14087                },
14088                ExcerptRange {
14089                    context: Point::new(9, 0)..Point::new(10, 3),
14090                    primary: None,
14091                },
14092            ],
14093            cx,
14094        );
14095        multibuffer.push_excerpts(
14096            buffer_2.clone(),
14097            [
14098                ExcerptRange {
14099                    context: Point::new(0, 0)..Point::new(3, 0),
14100                    primary: None,
14101                },
14102                ExcerptRange {
14103                    context: Point::new(5, 0)..Point::new(7, 0),
14104                    primary: None,
14105                },
14106                ExcerptRange {
14107                    context: Point::new(9, 0)..Point::new(10, 3),
14108                    primary: None,
14109                },
14110            ],
14111            cx,
14112        );
14113        multibuffer.push_excerpts(
14114            buffer_3.clone(),
14115            [
14116                ExcerptRange {
14117                    context: Point::new(0, 0)..Point::new(3, 0),
14118                    primary: None,
14119                },
14120                ExcerptRange {
14121                    context: Point::new(5, 0)..Point::new(7, 0),
14122                    primary: None,
14123                },
14124                ExcerptRange {
14125                    context: Point::new(9, 0)..Point::new(10, 3),
14126                    primary: None,
14127                },
14128            ],
14129            cx,
14130        );
14131        multibuffer
14132    });
14133
14134    let editor =
14135        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14136    editor
14137        .update(cx, |editor, _window, cx| {
14138            for (buffer, diff_base) in [
14139                (buffer_1.clone(), file_1_old),
14140                (buffer_2.clone(), file_2_old),
14141                (buffer_3.clone(), file_3_old),
14142            ] {
14143                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14144                editor
14145                    .buffer
14146                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14147            }
14148        })
14149        .unwrap();
14150
14151    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14152    cx.run_until_parked();
14153
14154    cx.assert_editor_state(
14155        &"
14156            ˇaaa
14157            ccc
14158            ddd
14159
14160            ggg
14161            hhh
14162
14163
14164            lll
14165            mmm
14166            NNN
14167
14168            qqq
14169            rrr
14170
14171            uuu
14172            111
14173            222
14174            333
14175
14176            666
14177            777
14178
14179            000
14180            !!!"
14181        .unindent(),
14182    );
14183
14184    cx.update_editor(|editor, window, cx| {
14185        editor.select_all(&SelectAll, window, cx);
14186        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14187    });
14188    cx.executor().run_until_parked();
14189
14190    cx.assert_state_with_diff(
14191        "
14192            «aaa
14193          - bbb
14194            ccc
14195            ddd
14196
14197            ggg
14198            hhh
14199
14200
14201            lll
14202            mmm
14203          - nnn
14204          + NNN
14205
14206            qqq
14207            rrr
14208
14209            uuu
14210            111
14211            222
14212            333
14213
14214          + 666
14215            777
14216
14217            000
14218            !!!ˇ»"
14219            .unindent(),
14220    );
14221}
14222
14223#[gpui::test]
14224async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14225    init_test(cx, |_| {});
14226
14227    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14228    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14229
14230    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14231    let multi_buffer = cx.new(|cx| {
14232        let mut multibuffer = MultiBuffer::new(ReadWrite);
14233        multibuffer.push_excerpts(
14234            buffer.clone(),
14235            [
14236                ExcerptRange {
14237                    context: Point::new(0, 0)..Point::new(2, 0),
14238                    primary: None,
14239                },
14240                ExcerptRange {
14241                    context: Point::new(4, 0)..Point::new(7, 0),
14242                    primary: None,
14243                },
14244                ExcerptRange {
14245                    context: Point::new(9, 0)..Point::new(10, 0),
14246                    primary: None,
14247                },
14248            ],
14249            cx,
14250        );
14251        multibuffer
14252    });
14253
14254    let editor =
14255        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14256    editor
14257        .update(cx, |editor, _window, cx| {
14258            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14259            editor
14260                .buffer
14261                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14262        })
14263        .unwrap();
14264
14265    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14266    cx.run_until_parked();
14267
14268    cx.update_editor(|editor, window, cx| {
14269        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14270    });
14271    cx.executor().run_until_parked();
14272
14273    // When the start of a hunk coincides with the start of its excerpt,
14274    // the hunk is expanded. When the start of a a hunk is earlier than
14275    // the start of its excerpt, the hunk is not expanded.
14276    cx.assert_state_with_diff(
14277        "
14278            ˇaaa
14279          - bbb
14280          + BBB
14281
14282          - ddd
14283          - eee
14284          + DDD
14285          + EEE
14286            fff
14287
14288            iii
14289        "
14290        .unindent(),
14291    );
14292}
14293
14294#[gpui::test]
14295async fn test_edits_around_expanded_insertion_hunks(
14296    executor: BackgroundExecutor,
14297    cx: &mut TestAppContext,
14298) {
14299    init_test(cx, |_| {});
14300
14301    let mut cx = EditorTestContext::new(cx).await;
14302
14303    let diff_base = r#"
14304        use some::mod1;
14305        use some::mod2;
14306
14307        const A: u32 = 42;
14308
14309        fn main() {
14310            println!("hello");
14311
14312            println!("world");
14313        }
14314        "#
14315    .unindent();
14316    executor.run_until_parked();
14317    cx.set_state(
14318        &r#"
14319        use some::mod1;
14320        use some::mod2;
14321
14322        const A: u32 = 42;
14323        const B: u32 = 42;
14324        const C: u32 = 42;
14325        ˇ
14326
14327        fn main() {
14328            println!("hello");
14329
14330            println!("world");
14331        }
14332        "#
14333        .unindent(),
14334    );
14335
14336    cx.set_head_text(&diff_base);
14337    executor.run_until_parked();
14338
14339    cx.update_editor(|editor, window, cx| {
14340        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14341    });
14342    executor.run_until_parked();
14343
14344    cx.assert_state_with_diff(
14345        r#"
14346        use some::mod1;
14347        use some::mod2;
14348
14349        const A: u32 = 42;
14350      + const B: u32 = 42;
14351      + const C: u32 = 42;
14352      + ˇ
14353
14354        fn main() {
14355            println!("hello");
14356
14357            println!("world");
14358        }
14359      "#
14360        .unindent(),
14361    );
14362
14363    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14364    executor.run_until_parked();
14365
14366    cx.assert_state_with_diff(
14367        r#"
14368        use some::mod1;
14369        use some::mod2;
14370
14371        const A: u32 = 42;
14372      + const B: u32 = 42;
14373      + const C: u32 = 42;
14374      + const D: u32 = 42;
14375      + ˇ
14376
14377        fn main() {
14378            println!("hello");
14379
14380            println!("world");
14381        }
14382      "#
14383        .unindent(),
14384    );
14385
14386    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14387    executor.run_until_parked();
14388
14389    cx.assert_state_with_diff(
14390        r#"
14391        use some::mod1;
14392        use some::mod2;
14393
14394        const A: u32 = 42;
14395      + const B: u32 = 42;
14396      + const C: u32 = 42;
14397      + const D: u32 = 42;
14398      + const E: u32 = 42;
14399      + ˇ
14400
14401        fn main() {
14402            println!("hello");
14403
14404            println!("world");
14405        }
14406      "#
14407        .unindent(),
14408    );
14409
14410    cx.update_editor(|editor, window, cx| {
14411        editor.delete_line(&DeleteLine, window, cx);
14412    });
14413    executor.run_until_parked();
14414
14415    cx.assert_state_with_diff(
14416        r#"
14417        use some::mod1;
14418        use some::mod2;
14419
14420        const A: u32 = 42;
14421      + const B: u32 = 42;
14422      + const C: u32 = 42;
14423      + const D: u32 = 42;
14424      + const E: u32 = 42;
14425        ˇ
14426        fn main() {
14427            println!("hello");
14428
14429            println!("world");
14430        }
14431      "#
14432        .unindent(),
14433    );
14434
14435    cx.update_editor(|editor, window, cx| {
14436        editor.move_up(&MoveUp, window, cx);
14437        editor.delete_line(&DeleteLine, window, cx);
14438        editor.move_up(&MoveUp, window, cx);
14439        editor.delete_line(&DeleteLine, window, cx);
14440        editor.move_up(&MoveUp, window, cx);
14441        editor.delete_line(&DeleteLine, window, cx);
14442    });
14443    executor.run_until_parked();
14444    cx.assert_state_with_diff(
14445        r#"
14446        use some::mod1;
14447        use some::mod2;
14448
14449        const A: u32 = 42;
14450      + const B: u32 = 42;
14451        ˇ
14452        fn main() {
14453            println!("hello");
14454
14455            println!("world");
14456        }
14457      "#
14458        .unindent(),
14459    );
14460
14461    cx.update_editor(|editor, window, cx| {
14462        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14463        editor.delete_line(&DeleteLine, window, cx);
14464    });
14465    executor.run_until_parked();
14466    cx.assert_state_with_diff(
14467        r#"
14468        ˇ
14469        fn main() {
14470            println!("hello");
14471
14472            println!("world");
14473        }
14474      "#
14475        .unindent(),
14476    );
14477}
14478
14479#[gpui::test]
14480async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14481    init_test(cx, |_| {});
14482
14483    let mut cx = EditorTestContext::new(cx).await;
14484    cx.set_head_text(indoc! { "
14485        one
14486        two
14487        three
14488        four
14489        five
14490        "
14491    });
14492    cx.set_state(indoc! { "
14493        one
14494        ˇthree
14495        five
14496    "});
14497    cx.run_until_parked();
14498    cx.update_editor(|editor, window, cx| {
14499        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14500    });
14501    cx.assert_state_with_diff(
14502        indoc! { "
14503        one
14504      - two
14505        ˇthree
14506      - four
14507        five
14508    "}
14509        .to_string(),
14510    );
14511    cx.update_editor(|editor, window, cx| {
14512        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14513    });
14514
14515    cx.assert_state_with_diff(
14516        indoc! { "
14517        one
14518        ˇthree
14519        five
14520    "}
14521        .to_string(),
14522    );
14523
14524    cx.set_state(indoc! { "
14525        one
14526        ˇTWO
14527        three
14528        four
14529        five
14530    "});
14531    cx.run_until_parked();
14532    cx.update_editor(|editor, window, cx| {
14533        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14534    });
14535
14536    cx.assert_state_with_diff(
14537        indoc! { "
14538            one
14539          - two
14540          + ˇTWO
14541            three
14542            four
14543            five
14544        "}
14545        .to_string(),
14546    );
14547    cx.update_editor(|editor, window, cx| {
14548        editor.move_up(&Default::default(), window, cx);
14549        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14550    });
14551    cx.assert_state_with_diff(
14552        indoc! { "
14553            one
14554            ˇTWO
14555            three
14556            four
14557            five
14558        "}
14559        .to_string(),
14560    );
14561}
14562
14563#[gpui::test]
14564async fn test_edits_around_expanded_deletion_hunks(
14565    executor: BackgroundExecutor,
14566    cx: &mut TestAppContext,
14567) {
14568    init_test(cx, |_| {});
14569
14570    let mut cx = EditorTestContext::new(cx).await;
14571
14572    let diff_base = r#"
14573        use some::mod1;
14574        use some::mod2;
14575
14576        const A: u32 = 42;
14577        const B: u32 = 42;
14578        const C: u32 = 42;
14579
14580
14581        fn main() {
14582            println!("hello");
14583
14584            println!("world");
14585        }
14586    "#
14587    .unindent();
14588    executor.run_until_parked();
14589    cx.set_state(
14590        &r#"
14591        use some::mod1;
14592        use some::mod2;
14593
14594        ˇconst B: u32 = 42;
14595        const C: u32 = 42;
14596
14597
14598        fn main() {
14599            println!("hello");
14600
14601            println!("world");
14602        }
14603        "#
14604        .unindent(),
14605    );
14606
14607    cx.set_head_text(&diff_base);
14608    executor.run_until_parked();
14609
14610    cx.update_editor(|editor, window, cx| {
14611        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14612    });
14613    executor.run_until_parked();
14614
14615    cx.assert_state_with_diff(
14616        r#"
14617        use some::mod1;
14618        use some::mod2;
14619
14620      - const A: u32 = 42;
14621        ˇconst B: u32 = 42;
14622        const C: u32 = 42;
14623
14624
14625        fn main() {
14626            println!("hello");
14627
14628            println!("world");
14629        }
14630      "#
14631        .unindent(),
14632    );
14633
14634    cx.update_editor(|editor, window, cx| {
14635        editor.delete_line(&DeleteLine, window, cx);
14636    });
14637    executor.run_until_parked();
14638    cx.assert_state_with_diff(
14639        r#"
14640        use some::mod1;
14641        use some::mod2;
14642
14643      - const A: u32 = 42;
14644      - const B: u32 = 42;
14645        ˇconst C: u32 = 42;
14646
14647
14648        fn main() {
14649            println!("hello");
14650
14651            println!("world");
14652        }
14653      "#
14654        .unindent(),
14655    );
14656
14657    cx.update_editor(|editor, window, cx| {
14658        editor.delete_line(&DeleteLine, window, cx);
14659    });
14660    executor.run_until_parked();
14661    cx.assert_state_with_diff(
14662        r#"
14663        use some::mod1;
14664        use some::mod2;
14665
14666      - const A: u32 = 42;
14667      - const B: u32 = 42;
14668      - const C: u32 = 42;
14669        ˇ
14670
14671        fn main() {
14672            println!("hello");
14673
14674            println!("world");
14675        }
14676      "#
14677        .unindent(),
14678    );
14679
14680    cx.update_editor(|editor, window, cx| {
14681        editor.handle_input("replacement", window, cx);
14682    });
14683    executor.run_until_parked();
14684    cx.assert_state_with_diff(
14685        r#"
14686        use some::mod1;
14687        use some::mod2;
14688
14689      - const A: u32 = 42;
14690      - const B: u32 = 42;
14691      - const C: u32 = 42;
14692      -
14693      + replacementˇ
14694
14695        fn main() {
14696            println!("hello");
14697
14698            println!("world");
14699        }
14700      "#
14701        .unindent(),
14702    );
14703}
14704
14705#[gpui::test]
14706async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14707    init_test(cx, |_| {});
14708
14709    let mut cx = EditorTestContext::new(cx).await;
14710
14711    let base_text = r#"
14712        one
14713        two
14714        three
14715        four
14716        five
14717    "#
14718    .unindent();
14719    executor.run_until_parked();
14720    cx.set_state(
14721        &r#"
14722        one
14723        two
14724        fˇour
14725        five
14726        "#
14727        .unindent(),
14728    );
14729
14730    cx.set_head_text(&base_text);
14731    executor.run_until_parked();
14732
14733    cx.update_editor(|editor, window, cx| {
14734        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14735    });
14736    executor.run_until_parked();
14737
14738    cx.assert_state_with_diff(
14739        r#"
14740          one
14741          two
14742        - three
14743          fˇour
14744          five
14745        "#
14746        .unindent(),
14747    );
14748
14749    cx.update_editor(|editor, window, cx| {
14750        editor.backspace(&Backspace, window, cx);
14751        editor.backspace(&Backspace, window, cx);
14752    });
14753    executor.run_until_parked();
14754    cx.assert_state_with_diff(
14755        r#"
14756          one
14757          two
14758        - threeˇ
14759        - four
14760        + our
14761          five
14762        "#
14763        .unindent(),
14764    );
14765}
14766
14767#[gpui::test]
14768async fn test_edit_after_expanded_modification_hunk(
14769    executor: BackgroundExecutor,
14770    cx: &mut TestAppContext,
14771) {
14772    init_test(cx, |_| {});
14773
14774    let mut cx = EditorTestContext::new(cx).await;
14775
14776    let diff_base = r#"
14777        use some::mod1;
14778        use some::mod2;
14779
14780        const A: u32 = 42;
14781        const B: u32 = 42;
14782        const C: u32 = 42;
14783        const D: u32 = 42;
14784
14785
14786        fn main() {
14787            println!("hello");
14788
14789            println!("world");
14790        }"#
14791    .unindent();
14792
14793    cx.set_state(
14794        &r#"
14795        use some::mod1;
14796        use some::mod2;
14797
14798        const A: u32 = 42;
14799        const B: u32 = 42;
14800        const C: u32 = 43ˇ
14801        const D: u32 = 42;
14802
14803
14804        fn main() {
14805            println!("hello");
14806
14807            println!("world");
14808        }"#
14809        .unindent(),
14810    );
14811
14812    cx.set_head_text(&diff_base);
14813    executor.run_until_parked();
14814    cx.update_editor(|editor, window, cx| {
14815        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14816    });
14817    executor.run_until_parked();
14818
14819    cx.assert_state_with_diff(
14820        r#"
14821        use some::mod1;
14822        use some::mod2;
14823
14824        const A: u32 = 42;
14825        const B: u32 = 42;
14826      - const C: u32 = 42;
14827      + const C: u32 = 43ˇ
14828        const D: u32 = 42;
14829
14830
14831        fn main() {
14832            println!("hello");
14833
14834            println!("world");
14835        }"#
14836        .unindent(),
14837    );
14838
14839    cx.update_editor(|editor, window, cx| {
14840        editor.handle_input("\nnew_line\n", window, cx);
14841    });
14842    executor.run_until_parked();
14843
14844    cx.assert_state_with_diff(
14845        r#"
14846        use some::mod1;
14847        use some::mod2;
14848
14849        const A: u32 = 42;
14850        const B: u32 = 42;
14851      - const C: u32 = 42;
14852      + const C: u32 = 43
14853      + new_line
14854      + ˇ
14855        const D: u32 = 42;
14856
14857
14858        fn main() {
14859            println!("hello");
14860
14861            println!("world");
14862        }"#
14863        .unindent(),
14864    );
14865}
14866
14867#[gpui::test]
14868async fn test_stage_and_unstage_added_file_hunk(
14869    executor: BackgroundExecutor,
14870    cx: &mut TestAppContext,
14871) {
14872    init_test(cx, |_| {});
14873
14874    let mut cx = EditorTestContext::new(cx).await;
14875    cx.update_editor(|editor, _, cx| {
14876        editor.set_expand_all_diff_hunks(cx);
14877    });
14878
14879    let working_copy = r#"
14880            ˇfn main() {
14881                println!("hello, world!");
14882            }
14883        "#
14884    .unindent();
14885
14886    cx.set_state(&working_copy);
14887    executor.run_until_parked();
14888
14889    cx.assert_state_with_diff(
14890        r#"
14891            + ˇfn main() {
14892            +     println!("hello, world!");
14893            + }
14894        "#
14895        .unindent(),
14896    );
14897    cx.assert_index_text(None);
14898
14899    cx.update_editor(|editor, window, cx| {
14900        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14901    });
14902    executor.run_until_parked();
14903    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14904    cx.assert_state_with_diff(
14905        r#"
14906            + ˇfn main() {
14907            +     println!("hello, world!");
14908            + }
14909        "#
14910        .unindent(),
14911    );
14912
14913    cx.update_editor(|editor, window, cx| {
14914        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14915    });
14916    executor.run_until_parked();
14917    cx.assert_index_text(None);
14918}
14919
14920async fn setup_indent_guides_editor(
14921    text: &str,
14922    cx: &mut TestAppContext,
14923) -> (BufferId, EditorTestContext) {
14924    init_test(cx, |_| {});
14925
14926    let mut cx = EditorTestContext::new(cx).await;
14927
14928    let buffer_id = cx.update_editor(|editor, window, cx| {
14929        editor.set_text(text, window, cx);
14930        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14931
14932        buffer_ids[0]
14933    });
14934
14935    (buffer_id, cx)
14936}
14937
14938fn assert_indent_guides(
14939    range: Range<u32>,
14940    expected: Vec<IndentGuide>,
14941    active_indices: Option<Vec<usize>>,
14942    cx: &mut EditorTestContext,
14943) {
14944    let indent_guides = cx.update_editor(|editor, window, cx| {
14945        let snapshot = editor.snapshot(window, cx).display_snapshot;
14946        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14947            editor,
14948            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14949            true,
14950            &snapshot,
14951            cx,
14952        );
14953
14954        indent_guides.sort_by(|a, b| {
14955            a.depth.cmp(&b.depth).then(
14956                a.start_row
14957                    .cmp(&b.start_row)
14958                    .then(a.end_row.cmp(&b.end_row)),
14959            )
14960        });
14961        indent_guides
14962    });
14963
14964    if let Some(expected) = active_indices {
14965        let active_indices = cx.update_editor(|editor, window, cx| {
14966            let snapshot = editor.snapshot(window, cx).display_snapshot;
14967            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14968        });
14969
14970        assert_eq!(
14971            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14972            expected,
14973            "Active indent guide indices do not match"
14974        );
14975    }
14976
14977    assert_eq!(indent_guides, expected, "Indent guides do not match");
14978}
14979
14980fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14981    IndentGuide {
14982        buffer_id,
14983        start_row: MultiBufferRow(start_row),
14984        end_row: MultiBufferRow(end_row),
14985        depth,
14986        tab_size: 4,
14987        settings: IndentGuideSettings {
14988            enabled: true,
14989            line_width: 1,
14990            active_line_width: 1,
14991            ..Default::default()
14992        },
14993    }
14994}
14995
14996#[gpui::test]
14997async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
14998    let (buffer_id, mut cx) = setup_indent_guides_editor(
14999        &"
15000    fn main() {
15001        let a = 1;
15002    }"
15003        .unindent(),
15004        cx,
15005    )
15006    .await;
15007
15008    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15009}
15010
15011#[gpui::test]
15012async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15013    let (buffer_id, mut cx) = setup_indent_guides_editor(
15014        &"
15015    fn main() {
15016        let a = 1;
15017        let b = 2;
15018    }"
15019        .unindent(),
15020        cx,
15021    )
15022    .await;
15023
15024    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15025}
15026
15027#[gpui::test]
15028async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15029    let (buffer_id, mut cx) = setup_indent_guides_editor(
15030        &"
15031    fn main() {
15032        let a = 1;
15033        if a == 3 {
15034            let b = 2;
15035        } else {
15036            let c = 3;
15037        }
15038    }"
15039        .unindent(),
15040        cx,
15041    )
15042    .await;
15043
15044    assert_indent_guides(
15045        0..8,
15046        vec![
15047            indent_guide(buffer_id, 1, 6, 0),
15048            indent_guide(buffer_id, 3, 3, 1),
15049            indent_guide(buffer_id, 5, 5, 1),
15050        ],
15051        None,
15052        &mut cx,
15053    );
15054}
15055
15056#[gpui::test]
15057async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15058    let (buffer_id, mut cx) = setup_indent_guides_editor(
15059        &"
15060    fn main() {
15061        let a = 1;
15062            let b = 2;
15063        let c = 3;
15064    }"
15065        .unindent(),
15066        cx,
15067    )
15068    .await;
15069
15070    assert_indent_guides(
15071        0..5,
15072        vec![
15073            indent_guide(buffer_id, 1, 3, 0),
15074            indent_guide(buffer_id, 2, 2, 1),
15075        ],
15076        None,
15077        &mut cx,
15078    );
15079}
15080
15081#[gpui::test]
15082async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15083    let (buffer_id, mut cx) = setup_indent_guides_editor(
15084        &"
15085        fn main() {
15086            let a = 1;
15087
15088            let c = 3;
15089        }"
15090        .unindent(),
15091        cx,
15092    )
15093    .await;
15094
15095    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15096}
15097
15098#[gpui::test]
15099async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15100    let (buffer_id, mut cx) = setup_indent_guides_editor(
15101        &"
15102        fn main() {
15103            let a = 1;
15104
15105            let c = 3;
15106
15107            if a == 3 {
15108                let b = 2;
15109            } else {
15110                let c = 3;
15111            }
15112        }"
15113        .unindent(),
15114        cx,
15115    )
15116    .await;
15117
15118    assert_indent_guides(
15119        0..11,
15120        vec![
15121            indent_guide(buffer_id, 1, 9, 0),
15122            indent_guide(buffer_id, 6, 6, 1),
15123            indent_guide(buffer_id, 8, 8, 1),
15124        ],
15125        None,
15126        &mut cx,
15127    );
15128}
15129
15130#[gpui::test]
15131async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15132    let (buffer_id, mut cx) = setup_indent_guides_editor(
15133        &"
15134        fn main() {
15135            let a = 1;
15136
15137            let c = 3;
15138
15139            if a == 3 {
15140                let b = 2;
15141            } else {
15142                let c = 3;
15143            }
15144        }"
15145        .unindent(),
15146        cx,
15147    )
15148    .await;
15149
15150    assert_indent_guides(
15151        1..11,
15152        vec![
15153            indent_guide(buffer_id, 1, 9, 0),
15154            indent_guide(buffer_id, 6, 6, 1),
15155            indent_guide(buffer_id, 8, 8, 1),
15156        ],
15157        None,
15158        &mut cx,
15159    );
15160}
15161
15162#[gpui::test]
15163async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15164    let (buffer_id, mut cx) = setup_indent_guides_editor(
15165        &"
15166        fn main() {
15167            let a = 1;
15168
15169            let c = 3;
15170
15171            if a == 3 {
15172                let b = 2;
15173            } else {
15174                let c = 3;
15175            }
15176        }"
15177        .unindent(),
15178        cx,
15179    )
15180    .await;
15181
15182    assert_indent_guides(
15183        1..10,
15184        vec![
15185            indent_guide(buffer_id, 1, 9, 0),
15186            indent_guide(buffer_id, 6, 6, 1),
15187            indent_guide(buffer_id, 8, 8, 1),
15188        ],
15189        None,
15190        &mut cx,
15191    );
15192}
15193
15194#[gpui::test]
15195async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15196    let (buffer_id, mut cx) = setup_indent_guides_editor(
15197        &"
15198        block1
15199            block2
15200                block3
15201                    block4
15202            block2
15203        block1
15204        block1"
15205            .unindent(),
15206        cx,
15207    )
15208    .await;
15209
15210    assert_indent_guides(
15211        1..10,
15212        vec![
15213            indent_guide(buffer_id, 1, 4, 0),
15214            indent_guide(buffer_id, 2, 3, 1),
15215            indent_guide(buffer_id, 3, 3, 2),
15216        ],
15217        None,
15218        &mut cx,
15219    );
15220}
15221
15222#[gpui::test]
15223async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15224    let (buffer_id, mut cx) = setup_indent_guides_editor(
15225        &"
15226        block1
15227            block2
15228                block3
15229
15230        block1
15231        block1"
15232            .unindent(),
15233        cx,
15234    )
15235    .await;
15236
15237    assert_indent_guides(
15238        0..6,
15239        vec![
15240            indent_guide(buffer_id, 1, 2, 0),
15241            indent_guide(buffer_id, 2, 2, 1),
15242        ],
15243        None,
15244        &mut cx,
15245    );
15246}
15247
15248#[gpui::test]
15249async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15250    let (buffer_id, mut cx) = setup_indent_guides_editor(
15251        &"
15252        block1
15253
15254
15255
15256            block2
15257        "
15258        .unindent(),
15259        cx,
15260    )
15261    .await;
15262
15263    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15264}
15265
15266#[gpui::test]
15267async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15268    let (buffer_id, mut cx) = setup_indent_guides_editor(
15269        &"
15270        def a:
15271        \tb = 3
15272        \tif True:
15273        \t\tc = 4
15274        \t\td = 5
15275        \tprint(b)
15276        "
15277        .unindent(),
15278        cx,
15279    )
15280    .await;
15281
15282    assert_indent_guides(
15283        0..6,
15284        vec![
15285            indent_guide(buffer_id, 1, 6, 0),
15286            indent_guide(buffer_id, 3, 4, 1),
15287        ],
15288        None,
15289        &mut cx,
15290    );
15291}
15292
15293#[gpui::test]
15294async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15295    let (buffer_id, mut cx) = setup_indent_guides_editor(
15296        &"
15297    fn main() {
15298        let a = 1;
15299    }"
15300        .unindent(),
15301        cx,
15302    )
15303    .await;
15304
15305    cx.update_editor(|editor, window, cx| {
15306        editor.change_selections(None, window, cx, |s| {
15307            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15308        });
15309    });
15310
15311    assert_indent_guides(
15312        0..3,
15313        vec![indent_guide(buffer_id, 1, 1, 0)],
15314        Some(vec![0]),
15315        &mut cx,
15316    );
15317}
15318
15319#[gpui::test]
15320async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15321    let (buffer_id, mut cx) = setup_indent_guides_editor(
15322        &"
15323    fn main() {
15324        if 1 == 2 {
15325            let a = 1;
15326        }
15327    }"
15328        .unindent(),
15329        cx,
15330    )
15331    .await;
15332
15333    cx.update_editor(|editor, window, cx| {
15334        editor.change_selections(None, window, cx, |s| {
15335            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15336        });
15337    });
15338
15339    assert_indent_guides(
15340        0..4,
15341        vec![
15342            indent_guide(buffer_id, 1, 3, 0),
15343            indent_guide(buffer_id, 2, 2, 1),
15344        ],
15345        Some(vec![1]),
15346        &mut cx,
15347    );
15348
15349    cx.update_editor(|editor, window, cx| {
15350        editor.change_selections(None, window, cx, |s| {
15351            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15352        });
15353    });
15354
15355    assert_indent_guides(
15356        0..4,
15357        vec![
15358            indent_guide(buffer_id, 1, 3, 0),
15359            indent_guide(buffer_id, 2, 2, 1),
15360        ],
15361        Some(vec![1]),
15362        &mut cx,
15363    );
15364
15365    cx.update_editor(|editor, window, cx| {
15366        editor.change_selections(None, window, cx, |s| {
15367            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15368        });
15369    });
15370
15371    assert_indent_guides(
15372        0..4,
15373        vec![
15374            indent_guide(buffer_id, 1, 3, 0),
15375            indent_guide(buffer_id, 2, 2, 1),
15376        ],
15377        Some(vec![0]),
15378        &mut cx,
15379    );
15380}
15381
15382#[gpui::test]
15383async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15384    let (buffer_id, mut cx) = setup_indent_guides_editor(
15385        &"
15386    fn main() {
15387        let a = 1;
15388
15389        let b = 2;
15390    }"
15391        .unindent(),
15392        cx,
15393    )
15394    .await;
15395
15396    cx.update_editor(|editor, window, cx| {
15397        editor.change_selections(None, window, cx, |s| {
15398            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15399        });
15400    });
15401
15402    assert_indent_guides(
15403        0..5,
15404        vec![indent_guide(buffer_id, 1, 3, 0)],
15405        Some(vec![0]),
15406        &mut cx,
15407    );
15408}
15409
15410#[gpui::test]
15411async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15412    let (buffer_id, mut cx) = setup_indent_guides_editor(
15413        &"
15414    def m:
15415        a = 1
15416        pass"
15417            .unindent(),
15418        cx,
15419    )
15420    .await;
15421
15422    cx.update_editor(|editor, window, cx| {
15423        editor.change_selections(None, window, cx, |s| {
15424            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15425        });
15426    });
15427
15428    assert_indent_guides(
15429        0..3,
15430        vec![indent_guide(buffer_id, 1, 2, 0)],
15431        Some(vec![0]),
15432        &mut cx,
15433    );
15434}
15435
15436#[gpui::test]
15437async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15438    init_test(cx, |_| {});
15439    let mut cx = EditorTestContext::new(cx).await;
15440    let text = indoc! {
15441        "
15442        impl A {
15443            fn b() {
15444                0;
15445                3;
15446                5;
15447                6;
15448                7;
15449            }
15450        }
15451        "
15452    };
15453    let base_text = indoc! {
15454        "
15455        impl A {
15456            fn b() {
15457                0;
15458                1;
15459                2;
15460                3;
15461                4;
15462            }
15463            fn c() {
15464                5;
15465                6;
15466                7;
15467            }
15468        }
15469        "
15470    };
15471
15472    cx.update_editor(|editor, window, cx| {
15473        editor.set_text(text, window, cx);
15474
15475        editor.buffer().update(cx, |multibuffer, cx| {
15476            let buffer = multibuffer.as_singleton().unwrap();
15477            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15478
15479            multibuffer.set_all_diff_hunks_expanded(cx);
15480            multibuffer.add_diff(diff, cx);
15481
15482            buffer.read(cx).remote_id()
15483        })
15484    });
15485    cx.run_until_parked();
15486
15487    cx.assert_state_with_diff(
15488        indoc! { "
15489          impl A {
15490              fn b() {
15491                  0;
15492        -         1;
15493        -         2;
15494                  3;
15495        -         4;
15496        -     }
15497        -     fn c() {
15498                  5;
15499                  6;
15500                  7;
15501              }
15502          }
15503          ˇ"
15504        }
15505        .to_string(),
15506    );
15507
15508    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15509        editor
15510            .snapshot(window, cx)
15511            .buffer_snapshot
15512            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15513            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15514            .collect::<Vec<_>>()
15515    });
15516    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15517    assert_eq!(
15518        actual_guides,
15519        vec![
15520            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15521            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15522            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15523        ]
15524    );
15525}
15526
15527#[gpui::test]
15528async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15529    init_test(cx, |_| {});
15530    let mut cx = EditorTestContext::new(cx).await;
15531
15532    let diff_base = r#"
15533        a
15534        b
15535        c
15536        "#
15537    .unindent();
15538
15539    cx.set_state(
15540        &r#"
15541        ˇA
15542        b
15543        C
15544        "#
15545        .unindent(),
15546    );
15547    cx.set_head_text(&diff_base);
15548    cx.update_editor(|editor, window, cx| {
15549        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15550    });
15551    executor.run_until_parked();
15552
15553    let both_hunks_expanded = r#"
15554        - a
15555        + ˇA
15556          b
15557        - c
15558        + C
15559        "#
15560    .unindent();
15561
15562    cx.assert_state_with_diff(both_hunks_expanded.clone());
15563
15564    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15565        let snapshot = editor.snapshot(window, cx);
15566        let hunks = editor
15567            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15568            .collect::<Vec<_>>();
15569        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15570        let buffer_id = hunks[0].buffer_id;
15571        hunks
15572            .into_iter()
15573            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15574            .collect::<Vec<_>>()
15575    });
15576    assert_eq!(hunk_ranges.len(), 2);
15577
15578    cx.update_editor(|editor, _, cx| {
15579        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15580    });
15581    executor.run_until_parked();
15582
15583    let second_hunk_expanded = r#"
15584          ˇA
15585          b
15586        - c
15587        + C
15588        "#
15589    .unindent();
15590
15591    cx.assert_state_with_diff(second_hunk_expanded);
15592
15593    cx.update_editor(|editor, _, cx| {
15594        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15595    });
15596    executor.run_until_parked();
15597
15598    cx.assert_state_with_diff(both_hunks_expanded.clone());
15599
15600    cx.update_editor(|editor, _, cx| {
15601        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15602    });
15603    executor.run_until_parked();
15604
15605    let first_hunk_expanded = r#"
15606        - a
15607        + ˇA
15608          b
15609          C
15610        "#
15611    .unindent();
15612
15613    cx.assert_state_with_diff(first_hunk_expanded);
15614
15615    cx.update_editor(|editor, _, cx| {
15616        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15617    });
15618    executor.run_until_parked();
15619
15620    cx.assert_state_with_diff(both_hunks_expanded);
15621
15622    cx.set_state(
15623        &r#"
15624        ˇA
15625        b
15626        "#
15627        .unindent(),
15628    );
15629    cx.run_until_parked();
15630
15631    // TODO this cursor position seems bad
15632    cx.assert_state_with_diff(
15633        r#"
15634        - ˇa
15635        + A
15636          b
15637        "#
15638        .unindent(),
15639    );
15640
15641    cx.update_editor(|editor, window, cx| {
15642        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15643    });
15644
15645    cx.assert_state_with_diff(
15646        r#"
15647            - ˇa
15648            + A
15649              b
15650            - c
15651            "#
15652        .unindent(),
15653    );
15654
15655    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15656        let snapshot = editor.snapshot(window, cx);
15657        let hunks = editor
15658            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15659            .collect::<Vec<_>>();
15660        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15661        let buffer_id = hunks[0].buffer_id;
15662        hunks
15663            .into_iter()
15664            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15665            .collect::<Vec<_>>()
15666    });
15667    assert_eq!(hunk_ranges.len(), 2);
15668
15669    cx.update_editor(|editor, _, cx| {
15670        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15671    });
15672    executor.run_until_parked();
15673
15674    cx.assert_state_with_diff(
15675        r#"
15676        - ˇa
15677        + A
15678          b
15679        "#
15680        .unindent(),
15681    );
15682}
15683
15684#[gpui::test]
15685async fn test_toggle_deletion_hunk_at_start_of_file(
15686    executor: BackgroundExecutor,
15687    cx: &mut TestAppContext,
15688) {
15689    init_test(cx, |_| {});
15690    let mut cx = EditorTestContext::new(cx).await;
15691
15692    let diff_base = r#"
15693        a
15694        b
15695        c
15696        "#
15697    .unindent();
15698
15699    cx.set_state(
15700        &r#"
15701        ˇb
15702        c
15703        "#
15704        .unindent(),
15705    );
15706    cx.set_head_text(&diff_base);
15707    cx.update_editor(|editor, window, cx| {
15708        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15709    });
15710    executor.run_until_parked();
15711
15712    let hunk_expanded = r#"
15713        - a
15714          ˇb
15715          c
15716        "#
15717    .unindent();
15718
15719    cx.assert_state_with_diff(hunk_expanded.clone());
15720
15721    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15722        let snapshot = editor.snapshot(window, cx);
15723        let hunks = editor
15724            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15725            .collect::<Vec<_>>();
15726        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15727        let buffer_id = hunks[0].buffer_id;
15728        hunks
15729            .into_iter()
15730            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15731            .collect::<Vec<_>>()
15732    });
15733    assert_eq!(hunk_ranges.len(), 1);
15734
15735    cx.update_editor(|editor, _, cx| {
15736        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15737    });
15738    executor.run_until_parked();
15739
15740    let hunk_collapsed = r#"
15741          ˇb
15742          c
15743        "#
15744    .unindent();
15745
15746    cx.assert_state_with_diff(hunk_collapsed);
15747
15748    cx.update_editor(|editor, _, cx| {
15749        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15750    });
15751    executor.run_until_parked();
15752
15753    cx.assert_state_with_diff(hunk_expanded.clone());
15754}
15755
15756#[gpui::test]
15757async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15758    init_test(cx, |_| {});
15759
15760    let fs = FakeFs::new(cx.executor());
15761    fs.insert_tree(
15762        path!("/test"),
15763        json!({
15764            ".git": {},
15765            "file-1": "ONE\n",
15766            "file-2": "TWO\n",
15767            "file-3": "THREE\n",
15768        }),
15769    )
15770    .await;
15771
15772    fs.set_head_for_repo(
15773        path!("/test/.git").as_ref(),
15774        &[
15775            ("file-1".into(), "one\n".into()),
15776            ("file-2".into(), "two\n".into()),
15777            ("file-3".into(), "three\n".into()),
15778        ],
15779    );
15780
15781    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15782    let mut buffers = vec![];
15783    for i in 1..=3 {
15784        let buffer = project
15785            .update(cx, |project, cx| {
15786                let path = format!(path!("/test/file-{}"), i);
15787                project.open_local_buffer(path, cx)
15788            })
15789            .await
15790            .unwrap();
15791        buffers.push(buffer);
15792    }
15793
15794    let multibuffer = cx.new(|cx| {
15795        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15796        multibuffer.set_all_diff_hunks_expanded(cx);
15797        for buffer in &buffers {
15798            let snapshot = buffer.read(cx).snapshot();
15799            multibuffer.set_excerpts_for_path(
15800                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15801                buffer.clone(),
15802                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15803                DEFAULT_MULTIBUFFER_CONTEXT,
15804                cx,
15805            );
15806        }
15807        multibuffer
15808    });
15809
15810    let editor = cx.add_window(|window, cx| {
15811        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15812    });
15813    cx.run_until_parked();
15814
15815    let snapshot = editor
15816        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15817        .unwrap();
15818    let hunks = snapshot
15819        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15820        .map(|hunk| match hunk {
15821            DisplayDiffHunk::Unfolded {
15822                display_row_range, ..
15823            } => display_row_range,
15824            DisplayDiffHunk::Folded { .. } => unreachable!(),
15825        })
15826        .collect::<Vec<_>>();
15827    assert_eq!(
15828        hunks,
15829        [
15830            DisplayRow(2)..DisplayRow(4),
15831            DisplayRow(7)..DisplayRow(9),
15832            DisplayRow(12)..DisplayRow(14),
15833        ]
15834    );
15835}
15836
15837#[gpui::test]
15838async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15839    init_test(cx, |_| {});
15840
15841    let mut cx = EditorTestContext::new(cx).await;
15842    cx.set_head_text(indoc! { "
15843        one
15844        two
15845        three
15846        four
15847        five
15848        "
15849    });
15850    cx.set_index_text(indoc! { "
15851        one
15852        two
15853        three
15854        four
15855        five
15856        "
15857    });
15858    cx.set_state(indoc! {"
15859        one
15860        TWO
15861        ˇTHREE
15862        FOUR
15863        five
15864    "});
15865    cx.run_until_parked();
15866    cx.update_editor(|editor, window, cx| {
15867        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15868    });
15869    cx.run_until_parked();
15870    cx.assert_index_text(Some(indoc! {"
15871        one
15872        TWO
15873        THREE
15874        FOUR
15875        five
15876    "}));
15877    cx.set_state(indoc! { "
15878        one
15879        TWO
15880        ˇTHREE-HUNDRED
15881        FOUR
15882        five
15883    "});
15884    cx.run_until_parked();
15885    cx.update_editor(|editor, window, cx| {
15886        let snapshot = editor.snapshot(window, cx);
15887        let hunks = editor
15888            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15889            .collect::<Vec<_>>();
15890        assert_eq!(hunks.len(), 1);
15891        assert_eq!(
15892            hunks[0].status(),
15893            DiffHunkStatus {
15894                kind: DiffHunkStatusKind::Modified,
15895                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15896            }
15897        );
15898
15899        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15900    });
15901    cx.run_until_parked();
15902    cx.assert_index_text(Some(indoc! {"
15903        one
15904        TWO
15905        THREE-HUNDRED
15906        FOUR
15907        five
15908    "}));
15909}
15910
15911#[gpui::test]
15912fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15913    init_test(cx, |_| {});
15914
15915    let editor = cx.add_window(|window, cx| {
15916        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15917        build_editor(buffer, window, cx)
15918    });
15919
15920    let render_args = Arc::new(Mutex::new(None));
15921    let snapshot = editor
15922        .update(cx, |editor, window, cx| {
15923            let snapshot = editor.buffer().read(cx).snapshot(cx);
15924            let range =
15925                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15926
15927            struct RenderArgs {
15928                row: MultiBufferRow,
15929                folded: bool,
15930                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15931            }
15932
15933            let crease = Crease::inline(
15934                range,
15935                FoldPlaceholder::test(),
15936                {
15937                    let toggle_callback = render_args.clone();
15938                    move |row, folded, callback, _window, _cx| {
15939                        *toggle_callback.lock() = Some(RenderArgs {
15940                            row,
15941                            folded,
15942                            callback,
15943                        });
15944                        div()
15945                    }
15946                },
15947                |_row, _folded, _window, _cx| div(),
15948            );
15949
15950            editor.insert_creases(Some(crease), cx);
15951            let snapshot = editor.snapshot(window, cx);
15952            let _div = snapshot.render_crease_toggle(
15953                MultiBufferRow(1),
15954                false,
15955                cx.entity().clone(),
15956                window,
15957                cx,
15958            );
15959            snapshot
15960        })
15961        .unwrap();
15962
15963    let render_args = render_args.lock().take().unwrap();
15964    assert_eq!(render_args.row, MultiBufferRow(1));
15965    assert!(!render_args.folded);
15966    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15967
15968    cx.update_window(*editor, |_, window, cx| {
15969        (render_args.callback)(true, window, cx)
15970    })
15971    .unwrap();
15972    let snapshot = editor
15973        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15974        .unwrap();
15975    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15976
15977    cx.update_window(*editor, |_, window, cx| {
15978        (render_args.callback)(false, window, cx)
15979    })
15980    .unwrap();
15981    let snapshot = editor
15982        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15983        .unwrap();
15984    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15985}
15986
15987#[gpui::test]
15988async fn test_input_text(cx: &mut TestAppContext) {
15989    init_test(cx, |_| {});
15990    let mut cx = EditorTestContext::new(cx).await;
15991
15992    cx.set_state(
15993        &r#"ˇone
15994        two
15995
15996        three
15997        fourˇ
15998        five
15999
16000        siˇx"#
16001            .unindent(),
16002    );
16003
16004    cx.dispatch_action(HandleInput(String::new()));
16005    cx.assert_editor_state(
16006        &r#"ˇone
16007        two
16008
16009        three
16010        fourˇ
16011        five
16012
16013        siˇx"#
16014            .unindent(),
16015    );
16016
16017    cx.dispatch_action(HandleInput("AAAA".to_string()));
16018    cx.assert_editor_state(
16019        &r#"AAAAˇone
16020        two
16021
16022        three
16023        fourAAAAˇ
16024        five
16025
16026        siAAAAˇx"#
16027            .unindent(),
16028    );
16029}
16030
16031#[gpui::test]
16032async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16033    init_test(cx, |_| {});
16034
16035    let mut cx = EditorTestContext::new(cx).await;
16036    cx.set_state(
16037        r#"let foo = 1;
16038let foo = 2;
16039let foo = 3;
16040let fooˇ = 4;
16041let foo = 5;
16042let foo = 6;
16043let foo = 7;
16044let foo = 8;
16045let foo = 9;
16046let foo = 10;
16047let foo = 11;
16048let foo = 12;
16049let foo = 13;
16050let foo = 14;
16051let foo = 15;"#,
16052    );
16053
16054    cx.update_editor(|e, window, cx| {
16055        assert_eq!(
16056            e.next_scroll_position,
16057            NextScrollCursorCenterTopBottom::Center,
16058            "Default next scroll direction is center",
16059        );
16060
16061        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16062        assert_eq!(
16063            e.next_scroll_position,
16064            NextScrollCursorCenterTopBottom::Top,
16065            "After center, next scroll direction should be top",
16066        );
16067
16068        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16069        assert_eq!(
16070            e.next_scroll_position,
16071            NextScrollCursorCenterTopBottom::Bottom,
16072            "After top, next scroll direction should be bottom",
16073        );
16074
16075        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16076        assert_eq!(
16077            e.next_scroll_position,
16078            NextScrollCursorCenterTopBottom::Center,
16079            "After bottom, scrolling should start over",
16080        );
16081
16082        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16083        assert_eq!(
16084            e.next_scroll_position,
16085            NextScrollCursorCenterTopBottom::Top,
16086            "Scrolling continues if retriggered fast enough"
16087        );
16088    });
16089
16090    cx.executor()
16091        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16092    cx.executor().run_until_parked();
16093    cx.update_editor(|e, _, _| {
16094        assert_eq!(
16095            e.next_scroll_position,
16096            NextScrollCursorCenterTopBottom::Center,
16097            "If scrolling is not triggered fast enough, it should reset"
16098        );
16099    });
16100}
16101
16102#[gpui::test]
16103async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16104    init_test(cx, |_| {});
16105    let mut cx = EditorLspTestContext::new_rust(
16106        lsp::ServerCapabilities {
16107            definition_provider: Some(lsp::OneOf::Left(true)),
16108            references_provider: Some(lsp::OneOf::Left(true)),
16109            ..lsp::ServerCapabilities::default()
16110        },
16111        cx,
16112    )
16113    .await;
16114
16115    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16116        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16117            move |params, _| async move {
16118                if empty_go_to_definition {
16119                    Ok(None)
16120                } else {
16121                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16122                        uri: params.text_document_position_params.text_document.uri,
16123                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16124                    })))
16125                }
16126            },
16127        );
16128        let references =
16129            cx.lsp
16130                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16131                    Ok(Some(vec![lsp::Location {
16132                        uri: params.text_document_position.text_document.uri,
16133                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16134                    }]))
16135                });
16136        (go_to_definition, references)
16137    };
16138
16139    cx.set_state(
16140        &r#"fn one() {
16141            let mut a = ˇtwo();
16142        }
16143
16144        fn two() {}"#
16145            .unindent(),
16146    );
16147    set_up_lsp_handlers(false, &mut cx);
16148    let navigated = cx
16149        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16150        .await
16151        .expect("Failed to navigate to definition");
16152    assert_eq!(
16153        navigated,
16154        Navigated::Yes,
16155        "Should have navigated to definition from the GetDefinition response"
16156    );
16157    cx.assert_editor_state(
16158        &r#"fn one() {
16159            let mut a = two();
16160        }
16161
16162        fn «twoˇ»() {}"#
16163            .unindent(),
16164    );
16165
16166    let editors = cx.update_workspace(|workspace, _, cx| {
16167        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16168    });
16169    cx.update_editor(|_, _, test_editor_cx| {
16170        assert_eq!(
16171            editors.len(),
16172            1,
16173            "Initially, only one, test, editor should be open in the workspace"
16174        );
16175        assert_eq!(
16176            test_editor_cx.entity(),
16177            editors.last().expect("Asserted len is 1").clone()
16178        );
16179    });
16180
16181    set_up_lsp_handlers(true, &mut cx);
16182    let navigated = cx
16183        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16184        .await
16185        .expect("Failed to navigate to lookup references");
16186    assert_eq!(
16187        navigated,
16188        Navigated::Yes,
16189        "Should have navigated to references as a fallback after empty GoToDefinition response"
16190    );
16191    // We should not change the selections in the existing file,
16192    // if opening another milti buffer with the references
16193    cx.assert_editor_state(
16194        &r#"fn one() {
16195            let mut a = two();
16196        }
16197
16198        fn «twoˇ»() {}"#
16199            .unindent(),
16200    );
16201    let editors = cx.update_workspace(|workspace, _, cx| {
16202        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16203    });
16204    cx.update_editor(|_, _, test_editor_cx| {
16205        assert_eq!(
16206            editors.len(),
16207            2,
16208            "After falling back to references search, we open a new editor with the results"
16209        );
16210        let references_fallback_text = editors
16211            .into_iter()
16212            .find(|new_editor| *new_editor != test_editor_cx.entity())
16213            .expect("Should have one non-test editor now")
16214            .read(test_editor_cx)
16215            .text(test_editor_cx);
16216        assert_eq!(
16217            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16218            "Should use the range from the references response and not the GoToDefinition one"
16219        );
16220    });
16221}
16222
16223#[gpui::test]
16224async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16225    init_test(cx, |_| {});
16226
16227    let language = Arc::new(Language::new(
16228        LanguageConfig::default(),
16229        Some(tree_sitter_rust::LANGUAGE.into()),
16230    ));
16231
16232    let text = r#"
16233        #[cfg(test)]
16234        mod tests() {
16235            #[test]
16236            fn runnable_1() {
16237                let a = 1;
16238            }
16239
16240            #[test]
16241            fn runnable_2() {
16242                let a = 1;
16243                let b = 2;
16244            }
16245        }
16246    "#
16247    .unindent();
16248
16249    let fs = FakeFs::new(cx.executor());
16250    fs.insert_file("/file.rs", Default::default()).await;
16251
16252    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16253    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16254    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16255    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16256    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16257
16258    let editor = cx.new_window_entity(|window, cx| {
16259        Editor::new(
16260            EditorMode::Full,
16261            multi_buffer,
16262            Some(project.clone()),
16263            window,
16264            cx,
16265        )
16266    });
16267
16268    editor.update_in(cx, |editor, window, cx| {
16269        let snapshot = editor.buffer().read(cx).snapshot(cx);
16270        editor.tasks.insert(
16271            (buffer.read(cx).remote_id(), 3),
16272            RunnableTasks {
16273                templates: vec![],
16274                offset: snapshot.anchor_before(43),
16275                column: 0,
16276                extra_variables: HashMap::default(),
16277                context_range: BufferOffset(43)..BufferOffset(85),
16278            },
16279        );
16280        editor.tasks.insert(
16281            (buffer.read(cx).remote_id(), 8),
16282            RunnableTasks {
16283                templates: vec![],
16284                offset: snapshot.anchor_before(86),
16285                column: 0,
16286                extra_variables: HashMap::default(),
16287                context_range: BufferOffset(86)..BufferOffset(191),
16288            },
16289        );
16290
16291        // Test finding task when cursor is inside function body
16292        editor.change_selections(None, window, cx, |s| {
16293            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16294        });
16295        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16296        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16297
16298        // Test finding task when cursor is on function name
16299        editor.change_selections(None, window, cx, |s| {
16300            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16301        });
16302        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16303        assert_eq!(row, 8, "Should find task when cursor is on function name");
16304    });
16305}
16306
16307#[gpui::test]
16308async fn test_folding_buffers(cx: &mut TestAppContext) {
16309    init_test(cx, |_| {});
16310
16311    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16312    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16313    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16314
16315    let fs = FakeFs::new(cx.executor());
16316    fs.insert_tree(
16317        path!("/a"),
16318        json!({
16319            "first.rs": sample_text_1,
16320            "second.rs": sample_text_2,
16321            "third.rs": sample_text_3,
16322        }),
16323    )
16324    .await;
16325    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16326    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16327    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16328    let worktree = project.update(cx, |project, cx| {
16329        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16330        assert_eq!(worktrees.len(), 1);
16331        worktrees.pop().unwrap()
16332    });
16333    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16334
16335    let buffer_1 = project
16336        .update(cx, |project, cx| {
16337            project.open_buffer((worktree_id, "first.rs"), cx)
16338        })
16339        .await
16340        .unwrap();
16341    let buffer_2 = project
16342        .update(cx, |project, cx| {
16343            project.open_buffer((worktree_id, "second.rs"), cx)
16344        })
16345        .await
16346        .unwrap();
16347    let buffer_3 = project
16348        .update(cx, |project, cx| {
16349            project.open_buffer((worktree_id, "third.rs"), cx)
16350        })
16351        .await
16352        .unwrap();
16353
16354    let multi_buffer = cx.new(|cx| {
16355        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16356        multi_buffer.push_excerpts(
16357            buffer_1.clone(),
16358            [
16359                ExcerptRange {
16360                    context: Point::new(0, 0)..Point::new(3, 0),
16361                    primary: None,
16362                },
16363                ExcerptRange {
16364                    context: Point::new(5, 0)..Point::new(7, 0),
16365                    primary: None,
16366                },
16367                ExcerptRange {
16368                    context: Point::new(9, 0)..Point::new(10, 4),
16369                    primary: None,
16370                },
16371            ],
16372            cx,
16373        );
16374        multi_buffer.push_excerpts(
16375            buffer_2.clone(),
16376            [
16377                ExcerptRange {
16378                    context: Point::new(0, 0)..Point::new(3, 0),
16379                    primary: None,
16380                },
16381                ExcerptRange {
16382                    context: Point::new(5, 0)..Point::new(7, 0),
16383                    primary: None,
16384                },
16385                ExcerptRange {
16386                    context: Point::new(9, 0)..Point::new(10, 4),
16387                    primary: None,
16388                },
16389            ],
16390            cx,
16391        );
16392        multi_buffer.push_excerpts(
16393            buffer_3.clone(),
16394            [
16395                ExcerptRange {
16396                    context: Point::new(0, 0)..Point::new(3, 0),
16397                    primary: None,
16398                },
16399                ExcerptRange {
16400                    context: Point::new(5, 0)..Point::new(7, 0),
16401                    primary: None,
16402                },
16403                ExcerptRange {
16404                    context: Point::new(9, 0)..Point::new(10, 4),
16405                    primary: None,
16406                },
16407            ],
16408            cx,
16409        );
16410        multi_buffer
16411    });
16412    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16413        Editor::new(
16414            EditorMode::Full,
16415            multi_buffer.clone(),
16416            Some(project.clone()),
16417            window,
16418            cx,
16419        )
16420    });
16421
16422    assert_eq!(
16423        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16424        "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16425    );
16426
16427    multi_buffer_editor.update(cx, |editor, cx| {
16428        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16429    });
16430    assert_eq!(
16431        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16432        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16433        "After folding the first buffer, its text should not be displayed"
16434    );
16435
16436    multi_buffer_editor.update(cx, |editor, cx| {
16437        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16438    });
16439    assert_eq!(
16440        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16441        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16442        "After folding the second buffer, its text should not be displayed"
16443    );
16444
16445    multi_buffer_editor.update(cx, |editor, cx| {
16446        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16447    });
16448    assert_eq!(
16449        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16450        "\n\n\n\n\n",
16451        "After folding the third buffer, its text should not be displayed"
16452    );
16453
16454    // Emulate selection inside the fold logic, that should work
16455    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16456        editor
16457            .snapshot(window, cx)
16458            .next_line_boundary(Point::new(0, 4));
16459    });
16460
16461    multi_buffer_editor.update(cx, |editor, cx| {
16462        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16463    });
16464    assert_eq!(
16465        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16466        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16467        "After unfolding the second buffer, its text should be displayed"
16468    );
16469
16470    // Typing inside of buffer 1 causes that buffer to be unfolded.
16471    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16472        assert_eq!(
16473            multi_buffer
16474                .read(cx)
16475                .snapshot(cx)
16476                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16477                .collect::<String>(),
16478            "bbbb"
16479        );
16480        editor.change_selections(None, window, cx, |selections| {
16481            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16482        });
16483        editor.handle_input("B", window, cx);
16484    });
16485
16486    assert_eq!(
16487        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16488        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16489        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16490    );
16491
16492    multi_buffer_editor.update(cx, |editor, cx| {
16493        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16494    });
16495    assert_eq!(
16496        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16497        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16498        "After unfolding the all buffers, all original text should be displayed"
16499    );
16500}
16501
16502#[gpui::test]
16503async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16504    init_test(cx, |_| {});
16505
16506    let sample_text_1 = "1111\n2222\n3333".to_string();
16507    let sample_text_2 = "4444\n5555\n6666".to_string();
16508    let sample_text_3 = "7777\n8888\n9999".to_string();
16509
16510    let fs = FakeFs::new(cx.executor());
16511    fs.insert_tree(
16512        path!("/a"),
16513        json!({
16514            "first.rs": sample_text_1,
16515            "second.rs": sample_text_2,
16516            "third.rs": sample_text_3,
16517        }),
16518    )
16519    .await;
16520    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16521    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16522    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16523    let worktree = project.update(cx, |project, cx| {
16524        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16525        assert_eq!(worktrees.len(), 1);
16526        worktrees.pop().unwrap()
16527    });
16528    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16529
16530    let buffer_1 = project
16531        .update(cx, |project, cx| {
16532            project.open_buffer((worktree_id, "first.rs"), cx)
16533        })
16534        .await
16535        .unwrap();
16536    let buffer_2 = project
16537        .update(cx, |project, cx| {
16538            project.open_buffer((worktree_id, "second.rs"), cx)
16539        })
16540        .await
16541        .unwrap();
16542    let buffer_3 = project
16543        .update(cx, |project, cx| {
16544            project.open_buffer((worktree_id, "third.rs"), cx)
16545        })
16546        .await
16547        .unwrap();
16548
16549    let multi_buffer = cx.new(|cx| {
16550        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16551        multi_buffer.push_excerpts(
16552            buffer_1.clone(),
16553            [ExcerptRange {
16554                context: Point::new(0, 0)..Point::new(3, 0),
16555                primary: None,
16556            }],
16557            cx,
16558        );
16559        multi_buffer.push_excerpts(
16560            buffer_2.clone(),
16561            [ExcerptRange {
16562                context: Point::new(0, 0)..Point::new(3, 0),
16563                primary: None,
16564            }],
16565            cx,
16566        );
16567        multi_buffer.push_excerpts(
16568            buffer_3.clone(),
16569            [ExcerptRange {
16570                context: Point::new(0, 0)..Point::new(3, 0),
16571                primary: None,
16572            }],
16573            cx,
16574        );
16575        multi_buffer
16576    });
16577
16578    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16579        Editor::new(
16580            EditorMode::Full,
16581            multi_buffer,
16582            Some(project.clone()),
16583            window,
16584            cx,
16585        )
16586    });
16587
16588    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16589    assert_eq!(
16590        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16591        full_text,
16592    );
16593
16594    multi_buffer_editor.update(cx, |editor, cx| {
16595        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16596    });
16597    assert_eq!(
16598        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16599        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16600        "After folding the first buffer, its text should not be displayed"
16601    );
16602
16603    multi_buffer_editor.update(cx, |editor, cx| {
16604        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16605    });
16606
16607    assert_eq!(
16608        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16609        "\n\n\n\n\n\n7777\n8888\n9999",
16610        "After folding the second buffer, its text should not be displayed"
16611    );
16612
16613    multi_buffer_editor.update(cx, |editor, cx| {
16614        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16615    });
16616    assert_eq!(
16617        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16618        "\n\n\n\n\n",
16619        "After folding the third buffer, its text should not be displayed"
16620    );
16621
16622    multi_buffer_editor.update(cx, |editor, cx| {
16623        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16624    });
16625    assert_eq!(
16626        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16627        "\n\n\n\n4444\n5555\n6666\n\n",
16628        "After unfolding the second buffer, its text should be displayed"
16629    );
16630
16631    multi_buffer_editor.update(cx, |editor, cx| {
16632        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16633    });
16634    assert_eq!(
16635        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16636        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16637        "After unfolding the first buffer, its text should be displayed"
16638    );
16639
16640    multi_buffer_editor.update(cx, |editor, cx| {
16641        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16642    });
16643    assert_eq!(
16644        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16645        full_text,
16646        "After unfolding all buffers, all original text should be displayed"
16647    );
16648}
16649
16650#[gpui::test]
16651async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16652    init_test(cx, |_| {});
16653
16654    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16655
16656    let fs = FakeFs::new(cx.executor());
16657    fs.insert_tree(
16658        path!("/a"),
16659        json!({
16660            "main.rs": sample_text,
16661        }),
16662    )
16663    .await;
16664    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16665    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16666    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16667    let worktree = project.update(cx, |project, cx| {
16668        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16669        assert_eq!(worktrees.len(), 1);
16670        worktrees.pop().unwrap()
16671    });
16672    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16673
16674    let buffer_1 = project
16675        .update(cx, |project, cx| {
16676            project.open_buffer((worktree_id, "main.rs"), cx)
16677        })
16678        .await
16679        .unwrap();
16680
16681    let multi_buffer = cx.new(|cx| {
16682        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16683        multi_buffer.push_excerpts(
16684            buffer_1.clone(),
16685            [ExcerptRange {
16686                context: Point::new(0, 0)
16687                    ..Point::new(
16688                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16689                        0,
16690                    ),
16691                primary: None,
16692            }],
16693            cx,
16694        );
16695        multi_buffer
16696    });
16697    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16698        Editor::new(
16699            EditorMode::Full,
16700            multi_buffer,
16701            Some(project.clone()),
16702            window,
16703            cx,
16704        )
16705    });
16706
16707    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16708    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16709        enum TestHighlight {}
16710        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16711        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16712        editor.highlight_text::<TestHighlight>(
16713            vec![highlight_range.clone()],
16714            HighlightStyle::color(Hsla::green()),
16715            cx,
16716        );
16717        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16718    });
16719
16720    let full_text = format!("\n\n{sample_text}");
16721    assert_eq!(
16722        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16723        full_text,
16724    );
16725}
16726
16727#[gpui::test]
16728async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16729    init_test(cx, |_| {});
16730    cx.update(|cx| {
16731        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16732            "keymaps/default-linux.json",
16733            cx,
16734        )
16735        .unwrap();
16736        cx.bind_keys(default_key_bindings);
16737    });
16738
16739    let (editor, cx) = cx.add_window_view(|window, cx| {
16740        let multi_buffer = MultiBuffer::build_multi(
16741            [
16742                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16743                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16744                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16745                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16746            ],
16747            cx,
16748        );
16749        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16750
16751        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16752        // fold all but the second buffer, so that we test navigating between two
16753        // adjacent folded buffers, as well as folded buffers at the start and
16754        // end the multibuffer
16755        editor.fold_buffer(buffer_ids[0], cx);
16756        editor.fold_buffer(buffer_ids[2], cx);
16757        editor.fold_buffer(buffer_ids[3], cx);
16758
16759        editor
16760    });
16761    cx.simulate_resize(size(px(1000.), px(1000.)));
16762
16763    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16764    cx.assert_excerpts_with_selections(indoc! {"
16765        [EXCERPT]
16766        ˇ[FOLDED]
16767        [EXCERPT]
16768        a1
16769        b1
16770        [EXCERPT]
16771        [FOLDED]
16772        [EXCERPT]
16773        [FOLDED]
16774        "
16775    });
16776    cx.simulate_keystroke("down");
16777    cx.assert_excerpts_with_selections(indoc! {"
16778        [EXCERPT]
16779        [FOLDED]
16780        [EXCERPT]
16781        ˇa1
16782        b1
16783        [EXCERPT]
16784        [FOLDED]
16785        [EXCERPT]
16786        [FOLDED]
16787        "
16788    });
16789    cx.simulate_keystroke("down");
16790    cx.assert_excerpts_with_selections(indoc! {"
16791        [EXCERPT]
16792        [FOLDED]
16793        [EXCERPT]
16794        a1
16795        ˇb1
16796        [EXCERPT]
16797        [FOLDED]
16798        [EXCERPT]
16799        [FOLDED]
16800        "
16801    });
16802    cx.simulate_keystroke("down");
16803    cx.assert_excerpts_with_selections(indoc! {"
16804        [EXCERPT]
16805        [FOLDED]
16806        [EXCERPT]
16807        a1
16808        b1
16809        ˇ[EXCERPT]
16810        [FOLDED]
16811        [EXCERPT]
16812        [FOLDED]
16813        "
16814    });
16815    cx.simulate_keystroke("down");
16816    cx.assert_excerpts_with_selections(indoc! {"
16817        [EXCERPT]
16818        [FOLDED]
16819        [EXCERPT]
16820        a1
16821        b1
16822        [EXCERPT]
16823        ˇ[FOLDED]
16824        [EXCERPT]
16825        [FOLDED]
16826        "
16827    });
16828    for _ in 0..5 {
16829        cx.simulate_keystroke("down");
16830        cx.assert_excerpts_with_selections(indoc! {"
16831            [EXCERPT]
16832            [FOLDED]
16833            [EXCERPT]
16834            a1
16835            b1
16836            [EXCERPT]
16837            [FOLDED]
16838            [EXCERPT]
16839            ˇ[FOLDED]
16840            "
16841        });
16842    }
16843
16844    cx.simulate_keystroke("up");
16845    cx.assert_excerpts_with_selections(indoc! {"
16846        [EXCERPT]
16847        [FOLDED]
16848        [EXCERPT]
16849        a1
16850        b1
16851        [EXCERPT]
16852        ˇ[FOLDED]
16853        [EXCERPT]
16854        [FOLDED]
16855        "
16856    });
16857    cx.simulate_keystroke("up");
16858    cx.assert_excerpts_with_selections(indoc! {"
16859        [EXCERPT]
16860        [FOLDED]
16861        [EXCERPT]
16862        a1
16863        b1
16864        ˇ[EXCERPT]
16865        [FOLDED]
16866        [EXCERPT]
16867        [FOLDED]
16868        "
16869    });
16870    cx.simulate_keystroke("up");
16871    cx.assert_excerpts_with_selections(indoc! {"
16872        [EXCERPT]
16873        [FOLDED]
16874        [EXCERPT]
16875        a1
16876        ˇb1
16877        [EXCERPT]
16878        [FOLDED]
16879        [EXCERPT]
16880        [FOLDED]
16881        "
16882    });
16883    cx.simulate_keystroke("up");
16884    cx.assert_excerpts_with_selections(indoc! {"
16885        [EXCERPT]
16886        [FOLDED]
16887        [EXCERPT]
16888        ˇa1
16889        b1
16890        [EXCERPT]
16891        [FOLDED]
16892        [EXCERPT]
16893        [FOLDED]
16894        "
16895    });
16896    for _ in 0..5 {
16897        cx.simulate_keystroke("up");
16898        cx.assert_excerpts_with_selections(indoc! {"
16899            [EXCERPT]
16900            ˇ[FOLDED]
16901            [EXCERPT]
16902            a1
16903            b1
16904            [EXCERPT]
16905            [FOLDED]
16906            [EXCERPT]
16907            [FOLDED]
16908            "
16909        });
16910    }
16911}
16912
16913#[gpui::test]
16914async fn test_inline_completion_text(cx: &mut TestAppContext) {
16915    init_test(cx, |_| {});
16916
16917    // Simple insertion
16918    assert_highlighted_edits(
16919        "Hello, world!",
16920        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16921        true,
16922        cx,
16923        |highlighted_edits, cx| {
16924            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16925            assert_eq!(highlighted_edits.highlights.len(), 1);
16926            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16927            assert_eq!(
16928                highlighted_edits.highlights[0].1.background_color,
16929                Some(cx.theme().status().created_background)
16930            );
16931        },
16932    )
16933    .await;
16934
16935    // Replacement
16936    assert_highlighted_edits(
16937        "This is a test.",
16938        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16939        false,
16940        cx,
16941        |highlighted_edits, cx| {
16942            assert_eq!(highlighted_edits.text, "That is a test.");
16943            assert_eq!(highlighted_edits.highlights.len(), 1);
16944            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16945            assert_eq!(
16946                highlighted_edits.highlights[0].1.background_color,
16947                Some(cx.theme().status().created_background)
16948            );
16949        },
16950    )
16951    .await;
16952
16953    // Multiple edits
16954    assert_highlighted_edits(
16955        "Hello, world!",
16956        vec![
16957            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16958            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16959        ],
16960        false,
16961        cx,
16962        |highlighted_edits, cx| {
16963            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16964            assert_eq!(highlighted_edits.highlights.len(), 2);
16965            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16966            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16967            assert_eq!(
16968                highlighted_edits.highlights[0].1.background_color,
16969                Some(cx.theme().status().created_background)
16970            );
16971            assert_eq!(
16972                highlighted_edits.highlights[1].1.background_color,
16973                Some(cx.theme().status().created_background)
16974            );
16975        },
16976    )
16977    .await;
16978
16979    // Multiple lines with edits
16980    assert_highlighted_edits(
16981        "First line\nSecond line\nThird line\nFourth line",
16982        vec![
16983            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16984            (
16985                Point::new(2, 0)..Point::new(2, 10),
16986                "New third line".to_string(),
16987            ),
16988            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16989        ],
16990        false,
16991        cx,
16992        |highlighted_edits, cx| {
16993            assert_eq!(
16994                highlighted_edits.text,
16995                "Second modified\nNew third line\nFourth updated line"
16996            );
16997            assert_eq!(highlighted_edits.highlights.len(), 3);
16998            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
16999            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17000            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17001            for highlight in &highlighted_edits.highlights {
17002                assert_eq!(
17003                    highlight.1.background_color,
17004                    Some(cx.theme().status().created_background)
17005                );
17006            }
17007        },
17008    )
17009    .await;
17010}
17011
17012#[gpui::test]
17013async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17014    init_test(cx, |_| {});
17015
17016    // Deletion
17017    assert_highlighted_edits(
17018        "Hello, world!",
17019        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17020        true,
17021        cx,
17022        |highlighted_edits, cx| {
17023            assert_eq!(highlighted_edits.text, "Hello, world!");
17024            assert_eq!(highlighted_edits.highlights.len(), 1);
17025            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17026            assert_eq!(
17027                highlighted_edits.highlights[0].1.background_color,
17028                Some(cx.theme().status().deleted_background)
17029            );
17030        },
17031    )
17032    .await;
17033
17034    // Insertion
17035    assert_highlighted_edits(
17036        "Hello, world!",
17037        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17038        true,
17039        cx,
17040        |highlighted_edits, cx| {
17041            assert_eq!(highlighted_edits.highlights.len(), 1);
17042            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17043            assert_eq!(
17044                highlighted_edits.highlights[0].1.background_color,
17045                Some(cx.theme().status().created_background)
17046            );
17047        },
17048    )
17049    .await;
17050}
17051
17052async fn assert_highlighted_edits(
17053    text: &str,
17054    edits: Vec<(Range<Point>, String)>,
17055    include_deletions: bool,
17056    cx: &mut TestAppContext,
17057    assertion_fn: impl Fn(HighlightedText, &App),
17058) {
17059    let window = cx.add_window(|window, cx| {
17060        let buffer = MultiBuffer::build_simple(text, cx);
17061        Editor::new(EditorMode::Full, buffer, None, window, cx)
17062    });
17063    let cx = &mut VisualTestContext::from_window(*window, cx);
17064
17065    let (buffer, snapshot) = window
17066        .update(cx, |editor, _window, cx| {
17067            (
17068                editor.buffer().clone(),
17069                editor.buffer().read(cx).snapshot(cx),
17070            )
17071        })
17072        .unwrap();
17073
17074    let edits = edits
17075        .into_iter()
17076        .map(|(range, edit)| {
17077            (
17078                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17079                edit,
17080            )
17081        })
17082        .collect::<Vec<_>>();
17083
17084    let text_anchor_edits = edits
17085        .clone()
17086        .into_iter()
17087        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17088        .collect::<Vec<_>>();
17089
17090    let edit_preview = window
17091        .update(cx, |_, _window, cx| {
17092            buffer
17093                .read(cx)
17094                .as_singleton()
17095                .unwrap()
17096                .read(cx)
17097                .preview_edits(text_anchor_edits.into(), cx)
17098        })
17099        .unwrap()
17100        .await;
17101
17102    cx.update(|_window, cx| {
17103        let highlighted_edits = inline_completion_edit_text(
17104            &snapshot.as_singleton().unwrap().2,
17105            &edits,
17106            &edit_preview,
17107            include_deletions,
17108            cx,
17109        );
17110        assertion_fn(highlighted_edits, cx)
17111    });
17112}
17113
17114#[gpui::test]
17115async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17116    init_test(cx, |_| {});
17117    let capabilities = lsp::ServerCapabilities {
17118        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17119            prepare_provider: Some(true),
17120            work_done_progress_options: Default::default(),
17121        })),
17122        ..Default::default()
17123    };
17124    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17125
17126    cx.set_state(indoc! {"
17127        struct Fˇoo {}
17128    "});
17129
17130    cx.update_editor(|editor, _, cx| {
17131        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17132        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17133        editor.highlight_background::<DocumentHighlightRead>(
17134            &[highlight_range],
17135            |c| c.editor_document_highlight_read_background,
17136            cx,
17137        );
17138    });
17139
17140    let mut prepare_rename_handler =
17141        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17142            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17143                start: lsp::Position {
17144                    line: 0,
17145                    character: 7,
17146                },
17147                end: lsp::Position {
17148                    line: 0,
17149                    character: 10,
17150                },
17151            })))
17152        });
17153    let prepare_rename_task = cx
17154        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17155        .expect("Prepare rename was not started");
17156    prepare_rename_handler.next().await.unwrap();
17157    prepare_rename_task.await.expect("Prepare rename failed");
17158
17159    let mut rename_handler =
17160        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17161            let edit = lsp::TextEdit {
17162                range: lsp::Range {
17163                    start: lsp::Position {
17164                        line: 0,
17165                        character: 7,
17166                    },
17167                    end: lsp::Position {
17168                        line: 0,
17169                        character: 10,
17170                    },
17171                },
17172                new_text: "FooRenamed".to_string(),
17173            };
17174            Ok(Some(lsp::WorkspaceEdit::new(
17175                // Specify the same edit twice
17176                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17177            )))
17178        });
17179    let rename_task = cx
17180        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17181        .expect("Confirm rename was not started");
17182    rename_handler.next().await.unwrap();
17183    rename_task.await.expect("Confirm rename failed");
17184    cx.run_until_parked();
17185
17186    // Despite two edits, only one is actually applied as those are identical
17187    cx.assert_editor_state(indoc! {"
17188        struct FooRenamedˇ {}
17189    "});
17190}
17191
17192#[gpui::test]
17193async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17194    init_test(cx, |_| {});
17195    // These capabilities indicate that the server does not support prepare rename.
17196    let capabilities = lsp::ServerCapabilities {
17197        rename_provider: Some(lsp::OneOf::Left(true)),
17198        ..Default::default()
17199    };
17200    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17201
17202    cx.set_state(indoc! {"
17203        struct Fˇoo {}
17204    "});
17205
17206    cx.update_editor(|editor, _window, cx| {
17207        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17208        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17209        editor.highlight_background::<DocumentHighlightRead>(
17210            &[highlight_range],
17211            |c| c.editor_document_highlight_read_background,
17212            cx,
17213        );
17214    });
17215
17216    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17217        .expect("Prepare rename was not started")
17218        .await
17219        .expect("Prepare rename failed");
17220
17221    let mut rename_handler =
17222        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17223            let edit = lsp::TextEdit {
17224                range: lsp::Range {
17225                    start: lsp::Position {
17226                        line: 0,
17227                        character: 7,
17228                    },
17229                    end: lsp::Position {
17230                        line: 0,
17231                        character: 10,
17232                    },
17233                },
17234                new_text: "FooRenamed".to_string(),
17235            };
17236            Ok(Some(lsp::WorkspaceEdit::new(
17237                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17238            )))
17239        });
17240    let rename_task = cx
17241        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17242        .expect("Confirm rename was not started");
17243    rename_handler.next().await.unwrap();
17244    rename_task.await.expect("Confirm rename failed");
17245    cx.run_until_parked();
17246
17247    // Correct range is renamed, as `surrounding_word` is used to find it.
17248    cx.assert_editor_state(indoc! {"
17249        struct FooRenamedˇ {}
17250    "});
17251}
17252
17253#[gpui::test]
17254async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17255    init_test(cx, |_| {});
17256    let mut cx = EditorTestContext::new(cx).await;
17257
17258    let language = Arc::new(
17259        Language::new(
17260            LanguageConfig::default(),
17261            Some(tree_sitter_html::LANGUAGE.into()),
17262        )
17263        .with_brackets_query(
17264            r#"
17265            ("<" @open "/>" @close)
17266            ("</" @open ">" @close)
17267            ("<" @open ">" @close)
17268            ("\"" @open "\"" @close)
17269            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17270        "#,
17271        )
17272        .unwrap(),
17273    );
17274    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17275
17276    cx.set_state(indoc! {"
17277        <span>ˇ</span>
17278    "});
17279    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17280    cx.assert_editor_state(indoc! {"
17281        <span>
17282        ˇ
17283        </span>
17284    "});
17285
17286    cx.set_state(indoc! {"
17287        <span><span></span>ˇ</span>
17288    "});
17289    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17290    cx.assert_editor_state(indoc! {"
17291        <span><span></span>
17292        ˇ</span>
17293    "});
17294
17295    cx.set_state(indoc! {"
17296        <span>ˇ
17297        </span>
17298    "});
17299    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17300    cx.assert_editor_state(indoc! {"
17301        <span>
17302        ˇ
17303        </span>
17304    "});
17305}
17306
17307#[gpui::test(iterations = 10)]
17308async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17309    init_test(cx, |_| {});
17310
17311    let fs = FakeFs::new(cx.executor());
17312    fs.insert_tree(
17313        path!("/dir"),
17314        json!({
17315            "a.ts": "a",
17316        }),
17317    )
17318    .await;
17319
17320    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17321    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17322    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17323
17324    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17325    language_registry.add(Arc::new(Language::new(
17326        LanguageConfig {
17327            name: "TypeScript".into(),
17328            matcher: LanguageMatcher {
17329                path_suffixes: vec!["ts".to_string()],
17330                ..Default::default()
17331            },
17332            ..Default::default()
17333        },
17334        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17335    )));
17336    let mut fake_language_servers = language_registry.register_fake_lsp(
17337        "TypeScript",
17338        FakeLspAdapter {
17339            capabilities: lsp::ServerCapabilities {
17340                code_lens_provider: Some(lsp::CodeLensOptions {
17341                    resolve_provider: Some(true),
17342                }),
17343                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17344                    commands: vec!["_the/command".to_string()],
17345                    ..lsp::ExecuteCommandOptions::default()
17346                }),
17347                ..lsp::ServerCapabilities::default()
17348            },
17349            ..FakeLspAdapter::default()
17350        },
17351    );
17352
17353    let (buffer, _handle) = project
17354        .update(cx, |p, cx| {
17355            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17356        })
17357        .await
17358        .unwrap();
17359    cx.executor().run_until_parked();
17360
17361    let fake_server = fake_language_servers.next().await.unwrap();
17362
17363    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17364    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17365    drop(buffer_snapshot);
17366    let actions = cx
17367        .update_window(*workspace, |_, window, cx| {
17368            project.code_actions(&buffer, anchor..anchor, window, cx)
17369        })
17370        .unwrap();
17371
17372    fake_server
17373        .handle_request::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17374            Ok(Some(vec![
17375                lsp::CodeLens {
17376                    range: lsp::Range::default(),
17377                    command: Some(lsp::Command {
17378                        title: "Code lens command".to_owned(),
17379                        command: "_the/command".to_owned(),
17380                        arguments: None,
17381                    }),
17382                    data: None,
17383                },
17384                lsp::CodeLens {
17385                    range: lsp::Range::default(),
17386                    command: Some(lsp::Command {
17387                        title: "Command not in capabilities".to_owned(),
17388                        command: "not in capabilities".to_owned(),
17389                        arguments: None,
17390                    }),
17391                    data: None,
17392                },
17393                lsp::CodeLens {
17394                    range: lsp::Range {
17395                        start: lsp::Position {
17396                            line: 1,
17397                            character: 1,
17398                        },
17399                        end: lsp::Position {
17400                            line: 1,
17401                            character: 1,
17402                        },
17403                    },
17404                    command: Some(lsp::Command {
17405                        title: "Command not in range".to_owned(),
17406                        command: "_the/command".to_owned(),
17407                        arguments: None,
17408                    }),
17409                    data: None,
17410                },
17411            ]))
17412        })
17413        .next()
17414        .await;
17415
17416    let actions = actions.await.unwrap();
17417    assert_eq!(
17418        actions.len(),
17419        1,
17420        "Should have only one valid action for the 0..0 range"
17421    );
17422    let action = actions[0].clone();
17423    let apply = project.update(cx, |project, cx| {
17424        project.apply_code_action(buffer.clone(), action, true, cx)
17425    });
17426
17427    // Resolving the code action does not populate its edits. In absence of
17428    // edits, we must execute the given command.
17429    fake_server.handle_request::<lsp::request::CodeLensResolve, _, _>(|mut lens, _| async move {
17430        let lens_command = lens.command.as_mut().expect("should have a command");
17431        assert_eq!(lens_command.title, "Code lens command");
17432        lens_command.arguments = Some(vec![json!("the-argument")]);
17433        Ok(lens)
17434    });
17435
17436    // While executing the command, the language server sends the editor
17437    // a `workspaceEdit` request.
17438    fake_server
17439        .handle_request::<lsp::request::ExecuteCommand, _, _>({
17440            let fake = fake_server.clone();
17441            move |params, _| {
17442                assert_eq!(params.command, "_the/command");
17443                let fake = fake.clone();
17444                async move {
17445                    fake.server
17446                        .request::<lsp::request::ApplyWorkspaceEdit>(
17447                            lsp::ApplyWorkspaceEditParams {
17448                                label: None,
17449                                edit: lsp::WorkspaceEdit {
17450                                    changes: Some(
17451                                        [(
17452                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
17453                                            vec![lsp::TextEdit {
17454                                                range: lsp::Range::new(
17455                                                    lsp::Position::new(0, 0),
17456                                                    lsp::Position::new(0, 0),
17457                                                ),
17458                                                new_text: "X".into(),
17459                                            }],
17460                                        )]
17461                                        .into_iter()
17462                                        .collect(),
17463                                    ),
17464                                    ..Default::default()
17465                                },
17466                            },
17467                        )
17468                        .await
17469                        .unwrap();
17470                    Ok(Some(json!(null)))
17471                }
17472            }
17473        })
17474        .next()
17475        .await;
17476
17477    // Applying the code lens command returns a project transaction containing the edits
17478    // sent by the language server in its `workspaceEdit` request.
17479    let transaction = apply.await.unwrap();
17480    assert!(transaction.0.contains_key(&buffer));
17481    buffer.update(cx, |buffer, cx| {
17482        assert_eq!(buffer.text(), "Xa");
17483        buffer.undo(cx);
17484        assert_eq!(buffer.text(), "a");
17485    });
17486}
17487
17488mod autoclose_tags {
17489    use super::*;
17490    use language::language_settings::JsxTagAutoCloseSettings;
17491    use languages::language;
17492
17493    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17494        init_test(cx, |settings| {
17495            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17496        });
17497
17498        let mut cx = EditorTestContext::new(cx).await;
17499        cx.update_buffer(|buffer, cx| {
17500            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17501
17502            buffer.set_language(Some(language), cx)
17503        });
17504
17505        cx
17506    }
17507
17508    macro_rules! check {
17509        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17510            #[gpui::test]
17511            async fn $name(cx: &mut TestAppContext) {
17512                let mut cx = test_setup(cx).await;
17513                cx.set_state($initial);
17514                cx.run_until_parked();
17515
17516                cx.update_editor(|editor, window, cx| {
17517                    editor.handle_input($input, window, cx);
17518                });
17519                cx.run_until_parked();
17520                cx.assert_editor_state($expected);
17521            }
17522        };
17523    }
17524
17525    check!(
17526        test_basic,
17527        "<divˇ" + ">" => "<div>ˇ</div>"
17528    );
17529
17530    check!(
17531        test_basic_nested,
17532        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17533    );
17534
17535    check!(
17536        test_basic_ignore_already_closed,
17537        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17538    );
17539
17540    check!(
17541        test_doesnt_autoclose_closing_tag,
17542        "</divˇ" + ">" => "</div>ˇ"
17543    );
17544
17545    check!(
17546        test_jsx_attr,
17547        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17548    );
17549
17550    check!(
17551        test_ignores_closing_tags_in_expr_block,
17552        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17553    );
17554
17555    check!(
17556        test_doesnt_autoclose_on_gt_in_expr,
17557        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17558    );
17559
17560    check!(
17561        test_ignores_closing_tags_with_different_tag_names,
17562        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17563    );
17564
17565    check!(
17566        test_autocloses_in_jsx_expression,
17567        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17568    );
17569
17570    check!(
17571        test_doesnt_autoclose_already_closed_in_jsx_expression,
17572        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17573    );
17574
17575    check!(
17576        test_autocloses_fragment,
17577        "" + ">" => "<>ˇ</>"
17578    );
17579
17580    check!(
17581        test_does_not_include_type_argument_in_autoclose_tag_name,
17582        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17583    );
17584
17585    check!(
17586        test_does_not_autoclose_doctype,
17587        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17588    );
17589
17590    check!(
17591        test_does_not_autoclose_comment,
17592        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17593    );
17594
17595    check!(
17596        test_multi_cursor_autoclose_same_tag,
17597        r#"
17598        <divˇ
17599        <divˇ
17600        "#
17601        + ">" =>
17602        r#"
17603        <div>ˇ</div>
17604        <div>ˇ</div>
17605        "#
17606    );
17607
17608    check!(
17609        test_multi_cursor_autoclose_different_tags,
17610        r#"
17611        <divˇ
17612        <spanˇ
17613        "#
17614        + ">" =>
17615        r#"
17616        <div>ˇ</div>
17617        <span>ˇ</span>
17618        "#
17619    );
17620
17621    check!(
17622        test_multi_cursor_autoclose_some_dont_autoclose_others,
17623        r#"
17624        <divˇ
17625        <div /ˇ
17626        <spanˇ</span>
17627        <!DOCTYPE htmlˇ
17628        </headˇ
17629        <Component<T>ˇ
17630        ˇ
17631        "#
17632        + ">" =>
17633        r#"
17634        <div>ˇ</div>
17635        <div />ˇ
17636        <span>ˇ</span>
17637        <!DOCTYPE html>ˇ
17638        </head>ˇ
17639        <Component<T>>ˇ</Component>
1764017641        "#
17642    );
17643
17644    check!(
17645        test_doesnt_mess_up_trailing_text,
17646        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17647    );
17648
17649    #[gpui::test]
17650    async fn test_multibuffer(cx: &mut TestAppContext) {
17651        init_test(cx, |settings| {
17652            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17653        });
17654
17655        let buffer_a = cx.new(|cx| {
17656            let mut buf = language::Buffer::local("<div", cx);
17657            buf.set_language(
17658                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17659                cx,
17660            );
17661            buf
17662        });
17663        let buffer_b = cx.new(|cx| {
17664            let mut buf = language::Buffer::local("<pre", cx);
17665            buf.set_language(
17666                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17667                cx,
17668            );
17669            buf
17670        });
17671        let buffer_c = cx.new(|cx| {
17672            let buf = language::Buffer::local("<span", cx);
17673            buf
17674        });
17675        let buffer = cx.new(|cx| {
17676            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
17677            buf.push_excerpts(
17678                buffer_a,
17679                [ExcerptRange {
17680                    context: text::Anchor::MIN..text::Anchor::MAX,
17681                    primary: None,
17682                }],
17683                cx,
17684            );
17685            buf.push_excerpts(
17686                buffer_b,
17687                [ExcerptRange {
17688                    context: text::Anchor::MIN..text::Anchor::MAX,
17689                    primary: None,
17690                }],
17691                cx,
17692            );
17693            buf.push_excerpts(
17694                buffer_c,
17695                [ExcerptRange {
17696                    context: text::Anchor::MIN..text::Anchor::MAX,
17697                    primary: None,
17698                }],
17699                cx,
17700            );
17701            buf
17702        });
17703        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
17704
17705        let mut cx = EditorTestContext::for_editor(editor, cx).await;
17706
17707        cx.update_editor(|editor, window, cx| {
17708            editor.change_selections(None, window, cx, |selections| {
17709                selections.select(vec![
17710                    Selection::from_offset(4),
17711                    Selection::from_offset(9),
17712                    Selection::from_offset(15),
17713                ])
17714            })
17715        });
17716        cx.run_until_parked();
17717
17718        cx.update_editor(|editor, window, cx| {
17719            editor.handle_input(">", window, cx);
17720        });
17721        cx.run_until_parked();
17722
17723        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
17724    }
17725}
17726
17727fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
17728    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
17729    point..point
17730}
17731
17732fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
17733    let (text, ranges) = marked_text_ranges(marked_text, true);
17734    assert_eq!(editor.text(cx), text);
17735    assert_eq!(
17736        editor.selections.ranges(cx),
17737        ranges,
17738        "Assert selections are {}",
17739        marked_text
17740    );
17741}
17742
17743pub fn handle_signature_help_request(
17744    cx: &mut EditorLspTestContext,
17745    mocked_response: lsp::SignatureHelp,
17746) -> impl Future<Output = ()> {
17747    let mut request =
17748        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
17749            let mocked_response = mocked_response.clone();
17750            async move { Ok(Some(mocked_response)) }
17751        });
17752
17753    async move {
17754        request.next().await;
17755    }
17756}
17757
17758/// Handle completion request passing a marked string specifying where the completion
17759/// should be triggered from using '|' character, what range should be replaced, and what completions
17760/// should be returned using '<' and '>' to delimit the range
17761pub fn handle_completion_request(
17762    cx: &mut EditorLspTestContext,
17763    marked_string: &str,
17764    completions: Vec<&'static str>,
17765    counter: Arc<AtomicUsize>,
17766) -> impl Future<Output = ()> {
17767    let complete_from_marker: TextRangeMarker = '|'.into();
17768    let replace_range_marker: TextRangeMarker = ('<', '>').into();
17769    let (_, mut marked_ranges) = marked_text_ranges_by(
17770        marked_string,
17771        vec![complete_from_marker.clone(), replace_range_marker.clone()],
17772    );
17773
17774    let complete_from_position =
17775        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
17776    let replace_range =
17777        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
17778
17779    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
17780        let completions = completions.clone();
17781        counter.fetch_add(1, atomic::Ordering::Release);
17782        async move {
17783            assert_eq!(params.text_document_position.text_document.uri, url.clone());
17784            assert_eq!(
17785                params.text_document_position.position,
17786                complete_from_position
17787            );
17788            Ok(Some(lsp::CompletionResponse::Array(
17789                completions
17790                    .iter()
17791                    .map(|completion_text| lsp::CompletionItem {
17792                        label: completion_text.to_string(),
17793                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
17794                            range: replace_range,
17795                            new_text: completion_text.to_string(),
17796                        })),
17797                        ..Default::default()
17798                    })
17799                    .collect(),
17800            )))
17801        }
17802    });
17803
17804    async move {
17805        request.next().await;
17806    }
17807}
17808
17809fn handle_resolve_completion_request(
17810    cx: &mut EditorLspTestContext,
17811    edits: Option<Vec<(&'static str, &'static str)>>,
17812) -> impl Future<Output = ()> {
17813    let edits = edits.map(|edits| {
17814        edits
17815            .iter()
17816            .map(|(marked_string, new_text)| {
17817                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
17818                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
17819                lsp::TextEdit::new(replace_range, new_text.to_string())
17820            })
17821            .collect::<Vec<_>>()
17822    });
17823
17824    let mut request =
17825        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
17826            let edits = edits.clone();
17827            async move {
17828                Ok(lsp::CompletionItem {
17829                    additional_text_edits: edits,
17830                    ..Default::default()
17831                })
17832            }
17833        });
17834
17835    async move {
17836        request.next().await;
17837    }
17838}
17839
17840pub(crate) fn update_test_language_settings(
17841    cx: &mut TestAppContext,
17842    f: impl Fn(&mut AllLanguageSettingsContent),
17843) {
17844    cx.update(|cx| {
17845        SettingsStore::update_global(cx, |store, cx| {
17846            store.update_user_settings::<AllLanguageSettings>(cx, f);
17847        });
17848    });
17849}
17850
17851pub(crate) fn update_test_project_settings(
17852    cx: &mut TestAppContext,
17853    f: impl Fn(&mut ProjectSettings),
17854) {
17855    cx.update(|cx| {
17856        SettingsStore::update_global(cx, |store, cx| {
17857            store.update_user_settings::<ProjectSettings>(cx, f);
17858        });
17859    });
17860}
17861
17862pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
17863    cx.update(|cx| {
17864        assets::Assets.load_test_fonts(cx);
17865        let store = SettingsStore::test(cx);
17866        cx.set_global(store);
17867        theme::init(theme::LoadThemes::JustBase, cx);
17868        release_channel::init(SemanticVersion::default(), cx);
17869        client::init_settings(cx);
17870        language::init(cx);
17871        Project::init_settings(cx);
17872        workspace::init_settings(cx);
17873        crate::init(cx);
17874    });
17875
17876    update_test_language_settings(cx, f);
17877}
17878
17879#[track_caller]
17880fn assert_hunk_revert(
17881    not_reverted_text_with_selections: &str,
17882    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
17883    expected_reverted_text_with_selections: &str,
17884    base_text: &str,
17885    cx: &mut EditorLspTestContext,
17886) {
17887    cx.set_state(not_reverted_text_with_selections);
17888    cx.set_head_text(base_text);
17889    cx.executor().run_until_parked();
17890
17891    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
17892        let snapshot = editor.snapshot(window, cx);
17893        let reverted_hunk_statuses = snapshot
17894            .buffer_snapshot
17895            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
17896            .map(|hunk| hunk.status().kind)
17897            .collect::<Vec<_>>();
17898
17899        editor.git_restore(&Default::default(), window, cx);
17900        reverted_hunk_statuses
17901    });
17902    cx.executor().run_until_parked();
17903    cx.assert_editor_state(expected_reverted_text_with_selections);
17904    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
17905}