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::{
   32    debugger::breakpoint_store::{BreakpointKind, SerializedBreakpoint},
   33    project_settings::{LspSettings, ProjectSettings},
   34    FakeFs,
   35};
   36use serde_json::{self, json};
   37use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   38use std::{
   39    iter,
   40    sync::atomic::{self, AtomicUsize},
   41};
   42use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   43use text::ToPoint as _;
   44use unindent::Unindent;
   45use util::{
   46    assert_set_eq, path,
   47    test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   48    uri,
   49};
   50use workspace::{
   51    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   52    NavigationEntry, ViewId,
   53};
   54
   55#[gpui::test]
   56fn test_edit_events(cx: &mut TestAppContext) {
   57    init_test(cx, |_| {});
   58
   59    let buffer = cx.new(|cx| {
   60        let mut buffer = language::Buffer::local("123456", cx);
   61        buffer.set_group_interval(Duration::from_secs(1));
   62        buffer
   63    });
   64
   65    let events = Rc::new(RefCell::new(Vec::new()));
   66    let editor1 = cx.add_window({
   67        let events = events.clone();
   68        |window, cx| {
   69            let entity = cx.entity().clone();
   70            cx.subscribe_in(
   71                &entity,
   72                window,
   73                move |_, _, event: &EditorEvent, _, _| match event {
   74                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   75                    EditorEvent::BufferEdited => {
   76                        events.borrow_mut().push(("editor1", "buffer edited"))
   77                    }
   78                    _ => {}
   79                },
   80            )
   81            .detach();
   82            Editor::for_buffer(buffer.clone(), None, window, cx)
   83        }
   84    });
   85
   86    let editor2 = cx.add_window({
   87        let events = events.clone();
   88        |window, cx| {
   89            cx.subscribe_in(
   90                &cx.entity().clone(),
   91                window,
   92                move |_, _, event: &EditorEvent, _, _| match event {
   93                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   94                    EditorEvent::BufferEdited => {
   95                        events.borrow_mut().push(("editor2", "buffer edited"))
   96                    }
   97                    _ => {}
   98                },
   99            )
  100            .detach();
  101            Editor::for_buffer(buffer.clone(), None, window, cx)
  102        }
  103    });
  104
  105    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  106
  107    // Mutating editor 1 will emit an `Edited` event only for that editor.
  108    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  109    assert_eq!(
  110        mem::take(&mut *events.borrow_mut()),
  111        [
  112            ("editor1", "edited"),
  113            ("editor1", "buffer edited"),
  114            ("editor2", "buffer edited"),
  115        ]
  116    );
  117
  118    // Mutating editor 2 will emit an `Edited` event only for that editor.
  119    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  120    assert_eq!(
  121        mem::take(&mut *events.borrow_mut()),
  122        [
  123            ("editor2", "edited"),
  124            ("editor1", "buffer edited"),
  125            ("editor2", "buffer edited"),
  126        ]
  127    );
  128
  129    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  130    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  131    assert_eq!(
  132        mem::take(&mut *events.borrow_mut()),
  133        [
  134            ("editor1", "edited"),
  135            ("editor1", "buffer edited"),
  136            ("editor2", "buffer edited"),
  137        ]
  138    );
  139
  140    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  141    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  142    assert_eq!(
  143        mem::take(&mut *events.borrow_mut()),
  144        [
  145            ("editor1", "edited"),
  146            ("editor1", "buffer edited"),
  147            ("editor2", "buffer edited"),
  148        ]
  149    );
  150
  151    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  152    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  153    assert_eq!(
  154        mem::take(&mut *events.borrow_mut()),
  155        [
  156            ("editor2", "edited"),
  157            ("editor1", "buffer edited"),
  158            ("editor2", "buffer edited"),
  159        ]
  160    );
  161
  162    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  163    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  164    assert_eq!(
  165        mem::take(&mut *events.borrow_mut()),
  166        [
  167            ("editor2", "edited"),
  168            ("editor1", "buffer edited"),
  169            ("editor2", "buffer edited"),
  170        ]
  171    );
  172
  173    // No event is emitted when the mutation is a no-op.
  174    _ = editor2.update(cx, |editor, window, cx| {
  175        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  176
  177        editor.backspace(&Backspace, window, cx);
  178    });
  179    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  180}
  181
  182#[gpui::test]
  183fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  184    init_test(cx, |_| {});
  185
  186    let mut now = Instant::now();
  187    let group_interval = Duration::from_millis(1);
  188    let buffer = cx.new(|cx| {
  189        let mut buf = language::Buffer::local("123456", cx);
  190        buf.set_group_interval(group_interval);
  191        buf
  192    });
  193    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  194    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  195
  196    _ = editor.update(cx, |editor, window, cx| {
  197        editor.start_transaction_at(now, window, cx);
  198        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  199
  200        editor.insert("cd", window, cx);
  201        editor.end_transaction_at(now, cx);
  202        assert_eq!(editor.text(cx), "12cd56");
  203        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  204
  205        editor.start_transaction_at(now, window, cx);
  206        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  207        editor.insert("e", window, cx);
  208        editor.end_transaction_at(now, cx);
  209        assert_eq!(editor.text(cx), "12cde6");
  210        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  211
  212        now += group_interval + Duration::from_millis(1);
  213        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  214
  215        // Simulate an edit in another editor
  216        buffer.update(cx, |buffer, cx| {
  217            buffer.start_transaction_at(now, cx);
  218            buffer.edit([(0..1, "a")], None, cx);
  219            buffer.edit([(1..1, "b")], None, cx);
  220            buffer.end_transaction_at(now, cx);
  221        });
  222
  223        assert_eq!(editor.text(cx), "ab2cde6");
  224        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  225
  226        // Last transaction happened past the group interval in a different editor.
  227        // Undo it individually and don't restore selections.
  228        editor.undo(&Undo, window, cx);
  229        assert_eq!(editor.text(cx), "12cde6");
  230        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  231
  232        // First two transactions happened within the group interval in this editor.
  233        // Undo them together and restore selections.
  234        editor.undo(&Undo, window, cx);
  235        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  236        assert_eq!(editor.text(cx), "123456");
  237        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  238
  239        // Redo the first two transactions together.
  240        editor.redo(&Redo, window, cx);
  241        assert_eq!(editor.text(cx), "12cde6");
  242        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  243
  244        // Redo the last transaction on its own.
  245        editor.redo(&Redo, window, cx);
  246        assert_eq!(editor.text(cx), "ab2cde6");
  247        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  248
  249        // Test empty transactions.
  250        editor.start_transaction_at(now, window, cx);
  251        editor.end_transaction_at(now, cx);
  252        editor.undo(&Undo, window, cx);
  253        assert_eq!(editor.text(cx), "12cde6");
  254    });
  255}
  256
  257#[gpui::test]
  258fn test_ime_composition(cx: &mut TestAppContext) {
  259    init_test(cx, |_| {});
  260
  261    let buffer = cx.new(|cx| {
  262        let mut buffer = language::Buffer::local("abcde", cx);
  263        // Ensure automatic grouping doesn't occur.
  264        buffer.set_group_interval(Duration::ZERO);
  265        buffer
  266    });
  267
  268    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  269    cx.add_window(|window, cx| {
  270        let mut editor = build_editor(buffer.clone(), window, cx);
  271
  272        // Start a new IME composition.
  273        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  274        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  275        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  276        assert_eq!(editor.text(cx), "äbcde");
  277        assert_eq!(
  278            editor.marked_text_ranges(cx),
  279            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  280        );
  281
  282        // Finalize IME composition.
  283        editor.replace_text_in_range(None, "ā", window, cx);
  284        assert_eq!(editor.text(cx), "ābcde");
  285        assert_eq!(editor.marked_text_ranges(cx), None);
  286
  287        // IME composition edits are grouped and are undone/redone at once.
  288        editor.undo(&Default::default(), window, cx);
  289        assert_eq!(editor.text(cx), "abcde");
  290        assert_eq!(editor.marked_text_ranges(cx), None);
  291        editor.redo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "ābcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294
  295        // Start a new IME composition.
  296        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  297        assert_eq!(
  298            editor.marked_text_ranges(cx),
  299            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  300        );
  301
  302        // Undoing during an IME composition cancels it.
  303        editor.undo(&Default::default(), window, cx);
  304        assert_eq!(editor.text(cx), "ābcde");
  305        assert_eq!(editor.marked_text_ranges(cx), None);
  306
  307        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  308        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  309        assert_eq!(editor.text(cx), "ābcdè");
  310        assert_eq!(
  311            editor.marked_text_ranges(cx),
  312            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  313        );
  314
  315        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  316        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  317        assert_eq!(editor.text(cx), "ābcdę");
  318        assert_eq!(editor.marked_text_ranges(cx), None);
  319
  320        // Start a new IME composition with multiple cursors.
  321        editor.change_selections(None, window, cx, |s| {
  322            s.select_ranges([
  323                OffsetUtf16(1)..OffsetUtf16(1),
  324                OffsetUtf16(3)..OffsetUtf16(3),
  325                OffsetUtf16(5)..OffsetUtf16(5),
  326            ])
  327        });
  328        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  329        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  330        assert_eq!(
  331            editor.marked_text_ranges(cx),
  332            Some(vec![
  333                OffsetUtf16(0)..OffsetUtf16(3),
  334                OffsetUtf16(4)..OffsetUtf16(7),
  335                OffsetUtf16(8)..OffsetUtf16(11)
  336            ])
  337        );
  338
  339        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  340        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  341        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  342        assert_eq!(
  343            editor.marked_text_ranges(cx),
  344            Some(vec![
  345                OffsetUtf16(1)..OffsetUtf16(2),
  346                OffsetUtf16(5)..OffsetUtf16(6),
  347                OffsetUtf16(9)..OffsetUtf16(10)
  348            ])
  349        );
  350
  351        // Finalize IME composition with multiple cursors.
  352        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  353        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  354        assert_eq!(editor.marked_text_ranges(cx), None);
  355
  356        editor
  357    });
  358}
  359
  360#[gpui::test]
  361fn test_selection_with_mouse(cx: &mut TestAppContext) {
  362    init_test(cx, |_| {});
  363
  364    let editor = cx.add_window(|window, cx| {
  365        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  366        build_editor(buffer, window, cx)
  367    });
  368
  369    _ = editor.update(cx, |editor, window, cx| {
  370        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  371    });
  372    assert_eq!(
  373        editor
  374            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  375            .unwrap(),
  376        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  377    );
  378
  379    _ = editor.update(cx, |editor, window, cx| {
  380        editor.update_selection(
  381            DisplayPoint::new(DisplayRow(3), 3),
  382            0,
  383            gpui::Point::<f32>::default(),
  384            window,
  385            cx,
  386        );
  387    });
  388
  389    assert_eq!(
  390        editor
  391            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  392            .unwrap(),
  393        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  394    );
  395
  396    _ = editor.update(cx, |editor, window, cx| {
  397        editor.update_selection(
  398            DisplayPoint::new(DisplayRow(1), 1),
  399            0,
  400            gpui::Point::<f32>::default(),
  401            window,
  402            cx,
  403        );
  404    });
  405
  406    assert_eq!(
  407        editor
  408            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  409            .unwrap(),
  410        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  411    );
  412
  413    _ = editor.update(cx, |editor, window, cx| {
  414        editor.end_selection(window, cx);
  415        editor.update_selection(
  416            DisplayPoint::new(DisplayRow(3), 3),
  417            0,
  418            gpui::Point::<f32>::default(),
  419            window,
  420            cx,
  421        );
  422    });
  423
  424    assert_eq!(
  425        editor
  426            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  427            .unwrap(),
  428        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  429    );
  430
  431    _ = editor.update(cx, |editor, window, cx| {
  432        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  433        editor.update_selection(
  434            DisplayPoint::new(DisplayRow(0), 0),
  435            0,
  436            gpui::Point::<f32>::default(),
  437            window,
  438            cx,
  439        );
  440    });
  441
  442    assert_eq!(
  443        editor
  444            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  445            .unwrap(),
  446        [
  447            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  448            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  449        ]
  450    );
  451
  452    _ = editor.update(cx, |editor, window, cx| {
  453        editor.end_selection(window, cx);
  454    });
  455
  456    assert_eq!(
  457        editor
  458            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  459            .unwrap(),
  460        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  461    );
  462}
  463
  464#[gpui::test]
  465fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  466    init_test(cx, |_| {});
  467
  468    let editor = cx.add_window(|window, cx| {
  469        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  470        build_editor(buffer, window, cx)
  471    });
  472
  473    _ = editor.update(cx, |editor, window, cx| {
  474        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  475    });
  476
  477    _ = editor.update(cx, |editor, window, cx| {
  478        editor.end_selection(window, cx);
  479    });
  480
  481    _ = editor.update(cx, |editor, window, cx| {
  482        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  483    });
  484
  485    _ = editor.update(cx, |editor, window, cx| {
  486        editor.end_selection(window, cx);
  487    });
  488
  489    assert_eq!(
  490        editor
  491            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  492            .unwrap(),
  493        [
  494            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  495            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  496        ]
  497    );
  498
  499    _ = editor.update(cx, |editor, window, cx| {
  500        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  501    });
  502
  503    _ = editor.update(cx, |editor, window, cx| {
  504        editor.end_selection(window, cx);
  505    });
  506
  507    assert_eq!(
  508        editor
  509            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  510            .unwrap(),
  511        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  512    );
  513}
  514
  515#[gpui::test]
  516fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  517    init_test(cx, |_| {});
  518
  519    let editor = cx.add_window(|window, cx| {
  520        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  521        build_editor(buffer, window, cx)
  522    });
  523
  524    _ = editor.update(cx, |editor, window, cx| {
  525        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  526        assert_eq!(
  527            editor.selections.display_ranges(cx),
  528            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  529        );
  530    });
  531
  532    _ = editor.update(cx, |editor, window, cx| {
  533        editor.update_selection(
  534            DisplayPoint::new(DisplayRow(3), 3),
  535            0,
  536            gpui::Point::<f32>::default(),
  537            window,
  538            cx,
  539        );
  540        assert_eq!(
  541            editor.selections.display_ranges(cx),
  542            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  543        );
  544    });
  545
  546    _ = editor.update(cx, |editor, window, cx| {
  547        editor.cancel(&Cancel, window, cx);
  548        editor.update_selection(
  549            DisplayPoint::new(DisplayRow(1), 1),
  550            0,
  551            gpui::Point::<f32>::default(),
  552            window,
  553            cx,
  554        );
  555        assert_eq!(
  556            editor.selections.display_ranges(cx),
  557            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  558        );
  559    });
  560}
  561
  562#[gpui::test]
  563fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  564    init_test(cx, |_| {});
  565
  566    let editor = cx.add_window(|window, cx| {
  567        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  568        build_editor(buffer, window, cx)
  569    });
  570
  571    _ = editor.update(cx, |editor, window, cx| {
  572        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  573        assert_eq!(
  574            editor.selections.display_ranges(cx),
  575            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  576        );
  577
  578        editor.move_down(&Default::default(), window, cx);
  579        assert_eq!(
  580            editor.selections.display_ranges(cx),
  581            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  582        );
  583
  584        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  585        assert_eq!(
  586            editor.selections.display_ranges(cx),
  587            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  588        );
  589
  590        editor.move_up(&Default::default(), window, cx);
  591        assert_eq!(
  592            editor.selections.display_ranges(cx),
  593            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  594        );
  595    });
  596}
  597
  598#[gpui::test]
  599fn test_clone(cx: &mut TestAppContext) {
  600    init_test(cx, |_| {});
  601
  602    let (text, selection_ranges) = marked_text_ranges(
  603        indoc! {"
  604            one
  605            two
  606            threeˇ
  607            four
  608            fiveˇ
  609        "},
  610        true,
  611    );
  612
  613    let editor = cx.add_window(|window, cx| {
  614        let buffer = MultiBuffer::build_simple(&text, cx);
  615        build_editor(buffer, window, cx)
  616    });
  617
  618    _ = editor.update(cx, |editor, window, cx| {
  619        editor.change_selections(None, window, cx, |s| {
  620            s.select_ranges(selection_ranges.clone())
  621        });
  622        editor.fold_creases(
  623            vec![
  624                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  625                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  626            ],
  627            true,
  628            window,
  629            cx,
  630        );
  631    });
  632
  633    let cloned_editor = editor
  634        .update(cx, |editor, _, cx| {
  635            cx.open_window(Default::default(), |window, cx| {
  636                cx.new(|cx| editor.clone(window, cx))
  637            })
  638        })
  639        .unwrap()
  640        .unwrap();
  641
  642    let snapshot = editor
  643        .update(cx, |e, window, cx| e.snapshot(window, cx))
  644        .unwrap();
  645    let cloned_snapshot = cloned_editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648
  649    assert_eq!(
  650        cloned_editor
  651            .update(cx, |e, _, cx| e.display_text(cx))
  652            .unwrap(),
  653        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  654    );
  655    assert_eq!(
  656        cloned_snapshot
  657            .folds_in_range(0..text.len())
  658            .collect::<Vec<_>>(),
  659        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  660    );
  661    assert_set_eq!(
  662        cloned_editor
  663            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  664            .unwrap(),
  665        editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  667            .unwrap()
  668    );
  669    assert_set_eq!(
  670        cloned_editor
  671            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  672            .unwrap(),
  673        editor
  674            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  675            .unwrap()
  676    );
  677}
  678
  679#[gpui::test]
  680async fn test_navigation_history(cx: &mut TestAppContext) {
  681    init_test(cx, |_| {});
  682
  683    use workspace::item::Item;
  684
  685    let fs = FakeFs::new(cx.executor());
  686    let project = Project::test(fs, [], cx).await;
  687    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  688    let pane = workspace
  689        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  690        .unwrap();
  691
  692    _ = workspace.update(cx, |_v, window, cx| {
  693        cx.new(|cx| {
  694            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  695            let mut editor = build_editor(buffer.clone(), window, cx);
  696            let handle = cx.entity();
  697            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  698
  699            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  700                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  701            }
  702
  703            // Move the cursor a small distance.
  704            // Nothing is added to the navigation history.
  705            editor.change_selections(None, window, cx, |s| {
  706                s.select_display_ranges([
  707                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  708                ])
  709            });
  710            editor.change_selections(None, window, cx, |s| {
  711                s.select_display_ranges([
  712                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  713                ])
  714            });
  715            assert!(pop_history(&mut editor, cx).is_none());
  716
  717            // Move the cursor a large distance.
  718            // The history can jump back to the previous position.
  719            editor.change_selections(None, window, cx, |s| {
  720                s.select_display_ranges([
  721                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  722                ])
  723            });
  724            let nav_entry = pop_history(&mut editor, cx).unwrap();
  725            editor.navigate(nav_entry.data.unwrap(), window, cx);
  726            assert_eq!(nav_entry.item.id(), cx.entity_id());
  727            assert_eq!(
  728                editor.selections.display_ranges(cx),
  729                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  730            );
  731            assert!(pop_history(&mut editor, cx).is_none());
  732
  733            // Move the cursor a small distance via the mouse.
  734            // Nothing is added to the navigation history.
  735            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  736            editor.end_selection(window, cx);
  737            assert_eq!(
  738                editor.selections.display_ranges(cx),
  739                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  740            );
  741            assert!(pop_history(&mut editor, cx).is_none());
  742
  743            // Move the cursor a large distance via the mouse.
  744            // The history can jump back to the previous position.
  745            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  746            editor.end_selection(window, cx);
  747            assert_eq!(
  748                editor.selections.display_ranges(cx),
  749                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  750            );
  751            let nav_entry = pop_history(&mut editor, cx).unwrap();
  752            editor.navigate(nav_entry.data.unwrap(), window, cx);
  753            assert_eq!(nav_entry.item.id(), cx.entity_id());
  754            assert_eq!(
  755                editor.selections.display_ranges(cx),
  756                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  757            );
  758            assert!(pop_history(&mut editor, cx).is_none());
  759
  760            // Set scroll position to check later
  761            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  762            let original_scroll_position = editor.scroll_manager.anchor();
  763
  764            // Jump to the end of the document and adjust scroll
  765            editor.move_to_end(&MoveToEnd, window, cx);
  766            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  767            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  768
  769            let nav_entry = pop_history(&mut editor, cx).unwrap();
  770            editor.navigate(nav_entry.data.unwrap(), window, cx);
  771            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  772
  773            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  774            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  775            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  776            let invalid_point = Point::new(9999, 0);
  777            editor.navigate(
  778                Box::new(NavigationData {
  779                    cursor_anchor: invalid_anchor,
  780                    cursor_position: invalid_point,
  781                    scroll_anchor: ScrollAnchor {
  782                        anchor: invalid_anchor,
  783                        offset: Default::default(),
  784                    },
  785                    scroll_top_row: invalid_point.row,
  786                }),
  787                window,
  788                cx,
  789            );
  790            assert_eq!(
  791                editor.selections.display_ranges(cx),
  792                &[editor.max_point(cx)..editor.max_point(cx)]
  793            );
  794            assert_eq!(
  795                editor.scroll_position(cx),
  796                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  797            );
  798
  799            editor
  800        })
  801    });
  802}
  803
  804#[gpui::test]
  805fn test_cancel(cx: &mut TestAppContext) {
  806    init_test(cx, |_| {});
  807
  808    let editor = cx.add_window(|window, cx| {
  809        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  810        build_editor(buffer, window, cx)
  811    });
  812
  813    _ = editor.update(cx, |editor, window, cx| {
  814        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  815        editor.update_selection(
  816            DisplayPoint::new(DisplayRow(1), 1),
  817            0,
  818            gpui::Point::<f32>::default(),
  819            window,
  820            cx,
  821        );
  822        editor.end_selection(window, cx);
  823
  824        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  825        editor.update_selection(
  826            DisplayPoint::new(DisplayRow(0), 3),
  827            0,
  828            gpui::Point::<f32>::default(),
  829            window,
  830            cx,
  831        );
  832        editor.end_selection(window, cx);
  833        assert_eq!(
  834            editor.selections.display_ranges(cx),
  835            [
  836                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  837                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  838            ]
  839        );
  840    });
  841
  842    _ = editor.update(cx, |editor, window, cx| {
  843        editor.cancel(&Cancel, window, cx);
  844        assert_eq!(
  845            editor.selections.display_ranges(cx),
  846            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  847        );
  848    });
  849
  850    _ = editor.update(cx, |editor, window, cx| {
  851        editor.cancel(&Cancel, window, cx);
  852        assert_eq!(
  853            editor.selections.display_ranges(cx),
  854            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  855        );
  856    });
  857}
  858
  859#[gpui::test]
  860fn test_fold_action(cx: &mut TestAppContext) {
  861    init_test(cx, |_| {});
  862
  863    let editor = cx.add_window(|window, cx| {
  864        let buffer = MultiBuffer::build_simple(
  865            &"
  866                impl Foo {
  867                    // Hello!
  868
  869                    fn a() {
  870                        1
  871                    }
  872
  873                    fn b() {
  874                        2
  875                    }
  876
  877                    fn c() {
  878                        3
  879                    }
  880                }
  881            "
  882            .unindent(),
  883            cx,
  884        );
  885        build_editor(buffer.clone(), window, cx)
  886    });
  887
  888    _ = editor.update(cx, |editor, window, cx| {
  889        editor.change_selections(None, window, cx, |s| {
  890            s.select_display_ranges([
  891                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  892            ]);
  893        });
  894        editor.fold(&Fold, window, cx);
  895        assert_eq!(
  896            editor.display_text(cx),
  897            "
  898                impl Foo {
  899                    // Hello!
  900
  901                    fn a() {
  902                        1
  903                    }
  904
  905                    fn b() {⋯
  906                    }
  907
  908                    fn c() {⋯
  909                    }
  910                }
  911            "
  912            .unindent(),
  913        );
  914
  915        editor.fold(&Fold, window, cx);
  916        assert_eq!(
  917            editor.display_text(cx),
  918            "
  919                impl Foo {⋯
  920                }
  921            "
  922            .unindent(),
  923        );
  924
  925        editor.unfold_lines(&UnfoldLines, window, cx);
  926        assert_eq!(
  927            editor.display_text(cx),
  928            "
  929                impl Foo {
  930                    // Hello!
  931
  932                    fn a() {
  933                        1
  934                    }
  935
  936                    fn b() {⋯
  937                    }
  938
  939                    fn c() {⋯
  940                    }
  941                }
  942            "
  943            .unindent(),
  944        );
  945
  946        editor.unfold_lines(&UnfoldLines, window, cx);
  947        assert_eq!(
  948            editor.display_text(cx),
  949            editor.buffer.read(cx).read(cx).text()
  950        );
  951    });
  952}
  953
  954#[gpui::test]
  955fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  956    init_test(cx, |_| {});
  957
  958    let editor = cx.add_window(|window, cx| {
  959        let buffer = MultiBuffer::build_simple(
  960            &"
  961                class Foo:
  962                    # Hello!
  963
  964                    def a():
  965                        print(1)
  966
  967                    def b():
  968                        print(2)
  969
  970                    def c():
  971                        print(3)
  972            "
  973            .unindent(),
  974            cx,
  975        );
  976        build_editor(buffer.clone(), window, cx)
  977    });
  978
  979    _ = editor.update(cx, |editor, window, cx| {
  980        editor.change_selections(None, window, cx, |s| {
  981            s.select_display_ranges([
  982                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  983            ]);
  984        });
  985        editor.fold(&Fold, window, cx);
  986        assert_eq!(
  987            editor.display_text(cx),
  988            "
  989                class Foo:
  990                    # Hello!
  991
  992                    def a():
  993                        print(1)
  994
  995                    def b():⋯
  996
  997                    def c():⋯
  998            "
  999            .unindent(),
 1000        );
 1001
 1002        editor.fold(&Fold, window, cx);
 1003        assert_eq!(
 1004            editor.display_text(cx),
 1005            "
 1006                class Foo:⋯
 1007            "
 1008            .unindent(),
 1009        );
 1010
 1011        editor.unfold_lines(&UnfoldLines, window, cx);
 1012        assert_eq!(
 1013            editor.display_text(cx),
 1014            "
 1015                class Foo:
 1016                    # Hello!
 1017
 1018                    def a():
 1019                        print(1)
 1020
 1021                    def b():⋯
 1022
 1023                    def c():⋯
 1024            "
 1025            .unindent(),
 1026        );
 1027
 1028        editor.unfold_lines(&UnfoldLines, window, cx);
 1029        assert_eq!(
 1030            editor.display_text(cx),
 1031            editor.buffer.read(cx).read(cx).text()
 1032        );
 1033    });
 1034}
 1035
 1036#[gpui::test]
 1037fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1038    init_test(cx, |_| {});
 1039
 1040    let editor = cx.add_window(|window, cx| {
 1041        let buffer = MultiBuffer::build_simple(
 1042            &"
 1043                class Foo:
 1044                    # Hello!
 1045
 1046                    def a():
 1047                        print(1)
 1048
 1049                    def b():
 1050                        print(2)
 1051
 1052
 1053                    def c():
 1054                        print(3)
 1055
 1056
 1057            "
 1058            .unindent(),
 1059            cx,
 1060        );
 1061        build_editor(buffer.clone(), window, cx)
 1062    });
 1063
 1064    _ = editor.update(cx, |editor, window, cx| {
 1065        editor.change_selections(None, window, cx, |s| {
 1066            s.select_display_ranges([
 1067                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1068            ]);
 1069        });
 1070        editor.fold(&Fold, window, cx);
 1071        assert_eq!(
 1072            editor.display_text(cx),
 1073            "
 1074                class Foo:
 1075                    # Hello!
 1076
 1077                    def a():
 1078                        print(1)
 1079
 1080                    def b():⋯
 1081
 1082
 1083                    def c():⋯
 1084
 1085
 1086            "
 1087            .unindent(),
 1088        );
 1089
 1090        editor.fold(&Fold, window, cx);
 1091        assert_eq!(
 1092            editor.display_text(cx),
 1093            "
 1094                class Foo:⋯
 1095
 1096
 1097            "
 1098            .unindent(),
 1099        );
 1100
 1101        editor.unfold_lines(&UnfoldLines, window, cx);
 1102        assert_eq!(
 1103            editor.display_text(cx),
 1104            "
 1105                class Foo:
 1106                    # Hello!
 1107
 1108                    def a():
 1109                        print(1)
 1110
 1111                    def b():⋯
 1112
 1113
 1114                    def c():⋯
 1115
 1116
 1117            "
 1118            .unindent(),
 1119        );
 1120
 1121        editor.unfold_lines(&UnfoldLines, window, cx);
 1122        assert_eq!(
 1123            editor.display_text(cx),
 1124            editor.buffer.read(cx).read(cx).text()
 1125        );
 1126    });
 1127}
 1128
 1129#[gpui::test]
 1130fn test_fold_at_level(cx: &mut TestAppContext) {
 1131    init_test(cx, |_| {});
 1132
 1133    let editor = cx.add_window(|window, cx| {
 1134        let buffer = MultiBuffer::build_simple(
 1135            &"
 1136                class Foo:
 1137                    # Hello!
 1138
 1139                    def a():
 1140                        print(1)
 1141
 1142                    def b():
 1143                        print(2)
 1144
 1145
 1146                class Bar:
 1147                    # World!
 1148
 1149                    def a():
 1150                        print(1)
 1151
 1152                    def b():
 1153                        print(2)
 1154
 1155
 1156            "
 1157            .unindent(),
 1158            cx,
 1159        );
 1160        build_editor(buffer.clone(), window, cx)
 1161    });
 1162
 1163    _ = editor.update(cx, |editor, window, cx| {
 1164        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1165        assert_eq!(
 1166            editor.display_text(cx),
 1167            "
 1168                class Foo:
 1169                    # Hello!
 1170
 1171                    def a():⋯
 1172
 1173                    def b():⋯
 1174
 1175
 1176                class Bar:
 1177                    # World!
 1178
 1179                    def a():⋯
 1180
 1181                    def b():⋯
 1182
 1183
 1184            "
 1185            .unindent(),
 1186        );
 1187
 1188        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1189        assert_eq!(
 1190            editor.display_text(cx),
 1191            "
 1192                class Foo:⋯
 1193
 1194
 1195                class Bar:⋯
 1196
 1197
 1198            "
 1199            .unindent(),
 1200        );
 1201
 1202        editor.unfold_all(&UnfoldAll, window, cx);
 1203        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1204        assert_eq!(
 1205            editor.display_text(cx),
 1206            "
 1207                class Foo:
 1208                    # Hello!
 1209
 1210                    def a():
 1211                        print(1)
 1212
 1213                    def b():
 1214                        print(2)
 1215
 1216
 1217                class Bar:
 1218                    # World!
 1219
 1220                    def a():
 1221                        print(1)
 1222
 1223                    def b():
 1224                        print(2)
 1225
 1226
 1227            "
 1228            .unindent(),
 1229        );
 1230
 1231        assert_eq!(
 1232            editor.display_text(cx),
 1233            editor.buffer.read(cx).read(cx).text()
 1234        );
 1235    });
 1236}
 1237
 1238#[gpui::test]
 1239fn test_move_cursor(cx: &mut TestAppContext) {
 1240    init_test(cx, |_| {});
 1241
 1242    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1243    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1244
 1245    buffer.update(cx, |buffer, cx| {
 1246        buffer.edit(
 1247            vec![
 1248                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1249                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1250            ],
 1251            None,
 1252            cx,
 1253        );
 1254    });
 1255    _ = editor.update(cx, |editor, window, cx| {
 1256        assert_eq!(
 1257            editor.selections.display_ranges(cx),
 1258            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1259        );
 1260
 1261        editor.move_down(&MoveDown, window, cx);
 1262        assert_eq!(
 1263            editor.selections.display_ranges(cx),
 1264            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1265        );
 1266
 1267        editor.move_right(&MoveRight, window, cx);
 1268        assert_eq!(
 1269            editor.selections.display_ranges(cx),
 1270            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1271        );
 1272
 1273        editor.move_left(&MoveLeft, window, cx);
 1274        assert_eq!(
 1275            editor.selections.display_ranges(cx),
 1276            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1277        );
 1278
 1279        editor.move_up(&MoveUp, window, cx);
 1280        assert_eq!(
 1281            editor.selections.display_ranges(cx),
 1282            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1283        );
 1284
 1285        editor.move_to_end(&MoveToEnd, window, cx);
 1286        assert_eq!(
 1287            editor.selections.display_ranges(cx),
 1288            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1289        );
 1290
 1291        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1292        assert_eq!(
 1293            editor.selections.display_ranges(cx),
 1294            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1295        );
 1296
 1297        editor.change_selections(None, window, cx, |s| {
 1298            s.select_display_ranges([
 1299                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1300            ]);
 1301        });
 1302        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1303        assert_eq!(
 1304            editor.selections.display_ranges(cx),
 1305            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1306        );
 1307
 1308        editor.select_to_end(&SelectToEnd, window, cx);
 1309        assert_eq!(
 1310            editor.selections.display_ranges(cx),
 1311            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1312        );
 1313    });
 1314}
 1315
 1316// TODO: Re-enable this test
 1317#[cfg(target_os = "macos")]
 1318#[gpui::test]
 1319fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1320    init_test(cx, |_| {});
 1321
 1322    let editor = cx.add_window(|window, cx| {
 1323        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1324        build_editor(buffer.clone(), window, cx)
 1325    });
 1326
 1327    assert_eq!('🟥'.len_utf8(), 4);
 1328    assert_eq!('α'.len_utf8(), 2);
 1329
 1330    _ = editor.update(cx, |editor, window, cx| {
 1331        editor.fold_creases(
 1332            vec![
 1333                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1334                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1336            ],
 1337            true,
 1338            window,
 1339            cx,
 1340        );
 1341        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1342
 1343        editor.move_right(&MoveRight, window, cx);
 1344        assert_eq!(
 1345            editor.selections.display_ranges(cx),
 1346            &[empty_range(0, "🟥".len())]
 1347        );
 1348        editor.move_right(&MoveRight, window, cx);
 1349        assert_eq!(
 1350            editor.selections.display_ranges(cx),
 1351            &[empty_range(0, "🟥🟧".len())]
 1352        );
 1353        editor.move_right(&MoveRight, window, cx);
 1354        assert_eq!(
 1355            editor.selections.display_ranges(cx),
 1356            &[empty_range(0, "🟥🟧⋯".len())]
 1357        );
 1358
 1359        editor.move_down(&MoveDown, window, cx);
 1360        assert_eq!(
 1361            editor.selections.display_ranges(cx),
 1362            &[empty_range(1, "ab⋯e".len())]
 1363        );
 1364        editor.move_left(&MoveLeft, window, cx);
 1365        assert_eq!(
 1366            editor.selections.display_ranges(cx),
 1367            &[empty_range(1, "ab⋯".len())]
 1368        );
 1369        editor.move_left(&MoveLeft, window, cx);
 1370        assert_eq!(
 1371            editor.selections.display_ranges(cx),
 1372            &[empty_range(1, "ab".len())]
 1373        );
 1374        editor.move_left(&MoveLeft, window, cx);
 1375        assert_eq!(
 1376            editor.selections.display_ranges(cx),
 1377            &[empty_range(1, "a".len())]
 1378        );
 1379
 1380        editor.move_down(&MoveDown, window, cx);
 1381        assert_eq!(
 1382            editor.selections.display_ranges(cx),
 1383            &[empty_range(2, "α".len())]
 1384        );
 1385        editor.move_right(&MoveRight, window, cx);
 1386        assert_eq!(
 1387            editor.selections.display_ranges(cx),
 1388            &[empty_range(2, "αβ".len())]
 1389        );
 1390        editor.move_right(&MoveRight, window, cx);
 1391        assert_eq!(
 1392            editor.selections.display_ranges(cx),
 1393            &[empty_range(2, "αβ⋯".len())]
 1394        );
 1395        editor.move_right(&MoveRight, window, cx);
 1396        assert_eq!(
 1397            editor.selections.display_ranges(cx),
 1398            &[empty_range(2, "αβ⋯ε".len())]
 1399        );
 1400
 1401        editor.move_up(&MoveUp, window, cx);
 1402        assert_eq!(
 1403            editor.selections.display_ranges(cx),
 1404            &[empty_range(1, "ab⋯e".len())]
 1405        );
 1406        editor.move_down(&MoveDown, window, cx);
 1407        assert_eq!(
 1408            editor.selections.display_ranges(cx),
 1409            &[empty_range(2, "αβ⋯ε".len())]
 1410        );
 1411        editor.move_up(&MoveUp, window, cx);
 1412        assert_eq!(
 1413            editor.selections.display_ranges(cx),
 1414            &[empty_range(1, "ab⋯e".len())]
 1415        );
 1416
 1417        editor.move_up(&MoveUp, window, cx);
 1418        assert_eq!(
 1419            editor.selections.display_ranges(cx),
 1420            &[empty_range(0, "🟥🟧".len())]
 1421        );
 1422        editor.move_left(&MoveLeft, window, cx);
 1423        assert_eq!(
 1424            editor.selections.display_ranges(cx),
 1425            &[empty_range(0, "🟥".len())]
 1426        );
 1427        editor.move_left(&MoveLeft, window, cx);
 1428        assert_eq!(
 1429            editor.selections.display_ranges(cx),
 1430            &[empty_range(0, "".len())]
 1431        );
 1432    });
 1433}
 1434
 1435#[gpui::test]
 1436fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1437    init_test(cx, |_| {});
 1438
 1439    let editor = cx.add_window(|window, cx| {
 1440        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1441        build_editor(buffer.clone(), window, cx)
 1442    });
 1443    _ = editor.update(cx, |editor, window, cx| {
 1444        editor.change_selections(None, window, cx, |s| {
 1445            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1446        });
 1447
 1448        // moving above start of document should move selection to start of document,
 1449        // but the next move down should still be at the original goal_x
 1450        editor.move_up(&MoveUp, window, cx);
 1451        assert_eq!(
 1452            editor.selections.display_ranges(cx),
 1453            &[empty_range(0, "".len())]
 1454        );
 1455
 1456        editor.move_down(&MoveDown, window, cx);
 1457        assert_eq!(
 1458            editor.selections.display_ranges(cx),
 1459            &[empty_range(1, "abcd".len())]
 1460        );
 1461
 1462        editor.move_down(&MoveDown, window, cx);
 1463        assert_eq!(
 1464            editor.selections.display_ranges(cx),
 1465            &[empty_range(2, "αβγ".len())]
 1466        );
 1467
 1468        editor.move_down(&MoveDown, window, cx);
 1469        assert_eq!(
 1470            editor.selections.display_ranges(cx),
 1471            &[empty_range(3, "abcd".len())]
 1472        );
 1473
 1474        editor.move_down(&MoveDown, window, cx);
 1475        assert_eq!(
 1476            editor.selections.display_ranges(cx),
 1477            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1478        );
 1479
 1480        // moving past end of document should not change goal_x
 1481        editor.move_down(&MoveDown, window, cx);
 1482        assert_eq!(
 1483            editor.selections.display_ranges(cx),
 1484            &[empty_range(5, "".len())]
 1485        );
 1486
 1487        editor.move_down(&MoveDown, window, cx);
 1488        assert_eq!(
 1489            editor.selections.display_ranges(cx),
 1490            &[empty_range(5, "".len())]
 1491        );
 1492
 1493        editor.move_up(&MoveUp, window, cx);
 1494        assert_eq!(
 1495            editor.selections.display_ranges(cx),
 1496            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1497        );
 1498
 1499        editor.move_up(&MoveUp, window, cx);
 1500        assert_eq!(
 1501            editor.selections.display_ranges(cx),
 1502            &[empty_range(3, "abcd".len())]
 1503        );
 1504
 1505        editor.move_up(&MoveUp, window, cx);
 1506        assert_eq!(
 1507            editor.selections.display_ranges(cx),
 1508            &[empty_range(2, "αβγ".len())]
 1509        );
 1510    });
 1511}
 1512
 1513#[gpui::test]
 1514fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1515    init_test(cx, |_| {});
 1516    let move_to_beg = MoveToBeginningOfLine {
 1517        stop_at_soft_wraps: true,
 1518        stop_at_indent: true,
 1519    };
 1520
 1521    let delete_to_beg = DeleteToBeginningOfLine {
 1522        stop_at_indent: false,
 1523    };
 1524
 1525    let move_to_end = MoveToEndOfLine {
 1526        stop_at_soft_wraps: true,
 1527    };
 1528
 1529    let editor = cx.add_window(|window, cx| {
 1530        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1531        build_editor(buffer, window, cx)
 1532    });
 1533    _ = editor.update(cx, |editor, window, cx| {
 1534        editor.change_selections(None, window, cx, |s| {
 1535            s.select_display_ranges([
 1536                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1537                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1538            ]);
 1539        });
 1540    });
 1541
 1542    _ = editor.update(cx, |editor, window, cx| {
 1543        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1544        assert_eq!(
 1545            editor.selections.display_ranges(cx),
 1546            &[
 1547                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1548                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1549            ]
 1550        );
 1551    });
 1552
 1553    _ = editor.update(cx, |editor, window, cx| {
 1554        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1555        assert_eq!(
 1556            editor.selections.display_ranges(cx),
 1557            &[
 1558                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1559                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1560            ]
 1561        );
 1562    });
 1563
 1564    _ = editor.update(cx, |editor, window, cx| {
 1565        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1566        assert_eq!(
 1567            editor.selections.display_ranges(cx),
 1568            &[
 1569                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1570                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1571            ]
 1572        );
 1573    });
 1574
 1575    _ = editor.update(cx, |editor, window, cx| {
 1576        editor.move_to_end_of_line(&move_to_end, window, cx);
 1577        assert_eq!(
 1578            editor.selections.display_ranges(cx),
 1579            &[
 1580                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1581                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1582            ]
 1583        );
 1584    });
 1585
 1586    // Moving to the end of line again is a no-op.
 1587    _ = editor.update(cx, |editor, window, cx| {
 1588        editor.move_to_end_of_line(&move_to_end, window, cx);
 1589        assert_eq!(
 1590            editor.selections.display_ranges(cx),
 1591            &[
 1592                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1593                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1594            ]
 1595        );
 1596    });
 1597
 1598    _ = editor.update(cx, |editor, window, cx| {
 1599        editor.move_left(&MoveLeft, window, cx);
 1600        editor.select_to_beginning_of_line(
 1601            &SelectToBeginningOfLine {
 1602                stop_at_soft_wraps: true,
 1603                stop_at_indent: true,
 1604            },
 1605            window,
 1606            cx,
 1607        );
 1608        assert_eq!(
 1609            editor.selections.display_ranges(cx),
 1610            &[
 1611                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1612                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1613            ]
 1614        );
 1615    });
 1616
 1617    _ = editor.update(cx, |editor, window, cx| {
 1618        editor.select_to_beginning_of_line(
 1619            &SelectToBeginningOfLine {
 1620                stop_at_soft_wraps: true,
 1621                stop_at_indent: true,
 1622            },
 1623            window,
 1624            cx,
 1625        );
 1626        assert_eq!(
 1627            editor.selections.display_ranges(cx),
 1628            &[
 1629                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1630                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1631            ]
 1632        );
 1633    });
 1634
 1635    _ = editor.update(cx, |editor, window, cx| {
 1636        editor.select_to_beginning_of_line(
 1637            &SelectToBeginningOfLine {
 1638                stop_at_soft_wraps: true,
 1639                stop_at_indent: true,
 1640            },
 1641            window,
 1642            cx,
 1643        );
 1644        assert_eq!(
 1645            editor.selections.display_ranges(cx),
 1646            &[
 1647                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1648                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1649            ]
 1650        );
 1651    });
 1652
 1653    _ = editor.update(cx, |editor, window, cx| {
 1654        editor.select_to_end_of_line(
 1655            &SelectToEndOfLine {
 1656                stop_at_soft_wraps: true,
 1657            },
 1658            window,
 1659            cx,
 1660        );
 1661        assert_eq!(
 1662            editor.selections.display_ranges(cx),
 1663            &[
 1664                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1665                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1666            ]
 1667        );
 1668    });
 1669
 1670    _ = editor.update(cx, |editor, window, cx| {
 1671        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1672        assert_eq!(editor.display_text(cx), "ab\n  de");
 1673        assert_eq!(
 1674            editor.selections.display_ranges(cx),
 1675            &[
 1676                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1677                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1678            ]
 1679        );
 1680    });
 1681
 1682    _ = editor.update(cx, |editor, window, cx| {
 1683        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1684        assert_eq!(editor.display_text(cx), "\n");
 1685        assert_eq!(
 1686            editor.selections.display_ranges(cx),
 1687            &[
 1688                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1689                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1690            ]
 1691        );
 1692    });
 1693}
 1694
 1695#[gpui::test]
 1696fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1697    init_test(cx, |_| {});
 1698    let move_to_beg = MoveToBeginningOfLine {
 1699        stop_at_soft_wraps: false,
 1700        stop_at_indent: false,
 1701    };
 1702
 1703    let move_to_end = MoveToEndOfLine {
 1704        stop_at_soft_wraps: false,
 1705    };
 1706
 1707    let editor = cx.add_window(|window, cx| {
 1708        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1709        build_editor(buffer, window, cx)
 1710    });
 1711
 1712    _ = editor.update(cx, |editor, window, cx| {
 1713        editor.set_wrap_width(Some(140.0.into()), cx);
 1714
 1715        // We expect the following lines after wrapping
 1716        // ```
 1717        // thequickbrownfox
 1718        // jumpedoverthelazydo
 1719        // gs
 1720        // ```
 1721        // The final `gs` was soft-wrapped onto a new line.
 1722        assert_eq!(
 1723            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1724            editor.display_text(cx),
 1725        );
 1726
 1727        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1728        // Start the cursor at the `k` on the first line
 1729        editor.change_selections(None, window, cx, |s| {
 1730            s.select_display_ranges([
 1731                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1732            ]);
 1733        });
 1734
 1735        // Moving to the beginning of the line should put us at the beginning of the line.
 1736        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1737        assert_eq!(
 1738            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1739            editor.selections.display_ranges(cx)
 1740        );
 1741
 1742        // Moving to the end of the line should put us at the end of the line.
 1743        editor.move_to_end_of_line(&move_to_end, window, cx);
 1744        assert_eq!(
 1745            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1746            editor.selections.display_ranges(cx)
 1747        );
 1748
 1749        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1750        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1751        editor.change_selections(None, window, cx, |s| {
 1752            s.select_display_ranges([
 1753                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1754            ]);
 1755        });
 1756
 1757        // Moving to the beginning of the line should put us at the start of the second line of
 1758        // display text, i.e., the `j`.
 1759        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1760        assert_eq!(
 1761            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1762            editor.selections.display_ranges(cx)
 1763        );
 1764
 1765        // Moving to the beginning of the line again should be a no-op.
 1766        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1767        assert_eq!(
 1768            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1769            editor.selections.display_ranges(cx)
 1770        );
 1771
 1772        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1773        // next display line.
 1774        editor.move_to_end_of_line(&move_to_end, window, cx);
 1775        assert_eq!(
 1776            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1777            editor.selections.display_ranges(cx)
 1778        );
 1779
 1780        // Moving to the end of the line again should be a no-op.
 1781        editor.move_to_end_of_line(&move_to_end, window, cx);
 1782        assert_eq!(
 1783            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1784            editor.selections.display_ranges(cx)
 1785        );
 1786    });
 1787}
 1788
 1789#[gpui::test]
 1790fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1791    init_test(cx, |_| {});
 1792
 1793    let move_to_beg = MoveToBeginningOfLine {
 1794        stop_at_soft_wraps: true,
 1795        stop_at_indent: true,
 1796    };
 1797
 1798    let select_to_beg = SelectToBeginningOfLine {
 1799        stop_at_soft_wraps: true,
 1800        stop_at_indent: true,
 1801    };
 1802
 1803    let delete_to_beg = DeleteToBeginningOfLine {
 1804        stop_at_indent: true,
 1805    };
 1806
 1807    let move_to_end = MoveToEndOfLine {
 1808        stop_at_soft_wraps: false,
 1809    };
 1810
 1811    let editor = cx.add_window(|window, cx| {
 1812        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1813        build_editor(buffer, window, cx)
 1814    });
 1815
 1816    _ = editor.update(cx, |editor, window, cx| {
 1817        editor.change_selections(None, window, cx, |s| {
 1818            s.select_display_ranges([
 1819                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1820                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1821            ]);
 1822        });
 1823
 1824        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1825        // and the second cursor at the first non-whitespace character in the line.
 1826        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1827        assert_eq!(
 1828            editor.selections.display_ranges(cx),
 1829            &[
 1830                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1831                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1832            ]
 1833        );
 1834
 1835        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1836        // and should move the second cursor to the beginning of the line.
 1837        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1838        assert_eq!(
 1839            editor.selections.display_ranges(cx),
 1840            &[
 1841                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1842                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1843            ]
 1844        );
 1845
 1846        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1847        // and should move the second cursor back to the first non-whitespace character in the line.
 1848        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1849        assert_eq!(
 1850            editor.selections.display_ranges(cx),
 1851            &[
 1852                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1853                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1854            ]
 1855        );
 1856
 1857        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1858        // and to the first non-whitespace character in the line for the second cursor.
 1859        editor.move_to_end_of_line(&move_to_end, window, cx);
 1860        editor.move_left(&MoveLeft, window, cx);
 1861        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1862        assert_eq!(
 1863            editor.selections.display_ranges(cx),
 1864            &[
 1865                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1866                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1867            ]
 1868        );
 1869
 1870        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1871        // and should select to the beginning of the line for the second cursor.
 1872        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1873        assert_eq!(
 1874            editor.selections.display_ranges(cx),
 1875            &[
 1876                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1877                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1878            ]
 1879        );
 1880
 1881        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1882        // and should delete to the first non-whitespace character in the line for the second cursor.
 1883        editor.move_to_end_of_line(&move_to_end, window, cx);
 1884        editor.move_left(&MoveLeft, window, cx);
 1885        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1886        assert_eq!(editor.text(cx), "c\n  f");
 1887    });
 1888}
 1889
 1890#[gpui::test]
 1891fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1892    init_test(cx, |_| {});
 1893
 1894    let editor = cx.add_window(|window, cx| {
 1895        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1896        build_editor(buffer, window, cx)
 1897    });
 1898    _ = editor.update(cx, |editor, window, cx| {
 1899        editor.change_selections(None, window, cx, |s| {
 1900            s.select_display_ranges([
 1901                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1902                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1903            ])
 1904        });
 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_previous_word_start(&MoveToPreviousWordStart, 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_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1929
 1930        editor.move_right(&MoveRight, window, cx);
 1931        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1932        assert_selection_ranges(
 1933            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1934            editor,
 1935            cx,
 1936        );
 1937
 1938        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1939        assert_selection_ranges(
 1940            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1941            editor,
 1942            cx,
 1943        );
 1944
 1945        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1946        assert_selection_ranges(
 1947            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1948            editor,
 1949            cx,
 1950        );
 1951    });
 1952}
 1953
 1954#[gpui::test]
 1955fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1956    init_test(cx, |_| {});
 1957
 1958    let editor = cx.add_window(|window, cx| {
 1959        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1960        build_editor(buffer, window, cx)
 1961    });
 1962
 1963    _ = editor.update(cx, |editor, window, cx| {
 1964        editor.set_wrap_width(Some(140.0.into()), cx);
 1965        assert_eq!(
 1966            editor.display_text(cx),
 1967            "use one::{\n    two::three::\n    four::five\n};"
 1968        );
 1969
 1970        editor.change_selections(None, window, cx, |s| {
 1971            s.select_display_ranges([
 1972                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1973            ]);
 1974        });
 1975
 1976        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1977        assert_eq!(
 1978            editor.selections.display_ranges(cx),
 1979            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1980        );
 1981
 1982        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1983        assert_eq!(
 1984            editor.selections.display_ranges(cx),
 1985            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1986        );
 1987
 1988        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1989        assert_eq!(
 1990            editor.selections.display_ranges(cx),
 1991            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1992        );
 1993
 1994        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1995        assert_eq!(
 1996            editor.selections.display_ranges(cx),
 1997            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1998        );
 1999
 2000        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2001        assert_eq!(
 2002            editor.selections.display_ranges(cx),
 2003            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2004        );
 2005
 2006        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2007        assert_eq!(
 2008            editor.selections.display_ranges(cx),
 2009            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2010        );
 2011    });
 2012}
 2013
 2014#[gpui::test]
 2015async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2016    init_test(cx, |_| {});
 2017    let mut cx = EditorTestContext::new(cx).await;
 2018
 2019    let line_height = cx.editor(|editor, window, _| {
 2020        editor
 2021            .style()
 2022            .unwrap()
 2023            .text
 2024            .line_height_in_pixels(window.rem_size())
 2025    });
 2026    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2027
 2028    cx.set_state(
 2029        &r#"ˇone
 2030        two
 2031
 2032        three
 2033        fourˇ
 2034        five
 2035
 2036        six"#
 2037            .unindent(),
 2038    );
 2039
 2040    cx.update_editor(|editor, window, cx| {
 2041        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2042    });
 2043    cx.assert_editor_state(
 2044        &r#"one
 2045        two
 2046        ˇ
 2047        three
 2048        four
 2049        five
 2050        ˇ
 2051        six"#
 2052            .unindent(),
 2053    );
 2054
 2055    cx.update_editor(|editor, window, cx| {
 2056        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2057    });
 2058    cx.assert_editor_state(
 2059        &r#"one
 2060        two
 2061
 2062        three
 2063        four
 2064        five
 2065        ˇ
 2066        sixˇ"#
 2067            .unindent(),
 2068    );
 2069
 2070    cx.update_editor(|editor, window, cx| {
 2071        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2072    });
 2073    cx.assert_editor_state(
 2074        &r#"one
 2075        two
 2076
 2077        three
 2078        four
 2079        five
 2080
 2081        sixˇ"#
 2082            .unindent(),
 2083    );
 2084
 2085    cx.update_editor(|editor, window, cx| {
 2086        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2087    });
 2088    cx.assert_editor_state(
 2089        &r#"one
 2090        two
 2091
 2092        three
 2093        four
 2094        five
 2095        ˇ
 2096        six"#
 2097            .unindent(),
 2098    );
 2099
 2100    cx.update_editor(|editor, window, cx| {
 2101        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2102    });
 2103    cx.assert_editor_state(
 2104        &r#"one
 2105        two
 2106        ˇ
 2107        three
 2108        four
 2109        five
 2110
 2111        six"#
 2112            .unindent(),
 2113    );
 2114
 2115    cx.update_editor(|editor, window, cx| {
 2116        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2117    });
 2118    cx.assert_editor_state(
 2119        &r#"ˇone
 2120        two
 2121
 2122        three
 2123        four
 2124        five
 2125
 2126        six"#
 2127            .unindent(),
 2128    );
 2129}
 2130
 2131#[gpui::test]
 2132async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2133    init_test(cx, |_| {});
 2134    let mut cx = EditorTestContext::new(cx).await;
 2135    let line_height = cx.editor(|editor, window, _| {
 2136        editor
 2137            .style()
 2138            .unwrap()
 2139            .text
 2140            .line_height_in_pixels(window.rem_size())
 2141    });
 2142    let window = cx.window;
 2143    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2144
 2145    cx.set_state(
 2146        r#"ˇone
 2147        two
 2148        three
 2149        four
 2150        five
 2151        six
 2152        seven
 2153        eight
 2154        nine
 2155        ten
 2156        "#,
 2157    );
 2158
 2159    cx.update_editor(|editor, window, cx| {
 2160        assert_eq!(
 2161            editor.snapshot(window, cx).scroll_position(),
 2162            gpui::Point::new(0., 0.)
 2163        );
 2164        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2165        assert_eq!(
 2166            editor.snapshot(window, cx).scroll_position(),
 2167            gpui::Point::new(0., 3.)
 2168        );
 2169        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2170        assert_eq!(
 2171            editor.snapshot(window, cx).scroll_position(),
 2172            gpui::Point::new(0., 6.)
 2173        );
 2174        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2175        assert_eq!(
 2176            editor.snapshot(window, cx).scroll_position(),
 2177            gpui::Point::new(0., 3.)
 2178        );
 2179
 2180        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2181        assert_eq!(
 2182            editor.snapshot(window, cx).scroll_position(),
 2183            gpui::Point::new(0., 1.)
 2184        );
 2185        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2186        assert_eq!(
 2187            editor.snapshot(window, cx).scroll_position(),
 2188            gpui::Point::new(0., 3.)
 2189        );
 2190    });
 2191}
 2192
 2193#[gpui::test]
 2194async fn test_autoscroll(cx: &mut TestAppContext) {
 2195    init_test(cx, |_| {});
 2196    let mut cx = EditorTestContext::new(cx).await;
 2197
 2198    let line_height = cx.update_editor(|editor, window, cx| {
 2199        editor.set_vertical_scroll_margin(2, cx);
 2200        editor
 2201            .style()
 2202            .unwrap()
 2203            .text
 2204            .line_height_in_pixels(window.rem_size())
 2205    });
 2206    let window = cx.window;
 2207    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2208
 2209    cx.set_state(
 2210        r#"ˇone
 2211            two
 2212            three
 2213            four
 2214            five
 2215            six
 2216            seven
 2217            eight
 2218            nine
 2219            ten
 2220        "#,
 2221    );
 2222    cx.update_editor(|editor, window, cx| {
 2223        assert_eq!(
 2224            editor.snapshot(window, cx).scroll_position(),
 2225            gpui::Point::new(0., 0.0)
 2226        );
 2227    });
 2228
 2229    // Add a cursor below the visible area. Since both cursors cannot fit
 2230    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2231    // allows the vertical scroll margin below that cursor.
 2232    cx.update_editor(|editor, window, cx| {
 2233        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2234            selections.select_ranges([
 2235                Point::new(0, 0)..Point::new(0, 0),
 2236                Point::new(6, 0)..Point::new(6, 0),
 2237            ]);
 2238        })
 2239    });
 2240    cx.update_editor(|editor, window, cx| {
 2241        assert_eq!(
 2242            editor.snapshot(window, cx).scroll_position(),
 2243            gpui::Point::new(0., 3.0)
 2244        );
 2245    });
 2246
 2247    // Move down. The editor cursor scrolls down to track the newest cursor.
 2248    cx.update_editor(|editor, window, cx| {
 2249        editor.move_down(&Default::default(), window, cx);
 2250    });
 2251    cx.update_editor(|editor, window, cx| {
 2252        assert_eq!(
 2253            editor.snapshot(window, cx).scroll_position(),
 2254            gpui::Point::new(0., 4.0)
 2255        );
 2256    });
 2257
 2258    // Add a cursor above the visible area. Since both cursors fit on screen,
 2259    // the editor scrolls to show both.
 2260    cx.update_editor(|editor, window, cx| {
 2261        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2262            selections.select_ranges([
 2263                Point::new(1, 0)..Point::new(1, 0),
 2264                Point::new(6, 0)..Point::new(6, 0),
 2265            ]);
 2266        })
 2267    });
 2268    cx.update_editor(|editor, window, cx| {
 2269        assert_eq!(
 2270            editor.snapshot(window, cx).scroll_position(),
 2271            gpui::Point::new(0., 1.0)
 2272        );
 2273    });
 2274}
 2275
 2276#[gpui::test]
 2277async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2278    init_test(cx, |_| {});
 2279    let mut cx = EditorTestContext::new(cx).await;
 2280
 2281    let line_height = cx.editor(|editor, window, _cx| {
 2282        editor
 2283            .style()
 2284            .unwrap()
 2285            .text
 2286            .line_height_in_pixels(window.rem_size())
 2287    });
 2288    let window = cx.window;
 2289    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2290    cx.set_state(
 2291        &r#"
 2292        ˇone
 2293        two
 2294        threeˇ
 2295        four
 2296        five
 2297        six
 2298        seven
 2299        eight
 2300        nine
 2301        ten
 2302        "#
 2303        .unindent(),
 2304    );
 2305
 2306    cx.update_editor(|editor, window, cx| {
 2307        editor.move_page_down(&MovePageDown::default(), window, cx)
 2308    });
 2309    cx.assert_editor_state(
 2310        &r#"
 2311        one
 2312        two
 2313        three
 2314        ˇfour
 2315        five
 2316        sixˇ
 2317        seven
 2318        eight
 2319        nine
 2320        ten
 2321        "#
 2322        .unindent(),
 2323    );
 2324
 2325    cx.update_editor(|editor, window, cx| {
 2326        editor.move_page_down(&MovePageDown::default(), window, cx)
 2327    });
 2328    cx.assert_editor_state(
 2329        &r#"
 2330        one
 2331        two
 2332        three
 2333        four
 2334        five
 2335        six
 2336        ˇseven
 2337        eight
 2338        nineˇ
 2339        ten
 2340        "#
 2341        .unindent(),
 2342    );
 2343
 2344    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2345    cx.assert_editor_state(
 2346        &r#"
 2347        one
 2348        two
 2349        three
 2350        ˇfour
 2351        five
 2352        sixˇ
 2353        seven
 2354        eight
 2355        nine
 2356        ten
 2357        "#
 2358        .unindent(),
 2359    );
 2360
 2361    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2362    cx.assert_editor_state(
 2363        &r#"
 2364        ˇone
 2365        two
 2366        threeˇ
 2367        four
 2368        five
 2369        six
 2370        seven
 2371        eight
 2372        nine
 2373        ten
 2374        "#
 2375        .unindent(),
 2376    );
 2377
 2378    // Test select collapsing
 2379    cx.update_editor(|editor, window, cx| {
 2380        editor.move_page_down(&MovePageDown::default(), window, cx);
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383    });
 2384    cx.assert_editor_state(
 2385        &r#"
 2386        one
 2387        two
 2388        three
 2389        four
 2390        five
 2391        six
 2392        seven
 2393        eight
 2394        nine
 2395        ˇten
 2396        ˇ"#
 2397        .unindent(),
 2398    );
 2399}
 2400
 2401#[gpui::test]
 2402async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2403    init_test(cx, |_| {});
 2404    let mut cx = EditorTestContext::new(cx).await;
 2405    cx.set_state("one «two threeˇ» four");
 2406    cx.update_editor(|editor, window, cx| {
 2407        editor.delete_to_beginning_of_line(
 2408            &DeleteToBeginningOfLine {
 2409                stop_at_indent: false,
 2410            },
 2411            window,
 2412            cx,
 2413        );
 2414        assert_eq!(editor.text(cx), " four");
 2415    });
 2416}
 2417
 2418#[gpui::test]
 2419fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2420    init_test(cx, |_| {});
 2421
 2422    let editor = cx.add_window(|window, cx| {
 2423        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2424        build_editor(buffer.clone(), window, cx)
 2425    });
 2426
 2427    _ = editor.update(cx, |editor, window, cx| {
 2428        editor.change_selections(None, window, cx, |s| {
 2429            s.select_display_ranges([
 2430                // an empty selection - the preceding word fragment is deleted
 2431                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2432                // characters selected - they are deleted
 2433                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2434            ])
 2435        });
 2436        editor.delete_to_previous_word_start(
 2437            &DeleteToPreviousWordStart {
 2438                ignore_newlines: false,
 2439            },
 2440            window,
 2441            cx,
 2442        );
 2443        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2444    });
 2445
 2446    _ = editor.update(cx, |editor, window, cx| {
 2447        editor.change_selections(None, window, cx, |s| {
 2448            s.select_display_ranges([
 2449                // an empty selection - the following word fragment is deleted
 2450                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2451                // characters selected - they are deleted
 2452                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2453            ])
 2454        });
 2455        editor.delete_to_next_word_end(
 2456            &DeleteToNextWordEnd {
 2457                ignore_newlines: false,
 2458            },
 2459            window,
 2460            cx,
 2461        );
 2462        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2463    });
 2464}
 2465
 2466#[gpui::test]
 2467fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2468    init_test(cx, |_| {});
 2469
 2470    let editor = cx.add_window(|window, cx| {
 2471        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2472        build_editor(buffer.clone(), window, cx)
 2473    });
 2474    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2475        ignore_newlines: false,
 2476    };
 2477    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2478        ignore_newlines: true,
 2479    };
 2480
 2481    _ = editor.update(cx, |editor, window, cx| {
 2482        editor.change_selections(None, window, cx, |s| {
 2483            s.select_display_ranges([
 2484                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2485            ])
 2486        });
 2487        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2488        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2489        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2490        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2491        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2492        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2493        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2494        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2495        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2496        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2497        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2498        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2499    });
 2500}
 2501
 2502#[gpui::test]
 2503fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2504    init_test(cx, |_| {});
 2505
 2506    let editor = cx.add_window(|window, cx| {
 2507        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2508        build_editor(buffer.clone(), window, cx)
 2509    });
 2510    let del_to_next_word_end = DeleteToNextWordEnd {
 2511        ignore_newlines: false,
 2512    };
 2513    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2514        ignore_newlines: true,
 2515    };
 2516
 2517    _ = editor.update(cx, |editor, window, cx| {
 2518        editor.change_selections(None, window, cx, |s| {
 2519            s.select_display_ranges([
 2520                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2521            ])
 2522        });
 2523        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2524        assert_eq!(
 2525            editor.buffer.read(cx).read(cx).text(),
 2526            "one\n   two\nthree\n   four"
 2527        );
 2528        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2529        assert_eq!(
 2530            editor.buffer.read(cx).read(cx).text(),
 2531            "\n   two\nthree\n   four"
 2532        );
 2533        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2534        assert_eq!(
 2535            editor.buffer.read(cx).read(cx).text(),
 2536            "two\nthree\n   four"
 2537        );
 2538        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2539        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2540        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2541        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2542        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2543        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2544    });
 2545}
 2546
 2547#[gpui::test]
 2548fn test_newline(cx: &mut TestAppContext) {
 2549    init_test(cx, |_| {});
 2550
 2551    let editor = cx.add_window(|window, cx| {
 2552        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2553        build_editor(buffer.clone(), window, cx)
 2554    });
 2555
 2556    _ = editor.update(cx, |editor, window, cx| {
 2557        editor.change_selections(None, window, cx, |s| {
 2558            s.select_display_ranges([
 2559                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2560                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2561                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2562            ])
 2563        });
 2564
 2565        editor.newline(&Newline, window, cx);
 2566        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2567    });
 2568}
 2569
 2570#[gpui::test]
 2571fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2572    init_test(cx, |_| {});
 2573
 2574    let editor = cx.add_window(|window, cx| {
 2575        let buffer = MultiBuffer::build_simple(
 2576            "
 2577                a
 2578                b(
 2579                    X
 2580                )
 2581                c(
 2582                    X
 2583                )
 2584            "
 2585            .unindent()
 2586            .as_str(),
 2587            cx,
 2588        );
 2589        let mut editor = build_editor(buffer.clone(), window, cx);
 2590        editor.change_selections(None, window, cx, |s| {
 2591            s.select_ranges([
 2592                Point::new(2, 4)..Point::new(2, 5),
 2593                Point::new(5, 4)..Point::new(5, 5),
 2594            ])
 2595        });
 2596        editor
 2597    });
 2598
 2599    _ = editor.update(cx, |editor, window, cx| {
 2600        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2601        editor.buffer.update(cx, |buffer, cx| {
 2602            buffer.edit(
 2603                [
 2604                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2605                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2606                ],
 2607                None,
 2608                cx,
 2609            );
 2610            assert_eq!(
 2611                buffer.read(cx).text(),
 2612                "
 2613                    a
 2614                    b()
 2615                    c()
 2616                "
 2617                .unindent()
 2618            );
 2619        });
 2620        assert_eq!(
 2621            editor.selections.ranges(cx),
 2622            &[
 2623                Point::new(1, 2)..Point::new(1, 2),
 2624                Point::new(2, 2)..Point::new(2, 2),
 2625            ],
 2626        );
 2627
 2628        editor.newline(&Newline, window, cx);
 2629        assert_eq!(
 2630            editor.text(cx),
 2631            "
 2632                a
 2633                b(
 2634                )
 2635                c(
 2636                )
 2637            "
 2638            .unindent()
 2639        );
 2640
 2641        // The selections are moved after the inserted newlines
 2642        assert_eq!(
 2643            editor.selections.ranges(cx),
 2644            &[
 2645                Point::new(2, 0)..Point::new(2, 0),
 2646                Point::new(4, 0)..Point::new(4, 0),
 2647            ],
 2648        );
 2649    });
 2650}
 2651
 2652#[gpui::test]
 2653async fn test_newline_above(cx: &mut TestAppContext) {
 2654    init_test(cx, |settings| {
 2655        settings.defaults.tab_size = NonZeroU32::new(4)
 2656    });
 2657
 2658    let language = Arc::new(
 2659        Language::new(
 2660            LanguageConfig::default(),
 2661            Some(tree_sitter_rust::LANGUAGE.into()),
 2662        )
 2663        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2664        .unwrap(),
 2665    );
 2666
 2667    let mut cx = EditorTestContext::new(cx).await;
 2668    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2669    cx.set_state(indoc! {"
 2670        const a: ˇA = (
 2671 2672                «const_functionˇ»(ˇ),
 2673                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2674 2675        ˇ);ˇ
 2676    "});
 2677
 2678    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2679    cx.assert_editor_state(indoc! {"
 2680        ˇ
 2681        const a: A = (
 2682            ˇ
 2683            (
 2684                ˇ
 2685                ˇ
 2686                const_function(),
 2687                ˇ
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                something_else,
 2692                ˇ
 2693            )
 2694            ˇ
 2695            ˇ
 2696        );
 2697    "});
 2698}
 2699
 2700#[gpui::test]
 2701async fn test_newline_below(cx: &mut TestAppContext) {
 2702    init_test(cx, |settings| {
 2703        settings.defaults.tab_size = NonZeroU32::new(4)
 2704    });
 2705
 2706    let language = Arc::new(
 2707        Language::new(
 2708            LanguageConfig::default(),
 2709            Some(tree_sitter_rust::LANGUAGE.into()),
 2710        )
 2711        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2712        .unwrap(),
 2713    );
 2714
 2715    let mut cx = EditorTestContext::new(cx).await;
 2716    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2717    cx.set_state(indoc! {"
 2718        const a: ˇA = (
 2719 2720                «const_functionˇ»(ˇ),
 2721                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2722 2723        ˇ);ˇ
 2724    "});
 2725
 2726    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2727    cx.assert_editor_state(indoc! {"
 2728        const a: A = (
 2729            ˇ
 2730            (
 2731                ˇ
 2732                const_function(),
 2733                ˇ
 2734                ˇ
 2735                something_else,
 2736                ˇ
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740            )
 2741            ˇ
 2742        );
 2743        ˇ
 2744        ˇ
 2745    "});
 2746}
 2747
 2748#[gpui::test]
 2749async fn test_newline_comments(cx: &mut TestAppContext) {
 2750    init_test(cx, |settings| {
 2751        settings.defaults.tab_size = NonZeroU32::new(4)
 2752    });
 2753
 2754    let language = Arc::new(Language::new(
 2755        LanguageConfig {
 2756            line_comments: vec!["//".into()],
 2757            ..LanguageConfig::default()
 2758        },
 2759        None,
 2760    ));
 2761    {
 2762        let mut cx = EditorTestContext::new(cx).await;
 2763        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2764        cx.set_state(indoc! {"
 2765        // Fooˇ
 2766    "});
 2767
 2768        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2769        cx.assert_editor_state(indoc! {"
 2770        // Foo
 2771        //ˇ
 2772    "});
 2773        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2774        cx.set_state(indoc! {"
 2775        ˇ// Foo
 2776    "});
 2777        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2778        cx.assert_editor_state(indoc! {"
 2779
 2780        ˇ// Foo
 2781    "});
 2782    }
 2783    // Ensure that comment continuations can be disabled.
 2784    update_test_language_settings(cx, |settings| {
 2785        settings.defaults.extend_comment_on_newline = Some(false);
 2786    });
 2787    let mut cx = EditorTestContext::new(cx).await;
 2788    cx.set_state(indoc! {"
 2789        // Fooˇ
 2790    "});
 2791    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2792    cx.assert_editor_state(indoc! {"
 2793        // Foo
 2794        ˇ
 2795    "});
 2796}
 2797
 2798#[gpui::test]
 2799fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2800    init_test(cx, |_| {});
 2801
 2802    let editor = cx.add_window(|window, cx| {
 2803        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2804        let mut editor = build_editor(buffer.clone(), window, cx);
 2805        editor.change_selections(None, window, cx, |s| {
 2806            s.select_ranges([3..4, 11..12, 19..20])
 2807        });
 2808        editor
 2809    });
 2810
 2811    _ = editor.update(cx, |editor, window, cx| {
 2812        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2813        editor.buffer.update(cx, |buffer, cx| {
 2814            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2815            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2816        });
 2817        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2818
 2819        editor.insert("Z", window, cx);
 2820        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2821
 2822        // The selections are moved after the inserted characters
 2823        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2824    });
 2825}
 2826
 2827#[gpui::test]
 2828async fn test_tab(cx: &mut TestAppContext) {
 2829    init_test(cx, |settings| {
 2830        settings.defaults.tab_size = NonZeroU32::new(3)
 2831    });
 2832
 2833    let mut cx = EditorTestContext::new(cx).await;
 2834    cx.set_state(indoc! {"
 2835        ˇabˇc
 2836        ˇ🏀ˇ🏀ˇefg
 2837 2838    "});
 2839    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2840    cx.assert_editor_state(indoc! {"
 2841           ˇab ˇc
 2842           ˇ🏀  ˇ🏀  ˇefg
 2843        d  ˇ
 2844    "});
 2845
 2846    cx.set_state(indoc! {"
 2847        a
 2848        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2849    "});
 2850    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2851    cx.assert_editor_state(indoc! {"
 2852        a
 2853           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2854    "});
 2855}
 2856
 2857#[gpui::test]
 2858async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2859    init_test(cx, |_| {});
 2860
 2861    let mut cx = EditorTestContext::new(cx).await;
 2862    let language = Arc::new(
 2863        Language::new(
 2864            LanguageConfig::default(),
 2865            Some(tree_sitter_rust::LANGUAGE.into()),
 2866        )
 2867        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2868        .unwrap(),
 2869    );
 2870    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2871
 2872    // cursors that are already at the suggested indent level insert
 2873    // a soft tab. cursors that are to the left of the suggested indent
 2874    // auto-indent their line.
 2875    cx.set_state(indoc! {"
 2876        ˇ
 2877        const a: B = (
 2878            c(
 2879                d(
 2880        ˇ
 2881                )
 2882        ˇ
 2883        ˇ    )
 2884        );
 2885    "});
 2886    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2887    cx.assert_editor_state(indoc! {"
 2888            ˇ
 2889        const a: B = (
 2890            c(
 2891                d(
 2892                    ˇ
 2893                )
 2894                ˇ
 2895            ˇ)
 2896        );
 2897    "});
 2898
 2899    // handle auto-indent when there are multiple cursors on the same line
 2900    cx.set_state(indoc! {"
 2901        const a: B = (
 2902            c(
 2903        ˇ    ˇ
 2904        ˇ    )
 2905        );
 2906    "});
 2907    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2908    cx.assert_editor_state(indoc! {"
 2909        const a: B = (
 2910            c(
 2911                ˇ
 2912            ˇ)
 2913        );
 2914    "});
 2915}
 2916
 2917#[gpui::test]
 2918async fn test_tab_with_mixed_whitespace(cx: &mut TestAppContext) {
 2919    init_test(cx, |settings| {
 2920        settings.defaults.tab_size = NonZeroU32::new(4)
 2921    });
 2922
 2923    let language = Arc::new(
 2924        Language::new(
 2925            LanguageConfig::default(),
 2926            Some(tree_sitter_rust::LANGUAGE.into()),
 2927        )
 2928        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2929        .unwrap(),
 2930    );
 2931
 2932    let mut cx = EditorTestContext::new(cx).await;
 2933    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2934    cx.set_state(indoc! {"
 2935        fn a() {
 2936            if b {
 2937        \t ˇc
 2938            }
 2939        }
 2940    "});
 2941
 2942    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2943    cx.assert_editor_state(indoc! {"
 2944        fn a() {
 2945            if b {
 2946                ˇc
 2947            }
 2948        }
 2949    "});
 2950}
 2951
 2952#[gpui::test]
 2953async fn test_indent_outdent(cx: &mut TestAppContext) {
 2954    init_test(cx, |settings| {
 2955        settings.defaults.tab_size = NonZeroU32::new(4);
 2956    });
 2957
 2958    let mut cx = EditorTestContext::new(cx).await;
 2959
 2960    cx.set_state(indoc! {"
 2961          «oneˇ» «twoˇ»
 2962        three
 2963         four
 2964    "});
 2965    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2966    cx.assert_editor_state(indoc! {"
 2967            «oneˇ» «twoˇ»
 2968        three
 2969         four
 2970    "});
 2971
 2972    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2973    cx.assert_editor_state(indoc! {"
 2974        «oneˇ» «twoˇ»
 2975        three
 2976         four
 2977    "});
 2978
 2979    // select across line ending
 2980    cx.set_state(indoc! {"
 2981        one two
 2982        t«hree
 2983        ˇ» four
 2984    "});
 2985    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2986    cx.assert_editor_state(indoc! {"
 2987        one two
 2988            t«hree
 2989        ˇ» four
 2990    "});
 2991
 2992    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2993    cx.assert_editor_state(indoc! {"
 2994        one two
 2995        t«hree
 2996        ˇ» four
 2997    "});
 2998
 2999    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3000    cx.set_state(indoc! {"
 3001        one two
 3002        ˇthree
 3003            four
 3004    "});
 3005    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3006    cx.assert_editor_state(indoc! {"
 3007        one two
 3008            ˇthree
 3009            four
 3010    "});
 3011
 3012    cx.set_state(indoc! {"
 3013        one two
 3014        ˇ    three
 3015            four
 3016    "});
 3017    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3018    cx.assert_editor_state(indoc! {"
 3019        one two
 3020        ˇthree
 3021            four
 3022    "});
 3023}
 3024
 3025#[gpui::test]
 3026async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3027    init_test(cx, |settings| {
 3028        settings.defaults.hard_tabs = Some(true);
 3029    });
 3030
 3031    let mut cx = EditorTestContext::new(cx).await;
 3032
 3033    // select two ranges on one line
 3034    cx.set_state(indoc! {"
 3035        «oneˇ» «twoˇ»
 3036        three
 3037        four
 3038    "});
 3039    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3040    cx.assert_editor_state(indoc! {"
 3041        \t«oneˇ» «twoˇ»
 3042        three
 3043        four
 3044    "});
 3045    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3046    cx.assert_editor_state(indoc! {"
 3047        \t\t«oneˇ» «twoˇ»
 3048        three
 3049        four
 3050    "});
 3051    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3052    cx.assert_editor_state(indoc! {"
 3053        \t«oneˇ» «twoˇ»
 3054        three
 3055        four
 3056    "});
 3057    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3058    cx.assert_editor_state(indoc! {"
 3059        «oneˇ» «twoˇ»
 3060        three
 3061        four
 3062    "});
 3063
 3064    // select across a line ending
 3065    cx.set_state(indoc! {"
 3066        one two
 3067        t«hree
 3068        ˇ»four
 3069    "});
 3070    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3071    cx.assert_editor_state(indoc! {"
 3072        one two
 3073        \tt«hree
 3074        ˇ»four
 3075    "});
 3076    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3077    cx.assert_editor_state(indoc! {"
 3078        one two
 3079        \t\tt«hree
 3080        ˇ»four
 3081    "});
 3082    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3083    cx.assert_editor_state(indoc! {"
 3084        one two
 3085        \tt«hree
 3086        ˇ»four
 3087    "});
 3088    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3089    cx.assert_editor_state(indoc! {"
 3090        one two
 3091        t«hree
 3092        ˇ»four
 3093    "});
 3094
 3095    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3096    cx.set_state(indoc! {"
 3097        one two
 3098        ˇthree
 3099        four
 3100    "});
 3101    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3102    cx.assert_editor_state(indoc! {"
 3103        one two
 3104        ˇthree
 3105        four
 3106    "});
 3107    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3108    cx.assert_editor_state(indoc! {"
 3109        one two
 3110        \tˇthree
 3111        four
 3112    "});
 3113    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3114    cx.assert_editor_state(indoc! {"
 3115        one two
 3116        ˇthree
 3117        four
 3118    "});
 3119}
 3120
 3121#[gpui::test]
 3122fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3123    init_test(cx, |settings| {
 3124        settings.languages.extend([
 3125            (
 3126                "TOML".into(),
 3127                LanguageSettingsContent {
 3128                    tab_size: NonZeroU32::new(2),
 3129                    ..Default::default()
 3130                },
 3131            ),
 3132            (
 3133                "Rust".into(),
 3134                LanguageSettingsContent {
 3135                    tab_size: NonZeroU32::new(4),
 3136                    ..Default::default()
 3137                },
 3138            ),
 3139        ]);
 3140    });
 3141
 3142    let toml_language = Arc::new(Language::new(
 3143        LanguageConfig {
 3144            name: "TOML".into(),
 3145            ..Default::default()
 3146        },
 3147        None,
 3148    ));
 3149    let rust_language = Arc::new(Language::new(
 3150        LanguageConfig {
 3151            name: "Rust".into(),
 3152            ..Default::default()
 3153        },
 3154        None,
 3155    ));
 3156
 3157    let toml_buffer =
 3158        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3159    let rust_buffer =
 3160        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3161    let multibuffer = cx.new(|cx| {
 3162        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3163        multibuffer.push_excerpts(
 3164            toml_buffer.clone(),
 3165            [ExcerptRange {
 3166                context: Point::new(0, 0)..Point::new(2, 0),
 3167                primary: None,
 3168            }],
 3169            cx,
 3170        );
 3171        multibuffer.push_excerpts(
 3172            rust_buffer.clone(),
 3173            [ExcerptRange {
 3174                context: Point::new(0, 0)..Point::new(1, 0),
 3175                primary: None,
 3176            }],
 3177            cx,
 3178        );
 3179        multibuffer
 3180    });
 3181
 3182    cx.add_window(|window, cx| {
 3183        let mut editor = build_editor(multibuffer, window, cx);
 3184
 3185        assert_eq!(
 3186            editor.text(cx),
 3187            indoc! {"
 3188                a = 1
 3189                b = 2
 3190
 3191                const c: usize = 3;
 3192            "}
 3193        );
 3194
 3195        select_ranges(
 3196            &mut editor,
 3197            indoc! {"
 3198                «aˇ» = 1
 3199                b = 2
 3200
 3201                «const c:ˇ» usize = 3;
 3202            "},
 3203            window,
 3204            cx,
 3205        );
 3206
 3207        editor.tab(&Tab, window, cx);
 3208        assert_text_with_selections(
 3209            &mut editor,
 3210            indoc! {"
 3211                  «aˇ» = 1
 3212                b = 2
 3213
 3214                    «const c:ˇ» usize = 3;
 3215            "},
 3216            cx,
 3217        );
 3218        editor.backtab(&Backtab, window, cx);
 3219        assert_text_with_selections(
 3220            &mut editor,
 3221            indoc! {"
 3222                «aˇ» = 1
 3223                b = 2
 3224
 3225                «const c:ˇ» usize = 3;
 3226            "},
 3227            cx,
 3228        );
 3229
 3230        editor
 3231    });
 3232}
 3233
 3234#[gpui::test]
 3235async fn test_backspace(cx: &mut TestAppContext) {
 3236    init_test(cx, |_| {});
 3237
 3238    let mut cx = EditorTestContext::new(cx).await;
 3239
 3240    // Basic backspace
 3241    cx.set_state(indoc! {"
 3242        onˇe two three
 3243        fou«rˇ» five six
 3244        seven «ˇeight nine
 3245        »ten
 3246    "});
 3247    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3248    cx.assert_editor_state(indoc! {"
 3249        oˇe two three
 3250        fouˇ five six
 3251        seven ˇten
 3252    "});
 3253
 3254    // Test backspace inside and around indents
 3255    cx.set_state(indoc! {"
 3256        zero
 3257            ˇone
 3258                ˇtwo
 3259            ˇ ˇ ˇ  three
 3260        ˇ  ˇ  four
 3261    "});
 3262    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3263    cx.assert_editor_state(indoc! {"
 3264        zero
 3265        ˇone
 3266            ˇtwo
 3267        ˇ  threeˇ  four
 3268    "});
 3269
 3270    // Test backspace with line_mode set to true
 3271    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3272    cx.set_state(indoc! {"
 3273        The ˇquick ˇbrown
 3274        fox jumps over
 3275        the lazy dog
 3276        ˇThe qu«ick bˇ»rown"});
 3277    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3278    cx.assert_editor_state(indoc! {"
 3279        ˇfox jumps over
 3280        the lazy dogˇ"});
 3281}
 3282
 3283#[gpui::test]
 3284async fn test_delete(cx: &mut TestAppContext) {
 3285    init_test(cx, |_| {});
 3286
 3287    let mut cx = EditorTestContext::new(cx).await;
 3288    cx.set_state(indoc! {"
 3289        onˇe two three
 3290        fou«rˇ» five six
 3291        seven «ˇeight nine
 3292        »ten
 3293    "});
 3294    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3295    cx.assert_editor_state(indoc! {"
 3296        onˇ two three
 3297        fouˇ five six
 3298        seven ˇten
 3299    "});
 3300
 3301    // Test backspace with line_mode set to true
 3302    cx.update_editor(|e, _, _| e.selections.line_mode = true);
 3303    cx.set_state(indoc! {"
 3304        The ˇquick ˇbrown
 3305        fox «ˇjum»ps over
 3306        the lazy dog
 3307        ˇThe qu«ick bˇ»rown"});
 3308    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3309    cx.assert_editor_state("ˇthe lazy dogˇ");
 3310}
 3311
 3312#[gpui::test]
 3313fn test_delete_line(cx: &mut TestAppContext) {
 3314    init_test(cx, |_| {});
 3315
 3316    let editor = cx.add_window(|window, cx| {
 3317        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3318        build_editor(buffer, window, cx)
 3319    });
 3320    _ = editor.update(cx, |editor, window, cx| {
 3321        editor.change_selections(None, window, cx, |s| {
 3322            s.select_display_ranges([
 3323                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3324                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3325                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3326            ])
 3327        });
 3328        editor.delete_line(&DeleteLine, window, cx);
 3329        assert_eq!(editor.display_text(cx), "ghi");
 3330        assert_eq!(
 3331            editor.selections.display_ranges(cx),
 3332            vec![
 3333                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3334                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3335            ]
 3336        );
 3337    });
 3338
 3339    let editor = cx.add_window(|window, cx| {
 3340        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3341        build_editor(buffer, window, cx)
 3342    });
 3343    _ = editor.update(cx, |editor, window, cx| {
 3344        editor.change_selections(None, window, cx, |s| {
 3345            s.select_display_ranges([
 3346                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3347            ])
 3348        });
 3349        editor.delete_line(&DeleteLine, window, cx);
 3350        assert_eq!(editor.display_text(cx), "ghi\n");
 3351        assert_eq!(
 3352            editor.selections.display_ranges(cx),
 3353            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3354        );
 3355    });
 3356}
 3357
 3358#[gpui::test]
 3359fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3360    init_test(cx, |_| {});
 3361
 3362    cx.add_window(|window, cx| {
 3363        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3364        let mut editor = build_editor(buffer.clone(), window, cx);
 3365        let buffer = buffer.read(cx).as_singleton().unwrap();
 3366
 3367        assert_eq!(
 3368            editor.selections.ranges::<Point>(cx),
 3369            &[Point::new(0, 0)..Point::new(0, 0)]
 3370        );
 3371
 3372        // When on single line, replace newline at end by space
 3373        editor.join_lines(&JoinLines, window, cx);
 3374        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3375        assert_eq!(
 3376            editor.selections.ranges::<Point>(cx),
 3377            &[Point::new(0, 3)..Point::new(0, 3)]
 3378        );
 3379
 3380        // When multiple lines are selected, remove newlines that are spanned by the selection
 3381        editor.change_selections(None, window, cx, |s| {
 3382            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3383        });
 3384        editor.join_lines(&JoinLines, window, cx);
 3385        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3386        assert_eq!(
 3387            editor.selections.ranges::<Point>(cx),
 3388            &[Point::new(0, 11)..Point::new(0, 11)]
 3389        );
 3390
 3391        // Undo should be transactional
 3392        editor.undo(&Undo, window, cx);
 3393        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3394        assert_eq!(
 3395            editor.selections.ranges::<Point>(cx),
 3396            &[Point::new(0, 5)..Point::new(2, 2)]
 3397        );
 3398
 3399        // When joining an empty line don't insert a space
 3400        editor.change_selections(None, window, cx, |s| {
 3401            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3402        });
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            [Point::new(2, 3)..Point::new(2, 3)]
 3408        );
 3409
 3410        // We can remove trailing newlines
 3411        editor.join_lines(&JoinLines, window, cx);
 3412        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3413        assert_eq!(
 3414            editor.selections.ranges::<Point>(cx),
 3415            [Point::new(2, 3)..Point::new(2, 3)]
 3416        );
 3417
 3418        // We don't blow up on the last line
 3419        editor.join_lines(&JoinLines, window, cx);
 3420        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3421        assert_eq!(
 3422            editor.selections.ranges::<Point>(cx),
 3423            [Point::new(2, 3)..Point::new(2, 3)]
 3424        );
 3425
 3426        // reset to test indentation
 3427        editor.buffer.update(cx, |buffer, cx| {
 3428            buffer.edit(
 3429                [
 3430                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3431                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3432                ],
 3433                None,
 3434                cx,
 3435            )
 3436        });
 3437
 3438        // We remove any leading spaces
 3439        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3440        editor.change_selections(None, window, cx, |s| {
 3441            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3442        });
 3443        editor.join_lines(&JoinLines, window, cx);
 3444        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3445
 3446        // We don't insert a space for a line containing only spaces
 3447        editor.join_lines(&JoinLines, window, cx);
 3448        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3449
 3450        // We ignore any leading tabs
 3451        editor.join_lines(&JoinLines, window, cx);
 3452        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3453
 3454        editor
 3455    });
 3456}
 3457
 3458#[gpui::test]
 3459fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3460    init_test(cx, |_| {});
 3461
 3462    cx.add_window(|window, cx| {
 3463        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3464        let mut editor = build_editor(buffer.clone(), window, cx);
 3465        let buffer = buffer.read(cx).as_singleton().unwrap();
 3466
 3467        editor.change_selections(None, window, cx, |s| {
 3468            s.select_ranges([
 3469                Point::new(0, 2)..Point::new(1, 1),
 3470                Point::new(1, 2)..Point::new(1, 2),
 3471                Point::new(3, 1)..Point::new(3, 2),
 3472            ])
 3473        });
 3474
 3475        editor.join_lines(&JoinLines, window, cx);
 3476        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3477
 3478        assert_eq!(
 3479            editor.selections.ranges::<Point>(cx),
 3480            [
 3481                Point::new(0, 7)..Point::new(0, 7),
 3482                Point::new(1, 3)..Point::new(1, 3)
 3483            ]
 3484        );
 3485        editor
 3486    });
 3487}
 3488
 3489#[gpui::test]
 3490async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3491    init_test(cx, |_| {});
 3492
 3493    let mut cx = EditorTestContext::new(cx).await;
 3494
 3495    let diff_base = r#"
 3496        Line 0
 3497        Line 1
 3498        Line 2
 3499        Line 3
 3500        "#
 3501    .unindent();
 3502
 3503    cx.set_state(
 3504        &r#"
 3505        ˇLine 0
 3506        Line 1
 3507        Line 2
 3508        Line 3
 3509        "#
 3510        .unindent(),
 3511    );
 3512
 3513    cx.set_head_text(&diff_base);
 3514    executor.run_until_parked();
 3515
 3516    // Join lines
 3517    cx.update_editor(|editor, window, cx| {
 3518        editor.join_lines(&JoinLines, window, cx);
 3519    });
 3520    executor.run_until_parked();
 3521
 3522    cx.assert_editor_state(
 3523        &r#"
 3524        Line 0ˇ Line 1
 3525        Line 2
 3526        Line 3
 3527        "#
 3528        .unindent(),
 3529    );
 3530    // Join again
 3531    cx.update_editor(|editor, window, cx| {
 3532        editor.join_lines(&JoinLines, window, cx);
 3533    });
 3534    executor.run_until_parked();
 3535
 3536    cx.assert_editor_state(
 3537        &r#"
 3538        Line 0 Line 1ˇ Line 2
 3539        Line 3
 3540        "#
 3541        .unindent(),
 3542    );
 3543}
 3544
 3545#[gpui::test]
 3546async fn test_custom_newlines_cause_no_false_positive_diffs(
 3547    executor: BackgroundExecutor,
 3548    cx: &mut TestAppContext,
 3549) {
 3550    init_test(cx, |_| {});
 3551    let mut cx = EditorTestContext::new(cx).await;
 3552    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3553    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3554    executor.run_until_parked();
 3555
 3556    cx.update_editor(|editor, window, cx| {
 3557        let snapshot = editor.snapshot(window, cx);
 3558        assert_eq!(
 3559            snapshot
 3560                .buffer_snapshot
 3561                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3562                .collect::<Vec<_>>(),
 3563            Vec::new(),
 3564            "Should not have any diffs for files with custom newlines"
 3565        );
 3566    });
 3567}
 3568
 3569#[gpui::test]
 3570async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let mut cx = EditorTestContext::new(cx).await;
 3574
 3575    // Test sort_lines_case_insensitive()
 3576    cx.set_state(indoc! {"
 3577        «z
 3578        y
 3579        x
 3580        Z
 3581        Y
 3582        Xˇ»
 3583    "});
 3584    cx.update_editor(|e, window, cx| {
 3585        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3586    });
 3587    cx.assert_editor_state(indoc! {"
 3588        «x
 3589        X
 3590        y
 3591        Y
 3592        z
 3593        Zˇ»
 3594    "});
 3595
 3596    // Test reverse_lines()
 3597    cx.set_state(indoc! {"
 3598        «5
 3599        4
 3600        3
 3601        2
 3602        1ˇ»
 3603    "});
 3604    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3605    cx.assert_editor_state(indoc! {"
 3606        «1
 3607        2
 3608        3
 3609        4
 3610        5ˇ»
 3611    "});
 3612
 3613    // Skip testing shuffle_line()
 3614
 3615    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3616    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3617
 3618    // Don't manipulate when cursor is on single line, but expand the selection
 3619    cx.set_state(indoc! {"
 3620        ddˇdd
 3621        ccc
 3622        bb
 3623        a
 3624    "});
 3625    cx.update_editor(|e, window, cx| {
 3626        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3627    });
 3628    cx.assert_editor_state(indoc! {"
 3629        «ddddˇ»
 3630        ccc
 3631        bb
 3632        a
 3633    "});
 3634
 3635    // Basic manipulate case
 3636    // Start selection moves to column 0
 3637    // End of selection shrinks to fit shorter line
 3638    cx.set_state(indoc! {"
 3639        dd«d
 3640        ccc
 3641        bb
 3642        aaaaaˇ»
 3643    "});
 3644    cx.update_editor(|e, window, cx| {
 3645        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3646    });
 3647    cx.assert_editor_state(indoc! {"
 3648        «aaaaa
 3649        bb
 3650        ccc
 3651        dddˇ»
 3652    "});
 3653
 3654    // Manipulate case with newlines
 3655    cx.set_state(indoc! {"
 3656        dd«d
 3657        ccc
 3658
 3659        bb
 3660        aaaaa
 3661
 3662        ˇ»
 3663    "});
 3664    cx.update_editor(|e, window, cx| {
 3665        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3666    });
 3667    cx.assert_editor_state(indoc! {"
 3668        «
 3669
 3670        aaaaa
 3671        bb
 3672        ccc
 3673        dddˇ»
 3674
 3675    "});
 3676
 3677    // Adding new line
 3678    cx.set_state(indoc! {"
 3679        aa«a
 3680        bbˇ»b
 3681    "});
 3682    cx.update_editor(|e, window, cx| {
 3683        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3684    });
 3685    cx.assert_editor_state(indoc! {"
 3686        «aaa
 3687        bbb
 3688        added_lineˇ»
 3689    "});
 3690
 3691    // Removing line
 3692    cx.set_state(indoc! {"
 3693        aa«a
 3694        bbbˇ»
 3695    "});
 3696    cx.update_editor(|e, window, cx| {
 3697        e.manipulate_lines(window, cx, |lines| {
 3698            lines.pop();
 3699        })
 3700    });
 3701    cx.assert_editor_state(indoc! {"
 3702        «aaaˇ»
 3703    "});
 3704
 3705    // Removing all lines
 3706    cx.set_state(indoc! {"
 3707        aa«a
 3708        bbbˇ»
 3709    "});
 3710    cx.update_editor(|e, window, cx| {
 3711        e.manipulate_lines(window, cx, |lines| {
 3712            lines.drain(..);
 3713        })
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        ˇ
 3717    "});
 3718}
 3719
 3720#[gpui::test]
 3721async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3722    init_test(cx, |_| {});
 3723
 3724    let mut cx = EditorTestContext::new(cx).await;
 3725
 3726    // Consider continuous selection as single selection
 3727    cx.set_state(indoc! {"
 3728        Aaa«aa
 3729        cˇ»c«c
 3730        bb
 3731        aaaˇ»aa
 3732    "});
 3733    cx.update_editor(|e, window, cx| {
 3734        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3735    });
 3736    cx.assert_editor_state(indoc! {"
 3737        «Aaaaa
 3738        ccc
 3739        bb
 3740        aaaaaˇ»
 3741    "});
 3742
 3743    cx.set_state(indoc! {"
 3744        Aaa«aa
 3745        cˇ»c«c
 3746        bb
 3747        aaaˇ»aa
 3748    "});
 3749    cx.update_editor(|e, window, cx| {
 3750        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3751    });
 3752    cx.assert_editor_state(indoc! {"
 3753        «Aaaaa
 3754        ccc
 3755        bbˇ»
 3756    "});
 3757
 3758    // Consider non continuous selection as distinct dedup operations
 3759    cx.set_state(indoc! {"
 3760        «aaaaa
 3761        bb
 3762        aaaaa
 3763        aaaaaˇ»
 3764
 3765        aaa«aaˇ»
 3766    "});
 3767    cx.update_editor(|e, window, cx| {
 3768        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3769    });
 3770    cx.assert_editor_state(indoc! {"
 3771        «aaaaa
 3772        bbˇ»
 3773
 3774        «aaaaaˇ»
 3775    "});
 3776}
 3777
 3778#[gpui::test]
 3779async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3780    init_test(cx, |_| {});
 3781
 3782    let mut cx = EditorTestContext::new(cx).await;
 3783
 3784    cx.set_state(indoc! {"
 3785        «Aaa
 3786        aAa
 3787        Aaaˇ»
 3788    "});
 3789    cx.update_editor(|e, window, cx| {
 3790        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3791    });
 3792    cx.assert_editor_state(indoc! {"
 3793        «Aaa
 3794        aAaˇ»
 3795    "});
 3796
 3797    cx.set_state(indoc! {"
 3798        «Aaa
 3799        aAa
 3800        aaAˇ»
 3801    "});
 3802    cx.update_editor(|e, window, cx| {
 3803        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3804    });
 3805    cx.assert_editor_state(indoc! {"
 3806        «Aaaˇ»
 3807    "});
 3808}
 3809
 3810#[gpui::test]
 3811async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3812    init_test(cx, |_| {});
 3813
 3814    let mut cx = EditorTestContext::new(cx).await;
 3815
 3816    // Manipulate with multiple selections on a single line
 3817    cx.set_state(indoc! {"
 3818        dd«dd
 3819        cˇ»c«c
 3820        bb
 3821        aaaˇ»aa
 3822    "});
 3823    cx.update_editor(|e, window, cx| {
 3824        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3825    });
 3826    cx.assert_editor_state(indoc! {"
 3827        «aaaaa
 3828        bb
 3829        ccc
 3830        ddddˇ»
 3831    "});
 3832
 3833    // Manipulate with multiple disjoin selections
 3834    cx.set_state(indoc! {"
 3835 3836        4
 3837        3
 3838        2
 3839        1ˇ»
 3840
 3841        dd«dd
 3842        ccc
 3843        bb
 3844        aaaˇ»aa
 3845    "});
 3846    cx.update_editor(|e, window, cx| {
 3847        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3848    });
 3849    cx.assert_editor_state(indoc! {"
 3850        «1
 3851        2
 3852        3
 3853        4
 3854        5ˇ»
 3855
 3856        «aaaaa
 3857        bb
 3858        ccc
 3859        ddddˇ»
 3860    "});
 3861
 3862    // Adding lines on each selection
 3863    cx.set_state(indoc! {"
 3864 3865        1ˇ»
 3866
 3867        bb«bb
 3868        aaaˇ»aa
 3869    "});
 3870    cx.update_editor(|e, window, cx| {
 3871        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3872    });
 3873    cx.assert_editor_state(indoc! {"
 3874        «2
 3875        1
 3876        added lineˇ»
 3877
 3878        «bbbb
 3879        aaaaa
 3880        added lineˇ»
 3881    "});
 3882
 3883    // Removing lines on each selection
 3884    cx.set_state(indoc! {"
 3885 3886        1ˇ»
 3887
 3888        bb«bb
 3889        aaaˇ»aa
 3890    "});
 3891    cx.update_editor(|e, window, cx| {
 3892        e.manipulate_lines(window, cx, |lines| {
 3893            lines.pop();
 3894        })
 3895    });
 3896    cx.assert_editor_state(indoc! {"
 3897        «2ˇ»
 3898
 3899        «bbbbˇ»
 3900    "});
 3901}
 3902
 3903#[gpui::test]
 3904async fn test_manipulate_text(cx: &mut TestAppContext) {
 3905    init_test(cx, |_| {});
 3906
 3907    let mut cx = EditorTestContext::new(cx).await;
 3908
 3909    // Test convert_to_upper_case()
 3910    cx.set_state(indoc! {"
 3911        «hello worldˇ»
 3912    "});
 3913    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3914    cx.assert_editor_state(indoc! {"
 3915        «HELLO WORLDˇ»
 3916    "});
 3917
 3918    // Test convert_to_lower_case()
 3919    cx.set_state(indoc! {"
 3920        «HELLO WORLDˇ»
 3921    "});
 3922    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3923    cx.assert_editor_state(indoc! {"
 3924        «hello worldˇ»
 3925    "});
 3926
 3927    // Test multiple line, single selection case
 3928    cx.set_state(indoc! {"
 3929        «The quick brown
 3930        fox jumps over
 3931        the lazy dogˇ»
 3932    "});
 3933    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3934    cx.assert_editor_state(indoc! {"
 3935        «The Quick Brown
 3936        Fox Jumps Over
 3937        The Lazy Dogˇ»
 3938    "});
 3939
 3940    // Test multiple line, single selection case
 3941    cx.set_state(indoc! {"
 3942        «The quick brown
 3943        fox jumps over
 3944        the lazy dogˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| {
 3947        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3948    });
 3949    cx.assert_editor_state(indoc! {"
 3950        «TheQuickBrown
 3951        FoxJumpsOver
 3952        TheLazyDogˇ»
 3953    "});
 3954
 3955    // From here on out, test more complex cases of manipulate_text()
 3956
 3957    // Test no selection case - should affect words cursors are in
 3958    // Cursor at beginning, middle, and end of word
 3959    cx.set_state(indoc! {"
 3960        ˇhello big beauˇtiful worldˇ
 3961    "});
 3962    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3963    cx.assert_editor_state(indoc! {"
 3964        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3965    "});
 3966
 3967    // Test multiple selections on a single line and across multiple lines
 3968    cx.set_state(indoc! {"
 3969        «Theˇ» quick «brown
 3970        foxˇ» jumps «overˇ»
 3971        the «lazyˇ» dog
 3972    "});
 3973    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3974    cx.assert_editor_state(indoc! {"
 3975        «THEˇ» quick «BROWN
 3976        FOXˇ» jumps «OVERˇ»
 3977        the «LAZYˇ» dog
 3978    "});
 3979
 3980    // Test case where text length grows
 3981    cx.set_state(indoc! {"
 3982        «tschüߡ»
 3983    "});
 3984    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3985    cx.assert_editor_state(indoc! {"
 3986        «TSCHÜSSˇ»
 3987    "});
 3988
 3989    // Test to make sure we don't crash when text shrinks
 3990    cx.set_state(indoc! {"
 3991        aaa_bbbˇ
 3992    "});
 3993    cx.update_editor(|e, window, cx| {
 3994        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3995    });
 3996    cx.assert_editor_state(indoc! {"
 3997        «aaaBbbˇ»
 3998    "});
 3999
 4000    // Test to make sure we all aware of the fact that each word can grow and shrink
 4001    // Final selections should be aware of this fact
 4002    cx.set_state(indoc! {"
 4003        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4004    "});
 4005    cx.update_editor(|e, window, cx| {
 4006        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4007    });
 4008    cx.assert_editor_state(indoc! {"
 4009        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4010    "});
 4011
 4012    cx.set_state(indoc! {"
 4013        «hElLo, WoRld!ˇ»
 4014    "});
 4015    cx.update_editor(|e, window, cx| {
 4016        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4017    });
 4018    cx.assert_editor_state(indoc! {"
 4019        «HeLlO, wOrLD!ˇ»
 4020    "});
 4021}
 4022
 4023#[gpui::test]
 4024fn test_duplicate_line(cx: &mut TestAppContext) {
 4025    init_test(cx, |_| {});
 4026
 4027    let editor = cx.add_window(|window, cx| {
 4028        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4029        build_editor(buffer, window, cx)
 4030    });
 4031    _ = editor.update(cx, |editor, window, cx| {
 4032        editor.change_selections(None, window, cx, |s| {
 4033            s.select_display_ranges([
 4034                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4035                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4036                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4037                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4038            ])
 4039        });
 4040        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4041        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4042        assert_eq!(
 4043            editor.selections.display_ranges(cx),
 4044            vec![
 4045                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4046                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4047                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4048                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4049            ]
 4050        );
 4051    });
 4052
 4053    let editor = cx.add_window(|window, cx| {
 4054        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4055        build_editor(buffer, window, cx)
 4056    });
 4057    _ = editor.update(cx, |editor, window, cx| {
 4058        editor.change_selections(None, window, cx, |s| {
 4059            s.select_display_ranges([
 4060                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4061                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4062            ])
 4063        });
 4064        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4065        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4066        assert_eq!(
 4067            editor.selections.display_ranges(cx),
 4068            vec![
 4069                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4070                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4071            ]
 4072        );
 4073    });
 4074
 4075    // With `move_upwards` the selections stay in place, except for
 4076    // the lines inserted above them
 4077    let editor = cx.add_window(|window, cx| {
 4078        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4079        build_editor(buffer, window, cx)
 4080    });
 4081    _ = editor.update(cx, |editor, window, cx| {
 4082        editor.change_selections(None, window, cx, |s| {
 4083            s.select_display_ranges([
 4084                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4085                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4086                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4087                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4088            ])
 4089        });
 4090        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4091        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4092        assert_eq!(
 4093            editor.selections.display_ranges(cx),
 4094            vec![
 4095                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4096                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4097                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4098                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4099            ]
 4100        );
 4101    });
 4102
 4103    let editor = cx.add_window(|window, cx| {
 4104        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4105        build_editor(buffer, window, cx)
 4106    });
 4107    _ = editor.update(cx, |editor, window, cx| {
 4108        editor.change_selections(None, window, cx, |s| {
 4109            s.select_display_ranges([
 4110                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4111                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4112            ])
 4113        });
 4114        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4115        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4116        assert_eq!(
 4117            editor.selections.display_ranges(cx),
 4118            vec![
 4119                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4120                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4121            ]
 4122        );
 4123    });
 4124
 4125    let editor = cx.add_window(|window, cx| {
 4126        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4127        build_editor(buffer, window, cx)
 4128    });
 4129    _ = editor.update(cx, |editor, window, cx| {
 4130        editor.change_selections(None, window, cx, |s| {
 4131            s.select_display_ranges([
 4132                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4133                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4134            ])
 4135        });
 4136        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4137        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4138        assert_eq!(
 4139            editor.selections.display_ranges(cx),
 4140            vec![
 4141                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4142                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4143            ]
 4144        );
 4145    });
 4146}
 4147
 4148#[gpui::test]
 4149fn test_move_line_up_down(cx: &mut TestAppContext) {
 4150    init_test(cx, |_| {});
 4151
 4152    let editor = cx.add_window(|window, cx| {
 4153        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4154        build_editor(buffer, window, cx)
 4155    });
 4156    _ = editor.update(cx, |editor, window, cx| {
 4157        editor.fold_creases(
 4158            vec![
 4159                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4160                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4161                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4162            ],
 4163            true,
 4164            window,
 4165            cx,
 4166        );
 4167        editor.change_selections(None, window, cx, |s| {
 4168            s.select_display_ranges([
 4169                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4170                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4171                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4172                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4173            ])
 4174        });
 4175        assert_eq!(
 4176            editor.display_text(cx),
 4177            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4178        );
 4179
 4180        editor.move_line_up(&MoveLineUp, window, cx);
 4181        assert_eq!(
 4182            editor.display_text(cx),
 4183            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4184        );
 4185        assert_eq!(
 4186            editor.selections.display_ranges(cx),
 4187            vec![
 4188                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4189                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4190                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4191                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4192            ]
 4193        );
 4194    });
 4195
 4196    _ = editor.update(cx, |editor, window, cx| {
 4197        editor.move_line_down(&MoveLineDown, window, cx);
 4198        assert_eq!(
 4199            editor.display_text(cx),
 4200            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4201        );
 4202        assert_eq!(
 4203            editor.selections.display_ranges(cx),
 4204            vec![
 4205                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4206                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4207                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4208                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4209            ]
 4210        );
 4211    });
 4212
 4213    _ = editor.update(cx, |editor, window, cx| {
 4214        editor.move_line_down(&MoveLineDown, window, cx);
 4215        assert_eq!(
 4216            editor.display_text(cx),
 4217            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4218        );
 4219        assert_eq!(
 4220            editor.selections.display_ranges(cx),
 4221            vec![
 4222                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4223                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4224                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4225                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4226            ]
 4227        );
 4228    });
 4229
 4230    _ = editor.update(cx, |editor, window, cx| {
 4231        editor.move_line_up(&MoveLineUp, window, cx);
 4232        assert_eq!(
 4233            editor.display_text(cx),
 4234            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4235        );
 4236        assert_eq!(
 4237            editor.selections.display_ranges(cx),
 4238            vec![
 4239                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4240                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4241                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4242                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4243            ]
 4244        );
 4245    });
 4246}
 4247
 4248#[gpui::test]
 4249fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4250    init_test(cx, |_| {});
 4251
 4252    let editor = cx.add_window(|window, cx| {
 4253        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4254        build_editor(buffer, window, cx)
 4255    });
 4256    _ = editor.update(cx, |editor, window, cx| {
 4257        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4258        editor.insert_blocks(
 4259            [BlockProperties {
 4260                style: BlockStyle::Fixed,
 4261                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4262                height: 1,
 4263                render: Arc::new(|_| div().into_any()),
 4264                priority: 0,
 4265            }],
 4266            Some(Autoscroll::fit()),
 4267            cx,
 4268        );
 4269        editor.change_selections(None, window, cx, |s| {
 4270            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4271        });
 4272        editor.move_line_down(&MoveLineDown, window, cx);
 4273    });
 4274}
 4275
 4276#[gpui::test]
 4277async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4278    init_test(cx, |_| {});
 4279
 4280    let mut cx = EditorTestContext::new(cx).await;
 4281    cx.set_state(
 4282        &"
 4283            ˇzero
 4284            one
 4285            two
 4286            three
 4287            four
 4288            five
 4289        "
 4290        .unindent(),
 4291    );
 4292
 4293    // Create a four-line block that replaces three lines of text.
 4294    cx.update_editor(|editor, window, cx| {
 4295        let snapshot = editor.snapshot(window, cx);
 4296        let snapshot = &snapshot.buffer_snapshot;
 4297        let placement = BlockPlacement::Replace(
 4298            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4299        );
 4300        editor.insert_blocks(
 4301            [BlockProperties {
 4302                placement,
 4303                height: 4,
 4304                style: BlockStyle::Sticky,
 4305                render: Arc::new(|_| gpui::div().into_any_element()),
 4306                priority: 0,
 4307            }],
 4308            None,
 4309            cx,
 4310        );
 4311    });
 4312
 4313    // Move down so that the cursor touches the block.
 4314    cx.update_editor(|editor, window, cx| {
 4315        editor.move_down(&Default::default(), window, cx);
 4316    });
 4317    cx.assert_editor_state(
 4318        &"
 4319            zero
 4320            «one
 4321            two
 4322            threeˇ»
 4323            four
 4324            five
 4325        "
 4326        .unindent(),
 4327    );
 4328
 4329    // Move down past the block.
 4330    cx.update_editor(|editor, window, cx| {
 4331        editor.move_down(&Default::default(), window, cx);
 4332    });
 4333    cx.assert_editor_state(
 4334        &"
 4335            zero
 4336            one
 4337            two
 4338            three
 4339            ˇfour
 4340            five
 4341        "
 4342        .unindent(),
 4343    );
 4344}
 4345
 4346#[gpui::test]
 4347fn test_transpose(cx: &mut TestAppContext) {
 4348    init_test(cx, |_| {});
 4349
 4350    _ = cx.add_window(|window, cx| {
 4351        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4352        editor.set_style(EditorStyle::default(), window, cx);
 4353        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4354        editor.transpose(&Default::default(), window, cx);
 4355        assert_eq!(editor.text(cx), "bac");
 4356        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4357
 4358        editor.transpose(&Default::default(), window, cx);
 4359        assert_eq!(editor.text(cx), "bca");
 4360        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4361
 4362        editor.transpose(&Default::default(), window, cx);
 4363        assert_eq!(editor.text(cx), "bac");
 4364        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4365
 4366        editor
 4367    });
 4368
 4369    _ = cx.add_window(|window, cx| {
 4370        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4371        editor.set_style(EditorStyle::default(), window, cx);
 4372        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4373        editor.transpose(&Default::default(), window, cx);
 4374        assert_eq!(editor.text(cx), "acb\nde");
 4375        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4376
 4377        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4378        editor.transpose(&Default::default(), window, cx);
 4379        assert_eq!(editor.text(cx), "acbd\ne");
 4380        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4381
 4382        editor.transpose(&Default::default(), window, cx);
 4383        assert_eq!(editor.text(cx), "acbde\n");
 4384        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4385
 4386        editor.transpose(&Default::default(), window, cx);
 4387        assert_eq!(editor.text(cx), "acbd\ne");
 4388        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4389
 4390        editor
 4391    });
 4392
 4393    _ = cx.add_window(|window, cx| {
 4394        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4395        editor.set_style(EditorStyle::default(), window, cx);
 4396        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4397        editor.transpose(&Default::default(), window, cx);
 4398        assert_eq!(editor.text(cx), "bacd\ne");
 4399        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4400
 4401        editor.transpose(&Default::default(), window, cx);
 4402        assert_eq!(editor.text(cx), "bcade\n");
 4403        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4404
 4405        editor.transpose(&Default::default(), window, cx);
 4406        assert_eq!(editor.text(cx), "bcda\ne");
 4407        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4408
 4409        editor.transpose(&Default::default(), window, cx);
 4410        assert_eq!(editor.text(cx), "bcade\n");
 4411        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4412
 4413        editor.transpose(&Default::default(), window, cx);
 4414        assert_eq!(editor.text(cx), "bcaed\n");
 4415        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4416
 4417        editor
 4418    });
 4419
 4420    _ = cx.add_window(|window, cx| {
 4421        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4422        editor.set_style(EditorStyle::default(), window, cx);
 4423        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4424        editor.transpose(&Default::default(), window, cx);
 4425        assert_eq!(editor.text(cx), "🏀🍐✋");
 4426        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4427
 4428        editor.transpose(&Default::default(), window, cx);
 4429        assert_eq!(editor.text(cx), "🏀✋🍐");
 4430        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4431
 4432        editor.transpose(&Default::default(), window, cx);
 4433        assert_eq!(editor.text(cx), "🏀🍐✋");
 4434        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4435
 4436        editor
 4437    });
 4438}
 4439
 4440#[gpui::test]
 4441async fn test_rewrap(cx: &mut TestAppContext) {
 4442    init_test(cx, |settings| {
 4443        settings.languages.extend([
 4444            (
 4445                "Markdown".into(),
 4446                LanguageSettingsContent {
 4447                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4448                    ..Default::default()
 4449                },
 4450            ),
 4451            (
 4452                "Plain Text".into(),
 4453                LanguageSettingsContent {
 4454                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4455                    ..Default::default()
 4456                },
 4457            ),
 4458        ])
 4459    });
 4460
 4461    let mut cx = EditorTestContext::new(cx).await;
 4462
 4463    let language_with_c_comments = Arc::new(Language::new(
 4464        LanguageConfig {
 4465            line_comments: vec!["// ".into()],
 4466            ..LanguageConfig::default()
 4467        },
 4468        None,
 4469    ));
 4470    let language_with_pound_comments = Arc::new(Language::new(
 4471        LanguageConfig {
 4472            line_comments: vec!["# ".into()],
 4473            ..LanguageConfig::default()
 4474        },
 4475        None,
 4476    ));
 4477    let markdown_language = Arc::new(Language::new(
 4478        LanguageConfig {
 4479            name: "Markdown".into(),
 4480            ..LanguageConfig::default()
 4481        },
 4482        None,
 4483    ));
 4484    let language_with_doc_comments = Arc::new(Language::new(
 4485        LanguageConfig {
 4486            line_comments: vec!["// ".into(), "/// ".into()],
 4487            ..LanguageConfig::default()
 4488        },
 4489        Some(tree_sitter_rust::LANGUAGE.into()),
 4490    ));
 4491
 4492    let plaintext_language = Arc::new(Language::new(
 4493        LanguageConfig {
 4494            name: "Plain Text".into(),
 4495            ..LanguageConfig::default()
 4496        },
 4497        None,
 4498    ));
 4499
 4500    assert_rewrap(
 4501        indoc! {"
 4502            // ˇ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.
 4503        "},
 4504        indoc! {"
 4505            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4506            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4507            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4508            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4509            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4510            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4511            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4512            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4513            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4514            // porttitor id. Aliquam id accumsan eros.
 4515        "},
 4516        language_with_c_comments.clone(),
 4517        &mut cx,
 4518    );
 4519
 4520    // Test that rewrapping works inside of a selection
 4521    assert_rewrap(
 4522        indoc! {"
 4523            «// 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.ˇ»
 4524        "},
 4525        indoc! {"
 4526            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4527            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4528            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4529            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4530            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4531            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4532            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4533            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4534            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4535            // porttitor id. Aliquam id accumsan eros.ˇ»
 4536        "},
 4537        language_with_c_comments.clone(),
 4538        &mut cx,
 4539    );
 4540
 4541    // Test that cursors that expand to the same region are collapsed.
 4542    assert_rewrap(
 4543        indoc! {"
 4544            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4545            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4546            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4547            // ˇ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.
 4548        "},
 4549        indoc! {"
 4550            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4551            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4552            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4553            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4554            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4555            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4556            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4557            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4558            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4559            // porttitor id. Aliquam id accumsan eros.
 4560        "},
 4561        language_with_c_comments.clone(),
 4562        &mut cx,
 4563    );
 4564
 4565    // Test that non-contiguous selections are treated separately.
 4566    assert_rewrap(
 4567        indoc! {"
 4568            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4569            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4570            //
 4571            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4572            // ˇ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.
 4573        "},
 4574        indoc! {"
 4575            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4576            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4577            // auctor, eu lacinia sapien scelerisque.
 4578            //
 4579            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4580            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4581            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4582            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4583            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4584            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4585            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4586        "},
 4587        language_with_c_comments.clone(),
 4588        &mut cx,
 4589    );
 4590
 4591    // Test that different comment prefixes are supported.
 4592    assert_rewrap(
 4593        indoc! {"
 4594            # ˇ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.
 4595        "},
 4596        indoc! {"
 4597            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4598            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4599            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4600            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4601            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4602            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4603            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4604            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4605            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4606            # accumsan eros.
 4607        "},
 4608        language_with_pound_comments.clone(),
 4609        &mut cx,
 4610    );
 4611
 4612    // Test that rewrapping is ignored outside of comments in most languages.
 4613    assert_rewrap(
 4614        indoc! {"
 4615            /// Adds two numbers.
 4616            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4617            fn add(a: u32, b: u32) -> u32 {
 4618                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ˇ
 4619            }
 4620        "},
 4621        indoc! {"
 4622            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4623            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4624            fn add(a: u32, b: u32) -> u32 {
 4625                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ˇ
 4626            }
 4627        "},
 4628        language_with_doc_comments.clone(),
 4629        &mut cx,
 4630    );
 4631
 4632    // Test that rewrapping works in Markdown and Plain Text languages.
 4633    assert_rewrap(
 4634        indoc! {"
 4635            # Hello
 4636
 4637            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.
 4638        "},
 4639        indoc! {"
 4640            # Hello
 4641
 4642            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4643            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4644            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4645            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4646            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4647            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4648            Integer sit amet scelerisque nisi.
 4649        "},
 4650        markdown_language,
 4651        &mut cx,
 4652    );
 4653
 4654    assert_rewrap(
 4655        indoc! {"
 4656            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.
 4657        "},
 4658        indoc! {"
 4659            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4660            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4661            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4662            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4663            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4664            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4665            Integer sit amet scelerisque nisi.
 4666        "},
 4667        plaintext_language,
 4668        &mut cx,
 4669    );
 4670
 4671    // Test rewrapping unaligned comments in a selection.
 4672    assert_rewrap(
 4673        indoc! {"
 4674            fn foo() {
 4675                if true {
 4676            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4677            // Praesent semper egestas tellus id dignissim.ˇ»
 4678                    do_something();
 4679                } else {
 4680                    //
 4681                }
 4682            }
 4683        "},
 4684        indoc! {"
 4685            fn foo() {
 4686                if true {
 4687            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4688                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4689                    // egestas tellus id dignissim.ˇ»
 4690                    do_something();
 4691                } else {
 4692                    //
 4693                }
 4694            }
 4695        "},
 4696        language_with_doc_comments.clone(),
 4697        &mut cx,
 4698    );
 4699
 4700    assert_rewrap(
 4701        indoc! {"
 4702            fn foo() {
 4703                if true {
 4704            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4705            // Praesent semper egestas tellus id dignissim.»
 4706                    do_something();
 4707                } else {
 4708                    //
 4709                }
 4710
 4711            }
 4712        "},
 4713        indoc! {"
 4714            fn foo() {
 4715                if true {
 4716            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4717                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4718                    // egestas tellus id dignissim.»
 4719                    do_something();
 4720                } else {
 4721                    //
 4722                }
 4723
 4724            }
 4725        "},
 4726        language_with_doc_comments.clone(),
 4727        &mut cx,
 4728    );
 4729
 4730    #[track_caller]
 4731    fn assert_rewrap(
 4732        unwrapped_text: &str,
 4733        wrapped_text: &str,
 4734        language: Arc<Language>,
 4735        cx: &mut EditorTestContext,
 4736    ) {
 4737        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4738        cx.set_state(unwrapped_text);
 4739        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4740        cx.assert_editor_state(wrapped_text);
 4741    }
 4742}
 4743
 4744#[gpui::test]
 4745async fn test_hard_wrap(cx: &mut TestAppContext) {
 4746    init_test(cx, |_| {});
 4747    let mut cx = EditorTestContext::new(cx).await;
 4748
 4749    cx.update_editor(|editor, _, cx| {
 4750        editor.set_hard_wrap(Some(14), cx);
 4751    });
 4752
 4753    cx.set_state(indoc!(
 4754        "
 4755        one two three ˇ
 4756        "
 4757    ));
 4758    cx.simulate_input("four");
 4759    cx.run_until_parked();
 4760
 4761    cx.assert_editor_state(indoc!(
 4762        "
 4763        one two three
 4764        fourˇ
 4765        "
 4766    ));
 4767}
 4768
 4769#[gpui::test]
 4770async fn test_clipboard(cx: &mut TestAppContext) {
 4771    init_test(cx, |_| {});
 4772
 4773    let mut cx = EditorTestContext::new(cx).await;
 4774
 4775    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4776    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4777    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4778
 4779    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4780    cx.set_state("two ˇfour ˇsix ˇ");
 4781    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4782    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4783
 4784    // Paste again but with only two cursors. Since the number of cursors doesn't
 4785    // match the number of slices in the clipboard, the entire clipboard text
 4786    // is pasted at each cursor.
 4787    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4788    cx.update_editor(|e, window, cx| {
 4789        e.handle_input("( ", window, cx);
 4790        e.paste(&Paste, window, cx);
 4791        e.handle_input(") ", window, cx);
 4792    });
 4793    cx.assert_editor_state(
 4794        &([
 4795            "( one✅ ",
 4796            "three ",
 4797            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4798            "three ",
 4799            "five ) ˇ",
 4800        ]
 4801        .join("\n")),
 4802    );
 4803
 4804    // Cut with three selections, one of which is full-line.
 4805    cx.set_state(indoc! {"
 4806        1«2ˇ»3
 4807        4ˇ567
 4808        «8ˇ»9"});
 4809    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4810    cx.assert_editor_state(indoc! {"
 4811        1ˇ3
 4812        ˇ9"});
 4813
 4814    // Paste with three selections, noticing how the copied selection that was full-line
 4815    // gets inserted before the second cursor.
 4816    cx.set_state(indoc! {"
 4817        1ˇ3
 4818 4819        «oˇ»ne"});
 4820    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4821    cx.assert_editor_state(indoc! {"
 4822        12ˇ3
 4823        4567
 4824 4825        8ˇne"});
 4826
 4827    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4828    cx.set_state(indoc! {"
 4829        The quick brown
 4830        fox juˇmps over
 4831        the lazy dog"});
 4832    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4833    assert_eq!(
 4834        cx.read_from_clipboard()
 4835            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4836        Some("fox jumps over\n".to_string())
 4837    );
 4838
 4839    // Paste with three selections, noticing how the copied full-line selection is inserted
 4840    // before the empty selections but replaces the selection that is non-empty.
 4841    cx.set_state(indoc! {"
 4842        Tˇhe quick brown
 4843        «foˇ»x jumps over
 4844        tˇhe lazy dog"});
 4845    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4846    cx.assert_editor_state(indoc! {"
 4847        fox jumps over
 4848        Tˇhe quick brown
 4849        fox jumps over
 4850        ˇx jumps over
 4851        fox jumps over
 4852        tˇhe lazy dog"});
 4853}
 4854
 4855#[gpui::test]
 4856async fn test_paste_multiline(cx: &mut TestAppContext) {
 4857    init_test(cx, |_| {});
 4858
 4859    let mut cx = EditorTestContext::new(cx).await;
 4860    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 4861
 4862    // Cut an indented block, without the leading whitespace.
 4863    cx.set_state(indoc! {"
 4864        const a: B = (
 4865            c(),
 4866            «d(
 4867                e,
 4868                f
 4869            )ˇ»
 4870        );
 4871    "});
 4872    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4873    cx.assert_editor_state(indoc! {"
 4874        const a: B = (
 4875            c(),
 4876            ˇ
 4877        );
 4878    "});
 4879
 4880    // Paste it at the same position.
 4881    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4882    cx.assert_editor_state(indoc! {"
 4883        const a: B = (
 4884            c(),
 4885            d(
 4886                e,
 4887                f
 4888 4889        );
 4890    "});
 4891
 4892    // Paste it at a line with a lower indent level.
 4893    cx.set_state(indoc! {"
 4894        ˇ
 4895        const a: B = (
 4896            c(),
 4897        );
 4898    "});
 4899    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4900    cx.assert_editor_state(indoc! {"
 4901        d(
 4902            e,
 4903            f
 4904 4905        const a: B = (
 4906            c(),
 4907        );
 4908    "});
 4909
 4910    // Cut an indented block, with the leading whitespace.
 4911    cx.set_state(indoc! {"
 4912        const a: B = (
 4913            c(),
 4914        «    d(
 4915                e,
 4916                f
 4917            )
 4918        ˇ»);
 4919    "});
 4920    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4921    cx.assert_editor_state(indoc! {"
 4922        const a: B = (
 4923            c(),
 4924        ˇ);
 4925    "});
 4926
 4927    // Paste it at the same position.
 4928    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4929    cx.assert_editor_state(indoc! {"
 4930        const a: B = (
 4931            c(),
 4932            d(
 4933                e,
 4934                f
 4935            )
 4936        ˇ);
 4937    "});
 4938
 4939    // Paste it at a line with a higher indent level.
 4940    cx.set_state(indoc! {"
 4941        const a: B = (
 4942            c(),
 4943            d(
 4944                e,
 4945 4946            )
 4947        );
 4948    "});
 4949    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4950    cx.assert_editor_state(indoc! {"
 4951        const a: B = (
 4952            c(),
 4953            d(
 4954                e,
 4955                f    d(
 4956                    e,
 4957                    f
 4958                )
 4959        ˇ
 4960            )
 4961        );
 4962    "});
 4963
 4964    // Copy an indented block, starting mid-line
 4965    cx.set_state(indoc! {"
 4966        const a: B = (
 4967            c(),
 4968            somethin«g(
 4969                e,
 4970                f
 4971            )ˇ»
 4972        );
 4973    "});
 4974    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4975
 4976    // Paste it on a line with a lower indent level
 4977    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 4978    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4979    cx.assert_editor_state(indoc! {"
 4980        const a: B = (
 4981            c(),
 4982            something(
 4983                e,
 4984                f
 4985            )
 4986        );
 4987        g(
 4988            e,
 4989            f
 4990"});
 4991}
 4992
 4993#[gpui::test]
 4994async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 4995    init_test(cx, |_| {});
 4996
 4997    cx.write_to_clipboard(ClipboardItem::new_string(
 4998        "    d(\n        e\n    );\n".into(),
 4999    ));
 5000
 5001    let mut cx = EditorTestContext::new(cx).await;
 5002    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5003
 5004    cx.set_state(indoc! {"
 5005        fn a() {
 5006            b();
 5007            if c() {
 5008                ˇ
 5009            }
 5010        }
 5011    "});
 5012
 5013    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5014    cx.assert_editor_state(indoc! {"
 5015        fn a() {
 5016            b();
 5017            if c() {
 5018                d(
 5019                    e
 5020                );
 5021        ˇ
 5022            }
 5023        }
 5024    "});
 5025
 5026    cx.set_state(indoc! {"
 5027        fn a() {
 5028            b();
 5029            ˇ
 5030        }
 5031    "});
 5032
 5033    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5034    cx.assert_editor_state(indoc! {"
 5035        fn a() {
 5036            b();
 5037            d(
 5038                e
 5039            );
 5040        ˇ
 5041        }
 5042    "});
 5043}
 5044
 5045#[gpui::test]
 5046fn test_select_all(cx: &mut TestAppContext) {
 5047    init_test(cx, |_| {});
 5048
 5049    let editor = cx.add_window(|window, cx| {
 5050        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5051        build_editor(buffer, window, cx)
 5052    });
 5053    _ = editor.update(cx, |editor, window, cx| {
 5054        editor.select_all(&SelectAll, window, cx);
 5055        assert_eq!(
 5056            editor.selections.display_ranges(cx),
 5057            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5058        );
 5059    });
 5060}
 5061
 5062#[gpui::test]
 5063fn test_select_line(cx: &mut TestAppContext) {
 5064    init_test(cx, |_| {});
 5065
 5066    let editor = cx.add_window(|window, cx| {
 5067        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5068        build_editor(buffer, window, cx)
 5069    });
 5070    _ = editor.update(cx, |editor, window, cx| {
 5071        editor.change_selections(None, window, cx, |s| {
 5072            s.select_display_ranges([
 5073                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5074                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5075                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5076                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5077            ])
 5078        });
 5079        editor.select_line(&SelectLine, window, cx);
 5080        assert_eq!(
 5081            editor.selections.display_ranges(cx),
 5082            vec![
 5083                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5084                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5085            ]
 5086        );
 5087    });
 5088
 5089    _ = editor.update(cx, |editor, window, cx| {
 5090        editor.select_line(&SelectLine, window, cx);
 5091        assert_eq!(
 5092            editor.selections.display_ranges(cx),
 5093            vec![
 5094                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5095                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5096            ]
 5097        );
 5098    });
 5099
 5100    _ = editor.update(cx, |editor, window, cx| {
 5101        editor.select_line(&SelectLine, window, cx);
 5102        assert_eq!(
 5103            editor.selections.display_ranges(cx),
 5104            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5105        );
 5106    });
 5107}
 5108
 5109#[gpui::test]
 5110async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5111    init_test(cx, |_| {});
 5112    let mut cx = EditorTestContext::new(cx).await;
 5113
 5114    #[track_caller]
 5115    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5116        cx.set_state(initial_state);
 5117        cx.update_editor(|e, window, cx| {
 5118            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5119        });
 5120        cx.assert_editor_state(expected_state);
 5121    }
 5122
 5123    // Selection starts and ends at the middle of lines, left-to-right
 5124    test(
 5125        &mut cx,
 5126        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5127        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5128    );
 5129    // Same thing, right-to-left
 5130    test(
 5131        &mut cx,
 5132        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5133        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5134    );
 5135
 5136    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5137    test(
 5138        &mut cx,
 5139        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5140        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5141    );
 5142    // Same thing, right-to-left
 5143    test(
 5144        &mut cx,
 5145        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5146        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5147    );
 5148
 5149    // Whole buffer, left-to-right, last line ends with newline
 5150    test(
 5151        &mut cx,
 5152        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5153        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5154    );
 5155    // Same thing, right-to-left
 5156    test(
 5157        &mut cx,
 5158        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5159        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5160    );
 5161
 5162    // Starts at the end of a line, ends at the start of another
 5163    test(
 5164        &mut cx,
 5165        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5166        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5167    );
 5168}
 5169
 5170#[gpui::test]
 5171async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5172    init_test(cx, |_| {});
 5173
 5174    let editor = cx.add_window(|window, cx| {
 5175        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5176        build_editor(buffer, window, cx)
 5177    });
 5178
 5179    // setup
 5180    _ = editor.update(cx, |editor, window, cx| {
 5181        editor.fold_creases(
 5182            vec![
 5183                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5184                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5185                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5186            ],
 5187            true,
 5188            window,
 5189            cx,
 5190        );
 5191        assert_eq!(
 5192            editor.display_text(cx),
 5193            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5194        );
 5195    });
 5196
 5197    _ = editor.update(cx, |editor, window, cx| {
 5198        editor.change_selections(None, window, cx, |s| {
 5199            s.select_display_ranges([
 5200                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5201                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5202                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5203                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5204            ])
 5205        });
 5206        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5207        assert_eq!(
 5208            editor.display_text(cx),
 5209            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5210        );
 5211    });
 5212    EditorTestContext::for_editor(editor, cx)
 5213        .await
 5214        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5215
 5216    _ = editor.update(cx, |editor, window, cx| {
 5217        editor.change_selections(None, window, cx, |s| {
 5218            s.select_display_ranges([
 5219                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5220            ])
 5221        });
 5222        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5223        assert_eq!(
 5224            editor.display_text(cx),
 5225            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5226        );
 5227        assert_eq!(
 5228            editor.selections.display_ranges(cx),
 5229            [
 5230                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5231                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5232                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5233                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5234                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5235                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5236                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5237            ]
 5238        );
 5239    });
 5240    EditorTestContext::for_editor(editor, cx)
 5241        .await
 5242        .assert_editor_state(
 5243            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5244        );
 5245}
 5246
 5247#[gpui::test]
 5248async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5249    init_test(cx, |_| {});
 5250
 5251    let mut cx = EditorTestContext::new(cx).await;
 5252
 5253    cx.set_state(indoc!(
 5254        r#"abc
 5255           defˇghi
 5256
 5257           jk
 5258           nlmo
 5259           "#
 5260    ));
 5261
 5262    cx.update_editor(|editor, window, cx| {
 5263        editor.add_selection_above(&Default::default(), window, cx);
 5264    });
 5265
 5266    cx.assert_editor_state(indoc!(
 5267        r#"abcˇ
 5268           defˇghi
 5269
 5270           jk
 5271           nlmo
 5272           "#
 5273    ));
 5274
 5275    cx.update_editor(|editor, window, cx| {
 5276        editor.add_selection_above(&Default::default(), window, cx);
 5277    });
 5278
 5279    cx.assert_editor_state(indoc!(
 5280        r#"abcˇ
 5281            defˇghi
 5282
 5283            jk
 5284            nlmo
 5285            "#
 5286    ));
 5287
 5288    cx.update_editor(|editor, window, cx| {
 5289        editor.add_selection_below(&Default::default(), window, cx);
 5290    });
 5291
 5292    cx.assert_editor_state(indoc!(
 5293        r#"abc
 5294           defˇghi
 5295
 5296           jk
 5297           nlmo
 5298           "#
 5299    ));
 5300
 5301    cx.update_editor(|editor, window, cx| {
 5302        editor.undo_selection(&Default::default(), window, cx);
 5303    });
 5304
 5305    cx.assert_editor_state(indoc!(
 5306        r#"abcˇ
 5307           defˇghi
 5308
 5309           jk
 5310           nlmo
 5311           "#
 5312    ));
 5313
 5314    cx.update_editor(|editor, window, cx| {
 5315        editor.redo_selection(&Default::default(), window, cx);
 5316    });
 5317
 5318    cx.assert_editor_state(indoc!(
 5319        r#"abc
 5320           defˇghi
 5321
 5322           jk
 5323           nlmo
 5324           "#
 5325    ));
 5326
 5327    cx.update_editor(|editor, window, cx| {
 5328        editor.add_selection_below(&Default::default(), window, cx);
 5329    });
 5330
 5331    cx.assert_editor_state(indoc!(
 5332        r#"abc
 5333           defˇghi
 5334
 5335           jk
 5336           nlmˇo
 5337           "#
 5338    ));
 5339
 5340    cx.update_editor(|editor, window, cx| {
 5341        editor.add_selection_below(&Default::default(), window, cx);
 5342    });
 5343
 5344    cx.assert_editor_state(indoc!(
 5345        r#"abc
 5346           defˇghi
 5347
 5348           jk
 5349           nlmˇo
 5350           "#
 5351    ));
 5352
 5353    // change selections
 5354    cx.set_state(indoc!(
 5355        r#"abc
 5356           def«ˇg»hi
 5357
 5358           jk
 5359           nlmo
 5360           "#
 5361    ));
 5362
 5363    cx.update_editor(|editor, window, cx| {
 5364        editor.add_selection_below(&Default::default(), window, cx);
 5365    });
 5366
 5367    cx.assert_editor_state(indoc!(
 5368        r#"abc
 5369           def«ˇg»hi
 5370
 5371           jk
 5372           nlm«ˇo»
 5373           "#
 5374    ));
 5375
 5376    cx.update_editor(|editor, window, cx| {
 5377        editor.add_selection_below(&Default::default(), window, cx);
 5378    });
 5379
 5380    cx.assert_editor_state(indoc!(
 5381        r#"abc
 5382           def«ˇg»hi
 5383
 5384           jk
 5385           nlm«ˇo»
 5386           "#
 5387    ));
 5388
 5389    cx.update_editor(|editor, window, cx| {
 5390        editor.add_selection_above(&Default::default(), window, cx);
 5391    });
 5392
 5393    cx.assert_editor_state(indoc!(
 5394        r#"abc
 5395           def«ˇg»hi
 5396
 5397           jk
 5398           nlmo
 5399           "#
 5400    ));
 5401
 5402    cx.update_editor(|editor, window, cx| {
 5403        editor.add_selection_above(&Default::default(), window, cx);
 5404    });
 5405
 5406    cx.assert_editor_state(indoc!(
 5407        r#"abc
 5408           def«ˇg»hi
 5409
 5410           jk
 5411           nlmo
 5412           "#
 5413    ));
 5414
 5415    // Change selections again
 5416    cx.set_state(indoc!(
 5417        r#"a«bc
 5418           defgˇ»hi
 5419
 5420           jk
 5421           nlmo
 5422           "#
 5423    ));
 5424
 5425    cx.update_editor(|editor, window, cx| {
 5426        editor.add_selection_below(&Default::default(), window, cx);
 5427    });
 5428
 5429    cx.assert_editor_state(indoc!(
 5430        r#"a«bcˇ»
 5431           d«efgˇ»hi
 5432
 5433           j«kˇ»
 5434           nlmo
 5435           "#
 5436    ));
 5437
 5438    cx.update_editor(|editor, window, cx| {
 5439        editor.add_selection_below(&Default::default(), window, cx);
 5440    });
 5441    cx.assert_editor_state(indoc!(
 5442        r#"a«bcˇ»
 5443           d«efgˇ»hi
 5444
 5445           j«kˇ»
 5446           n«lmoˇ»
 5447           "#
 5448    ));
 5449    cx.update_editor(|editor, window, cx| {
 5450        editor.add_selection_above(&Default::default(), window, cx);
 5451    });
 5452
 5453    cx.assert_editor_state(indoc!(
 5454        r#"a«bcˇ»
 5455           d«efgˇ»hi
 5456
 5457           j«kˇ»
 5458           nlmo
 5459           "#
 5460    ));
 5461
 5462    // Change selections again
 5463    cx.set_state(indoc!(
 5464        r#"abc
 5465           d«ˇefghi
 5466
 5467           jk
 5468           nlm»o
 5469           "#
 5470    ));
 5471
 5472    cx.update_editor(|editor, window, cx| {
 5473        editor.add_selection_above(&Default::default(), window, cx);
 5474    });
 5475
 5476    cx.assert_editor_state(indoc!(
 5477        r#"a«ˇbc»
 5478           d«ˇef»ghi
 5479
 5480           j«ˇk»
 5481           n«ˇlm»o
 5482           "#
 5483    ));
 5484
 5485    cx.update_editor(|editor, window, cx| {
 5486        editor.add_selection_below(&Default::default(), window, cx);
 5487    });
 5488
 5489    cx.assert_editor_state(indoc!(
 5490        r#"abc
 5491           d«ˇef»ghi
 5492
 5493           j«ˇk»
 5494           n«ˇlm»o
 5495           "#
 5496    ));
 5497}
 5498
 5499#[gpui::test]
 5500async fn test_select_next(cx: &mut TestAppContext) {
 5501    init_test(cx, |_| {});
 5502
 5503    let mut cx = EditorTestContext::new(cx).await;
 5504    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5505
 5506    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5507        .unwrap();
 5508    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5509
 5510    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5511        .unwrap();
 5512    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5513
 5514    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5515    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5516
 5517    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5518    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5519
 5520    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5521        .unwrap();
 5522    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5523
 5524    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5525        .unwrap();
 5526    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5527}
 5528
 5529#[gpui::test]
 5530async fn test_select_all_matches(cx: &mut TestAppContext) {
 5531    init_test(cx, |_| {});
 5532
 5533    let mut cx = EditorTestContext::new(cx).await;
 5534
 5535    // Test caret-only selections
 5536    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5537    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5538        .unwrap();
 5539    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5540
 5541    // Test left-to-right selections
 5542    cx.set_state("abc\n«abcˇ»\nabc");
 5543    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5544        .unwrap();
 5545    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5546
 5547    // Test right-to-left selections
 5548    cx.set_state("abc\n«ˇabc»\nabc");
 5549    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5550        .unwrap();
 5551    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5552
 5553    // Test selecting whitespace with caret selection
 5554    cx.set_state("abc\nˇ   abc\nabc");
 5555    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5556        .unwrap();
 5557    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5558
 5559    // Test selecting whitespace with left-to-right selection
 5560    cx.set_state("abc\n«ˇ  »abc\nabc");
 5561    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5562        .unwrap();
 5563    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5564
 5565    // Test no matches with right-to-left selection
 5566    cx.set_state("abc\n«  ˇ»abc\nabc");
 5567    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5568        .unwrap();
 5569    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5570}
 5571
 5572#[gpui::test]
 5573async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5574    init_test(cx, |_| {});
 5575
 5576    let mut cx = EditorTestContext::new(cx).await;
 5577    cx.set_state(
 5578        r#"let foo = 2;
 5579lˇet foo = 2;
 5580let fooˇ = 2;
 5581let foo = 2;
 5582let foo = ˇ2;"#,
 5583    );
 5584
 5585    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5586        .unwrap();
 5587    cx.assert_editor_state(
 5588        r#"let foo = 2;
 5589«letˇ» foo = 2;
 5590let «fooˇ» = 2;
 5591let foo = 2;
 5592let foo = «2ˇ»;"#,
 5593    );
 5594
 5595    // noop for multiple selections with different contents
 5596    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5597        .unwrap();
 5598    cx.assert_editor_state(
 5599        r#"let foo = 2;
 5600«letˇ» foo = 2;
 5601let «fooˇ» = 2;
 5602let foo = 2;
 5603let foo = «2ˇ»;"#,
 5604    );
 5605}
 5606
 5607#[gpui::test]
 5608async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5609    init_test(cx, |_| {});
 5610
 5611    let mut cx =
 5612        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5613
 5614    cx.assert_editor_state(indoc! {"
 5615        ˇbbb
 5616        ccc
 5617
 5618        bbb
 5619        ccc
 5620        "});
 5621    cx.dispatch_action(SelectPrevious::default());
 5622    cx.assert_editor_state(indoc! {"
 5623                «bbbˇ»
 5624                ccc
 5625
 5626                bbb
 5627                ccc
 5628                "});
 5629    cx.dispatch_action(SelectPrevious::default());
 5630    cx.assert_editor_state(indoc! {"
 5631                «bbbˇ»
 5632                ccc
 5633
 5634                «bbbˇ»
 5635                ccc
 5636                "});
 5637}
 5638
 5639#[gpui::test]
 5640async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5641    init_test(cx, |_| {});
 5642
 5643    let mut cx = EditorTestContext::new(cx).await;
 5644    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5645
 5646    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5647        .unwrap();
 5648    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5649
 5650    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5651        .unwrap();
 5652    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5653
 5654    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5655    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5656
 5657    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5658    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5659
 5660    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5661        .unwrap();
 5662    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5663
 5664    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5665        .unwrap();
 5666    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5667
 5668    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5669        .unwrap();
 5670    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5671}
 5672
 5673#[gpui::test]
 5674async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5675    init_test(cx, |_| {});
 5676
 5677    let mut cx = EditorTestContext::new(cx).await;
 5678    cx.set_state("");
 5679
 5680    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5681        .unwrap();
 5682    cx.assert_editor_state("«aˇ»");
 5683    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5684        .unwrap();
 5685    cx.assert_editor_state("«aˇ»");
 5686}
 5687
 5688#[gpui::test]
 5689async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5690    init_test(cx, |_| {});
 5691
 5692    let mut cx = EditorTestContext::new(cx).await;
 5693    cx.set_state(
 5694        r#"let foo = 2;
 5695lˇet foo = 2;
 5696let fooˇ = 2;
 5697let foo = 2;
 5698let foo = ˇ2;"#,
 5699    );
 5700
 5701    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5702        .unwrap();
 5703    cx.assert_editor_state(
 5704        r#"let foo = 2;
 5705«letˇ» foo = 2;
 5706let «fooˇ» = 2;
 5707let foo = 2;
 5708let foo = «2ˇ»;"#,
 5709    );
 5710
 5711    // noop for multiple selections with different contents
 5712    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5713        .unwrap();
 5714    cx.assert_editor_state(
 5715        r#"let foo = 2;
 5716«letˇ» foo = 2;
 5717let «fooˇ» = 2;
 5718let foo = 2;
 5719let foo = «2ˇ»;"#,
 5720    );
 5721}
 5722
 5723#[gpui::test]
 5724async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5725    init_test(cx, |_| {});
 5726
 5727    let mut cx = EditorTestContext::new(cx).await;
 5728    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5729
 5730    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5731        .unwrap();
 5732    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5733
 5734    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5735        .unwrap();
 5736    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5737
 5738    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5739    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5740
 5741    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5742    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5743
 5744    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5745        .unwrap();
 5746    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5747
 5748    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5749        .unwrap();
 5750    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5751}
 5752
 5753#[gpui::test]
 5754async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5755    init_test(cx, |_| {});
 5756
 5757    let language = Arc::new(Language::new(
 5758        LanguageConfig::default(),
 5759        Some(tree_sitter_rust::LANGUAGE.into()),
 5760    ));
 5761
 5762    let text = r#"
 5763        use mod1::mod2::{mod3, mod4};
 5764
 5765        fn fn_1(param1: bool, param2: &str) {
 5766            let var1 = "text";
 5767        }
 5768    "#
 5769    .unindent();
 5770
 5771    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5772    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5773    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5774
 5775    editor
 5776        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5777        .await;
 5778
 5779    editor.update_in(cx, |editor, window, cx| {
 5780        editor.change_selections(None, window, cx, |s| {
 5781            s.select_display_ranges([
 5782                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5783                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5784                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5785            ]);
 5786        });
 5787        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5788    });
 5789    editor.update(cx, |editor, cx| {
 5790        assert_text_with_selections(
 5791            editor,
 5792            indoc! {r#"
 5793                use mod1::mod2::{mod3, «mod4ˇ»};
 5794
 5795                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5796                    let var1 = "«textˇ»";
 5797                }
 5798            "#},
 5799            cx,
 5800        );
 5801    });
 5802
 5803    editor.update_in(cx, |editor, window, cx| {
 5804        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5805    });
 5806    editor.update(cx, |editor, cx| {
 5807        assert_text_with_selections(
 5808            editor,
 5809            indoc! {r#"
 5810                use mod1::mod2::«{mod3, mod4}ˇ»;
 5811
 5812                «ˇfn fn_1(param1: bool, param2: &str) {
 5813                    let var1 = "text";
 5814 5815            "#},
 5816            cx,
 5817        );
 5818    });
 5819
 5820    editor.update_in(cx, |editor, window, cx| {
 5821        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5822    });
 5823    assert_eq!(
 5824        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5825        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5826    );
 5827
 5828    // Trying to expand the selected syntax node one more time has no effect.
 5829    editor.update_in(cx, |editor, window, cx| {
 5830        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5831    });
 5832    assert_eq!(
 5833        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 5834        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 5835    );
 5836
 5837    editor.update_in(cx, |editor, window, cx| {
 5838        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5839    });
 5840    editor.update(cx, |editor, cx| {
 5841        assert_text_with_selections(
 5842            editor,
 5843            indoc! {r#"
 5844                use mod1::mod2::«{mod3, mod4}ˇ»;
 5845
 5846                «ˇfn fn_1(param1: bool, param2: &str) {
 5847                    let var1 = "text";
 5848 5849            "#},
 5850            cx,
 5851        );
 5852    });
 5853
 5854    editor.update_in(cx, |editor, window, cx| {
 5855        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5856    });
 5857    editor.update(cx, |editor, cx| {
 5858        assert_text_with_selections(
 5859            editor,
 5860            indoc! {r#"
 5861                use mod1::mod2::{mod3, «mod4ˇ»};
 5862
 5863                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5864                    let var1 = "«textˇ»";
 5865                }
 5866            "#},
 5867            cx,
 5868        );
 5869    });
 5870
 5871    editor.update_in(cx, |editor, window, cx| {
 5872        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5873    });
 5874    editor.update(cx, |editor, cx| {
 5875        assert_text_with_selections(
 5876            editor,
 5877            indoc! {r#"
 5878                use mod1::mod2::{mod3, mo«ˇ»d4};
 5879
 5880                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5881                    let var1 = "te«ˇ»xt";
 5882                }
 5883            "#},
 5884            cx,
 5885        );
 5886    });
 5887
 5888    // Trying to shrink the selected syntax node one more time has no effect.
 5889    editor.update_in(cx, |editor, window, cx| {
 5890        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 5891    });
 5892    editor.update_in(cx, |editor, _, cx| {
 5893        assert_text_with_selections(
 5894            editor,
 5895            indoc! {r#"
 5896                use mod1::mod2::{mod3, mo«ˇ»d4};
 5897
 5898                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 5899                    let var1 = "te«ˇ»xt";
 5900                }
 5901            "#},
 5902            cx,
 5903        );
 5904    });
 5905
 5906    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 5907    // a fold.
 5908    editor.update_in(cx, |editor, window, cx| {
 5909        editor.fold_creases(
 5910            vec![
 5911                Crease::simple(
 5912                    Point::new(0, 21)..Point::new(0, 24),
 5913                    FoldPlaceholder::test(),
 5914                ),
 5915                Crease::simple(
 5916                    Point::new(3, 20)..Point::new(3, 22),
 5917                    FoldPlaceholder::test(),
 5918                ),
 5919            ],
 5920            true,
 5921            window,
 5922            cx,
 5923        );
 5924        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 5925    });
 5926    editor.update(cx, |editor, cx| {
 5927        assert_text_with_selections(
 5928            editor,
 5929            indoc! {r#"
 5930                use mod1::mod2::«{mod3, mod4}ˇ»;
 5931
 5932                fn fn_1«ˇ(param1: bool, param2: &str)» {
 5933                    «let var1 = "text";ˇ»
 5934                }
 5935            "#},
 5936            cx,
 5937        );
 5938    });
 5939}
 5940
 5941#[gpui::test]
 5942async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 5943    init_test(cx, |_| {});
 5944
 5945    let base_text = r#"
 5946        impl A {
 5947            // this is an uncommitted comment
 5948
 5949            fn b() {
 5950                c();
 5951            }
 5952
 5953            // this is another uncommitted comment
 5954
 5955            fn d() {
 5956                // e
 5957                // f
 5958            }
 5959        }
 5960
 5961        fn g() {
 5962            // h
 5963        }
 5964    "#
 5965    .unindent();
 5966
 5967    let text = r#"
 5968        ˇimpl A {
 5969
 5970            fn b() {
 5971                c();
 5972            }
 5973
 5974            fn d() {
 5975                // e
 5976                // f
 5977            }
 5978        }
 5979
 5980        fn g() {
 5981            // h
 5982        }
 5983    "#
 5984    .unindent();
 5985
 5986    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 5987    cx.set_state(&text);
 5988    cx.set_head_text(&base_text);
 5989    cx.update_editor(|editor, window, cx| {
 5990        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 5991    });
 5992
 5993    cx.assert_state_with_diff(
 5994        "
 5995        ˇimpl A {
 5996      -     // this is an uncommitted comment
 5997
 5998            fn b() {
 5999                c();
 6000            }
 6001
 6002      -     // this is another uncommitted comment
 6003      -
 6004            fn d() {
 6005                // e
 6006                // f
 6007            }
 6008        }
 6009
 6010        fn g() {
 6011            // h
 6012        }
 6013    "
 6014        .unindent(),
 6015    );
 6016
 6017    let expected_display_text = "
 6018        impl A {
 6019            // this is an uncommitted comment
 6020
 6021            fn b() {
 6022 6023            }
 6024
 6025            // this is another uncommitted comment
 6026
 6027            fn d() {
 6028 6029            }
 6030        }
 6031
 6032        fn g() {
 6033 6034        }
 6035        "
 6036    .unindent();
 6037
 6038    cx.update_editor(|editor, window, cx| {
 6039        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6040        assert_eq!(editor.display_text(cx), expected_display_text);
 6041    });
 6042}
 6043
 6044#[gpui::test]
 6045async fn test_autoindent(cx: &mut TestAppContext) {
 6046    init_test(cx, |_| {});
 6047
 6048    let language = Arc::new(
 6049        Language::new(
 6050            LanguageConfig {
 6051                brackets: BracketPairConfig {
 6052                    pairs: vec![
 6053                        BracketPair {
 6054                            start: "{".to_string(),
 6055                            end: "}".to_string(),
 6056                            close: false,
 6057                            surround: false,
 6058                            newline: true,
 6059                        },
 6060                        BracketPair {
 6061                            start: "(".to_string(),
 6062                            end: ")".to_string(),
 6063                            close: false,
 6064                            surround: false,
 6065                            newline: true,
 6066                        },
 6067                    ],
 6068                    ..Default::default()
 6069                },
 6070                ..Default::default()
 6071            },
 6072            Some(tree_sitter_rust::LANGUAGE.into()),
 6073        )
 6074        .with_indents_query(
 6075            r#"
 6076                (_ "(" ")" @end) @indent
 6077                (_ "{" "}" @end) @indent
 6078            "#,
 6079        )
 6080        .unwrap(),
 6081    );
 6082
 6083    let text = "fn a() {}";
 6084
 6085    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6086    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6087    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6088    editor
 6089        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6090        .await;
 6091
 6092    editor.update_in(cx, |editor, window, cx| {
 6093        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6094        editor.newline(&Newline, window, cx);
 6095        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6096        assert_eq!(
 6097            editor.selections.ranges(cx),
 6098            &[
 6099                Point::new(1, 4)..Point::new(1, 4),
 6100                Point::new(3, 4)..Point::new(3, 4),
 6101                Point::new(5, 0)..Point::new(5, 0)
 6102            ]
 6103        );
 6104    });
 6105}
 6106
 6107#[gpui::test]
 6108async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6109    init_test(cx, |_| {});
 6110
 6111    {
 6112        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6113        cx.set_state(indoc! {"
 6114            impl A {
 6115
 6116                fn b() {}
 6117
 6118            «fn c() {
 6119
 6120            }ˇ»
 6121            }
 6122        "});
 6123
 6124        cx.update_editor(|editor, window, cx| {
 6125            editor.autoindent(&Default::default(), window, cx);
 6126        });
 6127
 6128        cx.assert_editor_state(indoc! {"
 6129            impl A {
 6130
 6131                fn b() {}
 6132
 6133                «fn c() {
 6134
 6135                }ˇ»
 6136            }
 6137        "});
 6138    }
 6139
 6140    {
 6141        let mut cx = EditorTestContext::new_multibuffer(
 6142            cx,
 6143            [indoc! { "
 6144                impl A {
 6145                «
 6146                // a
 6147                fn b(){}
 6148                »
 6149                «
 6150                    }
 6151                    fn c(){}
 6152                »
 6153            "}],
 6154        );
 6155
 6156        let buffer = cx.update_editor(|editor, _, cx| {
 6157            let buffer = editor.buffer().update(cx, |buffer, _| {
 6158                buffer.all_buffers().iter().next().unwrap().clone()
 6159            });
 6160            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6161            buffer
 6162        });
 6163
 6164        cx.run_until_parked();
 6165        cx.update_editor(|editor, window, cx| {
 6166            editor.select_all(&Default::default(), window, cx);
 6167            editor.autoindent(&Default::default(), window, cx)
 6168        });
 6169        cx.run_until_parked();
 6170
 6171        cx.update(|_, cx| {
 6172            pretty_assertions::assert_eq!(
 6173                buffer.read(cx).text(),
 6174                indoc! { "
 6175                    impl A {
 6176
 6177                        // a
 6178                        fn b(){}
 6179
 6180
 6181                    }
 6182                    fn c(){}
 6183
 6184                " }
 6185            )
 6186        });
 6187    }
 6188}
 6189
 6190#[gpui::test]
 6191async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6192    init_test(cx, |_| {});
 6193
 6194    let mut cx = EditorTestContext::new(cx).await;
 6195
 6196    let language = Arc::new(Language::new(
 6197        LanguageConfig {
 6198            brackets: BracketPairConfig {
 6199                pairs: vec![
 6200                    BracketPair {
 6201                        start: "{".to_string(),
 6202                        end: "}".to_string(),
 6203                        close: true,
 6204                        surround: true,
 6205                        newline: true,
 6206                    },
 6207                    BracketPair {
 6208                        start: "(".to_string(),
 6209                        end: ")".to_string(),
 6210                        close: true,
 6211                        surround: true,
 6212                        newline: true,
 6213                    },
 6214                    BracketPair {
 6215                        start: "/*".to_string(),
 6216                        end: " */".to_string(),
 6217                        close: true,
 6218                        surround: true,
 6219                        newline: true,
 6220                    },
 6221                    BracketPair {
 6222                        start: "[".to_string(),
 6223                        end: "]".to_string(),
 6224                        close: false,
 6225                        surround: false,
 6226                        newline: true,
 6227                    },
 6228                    BracketPair {
 6229                        start: "\"".to_string(),
 6230                        end: "\"".to_string(),
 6231                        close: true,
 6232                        surround: true,
 6233                        newline: false,
 6234                    },
 6235                    BracketPair {
 6236                        start: "<".to_string(),
 6237                        end: ">".to_string(),
 6238                        close: false,
 6239                        surround: true,
 6240                        newline: true,
 6241                    },
 6242                ],
 6243                ..Default::default()
 6244            },
 6245            autoclose_before: "})]".to_string(),
 6246            ..Default::default()
 6247        },
 6248        Some(tree_sitter_rust::LANGUAGE.into()),
 6249    ));
 6250
 6251    cx.language_registry().add(language.clone());
 6252    cx.update_buffer(|buffer, cx| {
 6253        buffer.set_language(Some(language), cx);
 6254    });
 6255
 6256    cx.set_state(
 6257        &r#"
 6258            🏀ˇ
 6259            εˇ
 6260            ❤️ˇ
 6261        "#
 6262        .unindent(),
 6263    );
 6264
 6265    // autoclose multiple nested brackets at multiple cursors
 6266    cx.update_editor(|editor, window, cx| {
 6267        editor.handle_input("{", window, cx);
 6268        editor.handle_input("{", window, cx);
 6269        editor.handle_input("{", window, cx);
 6270    });
 6271    cx.assert_editor_state(
 6272        &"
 6273            🏀{{{ˇ}}}
 6274            ε{{{ˇ}}}
 6275            ❤️{{{ˇ}}}
 6276        "
 6277        .unindent(),
 6278    );
 6279
 6280    // insert a different closing bracket
 6281    cx.update_editor(|editor, window, cx| {
 6282        editor.handle_input(")", window, cx);
 6283    });
 6284    cx.assert_editor_state(
 6285        &"
 6286            🏀{{{)ˇ}}}
 6287            ε{{{)ˇ}}}
 6288            ❤️{{{)ˇ}}}
 6289        "
 6290        .unindent(),
 6291    );
 6292
 6293    // skip over the auto-closed brackets when typing a closing bracket
 6294    cx.update_editor(|editor, window, cx| {
 6295        editor.move_right(&MoveRight, window, cx);
 6296        editor.handle_input("}", window, cx);
 6297        editor.handle_input("}", window, cx);
 6298        editor.handle_input("}", window, cx);
 6299    });
 6300    cx.assert_editor_state(
 6301        &"
 6302            🏀{{{)}}}}ˇ
 6303            ε{{{)}}}}ˇ
 6304            ❤️{{{)}}}}ˇ
 6305        "
 6306        .unindent(),
 6307    );
 6308
 6309    // autoclose multi-character pairs
 6310    cx.set_state(
 6311        &"
 6312            ˇ
 6313            ˇ
 6314        "
 6315        .unindent(),
 6316    );
 6317    cx.update_editor(|editor, window, cx| {
 6318        editor.handle_input("/", window, cx);
 6319        editor.handle_input("*", window, cx);
 6320    });
 6321    cx.assert_editor_state(
 6322        &"
 6323            /*ˇ */
 6324            /*ˇ */
 6325        "
 6326        .unindent(),
 6327    );
 6328
 6329    // one cursor autocloses a multi-character pair, one cursor
 6330    // does not autoclose.
 6331    cx.set_state(
 6332        &"
 6333 6334            ˇ
 6335        "
 6336        .unindent(),
 6337    );
 6338    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6339    cx.assert_editor_state(
 6340        &"
 6341            /*ˇ */
 6342 6343        "
 6344        .unindent(),
 6345    );
 6346
 6347    // Don't autoclose if the next character isn't whitespace and isn't
 6348    // listed in the language's "autoclose_before" section.
 6349    cx.set_state("ˇa b");
 6350    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6351    cx.assert_editor_state("{ˇa b");
 6352
 6353    // Don't autoclose if `close` is false for the bracket pair
 6354    cx.set_state("ˇ");
 6355    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6356    cx.assert_editor_state("");
 6357
 6358    // Surround with brackets if text is selected
 6359    cx.set_state("«aˇ» b");
 6360    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6361    cx.assert_editor_state("{«aˇ»} b");
 6362
 6363    // Autoclose when not immediately after a word character
 6364    cx.set_state("a ˇ");
 6365    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6366    cx.assert_editor_state("a \"ˇ\"");
 6367
 6368    // Autoclose pair where the start and end characters are the same
 6369    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6370    cx.assert_editor_state("a \"\"ˇ");
 6371
 6372    // Don't autoclose when immediately after a word character
 6373    cx.set_state("");
 6374    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6375    cx.assert_editor_state("a\"ˇ");
 6376
 6377    // Do autoclose when after a non-word character
 6378    cx.set_state("");
 6379    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6380    cx.assert_editor_state("{\"ˇ\"");
 6381
 6382    // Non identical pairs autoclose regardless of preceding character
 6383    cx.set_state("");
 6384    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6385    cx.assert_editor_state("a{ˇ}");
 6386
 6387    // Don't autoclose pair if autoclose is disabled
 6388    cx.set_state("ˇ");
 6389    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6390    cx.assert_editor_state("");
 6391
 6392    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6393    cx.set_state("«aˇ» b");
 6394    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6395    cx.assert_editor_state("<«aˇ»> b");
 6396}
 6397
 6398#[gpui::test]
 6399async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6400    init_test(cx, |settings| {
 6401        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6402    });
 6403
 6404    let mut cx = EditorTestContext::new(cx).await;
 6405
 6406    let language = Arc::new(Language::new(
 6407        LanguageConfig {
 6408            brackets: BracketPairConfig {
 6409                pairs: vec![
 6410                    BracketPair {
 6411                        start: "{".to_string(),
 6412                        end: "}".to_string(),
 6413                        close: true,
 6414                        surround: true,
 6415                        newline: true,
 6416                    },
 6417                    BracketPair {
 6418                        start: "(".to_string(),
 6419                        end: ")".to_string(),
 6420                        close: true,
 6421                        surround: true,
 6422                        newline: true,
 6423                    },
 6424                    BracketPair {
 6425                        start: "[".to_string(),
 6426                        end: "]".to_string(),
 6427                        close: false,
 6428                        surround: false,
 6429                        newline: true,
 6430                    },
 6431                ],
 6432                ..Default::default()
 6433            },
 6434            autoclose_before: "})]".to_string(),
 6435            ..Default::default()
 6436        },
 6437        Some(tree_sitter_rust::LANGUAGE.into()),
 6438    ));
 6439
 6440    cx.language_registry().add(language.clone());
 6441    cx.update_buffer(|buffer, cx| {
 6442        buffer.set_language(Some(language), cx);
 6443    });
 6444
 6445    cx.set_state(
 6446        &"
 6447            ˇ
 6448            ˇ
 6449            ˇ
 6450        "
 6451        .unindent(),
 6452    );
 6453
 6454    // ensure only matching closing brackets are skipped over
 6455    cx.update_editor(|editor, window, cx| {
 6456        editor.handle_input("}", window, cx);
 6457        editor.move_left(&MoveLeft, window, cx);
 6458        editor.handle_input(")", window, cx);
 6459        editor.move_left(&MoveLeft, window, cx);
 6460    });
 6461    cx.assert_editor_state(
 6462        &"
 6463            ˇ)}
 6464            ˇ)}
 6465            ˇ)}
 6466        "
 6467        .unindent(),
 6468    );
 6469
 6470    // skip-over closing brackets at multiple cursors
 6471    cx.update_editor(|editor, window, cx| {
 6472        editor.handle_input(")", window, cx);
 6473        editor.handle_input("}", window, cx);
 6474    });
 6475    cx.assert_editor_state(
 6476        &"
 6477            )}ˇ
 6478            )}ˇ
 6479            )}ˇ
 6480        "
 6481        .unindent(),
 6482    );
 6483
 6484    // ignore non-close brackets
 6485    cx.update_editor(|editor, window, cx| {
 6486        editor.handle_input("]", window, cx);
 6487        editor.move_left(&MoveLeft, window, cx);
 6488        editor.handle_input("]", window, cx);
 6489    });
 6490    cx.assert_editor_state(
 6491        &"
 6492            )}]ˇ]
 6493            )}]ˇ]
 6494            )}]ˇ]
 6495        "
 6496        .unindent(),
 6497    );
 6498}
 6499
 6500#[gpui::test]
 6501async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6502    init_test(cx, |_| {});
 6503
 6504    let mut cx = EditorTestContext::new(cx).await;
 6505
 6506    let html_language = Arc::new(
 6507        Language::new(
 6508            LanguageConfig {
 6509                name: "HTML".into(),
 6510                brackets: BracketPairConfig {
 6511                    pairs: vec![
 6512                        BracketPair {
 6513                            start: "<".into(),
 6514                            end: ">".into(),
 6515                            close: true,
 6516                            ..Default::default()
 6517                        },
 6518                        BracketPair {
 6519                            start: "{".into(),
 6520                            end: "}".into(),
 6521                            close: true,
 6522                            ..Default::default()
 6523                        },
 6524                        BracketPair {
 6525                            start: "(".into(),
 6526                            end: ")".into(),
 6527                            close: true,
 6528                            ..Default::default()
 6529                        },
 6530                    ],
 6531                    ..Default::default()
 6532                },
 6533                autoclose_before: "})]>".into(),
 6534                ..Default::default()
 6535            },
 6536            Some(tree_sitter_html::LANGUAGE.into()),
 6537        )
 6538        .with_injection_query(
 6539            r#"
 6540            (script_element
 6541                (raw_text) @injection.content
 6542                (#set! injection.language "javascript"))
 6543            "#,
 6544        )
 6545        .unwrap(),
 6546    );
 6547
 6548    let javascript_language = Arc::new(Language::new(
 6549        LanguageConfig {
 6550            name: "JavaScript".into(),
 6551            brackets: BracketPairConfig {
 6552                pairs: vec![
 6553                    BracketPair {
 6554                        start: "/*".into(),
 6555                        end: " */".into(),
 6556                        close: true,
 6557                        ..Default::default()
 6558                    },
 6559                    BracketPair {
 6560                        start: "{".into(),
 6561                        end: "}".into(),
 6562                        close: true,
 6563                        ..Default::default()
 6564                    },
 6565                    BracketPair {
 6566                        start: "(".into(),
 6567                        end: ")".into(),
 6568                        close: true,
 6569                        ..Default::default()
 6570                    },
 6571                ],
 6572                ..Default::default()
 6573            },
 6574            autoclose_before: "})]>".into(),
 6575            ..Default::default()
 6576        },
 6577        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6578    ));
 6579
 6580    cx.language_registry().add(html_language.clone());
 6581    cx.language_registry().add(javascript_language.clone());
 6582
 6583    cx.update_buffer(|buffer, cx| {
 6584        buffer.set_language(Some(html_language), cx);
 6585    });
 6586
 6587    cx.set_state(
 6588        &r#"
 6589            <body>ˇ
 6590                <script>
 6591                    var x = 1;ˇ
 6592                </script>
 6593            </body>ˇ
 6594        "#
 6595        .unindent(),
 6596    );
 6597
 6598    // Precondition: different languages are active at different locations.
 6599    cx.update_editor(|editor, window, cx| {
 6600        let snapshot = editor.snapshot(window, cx);
 6601        let cursors = editor.selections.ranges::<usize>(cx);
 6602        let languages = cursors
 6603            .iter()
 6604            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6605            .collect::<Vec<_>>();
 6606        assert_eq!(
 6607            languages,
 6608            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6609        );
 6610    });
 6611
 6612    // Angle brackets autoclose in HTML, but not JavaScript.
 6613    cx.update_editor(|editor, window, cx| {
 6614        editor.handle_input("<", window, cx);
 6615        editor.handle_input("a", window, cx);
 6616    });
 6617    cx.assert_editor_state(
 6618        &r#"
 6619            <body><aˇ>
 6620                <script>
 6621                    var x = 1;<aˇ
 6622                </script>
 6623            </body><aˇ>
 6624        "#
 6625        .unindent(),
 6626    );
 6627
 6628    // Curly braces and parens autoclose in both HTML and JavaScript.
 6629    cx.update_editor(|editor, window, cx| {
 6630        editor.handle_input(" b=", window, cx);
 6631        editor.handle_input("{", window, cx);
 6632        editor.handle_input("c", window, cx);
 6633        editor.handle_input("(", window, cx);
 6634    });
 6635    cx.assert_editor_state(
 6636        &r#"
 6637            <body><a b={c(ˇ)}>
 6638                <script>
 6639                    var x = 1;<a b={c(ˇ)}
 6640                </script>
 6641            </body><a b={c(ˇ)}>
 6642        "#
 6643        .unindent(),
 6644    );
 6645
 6646    // Brackets that were already autoclosed are skipped.
 6647    cx.update_editor(|editor, window, cx| {
 6648        editor.handle_input(")", window, cx);
 6649        editor.handle_input("d", window, cx);
 6650        editor.handle_input("}", window, cx);
 6651    });
 6652    cx.assert_editor_state(
 6653        &r#"
 6654            <body><a b={c()d}ˇ>
 6655                <script>
 6656                    var x = 1;<a b={c()d}ˇ
 6657                </script>
 6658            </body><a b={c()d}ˇ>
 6659        "#
 6660        .unindent(),
 6661    );
 6662    cx.update_editor(|editor, window, cx| {
 6663        editor.handle_input(">", window, cx);
 6664    });
 6665    cx.assert_editor_state(
 6666        &r#"
 6667            <body><a b={c()d}>ˇ
 6668                <script>
 6669                    var x = 1;<a b={c()d}>ˇ
 6670                </script>
 6671            </body><a b={c()d}>ˇ
 6672        "#
 6673        .unindent(),
 6674    );
 6675
 6676    // Reset
 6677    cx.set_state(
 6678        &r#"
 6679            <body>ˇ
 6680                <script>
 6681                    var x = 1;ˇ
 6682                </script>
 6683            </body>ˇ
 6684        "#
 6685        .unindent(),
 6686    );
 6687
 6688    cx.update_editor(|editor, window, cx| {
 6689        editor.handle_input("<", window, cx);
 6690    });
 6691    cx.assert_editor_state(
 6692        &r#"
 6693            <body><ˇ>
 6694                <script>
 6695                    var x = 1;<ˇ
 6696                </script>
 6697            </body><ˇ>
 6698        "#
 6699        .unindent(),
 6700    );
 6701
 6702    // When backspacing, the closing angle brackets are removed.
 6703    cx.update_editor(|editor, window, cx| {
 6704        editor.backspace(&Backspace, window, cx);
 6705    });
 6706    cx.assert_editor_state(
 6707        &r#"
 6708            <body>ˇ
 6709                <script>
 6710                    var x = 1;ˇ
 6711                </script>
 6712            </body>ˇ
 6713        "#
 6714        .unindent(),
 6715    );
 6716
 6717    // Block comments autoclose in JavaScript, but not HTML.
 6718    cx.update_editor(|editor, window, cx| {
 6719        editor.handle_input("/", window, cx);
 6720        editor.handle_input("*", window, cx);
 6721    });
 6722    cx.assert_editor_state(
 6723        &r#"
 6724            <body>/*ˇ
 6725                <script>
 6726                    var x = 1;/*ˇ */
 6727                </script>
 6728            </body>/*ˇ
 6729        "#
 6730        .unindent(),
 6731    );
 6732}
 6733
 6734#[gpui::test]
 6735async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6736    init_test(cx, |_| {});
 6737
 6738    let mut cx = EditorTestContext::new(cx).await;
 6739
 6740    let rust_language = Arc::new(
 6741        Language::new(
 6742            LanguageConfig {
 6743                name: "Rust".into(),
 6744                brackets: serde_json::from_value(json!([
 6745                    { "start": "{", "end": "}", "close": true, "newline": true },
 6746                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6747                ]))
 6748                .unwrap(),
 6749                autoclose_before: "})]>".into(),
 6750                ..Default::default()
 6751            },
 6752            Some(tree_sitter_rust::LANGUAGE.into()),
 6753        )
 6754        .with_override_query("(string_literal) @string")
 6755        .unwrap(),
 6756    );
 6757
 6758    cx.language_registry().add(rust_language.clone());
 6759    cx.update_buffer(|buffer, cx| {
 6760        buffer.set_language(Some(rust_language), cx);
 6761    });
 6762
 6763    cx.set_state(
 6764        &r#"
 6765            let x = ˇ
 6766        "#
 6767        .unindent(),
 6768    );
 6769
 6770    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6771    cx.update_editor(|editor, window, cx| {
 6772        editor.handle_input("\"", window, cx);
 6773    });
 6774    cx.assert_editor_state(
 6775        &r#"
 6776            let x = "ˇ"
 6777        "#
 6778        .unindent(),
 6779    );
 6780
 6781    // Inserting another quotation mark. The cursor moves across the existing
 6782    // automatically-inserted quotation mark.
 6783    cx.update_editor(|editor, window, cx| {
 6784        editor.handle_input("\"", window, cx);
 6785    });
 6786    cx.assert_editor_state(
 6787        &r#"
 6788            let x = ""ˇ
 6789        "#
 6790        .unindent(),
 6791    );
 6792
 6793    // Reset
 6794    cx.set_state(
 6795        &r#"
 6796            let x = ˇ
 6797        "#
 6798        .unindent(),
 6799    );
 6800
 6801    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 6802    cx.update_editor(|editor, window, cx| {
 6803        editor.handle_input("\"", window, cx);
 6804        editor.handle_input(" ", window, cx);
 6805        editor.move_left(&Default::default(), window, cx);
 6806        editor.handle_input("\\", window, cx);
 6807        editor.handle_input("\"", window, cx);
 6808    });
 6809    cx.assert_editor_state(
 6810        &r#"
 6811            let x = "\"ˇ "
 6812        "#
 6813        .unindent(),
 6814    );
 6815
 6816    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 6817    // mark. Nothing is inserted.
 6818    cx.update_editor(|editor, window, cx| {
 6819        editor.move_right(&Default::default(), window, cx);
 6820        editor.handle_input("\"", window, cx);
 6821    });
 6822    cx.assert_editor_state(
 6823        &r#"
 6824            let x = "\" "ˇ
 6825        "#
 6826        .unindent(),
 6827    );
 6828}
 6829
 6830#[gpui::test]
 6831async fn test_surround_with_pair(cx: &mut TestAppContext) {
 6832    init_test(cx, |_| {});
 6833
 6834    let language = Arc::new(Language::new(
 6835        LanguageConfig {
 6836            brackets: BracketPairConfig {
 6837                pairs: vec![
 6838                    BracketPair {
 6839                        start: "{".to_string(),
 6840                        end: "}".to_string(),
 6841                        close: true,
 6842                        surround: true,
 6843                        newline: true,
 6844                    },
 6845                    BracketPair {
 6846                        start: "/* ".to_string(),
 6847                        end: "*/".to_string(),
 6848                        close: true,
 6849                        surround: true,
 6850                        ..Default::default()
 6851                    },
 6852                ],
 6853                ..Default::default()
 6854            },
 6855            ..Default::default()
 6856        },
 6857        Some(tree_sitter_rust::LANGUAGE.into()),
 6858    ));
 6859
 6860    let text = r#"
 6861        a
 6862        b
 6863        c
 6864    "#
 6865    .unindent();
 6866
 6867    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6868    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6869    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6870    editor
 6871        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6872        .await;
 6873
 6874    editor.update_in(cx, |editor, window, cx| {
 6875        editor.change_selections(None, window, cx, |s| {
 6876            s.select_display_ranges([
 6877                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6878                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6879                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 6880            ])
 6881        });
 6882
 6883        editor.handle_input("{", window, cx);
 6884        editor.handle_input("{", window, cx);
 6885        editor.handle_input("{", window, cx);
 6886        assert_eq!(
 6887            editor.text(cx),
 6888            "
 6889                {{{a}}}
 6890                {{{b}}}
 6891                {{{c}}}
 6892            "
 6893            .unindent()
 6894        );
 6895        assert_eq!(
 6896            editor.selections.display_ranges(cx),
 6897            [
 6898                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 6899                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 6900                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 6901            ]
 6902        );
 6903
 6904        editor.undo(&Undo, window, cx);
 6905        editor.undo(&Undo, window, cx);
 6906        editor.undo(&Undo, window, cx);
 6907        assert_eq!(
 6908            editor.text(cx),
 6909            "
 6910                a
 6911                b
 6912                c
 6913            "
 6914            .unindent()
 6915        );
 6916        assert_eq!(
 6917            editor.selections.display_ranges(cx),
 6918            [
 6919                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6920                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6921                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6922            ]
 6923        );
 6924
 6925        // Ensure inserting the first character of a multi-byte bracket pair
 6926        // doesn't surround the selections with the bracket.
 6927        editor.handle_input("/", window, cx);
 6928        assert_eq!(
 6929            editor.text(cx),
 6930            "
 6931                /
 6932                /
 6933                /
 6934            "
 6935            .unindent()
 6936        );
 6937        assert_eq!(
 6938            editor.selections.display_ranges(cx),
 6939            [
 6940                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6941                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6942                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6943            ]
 6944        );
 6945
 6946        editor.undo(&Undo, window, cx);
 6947        assert_eq!(
 6948            editor.text(cx),
 6949            "
 6950                a
 6951                b
 6952                c
 6953            "
 6954            .unindent()
 6955        );
 6956        assert_eq!(
 6957            editor.selections.display_ranges(cx),
 6958            [
 6959                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 6960                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 6961                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 6962            ]
 6963        );
 6964
 6965        // Ensure inserting the last character of a multi-byte bracket pair
 6966        // doesn't surround the selections with the bracket.
 6967        editor.handle_input("*", window, cx);
 6968        assert_eq!(
 6969            editor.text(cx),
 6970            "
 6971                *
 6972                *
 6973                *
 6974            "
 6975            .unindent()
 6976        );
 6977        assert_eq!(
 6978            editor.selections.display_ranges(cx),
 6979            [
 6980                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 6981                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 6982                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 6983            ]
 6984        );
 6985    });
 6986}
 6987
 6988#[gpui::test]
 6989async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 6990    init_test(cx, |_| {});
 6991
 6992    let language = Arc::new(Language::new(
 6993        LanguageConfig {
 6994            brackets: BracketPairConfig {
 6995                pairs: vec![BracketPair {
 6996                    start: "{".to_string(),
 6997                    end: "}".to_string(),
 6998                    close: true,
 6999                    surround: true,
 7000                    newline: true,
 7001                }],
 7002                ..Default::default()
 7003            },
 7004            autoclose_before: "}".to_string(),
 7005            ..Default::default()
 7006        },
 7007        Some(tree_sitter_rust::LANGUAGE.into()),
 7008    ));
 7009
 7010    let text = r#"
 7011        a
 7012        b
 7013        c
 7014    "#
 7015    .unindent();
 7016
 7017    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7018    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7019    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7020    editor
 7021        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7022        .await;
 7023
 7024    editor.update_in(cx, |editor, window, cx| {
 7025        editor.change_selections(None, window, cx, |s| {
 7026            s.select_ranges([
 7027                Point::new(0, 1)..Point::new(0, 1),
 7028                Point::new(1, 1)..Point::new(1, 1),
 7029                Point::new(2, 1)..Point::new(2, 1),
 7030            ])
 7031        });
 7032
 7033        editor.handle_input("{", window, cx);
 7034        editor.handle_input("{", window, cx);
 7035        editor.handle_input("_", window, cx);
 7036        assert_eq!(
 7037            editor.text(cx),
 7038            "
 7039                a{{_}}
 7040                b{{_}}
 7041                c{{_}}
 7042            "
 7043            .unindent()
 7044        );
 7045        assert_eq!(
 7046            editor.selections.ranges::<Point>(cx),
 7047            [
 7048                Point::new(0, 4)..Point::new(0, 4),
 7049                Point::new(1, 4)..Point::new(1, 4),
 7050                Point::new(2, 4)..Point::new(2, 4)
 7051            ]
 7052        );
 7053
 7054        editor.backspace(&Default::default(), window, cx);
 7055        editor.backspace(&Default::default(), window, cx);
 7056        assert_eq!(
 7057            editor.text(cx),
 7058            "
 7059                a{}
 7060                b{}
 7061                c{}
 7062            "
 7063            .unindent()
 7064        );
 7065        assert_eq!(
 7066            editor.selections.ranges::<Point>(cx),
 7067            [
 7068                Point::new(0, 2)..Point::new(0, 2),
 7069                Point::new(1, 2)..Point::new(1, 2),
 7070                Point::new(2, 2)..Point::new(2, 2)
 7071            ]
 7072        );
 7073
 7074        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7075        assert_eq!(
 7076            editor.text(cx),
 7077            "
 7078                a
 7079                b
 7080                c
 7081            "
 7082            .unindent()
 7083        );
 7084        assert_eq!(
 7085            editor.selections.ranges::<Point>(cx),
 7086            [
 7087                Point::new(0, 1)..Point::new(0, 1),
 7088                Point::new(1, 1)..Point::new(1, 1),
 7089                Point::new(2, 1)..Point::new(2, 1)
 7090            ]
 7091        );
 7092    });
 7093}
 7094
 7095#[gpui::test]
 7096async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7097    init_test(cx, |settings| {
 7098        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7099    });
 7100
 7101    let mut cx = EditorTestContext::new(cx).await;
 7102
 7103    let language = Arc::new(Language::new(
 7104        LanguageConfig {
 7105            brackets: BracketPairConfig {
 7106                pairs: vec![
 7107                    BracketPair {
 7108                        start: "{".to_string(),
 7109                        end: "}".to_string(),
 7110                        close: true,
 7111                        surround: true,
 7112                        newline: true,
 7113                    },
 7114                    BracketPair {
 7115                        start: "(".to_string(),
 7116                        end: ")".to_string(),
 7117                        close: true,
 7118                        surround: true,
 7119                        newline: true,
 7120                    },
 7121                    BracketPair {
 7122                        start: "[".to_string(),
 7123                        end: "]".to_string(),
 7124                        close: false,
 7125                        surround: true,
 7126                        newline: true,
 7127                    },
 7128                ],
 7129                ..Default::default()
 7130            },
 7131            autoclose_before: "})]".to_string(),
 7132            ..Default::default()
 7133        },
 7134        Some(tree_sitter_rust::LANGUAGE.into()),
 7135    ));
 7136
 7137    cx.language_registry().add(language.clone());
 7138    cx.update_buffer(|buffer, cx| {
 7139        buffer.set_language(Some(language), cx);
 7140    });
 7141
 7142    cx.set_state(
 7143        &"
 7144            {(ˇ)}
 7145            [[ˇ]]
 7146            {(ˇ)}
 7147        "
 7148        .unindent(),
 7149    );
 7150
 7151    cx.update_editor(|editor, window, cx| {
 7152        editor.backspace(&Default::default(), window, cx);
 7153        editor.backspace(&Default::default(), window, cx);
 7154    });
 7155
 7156    cx.assert_editor_state(
 7157        &"
 7158            ˇ
 7159            ˇ]]
 7160            ˇ
 7161        "
 7162        .unindent(),
 7163    );
 7164
 7165    cx.update_editor(|editor, window, cx| {
 7166        editor.handle_input("{", window, cx);
 7167        editor.handle_input("{", window, cx);
 7168        editor.move_right(&MoveRight, window, cx);
 7169        editor.move_right(&MoveRight, window, cx);
 7170        editor.move_left(&MoveLeft, window, cx);
 7171        editor.move_left(&MoveLeft, window, cx);
 7172        editor.backspace(&Default::default(), window, cx);
 7173    });
 7174
 7175    cx.assert_editor_state(
 7176        &"
 7177            {ˇ}
 7178            {ˇ}]]
 7179            {ˇ}
 7180        "
 7181        .unindent(),
 7182    );
 7183
 7184    cx.update_editor(|editor, window, cx| {
 7185        editor.backspace(&Default::default(), window, cx);
 7186    });
 7187
 7188    cx.assert_editor_state(
 7189        &"
 7190            ˇ
 7191            ˇ]]
 7192            ˇ
 7193        "
 7194        .unindent(),
 7195    );
 7196}
 7197
 7198#[gpui::test]
 7199async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7200    init_test(cx, |_| {});
 7201
 7202    let language = Arc::new(Language::new(
 7203        LanguageConfig::default(),
 7204        Some(tree_sitter_rust::LANGUAGE.into()),
 7205    ));
 7206
 7207    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7208    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7209    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7210    editor
 7211        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7212        .await;
 7213
 7214    editor.update_in(cx, |editor, window, cx| {
 7215        editor.set_auto_replace_emoji_shortcode(true);
 7216
 7217        editor.handle_input("Hello ", window, cx);
 7218        editor.handle_input(":wave", window, cx);
 7219        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7220
 7221        editor.handle_input(":", window, cx);
 7222        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7223
 7224        editor.handle_input(" :smile", window, cx);
 7225        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7226
 7227        editor.handle_input(":", window, cx);
 7228        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7229
 7230        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7231        editor.handle_input(":wave", window, cx);
 7232        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7233
 7234        editor.handle_input(":", window, cx);
 7235        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7236
 7237        editor.handle_input(":1", window, cx);
 7238        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7239
 7240        editor.handle_input(":", window, cx);
 7241        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7242
 7243        // Ensure shortcode does not get replaced when it is part of a word
 7244        editor.handle_input(" Test:wave", window, cx);
 7245        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7246
 7247        editor.handle_input(":", window, cx);
 7248        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7249
 7250        editor.set_auto_replace_emoji_shortcode(false);
 7251
 7252        // Ensure shortcode does not get replaced when auto replace is off
 7253        editor.handle_input(" :wave", window, cx);
 7254        assert_eq!(
 7255            editor.text(cx),
 7256            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7257        );
 7258
 7259        editor.handle_input(":", window, cx);
 7260        assert_eq!(
 7261            editor.text(cx),
 7262            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7263        );
 7264    });
 7265}
 7266
 7267#[gpui::test]
 7268async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7269    init_test(cx, |_| {});
 7270
 7271    let (text, insertion_ranges) = marked_text_ranges(
 7272        indoc! {"
 7273            ˇ
 7274        "},
 7275        false,
 7276    );
 7277
 7278    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7279    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7280
 7281    _ = editor.update_in(cx, |editor, window, cx| {
 7282        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7283
 7284        editor
 7285            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7286            .unwrap();
 7287
 7288        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7289            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7290            assert_eq!(editor.text(cx), expected_text);
 7291            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7292        }
 7293
 7294        assert(
 7295            editor,
 7296            cx,
 7297            indoc! {"
 7298            type «» =•
 7299            "},
 7300        );
 7301
 7302        assert!(editor.context_menu_visible(), "There should be a matches");
 7303    });
 7304}
 7305
 7306#[gpui::test]
 7307async fn test_snippets(cx: &mut TestAppContext) {
 7308    init_test(cx, |_| {});
 7309
 7310    let (text, insertion_ranges) = marked_text_ranges(
 7311        indoc! {"
 7312            a.ˇ b
 7313            a.ˇ b
 7314            a.ˇ b
 7315        "},
 7316        false,
 7317    );
 7318
 7319    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7320    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7321
 7322    editor.update_in(cx, |editor, window, cx| {
 7323        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7324
 7325        editor
 7326            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7327            .unwrap();
 7328
 7329        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7330            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7331            assert_eq!(editor.text(cx), expected_text);
 7332            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7333        }
 7334
 7335        assert(
 7336            editor,
 7337            cx,
 7338            indoc! {"
 7339                a.f(«one», two, «three») b
 7340                a.f(«one», two, «three») b
 7341                a.f(«one», two, «three») b
 7342            "},
 7343        );
 7344
 7345        // Can't move earlier than the first tab stop
 7346        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7347        assert(
 7348            editor,
 7349            cx,
 7350            indoc! {"
 7351                a.f(«one», two, «three») b
 7352                a.f(«one», two, «three») b
 7353                a.f(«one», two, «three») b
 7354            "},
 7355        );
 7356
 7357        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7358        assert(
 7359            editor,
 7360            cx,
 7361            indoc! {"
 7362                a.f(one, «two», three) b
 7363                a.f(one, «two», three) b
 7364                a.f(one, «two», three) b
 7365            "},
 7366        );
 7367
 7368        editor.move_to_prev_snippet_tabstop(window, cx);
 7369        assert(
 7370            editor,
 7371            cx,
 7372            indoc! {"
 7373                a.f(«one», two, «three») b
 7374                a.f(«one», two, «three») b
 7375                a.f(«one», two, «three») b
 7376            "},
 7377        );
 7378
 7379        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7380        assert(
 7381            editor,
 7382            cx,
 7383            indoc! {"
 7384                a.f(one, «two», three) b
 7385                a.f(one, «two», three) b
 7386                a.f(one, «two», three) b
 7387            "},
 7388        );
 7389        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7390        assert(
 7391            editor,
 7392            cx,
 7393            indoc! {"
 7394                a.f(one, two, three)ˇ b
 7395                a.f(one, two, three)ˇ b
 7396                a.f(one, two, three)ˇ b
 7397            "},
 7398        );
 7399
 7400        // As soon as the last tab stop is reached, snippet state is gone
 7401        editor.move_to_prev_snippet_tabstop(window, cx);
 7402        assert(
 7403            editor,
 7404            cx,
 7405            indoc! {"
 7406                a.f(one, two, three)ˇ b
 7407                a.f(one, two, three)ˇ b
 7408                a.f(one, two, three)ˇ b
 7409            "},
 7410        );
 7411    });
 7412}
 7413
 7414#[gpui::test]
 7415async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7416    init_test(cx, |_| {});
 7417
 7418    let fs = FakeFs::new(cx.executor());
 7419    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7420
 7421    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7422
 7423    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7424    language_registry.add(rust_lang());
 7425    let mut fake_servers = language_registry.register_fake_lsp(
 7426        "Rust",
 7427        FakeLspAdapter {
 7428            capabilities: lsp::ServerCapabilities {
 7429                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7430                ..Default::default()
 7431            },
 7432            ..Default::default()
 7433        },
 7434    );
 7435
 7436    let buffer = project
 7437        .update(cx, |project, cx| {
 7438            project.open_local_buffer(path!("/file.rs"), cx)
 7439        })
 7440        .await
 7441        .unwrap();
 7442
 7443    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7444    let (editor, cx) = cx.add_window_view(|window, cx| {
 7445        build_editor_with_project(project.clone(), buffer, window, cx)
 7446    });
 7447    editor.update_in(cx, |editor, window, cx| {
 7448        editor.set_text("one\ntwo\nthree\n", window, cx)
 7449    });
 7450    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7451
 7452    cx.executor().start_waiting();
 7453    let fake_server = fake_servers.next().await.unwrap();
 7454
 7455    let save = editor
 7456        .update_in(cx, |editor, window, cx| {
 7457            editor.save(true, project.clone(), window, cx)
 7458        })
 7459        .unwrap();
 7460    fake_server
 7461        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7462            assert_eq!(
 7463                params.text_document.uri,
 7464                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7465            );
 7466            assert_eq!(params.options.tab_size, 4);
 7467            Ok(Some(vec![lsp::TextEdit::new(
 7468                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7469                ", ".to_string(),
 7470            )]))
 7471        })
 7472        .next()
 7473        .await;
 7474    cx.executor().start_waiting();
 7475    save.await;
 7476
 7477    assert_eq!(
 7478        editor.update(cx, |editor, cx| editor.text(cx)),
 7479        "one, two\nthree\n"
 7480    );
 7481    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7482
 7483    editor.update_in(cx, |editor, window, cx| {
 7484        editor.set_text("one\ntwo\nthree\n", window, cx)
 7485    });
 7486    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7487
 7488    // Ensure we can still save even if formatting hangs.
 7489    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7490        assert_eq!(
 7491            params.text_document.uri,
 7492            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7493        );
 7494        futures::future::pending::<()>().await;
 7495        unreachable!()
 7496    });
 7497    let save = editor
 7498        .update_in(cx, |editor, window, cx| {
 7499            editor.save(true, project.clone(), window, cx)
 7500        })
 7501        .unwrap();
 7502    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7503    cx.executor().start_waiting();
 7504    save.await;
 7505    assert_eq!(
 7506        editor.update(cx, |editor, cx| editor.text(cx)),
 7507        "one\ntwo\nthree\n"
 7508    );
 7509    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7510
 7511    // For non-dirty buffer, no formatting request should be sent
 7512    let save = editor
 7513        .update_in(cx, |editor, window, cx| {
 7514            editor.save(true, project.clone(), window, cx)
 7515        })
 7516        .unwrap();
 7517    let _pending_format_request = fake_server
 7518        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7519            panic!("Should not be invoked on non-dirty buffer");
 7520        })
 7521        .next();
 7522    cx.executor().start_waiting();
 7523    save.await;
 7524
 7525    // Set rust language override and assert overridden tabsize is sent to language server
 7526    update_test_language_settings(cx, |settings| {
 7527        settings.languages.insert(
 7528            "Rust".into(),
 7529            LanguageSettingsContent {
 7530                tab_size: NonZeroU32::new(8),
 7531                ..Default::default()
 7532            },
 7533        );
 7534    });
 7535
 7536    editor.update_in(cx, |editor, window, cx| {
 7537        editor.set_text("somehting_new\n", window, cx)
 7538    });
 7539    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7540    let save = editor
 7541        .update_in(cx, |editor, window, cx| {
 7542            editor.save(true, project.clone(), window, cx)
 7543        })
 7544        .unwrap();
 7545    fake_server
 7546        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7547            assert_eq!(
 7548                params.text_document.uri,
 7549                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7550            );
 7551            assert_eq!(params.options.tab_size, 8);
 7552            Ok(Some(vec![]))
 7553        })
 7554        .next()
 7555        .await;
 7556    cx.executor().start_waiting();
 7557    save.await;
 7558}
 7559
 7560#[gpui::test]
 7561async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7562    init_test(cx, |_| {});
 7563
 7564    let cols = 4;
 7565    let rows = 10;
 7566    let sample_text_1 = sample_text(rows, cols, 'a');
 7567    assert_eq!(
 7568        sample_text_1,
 7569        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7570    );
 7571    let sample_text_2 = sample_text(rows, cols, 'l');
 7572    assert_eq!(
 7573        sample_text_2,
 7574        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7575    );
 7576    let sample_text_3 = sample_text(rows, cols, 'v');
 7577    assert_eq!(
 7578        sample_text_3,
 7579        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7580    );
 7581
 7582    let fs = FakeFs::new(cx.executor());
 7583    fs.insert_tree(
 7584        path!("/a"),
 7585        json!({
 7586            "main.rs": sample_text_1,
 7587            "other.rs": sample_text_2,
 7588            "lib.rs": sample_text_3,
 7589        }),
 7590    )
 7591    .await;
 7592
 7593    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7594    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7595    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7596
 7597    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7598    language_registry.add(rust_lang());
 7599    let mut fake_servers = language_registry.register_fake_lsp(
 7600        "Rust",
 7601        FakeLspAdapter {
 7602            capabilities: lsp::ServerCapabilities {
 7603                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7604                ..Default::default()
 7605            },
 7606            ..Default::default()
 7607        },
 7608    );
 7609
 7610    let worktree = project.update(cx, |project, cx| {
 7611        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7612        assert_eq!(worktrees.len(), 1);
 7613        worktrees.pop().unwrap()
 7614    });
 7615    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7616
 7617    let buffer_1 = project
 7618        .update(cx, |project, cx| {
 7619            project.open_buffer((worktree_id, "main.rs"), cx)
 7620        })
 7621        .await
 7622        .unwrap();
 7623    let buffer_2 = project
 7624        .update(cx, |project, cx| {
 7625            project.open_buffer((worktree_id, "other.rs"), cx)
 7626        })
 7627        .await
 7628        .unwrap();
 7629    let buffer_3 = project
 7630        .update(cx, |project, cx| {
 7631            project.open_buffer((worktree_id, "lib.rs"), cx)
 7632        })
 7633        .await
 7634        .unwrap();
 7635
 7636    let multi_buffer = cx.new(|cx| {
 7637        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7638        multi_buffer.push_excerpts(
 7639            buffer_1.clone(),
 7640            [
 7641                ExcerptRange {
 7642                    context: Point::new(0, 0)..Point::new(3, 0),
 7643                    primary: None,
 7644                },
 7645                ExcerptRange {
 7646                    context: Point::new(5, 0)..Point::new(7, 0),
 7647                    primary: None,
 7648                },
 7649                ExcerptRange {
 7650                    context: Point::new(9, 0)..Point::new(10, 4),
 7651                    primary: None,
 7652                },
 7653            ],
 7654            cx,
 7655        );
 7656        multi_buffer.push_excerpts(
 7657            buffer_2.clone(),
 7658            [
 7659                ExcerptRange {
 7660                    context: Point::new(0, 0)..Point::new(3, 0),
 7661                    primary: None,
 7662                },
 7663                ExcerptRange {
 7664                    context: Point::new(5, 0)..Point::new(7, 0),
 7665                    primary: None,
 7666                },
 7667                ExcerptRange {
 7668                    context: Point::new(9, 0)..Point::new(10, 4),
 7669                    primary: None,
 7670                },
 7671            ],
 7672            cx,
 7673        );
 7674        multi_buffer.push_excerpts(
 7675            buffer_3.clone(),
 7676            [
 7677                ExcerptRange {
 7678                    context: Point::new(0, 0)..Point::new(3, 0),
 7679                    primary: None,
 7680                },
 7681                ExcerptRange {
 7682                    context: Point::new(5, 0)..Point::new(7, 0),
 7683                    primary: None,
 7684                },
 7685                ExcerptRange {
 7686                    context: Point::new(9, 0)..Point::new(10, 4),
 7687                    primary: None,
 7688                },
 7689            ],
 7690            cx,
 7691        );
 7692        multi_buffer
 7693    });
 7694    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7695        Editor::new(
 7696            EditorMode::Full,
 7697            multi_buffer,
 7698            Some(project.clone()),
 7699            window,
 7700            cx,
 7701        )
 7702    });
 7703
 7704    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7705        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7706            s.select_ranges(Some(1..2))
 7707        });
 7708        editor.insert("|one|two|three|", window, cx);
 7709    });
 7710    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7711    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7712        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7713            s.select_ranges(Some(60..70))
 7714        });
 7715        editor.insert("|four|five|six|", window, cx);
 7716    });
 7717    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7718
 7719    // First two buffers should be edited, but not the third one.
 7720    assert_eq!(
 7721        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7722        "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}",
 7723    );
 7724    buffer_1.update(cx, |buffer, _| {
 7725        assert!(buffer.is_dirty());
 7726        assert_eq!(
 7727            buffer.text(),
 7728            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7729        )
 7730    });
 7731    buffer_2.update(cx, |buffer, _| {
 7732        assert!(buffer.is_dirty());
 7733        assert_eq!(
 7734            buffer.text(),
 7735            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7736        )
 7737    });
 7738    buffer_3.update(cx, |buffer, _| {
 7739        assert!(!buffer.is_dirty());
 7740        assert_eq!(buffer.text(), sample_text_3,)
 7741    });
 7742    cx.executor().run_until_parked();
 7743
 7744    cx.executor().start_waiting();
 7745    let save = multi_buffer_editor
 7746        .update_in(cx, |editor, window, cx| {
 7747            editor.save(true, project.clone(), window, cx)
 7748        })
 7749        .unwrap();
 7750
 7751    let fake_server = fake_servers.next().await.unwrap();
 7752    fake_server
 7753        .server
 7754        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7755            Ok(Some(vec![lsp::TextEdit::new(
 7756                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7757                format!("[{} formatted]", params.text_document.uri),
 7758            )]))
 7759        })
 7760        .detach();
 7761    save.await;
 7762
 7763    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7764    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7765    assert_eq!(
 7766        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7767        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}"),
 7768    );
 7769    buffer_1.update(cx, |buffer, _| {
 7770        assert!(!buffer.is_dirty());
 7771        assert_eq!(
 7772            buffer.text(),
 7773            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7774        )
 7775    });
 7776    buffer_2.update(cx, |buffer, _| {
 7777        assert!(!buffer.is_dirty());
 7778        assert_eq!(
 7779            buffer.text(),
 7780            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7781        )
 7782    });
 7783    buffer_3.update(cx, |buffer, _| {
 7784        assert!(!buffer.is_dirty());
 7785        assert_eq!(buffer.text(), sample_text_3,)
 7786    });
 7787}
 7788
 7789#[gpui::test]
 7790async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7791    init_test(cx, |_| {});
 7792
 7793    let fs = FakeFs::new(cx.executor());
 7794    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7795
 7796    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7797
 7798    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7799    language_registry.add(rust_lang());
 7800    let mut fake_servers = language_registry.register_fake_lsp(
 7801        "Rust",
 7802        FakeLspAdapter {
 7803            capabilities: lsp::ServerCapabilities {
 7804                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7805                ..Default::default()
 7806            },
 7807            ..Default::default()
 7808        },
 7809    );
 7810
 7811    let buffer = project
 7812        .update(cx, |project, cx| {
 7813            project.open_local_buffer(path!("/file.rs"), cx)
 7814        })
 7815        .await
 7816        .unwrap();
 7817
 7818    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7819    let (editor, cx) = cx.add_window_view(|window, cx| {
 7820        build_editor_with_project(project.clone(), buffer, window, cx)
 7821    });
 7822    editor.update_in(cx, |editor, window, cx| {
 7823        editor.set_text("one\ntwo\nthree\n", window, cx)
 7824    });
 7825    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7826
 7827    cx.executor().start_waiting();
 7828    let fake_server = fake_servers.next().await.unwrap();
 7829
 7830    let save = editor
 7831        .update_in(cx, |editor, window, cx| {
 7832            editor.save(true, project.clone(), window, cx)
 7833        })
 7834        .unwrap();
 7835    fake_server
 7836        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7837            assert_eq!(
 7838                params.text_document.uri,
 7839                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7840            );
 7841            assert_eq!(params.options.tab_size, 4);
 7842            Ok(Some(vec![lsp::TextEdit::new(
 7843                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7844                ", ".to_string(),
 7845            )]))
 7846        })
 7847        .next()
 7848        .await;
 7849    cx.executor().start_waiting();
 7850    save.await;
 7851    assert_eq!(
 7852        editor.update(cx, |editor, cx| editor.text(cx)),
 7853        "one, two\nthree\n"
 7854    );
 7855    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7856
 7857    editor.update_in(cx, |editor, window, cx| {
 7858        editor.set_text("one\ntwo\nthree\n", window, cx)
 7859    });
 7860    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7861
 7862    // Ensure we can still save even if formatting hangs.
 7863    fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
 7864        move |params, _| async move {
 7865            assert_eq!(
 7866                params.text_document.uri,
 7867                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7868            );
 7869            futures::future::pending::<()>().await;
 7870            unreachable!()
 7871        },
 7872    );
 7873    let save = editor
 7874        .update_in(cx, |editor, window, cx| {
 7875            editor.save(true, project.clone(), window, cx)
 7876        })
 7877        .unwrap();
 7878    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7879    cx.executor().start_waiting();
 7880    save.await;
 7881    assert_eq!(
 7882        editor.update(cx, |editor, cx| editor.text(cx)),
 7883        "one\ntwo\nthree\n"
 7884    );
 7885    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7886
 7887    // For non-dirty buffer, no formatting request should be sent
 7888    let save = editor
 7889        .update_in(cx, |editor, window, cx| {
 7890            editor.save(true, project.clone(), window, cx)
 7891        })
 7892        .unwrap();
 7893    let _pending_format_request = fake_server
 7894        .handle_request::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7895            panic!("Should not be invoked on non-dirty buffer");
 7896        })
 7897        .next();
 7898    cx.executor().start_waiting();
 7899    save.await;
 7900
 7901    // Set Rust language override and assert overridden tabsize is sent to language server
 7902    update_test_language_settings(cx, |settings| {
 7903        settings.languages.insert(
 7904            "Rust".into(),
 7905            LanguageSettingsContent {
 7906                tab_size: NonZeroU32::new(8),
 7907                ..Default::default()
 7908            },
 7909        );
 7910    });
 7911
 7912    editor.update_in(cx, |editor, window, cx| {
 7913        editor.set_text("somehting_new\n", window, cx)
 7914    });
 7915    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7916    let save = editor
 7917        .update_in(cx, |editor, window, cx| {
 7918            editor.save(true, project.clone(), window, cx)
 7919        })
 7920        .unwrap();
 7921    fake_server
 7922        .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 7923            assert_eq!(
 7924                params.text_document.uri,
 7925                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7926            );
 7927            assert_eq!(params.options.tab_size, 8);
 7928            Ok(Some(vec![]))
 7929        })
 7930        .next()
 7931        .await;
 7932    cx.executor().start_waiting();
 7933    save.await;
 7934}
 7935
 7936#[gpui::test]
 7937async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 7938    init_test(cx, |settings| {
 7939        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 7940            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 7941        ))
 7942    });
 7943
 7944    let fs = FakeFs::new(cx.executor());
 7945    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7946
 7947    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7948
 7949    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7950    language_registry.add(Arc::new(Language::new(
 7951        LanguageConfig {
 7952            name: "Rust".into(),
 7953            matcher: LanguageMatcher {
 7954                path_suffixes: vec!["rs".to_string()],
 7955                ..Default::default()
 7956            },
 7957            ..LanguageConfig::default()
 7958        },
 7959        Some(tree_sitter_rust::LANGUAGE.into()),
 7960    )));
 7961    update_test_language_settings(cx, |settings| {
 7962        // Enable Prettier formatting for the same buffer, and ensure
 7963        // LSP is called instead of Prettier.
 7964        settings.defaults.prettier = Some(PrettierSettings {
 7965            allowed: true,
 7966            ..PrettierSettings::default()
 7967        });
 7968    });
 7969    let mut fake_servers = language_registry.register_fake_lsp(
 7970        "Rust",
 7971        FakeLspAdapter {
 7972            capabilities: lsp::ServerCapabilities {
 7973                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7974                ..Default::default()
 7975            },
 7976            ..Default::default()
 7977        },
 7978    );
 7979
 7980    let buffer = project
 7981        .update(cx, |project, cx| {
 7982            project.open_local_buffer(path!("/file.rs"), cx)
 7983        })
 7984        .await
 7985        .unwrap();
 7986
 7987    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7988    let (editor, cx) = cx.add_window_view(|window, cx| {
 7989        build_editor_with_project(project.clone(), buffer, window, cx)
 7990    });
 7991    editor.update_in(cx, |editor, window, cx| {
 7992        editor.set_text("one\ntwo\nthree\n", window, cx)
 7993    });
 7994
 7995    cx.executor().start_waiting();
 7996    let fake_server = fake_servers.next().await.unwrap();
 7997
 7998    let format = editor
 7999        .update_in(cx, |editor, window, cx| {
 8000            editor.perform_format(
 8001                project.clone(),
 8002                FormatTrigger::Manual,
 8003                FormatTarget::Buffers,
 8004                window,
 8005                cx,
 8006            )
 8007        })
 8008        .unwrap();
 8009    fake_server
 8010        .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8011            assert_eq!(
 8012                params.text_document.uri,
 8013                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8014            );
 8015            assert_eq!(params.options.tab_size, 4);
 8016            Ok(Some(vec![lsp::TextEdit::new(
 8017                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8018                ", ".to_string(),
 8019            )]))
 8020        })
 8021        .next()
 8022        .await;
 8023    cx.executor().start_waiting();
 8024    format.await;
 8025    assert_eq!(
 8026        editor.update(cx, |editor, cx| editor.text(cx)),
 8027        "one, two\nthree\n"
 8028    );
 8029
 8030    editor.update_in(cx, |editor, window, cx| {
 8031        editor.set_text("one\ntwo\nthree\n", window, cx)
 8032    });
 8033    // Ensure we don't lock if formatting hangs.
 8034    fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8035        assert_eq!(
 8036            params.text_document.uri,
 8037            lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8038        );
 8039        futures::future::pending::<()>().await;
 8040        unreachable!()
 8041    });
 8042    let format = editor
 8043        .update_in(cx, |editor, window, cx| {
 8044            editor.perform_format(
 8045                project,
 8046                FormatTrigger::Manual,
 8047                FormatTarget::Buffers,
 8048                window,
 8049                cx,
 8050            )
 8051        })
 8052        .unwrap();
 8053    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8054    cx.executor().start_waiting();
 8055    format.await;
 8056    assert_eq!(
 8057        editor.update(cx, |editor, cx| editor.text(cx)),
 8058        "one\ntwo\nthree\n"
 8059    );
 8060}
 8061
 8062#[gpui::test]
 8063async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8064    init_test(cx, |settings| {
 8065        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8066            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8067        ))
 8068    });
 8069
 8070    let fs = FakeFs::new(cx.executor());
 8071    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8072
 8073    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8074
 8075    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8076    language_registry.add(Arc::new(Language::new(
 8077        LanguageConfig {
 8078            name: "TypeScript".into(),
 8079            matcher: LanguageMatcher {
 8080                path_suffixes: vec!["ts".to_string()],
 8081                ..Default::default()
 8082            },
 8083            ..LanguageConfig::default()
 8084        },
 8085        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8086    )));
 8087    update_test_language_settings(cx, |settings| {
 8088        settings.defaults.prettier = Some(PrettierSettings {
 8089            allowed: true,
 8090            ..PrettierSettings::default()
 8091        });
 8092    });
 8093    let mut fake_servers = language_registry.register_fake_lsp(
 8094        "TypeScript",
 8095        FakeLspAdapter {
 8096            capabilities: lsp::ServerCapabilities {
 8097                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8098                ..Default::default()
 8099            },
 8100            ..Default::default()
 8101        },
 8102    );
 8103
 8104    let buffer = project
 8105        .update(cx, |project, cx| {
 8106            project.open_local_buffer(path!("/file.ts"), cx)
 8107        })
 8108        .await
 8109        .unwrap();
 8110
 8111    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8112    let (editor, cx) = cx.add_window_view(|window, cx| {
 8113        build_editor_with_project(project.clone(), buffer, window, cx)
 8114    });
 8115    editor.update_in(cx, |editor, window, cx| {
 8116        editor.set_text(
 8117            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8118            window,
 8119            cx,
 8120        )
 8121    });
 8122
 8123    cx.executor().start_waiting();
 8124    let fake_server = fake_servers.next().await.unwrap();
 8125
 8126    let format = editor
 8127        .update_in(cx, |editor, window, cx| {
 8128            editor.perform_code_action_kind(
 8129                project.clone(),
 8130                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8131                window,
 8132                cx,
 8133            )
 8134        })
 8135        .unwrap();
 8136    fake_server
 8137        .handle_request::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8138            assert_eq!(
 8139                params.text_document.uri,
 8140                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8141            );
 8142            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8143                lsp::CodeAction {
 8144                    title: "Organize Imports".to_string(),
 8145                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8146                    edit: Some(lsp::WorkspaceEdit {
 8147                        changes: Some(
 8148                            [(
 8149                                params.text_document.uri.clone(),
 8150                                vec![lsp::TextEdit::new(
 8151                                    lsp::Range::new(
 8152                                        lsp::Position::new(1, 0),
 8153                                        lsp::Position::new(2, 0),
 8154                                    ),
 8155                                    "".to_string(),
 8156                                )],
 8157                            )]
 8158                            .into_iter()
 8159                            .collect(),
 8160                        ),
 8161                        ..Default::default()
 8162                    }),
 8163                    ..Default::default()
 8164                },
 8165            )]))
 8166        })
 8167        .next()
 8168        .await;
 8169    cx.executor().start_waiting();
 8170    format.await;
 8171    assert_eq!(
 8172        editor.update(cx, |editor, cx| editor.text(cx)),
 8173        "import { a } from 'module';\n\nconst x = a;\n"
 8174    );
 8175
 8176    editor.update_in(cx, |editor, window, cx| {
 8177        editor.set_text(
 8178            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8179            window,
 8180            cx,
 8181        )
 8182    });
 8183    // Ensure we don't lock if code action hangs.
 8184    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
 8185        move |params, _| async move {
 8186            assert_eq!(
 8187                params.text_document.uri,
 8188                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8189            );
 8190            futures::future::pending::<()>().await;
 8191            unreachable!()
 8192        },
 8193    );
 8194    let format = editor
 8195        .update_in(cx, |editor, window, cx| {
 8196            editor.perform_code_action_kind(
 8197                project,
 8198                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8199                window,
 8200                cx,
 8201            )
 8202        })
 8203        .unwrap();
 8204    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8205    cx.executor().start_waiting();
 8206    format.await;
 8207    assert_eq!(
 8208        editor.update(cx, |editor, cx| editor.text(cx)),
 8209        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8210    );
 8211}
 8212
 8213#[gpui::test]
 8214async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8215    init_test(cx, |_| {});
 8216
 8217    let mut cx = EditorLspTestContext::new_rust(
 8218        lsp::ServerCapabilities {
 8219            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8220            ..Default::default()
 8221        },
 8222        cx,
 8223    )
 8224    .await;
 8225
 8226    cx.set_state(indoc! {"
 8227        one.twoˇ
 8228    "});
 8229
 8230    // The format request takes a long time. When it completes, it inserts
 8231    // a newline and an indent before the `.`
 8232    cx.lsp
 8233        .handle_request::<lsp::request::Formatting, _, _>(move |_, cx| {
 8234            let executor = cx.background_executor().clone();
 8235            async move {
 8236                executor.timer(Duration::from_millis(100)).await;
 8237                Ok(Some(vec![lsp::TextEdit {
 8238                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8239                    new_text: "\n    ".into(),
 8240                }]))
 8241            }
 8242        });
 8243
 8244    // Submit a format request.
 8245    let format_1 = cx
 8246        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8247        .unwrap();
 8248    cx.executor().run_until_parked();
 8249
 8250    // Submit a second format request.
 8251    let format_2 = cx
 8252        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8253        .unwrap();
 8254    cx.executor().run_until_parked();
 8255
 8256    // Wait for both format requests to complete
 8257    cx.executor().advance_clock(Duration::from_millis(200));
 8258    cx.executor().start_waiting();
 8259    format_1.await.unwrap();
 8260    cx.executor().start_waiting();
 8261    format_2.await.unwrap();
 8262
 8263    // The formatting edits only happens once.
 8264    cx.assert_editor_state(indoc! {"
 8265        one
 8266            .twoˇ
 8267    "});
 8268}
 8269
 8270#[gpui::test]
 8271async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8272    init_test(cx, |settings| {
 8273        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8274    });
 8275
 8276    let mut cx = EditorLspTestContext::new_rust(
 8277        lsp::ServerCapabilities {
 8278            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8279            ..Default::default()
 8280        },
 8281        cx,
 8282    )
 8283    .await;
 8284
 8285    // Set up a buffer white some trailing whitespace and no trailing newline.
 8286    cx.set_state(
 8287        &[
 8288            "one ",   //
 8289            "twoˇ",   //
 8290            "three ", //
 8291            "four",   //
 8292        ]
 8293        .join("\n"),
 8294    );
 8295
 8296    // Submit a format request.
 8297    let format = cx
 8298        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8299        .unwrap();
 8300
 8301    // Record which buffer changes have been sent to the language server
 8302    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8303    cx.lsp
 8304        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8305            let buffer_changes = buffer_changes.clone();
 8306            move |params, _| {
 8307                buffer_changes.lock().extend(
 8308                    params
 8309                        .content_changes
 8310                        .into_iter()
 8311                        .map(|e| (e.range.unwrap(), e.text)),
 8312                );
 8313            }
 8314        });
 8315
 8316    // Handle formatting requests to the language server.
 8317    cx.lsp.handle_request::<lsp::request::Formatting, _, _>({
 8318        let buffer_changes = buffer_changes.clone();
 8319        move |_, _| {
 8320            // When formatting is requested, trailing whitespace has already been stripped,
 8321            // and the trailing newline has already been added.
 8322            assert_eq!(
 8323                &buffer_changes.lock()[1..],
 8324                &[
 8325                    (
 8326                        lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8327                        "".into()
 8328                    ),
 8329                    (
 8330                        lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8331                        "".into()
 8332                    ),
 8333                    (
 8334                        lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8335                        "\n".into()
 8336                    ),
 8337                ]
 8338            );
 8339
 8340            // Insert blank lines between each line of the buffer.
 8341            async move {
 8342                Ok(Some(vec![
 8343                    lsp::TextEdit {
 8344                        range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 0)),
 8345                        new_text: "\n".into(),
 8346                    },
 8347                    lsp::TextEdit {
 8348                        range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(2, 0)),
 8349                        new_text: "\n".into(),
 8350                    },
 8351                ]))
 8352            }
 8353        }
 8354    });
 8355
 8356    // After formatting the buffer, the trailing whitespace is stripped,
 8357    // a newline is appended, and the edits provided by the language server
 8358    // have been applied.
 8359    format.await.unwrap();
 8360    cx.assert_editor_state(
 8361        &[
 8362            "one",   //
 8363            "",      //
 8364            "twoˇ",  //
 8365            "",      //
 8366            "three", //
 8367            "four",  //
 8368            "",      //
 8369        ]
 8370        .join("\n"),
 8371    );
 8372
 8373    // Undoing the formatting undoes the trailing whitespace removal, the
 8374    // trailing newline, and the LSP edits.
 8375    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8376    cx.assert_editor_state(
 8377        &[
 8378            "one ",   //
 8379            "twoˇ",   //
 8380            "three ", //
 8381            "four",   //
 8382        ]
 8383        .join("\n"),
 8384    );
 8385}
 8386
 8387#[gpui::test]
 8388async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8389    cx: &mut TestAppContext,
 8390) {
 8391    init_test(cx, |_| {});
 8392
 8393    cx.update(|cx| {
 8394        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8395            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8396                settings.auto_signature_help = Some(true);
 8397            });
 8398        });
 8399    });
 8400
 8401    let mut cx = EditorLspTestContext::new_rust(
 8402        lsp::ServerCapabilities {
 8403            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8404                ..Default::default()
 8405            }),
 8406            ..Default::default()
 8407        },
 8408        cx,
 8409    )
 8410    .await;
 8411
 8412    let language = Language::new(
 8413        LanguageConfig {
 8414            name: "Rust".into(),
 8415            brackets: BracketPairConfig {
 8416                pairs: vec![
 8417                    BracketPair {
 8418                        start: "{".to_string(),
 8419                        end: "}".to_string(),
 8420                        close: true,
 8421                        surround: true,
 8422                        newline: true,
 8423                    },
 8424                    BracketPair {
 8425                        start: "(".to_string(),
 8426                        end: ")".to_string(),
 8427                        close: true,
 8428                        surround: true,
 8429                        newline: true,
 8430                    },
 8431                    BracketPair {
 8432                        start: "/*".to_string(),
 8433                        end: " */".to_string(),
 8434                        close: true,
 8435                        surround: true,
 8436                        newline: true,
 8437                    },
 8438                    BracketPair {
 8439                        start: "[".to_string(),
 8440                        end: "]".to_string(),
 8441                        close: false,
 8442                        surround: false,
 8443                        newline: true,
 8444                    },
 8445                    BracketPair {
 8446                        start: "\"".to_string(),
 8447                        end: "\"".to_string(),
 8448                        close: true,
 8449                        surround: true,
 8450                        newline: false,
 8451                    },
 8452                    BracketPair {
 8453                        start: "<".to_string(),
 8454                        end: ">".to_string(),
 8455                        close: false,
 8456                        surround: true,
 8457                        newline: true,
 8458                    },
 8459                ],
 8460                ..Default::default()
 8461            },
 8462            autoclose_before: "})]".to_string(),
 8463            ..Default::default()
 8464        },
 8465        Some(tree_sitter_rust::LANGUAGE.into()),
 8466    );
 8467    let language = Arc::new(language);
 8468
 8469    cx.language_registry().add(language.clone());
 8470    cx.update_buffer(|buffer, cx| {
 8471        buffer.set_language(Some(language), cx);
 8472    });
 8473
 8474    cx.set_state(
 8475        &r#"
 8476            fn main() {
 8477                sampleˇ
 8478            }
 8479        "#
 8480        .unindent(),
 8481    );
 8482
 8483    cx.update_editor(|editor, window, cx| {
 8484        editor.handle_input("(", window, cx);
 8485    });
 8486    cx.assert_editor_state(
 8487        &"
 8488            fn main() {
 8489                sample(ˇ)
 8490            }
 8491        "
 8492        .unindent(),
 8493    );
 8494
 8495    let mocked_response = lsp::SignatureHelp {
 8496        signatures: vec![lsp::SignatureInformation {
 8497            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8498            documentation: None,
 8499            parameters: Some(vec![
 8500                lsp::ParameterInformation {
 8501                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8502                    documentation: None,
 8503                },
 8504                lsp::ParameterInformation {
 8505                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8506                    documentation: None,
 8507                },
 8508            ]),
 8509            active_parameter: None,
 8510        }],
 8511        active_signature: Some(0),
 8512        active_parameter: Some(0),
 8513    };
 8514    handle_signature_help_request(&mut cx, mocked_response).await;
 8515
 8516    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8517        .await;
 8518
 8519    cx.editor(|editor, _, _| {
 8520        let signature_help_state = editor.signature_help_state.popover().cloned();
 8521        assert_eq!(
 8522            signature_help_state.unwrap().label,
 8523            "param1: u8, param2: u8"
 8524        );
 8525    });
 8526}
 8527
 8528#[gpui::test]
 8529async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8530    init_test(cx, |_| {});
 8531
 8532    cx.update(|cx| {
 8533        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8534            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8535                settings.auto_signature_help = Some(false);
 8536                settings.show_signature_help_after_edits = Some(false);
 8537            });
 8538        });
 8539    });
 8540
 8541    let mut cx = EditorLspTestContext::new_rust(
 8542        lsp::ServerCapabilities {
 8543            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8544                ..Default::default()
 8545            }),
 8546            ..Default::default()
 8547        },
 8548        cx,
 8549    )
 8550    .await;
 8551
 8552    let language = Language::new(
 8553        LanguageConfig {
 8554            name: "Rust".into(),
 8555            brackets: BracketPairConfig {
 8556                pairs: vec![
 8557                    BracketPair {
 8558                        start: "{".to_string(),
 8559                        end: "}".to_string(),
 8560                        close: true,
 8561                        surround: true,
 8562                        newline: true,
 8563                    },
 8564                    BracketPair {
 8565                        start: "(".to_string(),
 8566                        end: ")".to_string(),
 8567                        close: true,
 8568                        surround: true,
 8569                        newline: true,
 8570                    },
 8571                    BracketPair {
 8572                        start: "/*".to_string(),
 8573                        end: " */".to_string(),
 8574                        close: true,
 8575                        surround: true,
 8576                        newline: true,
 8577                    },
 8578                    BracketPair {
 8579                        start: "[".to_string(),
 8580                        end: "]".to_string(),
 8581                        close: false,
 8582                        surround: false,
 8583                        newline: true,
 8584                    },
 8585                    BracketPair {
 8586                        start: "\"".to_string(),
 8587                        end: "\"".to_string(),
 8588                        close: true,
 8589                        surround: true,
 8590                        newline: false,
 8591                    },
 8592                    BracketPair {
 8593                        start: "<".to_string(),
 8594                        end: ">".to_string(),
 8595                        close: false,
 8596                        surround: true,
 8597                        newline: true,
 8598                    },
 8599                ],
 8600                ..Default::default()
 8601            },
 8602            autoclose_before: "})]".to_string(),
 8603            ..Default::default()
 8604        },
 8605        Some(tree_sitter_rust::LANGUAGE.into()),
 8606    );
 8607    let language = Arc::new(language);
 8608
 8609    cx.language_registry().add(language.clone());
 8610    cx.update_buffer(|buffer, cx| {
 8611        buffer.set_language(Some(language), cx);
 8612    });
 8613
 8614    // Ensure that signature_help is not called when no signature help is enabled.
 8615    cx.set_state(
 8616        &r#"
 8617            fn main() {
 8618                sampleˇ
 8619            }
 8620        "#
 8621        .unindent(),
 8622    );
 8623    cx.update_editor(|editor, window, cx| {
 8624        editor.handle_input("(", window, cx);
 8625    });
 8626    cx.assert_editor_state(
 8627        &"
 8628            fn main() {
 8629                sample(ˇ)
 8630            }
 8631        "
 8632        .unindent(),
 8633    );
 8634    cx.editor(|editor, _, _| {
 8635        assert!(editor.signature_help_state.task().is_none());
 8636    });
 8637
 8638    let mocked_response = lsp::SignatureHelp {
 8639        signatures: vec![lsp::SignatureInformation {
 8640            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8641            documentation: None,
 8642            parameters: Some(vec![
 8643                lsp::ParameterInformation {
 8644                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8645                    documentation: None,
 8646                },
 8647                lsp::ParameterInformation {
 8648                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8649                    documentation: None,
 8650                },
 8651            ]),
 8652            active_parameter: None,
 8653        }],
 8654        active_signature: Some(0),
 8655        active_parameter: Some(0),
 8656    };
 8657
 8658    // Ensure that signature_help is called when enabled afte edits
 8659    cx.update(|_, cx| {
 8660        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8661            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8662                settings.auto_signature_help = Some(false);
 8663                settings.show_signature_help_after_edits = Some(true);
 8664            });
 8665        });
 8666    });
 8667    cx.set_state(
 8668        &r#"
 8669            fn main() {
 8670                sampleˇ
 8671            }
 8672        "#
 8673        .unindent(),
 8674    );
 8675    cx.update_editor(|editor, window, cx| {
 8676        editor.handle_input("(", window, cx);
 8677    });
 8678    cx.assert_editor_state(
 8679        &"
 8680            fn main() {
 8681                sample(ˇ)
 8682            }
 8683        "
 8684        .unindent(),
 8685    );
 8686    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8687    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8688        .await;
 8689    cx.update_editor(|editor, _, _| {
 8690        let signature_help_state = editor.signature_help_state.popover().cloned();
 8691        assert!(signature_help_state.is_some());
 8692        assert_eq!(
 8693            signature_help_state.unwrap().label,
 8694            "param1: u8, param2: u8"
 8695        );
 8696        editor.signature_help_state = SignatureHelpState::default();
 8697    });
 8698
 8699    // Ensure that signature_help is called when auto signature help override is enabled
 8700    cx.update(|_, cx| {
 8701        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8702            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8703                settings.auto_signature_help = Some(true);
 8704                settings.show_signature_help_after_edits = Some(false);
 8705            });
 8706        });
 8707    });
 8708    cx.set_state(
 8709        &r#"
 8710            fn main() {
 8711                sampleˇ
 8712            }
 8713        "#
 8714        .unindent(),
 8715    );
 8716    cx.update_editor(|editor, window, cx| {
 8717        editor.handle_input("(", window, cx);
 8718    });
 8719    cx.assert_editor_state(
 8720        &"
 8721            fn main() {
 8722                sample(ˇ)
 8723            }
 8724        "
 8725        .unindent(),
 8726    );
 8727    handle_signature_help_request(&mut cx, mocked_response).await;
 8728    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8729        .await;
 8730    cx.editor(|editor, _, _| {
 8731        let signature_help_state = editor.signature_help_state.popover().cloned();
 8732        assert!(signature_help_state.is_some());
 8733        assert_eq!(
 8734            signature_help_state.unwrap().label,
 8735            "param1: u8, param2: u8"
 8736        );
 8737    });
 8738}
 8739
 8740#[gpui::test]
 8741async fn test_signature_help(cx: &mut TestAppContext) {
 8742    init_test(cx, |_| {});
 8743    cx.update(|cx| {
 8744        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8745            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8746                settings.auto_signature_help = Some(true);
 8747            });
 8748        });
 8749    });
 8750
 8751    let mut cx = EditorLspTestContext::new_rust(
 8752        lsp::ServerCapabilities {
 8753            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8754                ..Default::default()
 8755            }),
 8756            ..Default::default()
 8757        },
 8758        cx,
 8759    )
 8760    .await;
 8761
 8762    // A test that directly calls `show_signature_help`
 8763    cx.update_editor(|editor, window, cx| {
 8764        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8765    });
 8766
 8767    let mocked_response = lsp::SignatureHelp {
 8768        signatures: vec![lsp::SignatureInformation {
 8769            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8770            documentation: None,
 8771            parameters: Some(vec![
 8772                lsp::ParameterInformation {
 8773                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8774                    documentation: None,
 8775                },
 8776                lsp::ParameterInformation {
 8777                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8778                    documentation: None,
 8779                },
 8780            ]),
 8781            active_parameter: None,
 8782        }],
 8783        active_signature: Some(0),
 8784        active_parameter: Some(0),
 8785    };
 8786    handle_signature_help_request(&mut cx, mocked_response).await;
 8787
 8788    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8789        .await;
 8790
 8791    cx.editor(|editor, _, _| {
 8792        let signature_help_state = editor.signature_help_state.popover().cloned();
 8793        assert!(signature_help_state.is_some());
 8794        assert_eq!(
 8795            signature_help_state.unwrap().label,
 8796            "param1: u8, param2: u8"
 8797        );
 8798    });
 8799
 8800    // When exiting outside from inside the brackets, `signature_help` is closed.
 8801    cx.set_state(indoc! {"
 8802        fn main() {
 8803            sample(ˇ);
 8804        }
 8805
 8806        fn sample(param1: u8, param2: u8) {}
 8807    "});
 8808
 8809    cx.update_editor(|editor, window, cx| {
 8810        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 8811    });
 8812
 8813    let mocked_response = lsp::SignatureHelp {
 8814        signatures: Vec::new(),
 8815        active_signature: None,
 8816        active_parameter: None,
 8817    };
 8818    handle_signature_help_request(&mut cx, mocked_response).await;
 8819
 8820    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8821        .await;
 8822
 8823    cx.editor(|editor, _, _| {
 8824        assert!(!editor.signature_help_state.is_shown());
 8825    });
 8826
 8827    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 8828    cx.set_state(indoc! {"
 8829        fn main() {
 8830            sample(ˇ);
 8831        }
 8832
 8833        fn sample(param1: u8, param2: u8) {}
 8834    "});
 8835
 8836    let mocked_response = lsp::SignatureHelp {
 8837        signatures: vec![lsp::SignatureInformation {
 8838            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8839            documentation: None,
 8840            parameters: Some(vec![
 8841                lsp::ParameterInformation {
 8842                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8843                    documentation: None,
 8844                },
 8845                lsp::ParameterInformation {
 8846                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8847                    documentation: None,
 8848                },
 8849            ]),
 8850            active_parameter: None,
 8851        }],
 8852        active_signature: Some(0),
 8853        active_parameter: Some(0),
 8854    };
 8855    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8856    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8857        .await;
 8858    cx.editor(|editor, _, _| {
 8859        assert!(editor.signature_help_state.is_shown());
 8860    });
 8861
 8862    // Restore the popover with more parameter input
 8863    cx.set_state(indoc! {"
 8864        fn main() {
 8865            sample(param1, param2ˇ);
 8866        }
 8867
 8868        fn sample(param1: u8, param2: u8) {}
 8869    "});
 8870
 8871    let mocked_response = lsp::SignatureHelp {
 8872        signatures: vec![lsp::SignatureInformation {
 8873            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8874            documentation: None,
 8875            parameters: Some(vec![
 8876                lsp::ParameterInformation {
 8877                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8878                    documentation: None,
 8879                },
 8880                lsp::ParameterInformation {
 8881                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8882                    documentation: None,
 8883                },
 8884            ]),
 8885            active_parameter: None,
 8886        }],
 8887        active_signature: Some(0),
 8888        active_parameter: Some(1),
 8889    };
 8890    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8891    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8892        .await;
 8893
 8894    // When selecting a range, the popover is gone.
 8895    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 8896    cx.update_editor(|editor, window, cx| {
 8897        editor.change_selections(None, window, cx, |s| {
 8898            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8899        })
 8900    });
 8901    cx.assert_editor_state(indoc! {"
 8902        fn main() {
 8903            sample(param1, «ˇparam2»);
 8904        }
 8905
 8906        fn sample(param1: u8, param2: u8) {}
 8907    "});
 8908    cx.editor(|editor, _, _| {
 8909        assert!(!editor.signature_help_state.is_shown());
 8910    });
 8911
 8912    // When unselecting again, the popover is back if within the brackets.
 8913    cx.update_editor(|editor, window, cx| {
 8914        editor.change_selections(None, window, cx, |s| {
 8915            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8916        })
 8917    });
 8918    cx.assert_editor_state(indoc! {"
 8919        fn main() {
 8920            sample(param1, ˇparam2);
 8921        }
 8922
 8923        fn sample(param1: u8, param2: u8) {}
 8924    "});
 8925    handle_signature_help_request(&mut cx, mocked_response).await;
 8926    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8927        .await;
 8928    cx.editor(|editor, _, _| {
 8929        assert!(editor.signature_help_state.is_shown());
 8930    });
 8931
 8932    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 8933    cx.update_editor(|editor, window, cx| {
 8934        editor.change_selections(None, window, cx, |s| {
 8935            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 8936            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8937        })
 8938    });
 8939    cx.assert_editor_state(indoc! {"
 8940        fn main() {
 8941            sample(param1, ˇparam2);
 8942        }
 8943
 8944        fn sample(param1: u8, param2: u8) {}
 8945    "});
 8946
 8947    let mocked_response = lsp::SignatureHelp {
 8948        signatures: vec![lsp::SignatureInformation {
 8949            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8950            documentation: None,
 8951            parameters: Some(vec![
 8952                lsp::ParameterInformation {
 8953                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8954                    documentation: None,
 8955                },
 8956                lsp::ParameterInformation {
 8957                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8958                    documentation: None,
 8959                },
 8960            ]),
 8961            active_parameter: None,
 8962        }],
 8963        active_signature: Some(0),
 8964        active_parameter: Some(1),
 8965    };
 8966    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8967    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8968        .await;
 8969    cx.update_editor(|editor, _, cx| {
 8970        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 8971    });
 8972    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 8973        .await;
 8974    cx.update_editor(|editor, window, cx| {
 8975        editor.change_selections(None, window, cx, |s| {
 8976            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 8977        })
 8978    });
 8979    cx.assert_editor_state(indoc! {"
 8980        fn main() {
 8981            sample(param1, «ˇparam2»);
 8982        }
 8983
 8984        fn sample(param1: u8, param2: u8) {}
 8985    "});
 8986    cx.update_editor(|editor, window, cx| {
 8987        editor.change_selections(None, window, cx, |s| {
 8988            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 8989        })
 8990    });
 8991    cx.assert_editor_state(indoc! {"
 8992        fn main() {
 8993            sample(param1, ˇparam2);
 8994        }
 8995
 8996        fn sample(param1: u8, param2: u8) {}
 8997    "});
 8998    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 8999        .await;
 9000}
 9001
 9002#[gpui::test]
 9003async fn test_completion(cx: &mut TestAppContext) {
 9004    init_test(cx, |_| {});
 9005
 9006    let mut cx = EditorLspTestContext::new_rust(
 9007        lsp::ServerCapabilities {
 9008            completion_provider: Some(lsp::CompletionOptions {
 9009                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9010                resolve_provider: Some(true),
 9011                ..Default::default()
 9012            }),
 9013            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9014            ..Default::default()
 9015        },
 9016        cx,
 9017    )
 9018    .await;
 9019    let counter = Arc::new(AtomicUsize::new(0));
 9020
 9021    cx.set_state(indoc! {"
 9022        oneˇ
 9023        two
 9024        three
 9025    "});
 9026    cx.simulate_keystroke(".");
 9027    handle_completion_request(
 9028        &mut cx,
 9029        indoc! {"
 9030            one.|<>
 9031            two
 9032            three
 9033        "},
 9034        vec!["first_completion", "second_completion"],
 9035        counter.clone(),
 9036    )
 9037    .await;
 9038    cx.condition(|editor, _| editor.context_menu_visible())
 9039        .await;
 9040    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9041
 9042    let _handler = handle_signature_help_request(
 9043        &mut cx,
 9044        lsp::SignatureHelp {
 9045            signatures: vec![lsp::SignatureInformation {
 9046                label: "test signature".to_string(),
 9047                documentation: None,
 9048                parameters: Some(vec![lsp::ParameterInformation {
 9049                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9050                    documentation: None,
 9051                }]),
 9052                active_parameter: None,
 9053            }],
 9054            active_signature: None,
 9055            active_parameter: None,
 9056        },
 9057    );
 9058    cx.update_editor(|editor, window, cx| {
 9059        assert!(
 9060            !editor.signature_help_state.is_shown(),
 9061            "No signature help was called for"
 9062        );
 9063        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9064    });
 9065    cx.run_until_parked();
 9066    cx.update_editor(|editor, _, _| {
 9067        assert!(
 9068            !editor.signature_help_state.is_shown(),
 9069            "No signature help should be shown when completions menu is open"
 9070        );
 9071    });
 9072
 9073    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9074        editor.context_menu_next(&Default::default(), window, cx);
 9075        editor
 9076            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9077            .unwrap()
 9078    });
 9079    cx.assert_editor_state(indoc! {"
 9080        one.second_completionˇ
 9081        two
 9082        three
 9083    "});
 9084
 9085    handle_resolve_completion_request(
 9086        &mut cx,
 9087        Some(vec![
 9088            (
 9089                //This overlaps with the primary completion edit which is
 9090                //misbehavior from the LSP spec, test that we filter it out
 9091                indoc! {"
 9092                    one.second_ˇcompletion
 9093                    two
 9094                    threeˇ
 9095                "},
 9096                "overlapping additional edit",
 9097            ),
 9098            (
 9099                indoc! {"
 9100                    one.second_completion
 9101                    two
 9102                    threeˇ
 9103                "},
 9104                "\nadditional edit",
 9105            ),
 9106        ]),
 9107    )
 9108    .await;
 9109    apply_additional_edits.await.unwrap();
 9110    cx.assert_editor_state(indoc! {"
 9111        one.second_completionˇ
 9112        two
 9113        three
 9114        additional edit
 9115    "});
 9116
 9117    cx.set_state(indoc! {"
 9118        one.second_completion
 9119        twoˇ
 9120        threeˇ
 9121        additional edit
 9122    "});
 9123    cx.simulate_keystroke(" ");
 9124    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9125    cx.simulate_keystroke("s");
 9126    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9127
 9128    cx.assert_editor_state(indoc! {"
 9129        one.second_completion
 9130        two sˇ
 9131        three sˇ
 9132        additional edit
 9133    "});
 9134    handle_completion_request(
 9135        &mut cx,
 9136        indoc! {"
 9137            one.second_completion
 9138            two s
 9139            three <s|>
 9140            additional edit
 9141        "},
 9142        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9143        counter.clone(),
 9144    )
 9145    .await;
 9146    cx.condition(|editor, _| editor.context_menu_visible())
 9147        .await;
 9148    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9149
 9150    cx.simulate_keystroke("i");
 9151
 9152    handle_completion_request(
 9153        &mut cx,
 9154        indoc! {"
 9155            one.second_completion
 9156            two si
 9157            three <si|>
 9158            additional edit
 9159        "},
 9160        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9161        counter.clone(),
 9162    )
 9163    .await;
 9164    cx.condition(|editor, _| editor.context_menu_visible())
 9165        .await;
 9166    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9167
 9168    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9169        editor
 9170            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9171            .unwrap()
 9172    });
 9173    cx.assert_editor_state(indoc! {"
 9174        one.second_completion
 9175        two sixth_completionˇ
 9176        three sixth_completionˇ
 9177        additional edit
 9178    "});
 9179
 9180    apply_additional_edits.await.unwrap();
 9181
 9182    update_test_language_settings(&mut cx, |settings| {
 9183        settings.defaults.show_completions_on_input = Some(false);
 9184    });
 9185    cx.set_state("editorˇ");
 9186    cx.simulate_keystroke(".");
 9187    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9188    cx.simulate_keystroke("c");
 9189    cx.simulate_keystroke("l");
 9190    cx.simulate_keystroke("o");
 9191    cx.assert_editor_state("editor.cloˇ");
 9192    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9193    cx.update_editor(|editor, window, cx| {
 9194        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9195    });
 9196    handle_completion_request(
 9197        &mut cx,
 9198        "editor.<clo|>",
 9199        vec!["close", "clobber"],
 9200        counter.clone(),
 9201    )
 9202    .await;
 9203    cx.condition(|editor, _| editor.context_menu_visible())
 9204        .await;
 9205    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9206
 9207    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9208        editor
 9209            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9210            .unwrap()
 9211    });
 9212    cx.assert_editor_state("editor.closeˇ");
 9213    handle_resolve_completion_request(&mut cx, None).await;
 9214    apply_additional_edits.await.unwrap();
 9215}
 9216
 9217#[gpui::test]
 9218async fn test_word_completion(cx: &mut TestAppContext) {
 9219    let lsp_fetch_timeout_ms = 10;
 9220    init_test(cx, |language_settings| {
 9221        language_settings.defaults.completions = Some(CompletionSettings {
 9222            words: WordsCompletionMode::Fallback,
 9223            lsp: true,
 9224            lsp_fetch_timeout_ms: 10,
 9225        });
 9226    });
 9227
 9228    let mut cx = EditorLspTestContext::new_rust(
 9229        lsp::ServerCapabilities {
 9230            completion_provider: Some(lsp::CompletionOptions {
 9231                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9232                ..lsp::CompletionOptions::default()
 9233            }),
 9234            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9235            ..lsp::ServerCapabilities::default()
 9236        },
 9237        cx,
 9238    )
 9239    .await;
 9240
 9241    let throttle_completions = Arc::new(AtomicBool::new(false));
 9242
 9243    let lsp_throttle_completions = throttle_completions.clone();
 9244    let _completion_requests_handler =
 9245        cx.lsp
 9246            .server
 9247            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9248                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9249                async move {
 9250                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9251                        cx.background_executor()
 9252                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9253                            .await;
 9254                    }
 9255                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9256                        lsp::CompletionItem {
 9257                            label: "first".into(),
 9258                            ..lsp::CompletionItem::default()
 9259                        },
 9260                        lsp::CompletionItem {
 9261                            label: "last".into(),
 9262                            ..lsp::CompletionItem::default()
 9263                        },
 9264                    ])))
 9265                }
 9266            });
 9267
 9268    cx.set_state(indoc! {"
 9269        oneˇ
 9270        two
 9271        three
 9272    "});
 9273    cx.simulate_keystroke(".");
 9274    cx.executor().run_until_parked();
 9275    cx.condition(|editor, _| editor.context_menu_visible())
 9276        .await;
 9277    cx.update_editor(|editor, window, cx| {
 9278        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9279        {
 9280            assert_eq!(
 9281                completion_menu_entries(&menu),
 9282                &["first", "last"],
 9283                "When LSP server is fast to reply, no fallback word completions are used"
 9284            );
 9285        } else {
 9286            panic!("expected completion menu to be open");
 9287        }
 9288        editor.cancel(&Cancel, window, cx);
 9289    });
 9290    cx.executor().run_until_parked();
 9291    cx.condition(|editor, _| !editor.context_menu_visible())
 9292        .await;
 9293
 9294    throttle_completions.store(true, atomic::Ordering::Release);
 9295    cx.simulate_keystroke(".");
 9296    cx.executor()
 9297        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9298    cx.executor().run_until_parked();
 9299    cx.condition(|editor, _| editor.context_menu_visible())
 9300        .await;
 9301    cx.update_editor(|editor, _, _| {
 9302        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9303        {
 9304            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9305                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9306        } else {
 9307            panic!("expected completion menu to be open");
 9308        }
 9309    });
 9310}
 9311
 9312#[gpui::test]
 9313async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9314    init_test(cx, |language_settings| {
 9315        language_settings.defaults.completions = Some(CompletionSettings {
 9316            words: WordsCompletionMode::Enabled,
 9317            lsp: true,
 9318            lsp_fetch_timeout_ms: 0,
 9319        });
 9320    });
 9321
 9322    let mut cx = EditorLspTestContext::new_rust(
 9323        lsp::ServerCapabilities {
 9324            completion_provider: Some(lsp::CompletionOptions {
 9325                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9326                ..lsp::CompletionOptions::default()
 9327            }),
 9328            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9329            ..lsp::ServerCapabilities::default()
 9330        },
 9331        cx,
 9332    )
 9333    .await;
 9334
 9335    let _completion_requests_handler =
 9336        cx.lsp
 9337            .server
 9338            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9339                Ok(Some(lsp::CompletionResponse::Array(vec![
 9340                    lsp::CompletionItem {
 9341                        label: "first".into(),
 9342                        ..lsp::CompletionItem::default()
 9343                    },
 9344                    lsp::CompletionItem {
 9345                        label: "last".into(),
 9346                        ..lsp::CompletionItem::default()
 9347                    },
 9348                ])))
 9349            });
 9350
 9351    cx.set_state(indoc! {"ˇ
 9352        first
 9353        last
 9354        second
 9355    "});
 9356    cx.simulate_keystroke(".");
 9357    cx.executor().run_until_parked();
 9358    cx.condition(|editor, _| editor.context_menu_visible())
 9359        .await;
 9360    cx.update_editor(|editor, _, _| {
 9361        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9362        {
 9363            assert_eq!(
 9364                completion_menu_entries(&menu),
 9365                &["first", "last", "second"],
 9366                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9367            );
 9368        } else {
 9369            panic!("expected completion menu to be open");
 9370        }
 9371    });
 9372}
 9373
 9374#[gpui::test]
 9375async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9376    init_test(cx, |language_settings| {
 9377        language_settings.defaults.completions = Some(CompletionSettings {
 9378            words: WordsCompletionMode::Disabled,
 9379            lsp: true,
 9380            lsp_fetch_timeout_ms: 0,
 9381        });
 9382    });
 9383
 9384    let mut cx = EditorLspTestContext::new_rust(
 9385        lsp::ServerCapabilities {
 9386            completion_provider: Some(lsp::CompletionOptions {
 9387                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9388                ..lsp::CompletionOptions::default()
 9389            }),
 9390            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9391            ..lsp::ServerCapabilities::default()
 9392        },
 9393        cx,
 9394    )
 9395    .await;
 9396
 9397    let _completion_requests_handler =
 9398        cx.lsp
 9399            .server
 9400            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9401                panic!("LSP completions should not be queried when dealing with word completions")
 9402            });
 9403
 9404    cx.set_state(indoc! {"ˇ
 9405        first
 9406        last
 9407        second
 9408    "});
 9409    cx.update_editor(|editor, window, cx| {
 9410        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9411    });
 9412    cx.executor().run_until_parked();
 9413    cx.condition(|editor, _| editor.context_menu_visible())
 9414        .await;
 9415    cx.update_editor(|editor, _, _| {
 9416        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9417        {
 9418            assert_eq!(
 9419                completion_menu_entries(&menu),
 9420                &["first", "last", "second"],
 9421                "`ShowWordCompletions` action should show word completions"
 9422            );
 9423        } else {
 9424            panic!("expected completion menu to be open");
 9425        }
 9426    });
 9427
 9428    cx.simulate_keystroke("s");
 9429    cx.executor().run_until_parked();
 9430    cx.condition(|editor, _| editor.context_menu_visible())
 9431        .await;
 9432    cx.update_editor(|editor, _, _| {
 9433        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9434        {
 9435            assert_eq!(
 9436                completion_menu_entries(&menu),
 9437                &["second"],
 9438                "After showing word completions, further editing should filter them and not query the LSP"
 9439            );
 9440        } else {
 9441            panic!("expected completion menu to be open");
 9442        }
 9443    });
 9444}
 9445
 9446#[gpui::test]
 9447async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9448    init_test(cx, |language_settings| {
 9449        language_settings.defaults.completions = Some(CompletionSettings {
 9450            words: WordsCompletionMode::Fallback,
 9451            lsp: false,
 9452            lsp_fetch_timeout_ms: 0,
 9453        });
 9454    });
 9455
 9456    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9457
 9458    cx.set_state(indoc! {"ˇ
 9459        0_usize
 9460        let
 9461        33
 9462        4.5f32
 9463    "});
 9464    cx.update_editor(|editor, window, cx| {
 9465        editor.show_completions(&ShowCompletions::default(), window, cx);
 9466    });
 9467    cx.executor().run_until_parked();
 9468    cx.condition(|editor, _| editor.context_menu_visible())
 9469        .await;
 9470    cx.update_editor(|editor, window, cx| {
 9471        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9472        {
 9473            assert_eq!(
 9474                completion_menu_entries(&menu),
 9475                &["let"],
 9476                "With no digits in the completion query, no digits should be in the word completions"
 9477            );
 9478        } else {
 9479            panic!("expected completion menu to be open");
 9480        }
 9481        editor.cancel(&Cancel, window, cx);
 9482    });
 9483
 9484    cx.set_state(indoc! {" 9485        0_usize
 9486        let
 9487        3
 9488        33.35f32
 9489    "});
 9490    cx.update_editor(|editor, window, cx| {
 9491        editor.show_completions(&ShowCompletions::default(), window, cx);
 9492    });
 9493    cx.executor().run_until_parked();
 9494    cx.condition(|editor, _| editor.context_menu_visible())
 9495        .await;
 9496    cx.update_editor(|editor, _, _| {
 9497        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9498        {
 9499            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9500                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9501        } else {
 9502            panic!("expected completion menu to be open");
 9503        }
 9504    });
 9505}
 9506
 9507#[gpui::test]
 9508async fn test_multiline_completion(cx: &mut TestAppContext) {
 9509    init_test(cx, |_| {});
 9510
 9511    let fs = FakeFs::new(cx.executor());
 9512    fs.insert_tree(
 9513        path!("/a"),
 9514        json!({
 9515            "main.ts": "a",
 9516        }),
 9517    )
 9518    .await;
 9519
 9520    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9521    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9522    let typescript_language = Arc::new(Language::new(
 9523        LanguageConfig {
 9524            name: "TypeScript".into(),
 9525            matcher: LanguageMatcher {
 9526                path_suffixes: vec!["ts".to_string()],
 9527                ..LanguageMatcher::default()
 9528            },
 9529            line_comments: vec!["// ".into()],
 9530            ..LanguageConfig::default()
 9531        },
 9532        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9533    ));
 9534    language_registry.add(typescript_language.clone());
 9535    let mut fake_servers = language_registry.register_fake_lsp(
 9536        "TypeScript",
 9537        FakeLspAdapter {
 9538            capabilities: lsp::ServerCapabilities {
 9539                completion_provider: Some(lsp::CompletionOptions {
 9540                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9541                    ..lsp::CompletionOptions::default()
 9542                }),
 9543                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9544                ..lsp::ServerCapabilities::default()
 9545            },
 9546            // Emulate vtsls label generation
 9547            label_for_completion: Some(Box::new(|item, _| {
 9548                let text = if let Some(description) = item
 9549                    .label_details
 9550                    .as_ref()
 9551                    .and_then(|label_details| label_details.description.as_ref())
 9552                {
 9553                    format!("{} {}", item.label, description)
 9554                } else if let Some(detail) = &item.detail {
 9555                    format!("{} {}", item.label, detail)
 9556                } else {
 9557                    item.label.clone()
 9558                };
 9559                let len = text.len();
 9560                Some(language::CodeLabel {
 9561                    text,
 9562                    runs: Vec::new(),
 9563                    filter_range: 0..len,
 9564                })
 9565            })),
 9566            ..FakeLspAdapter::default()
 9567        },
 9568    );
 9569    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9570    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9571    let worktree_id = workspace
 9572        .update(cx, |workspace, _window, cx| {
 9573            workspace.project().update(cx, |project, cx| {
 9574                project.worktrees(cx).next().unwrap().read(cx).id()
 9575            })
 9576        })
 9577        .unwrap();
 9578    let _buffer = project
 9579        .update(cx, |project, cx| {
 9580            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9581        })
 9582        .await
 9583        .unwrap();
 9584    let editor = workspace
 9585        .update(cx, |workspace, window, cx| {
 9586            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9587        })
 9588        .unwrap()
 9589        .await
 9590        .unwrap()
 9591        .downcast::<Editor>()
 9592        .unwrap();
 9593    let fake_server = fake_servers.next().await.unwrap();
 9594
 9595    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9596    let multiline_label_2 = "a\nb\nc\n";
 9597    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9598    let multiline_description = "d\ne\nf\n";
 9599    let multiline_detail_2 = "g\nh\ni\n";
 9600
 9601    let mut completion_handle =
 9602        fake_server.handle_request::<lsp::request::Completion, _, _>(move |params, _| async move {
 9603            Ok(Some(lsp::CompletionResponse::Array(vec![
 9604                lsp::CompletionItem {
 9605                    label: multiline_label.to_string(),
 9606                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9607                        range: lsp::Range {
 9608                            start: lsp::Position {
 9609                                line: params.text_document_position.position.line,
 9610                                character: params.text_document_position.position.character,
 9611                            },
 9612                            end: lsp::Position {
 9613                                line: params.text_document_position.position.line,
 9614                                character: params.text_document_position.position.character,
 9615                            },
 9616                        },
 9617                        new_text: "new_text_1".to_string(),
 9618                    })),
 9619                    ..lsp::CompletionItem::default()
 9620                },
 9621                lsp::CompletionItem {
 9622                    label: "single line label 1".to_string(),
 9623                    detail: Some(multiline_detail.to_string()),
 9624                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9625                        range: lsp::Range {
 9626                            start: lsp::Position {
 9627                                line: params.text_document_position.position.line,
 9628                                character: params.text_document_position.position.character,
 9629                            },
 9630                            end: lsp::Position {
 9631                                line: params.text_document_position.position.line,
 9632                                character: params.text_document_position.position.character,
 9633                            },
 9634                        },
 9635                        new_text: "new_text_2".to_string(),
 9636                    })),
 9637                    ..lsp::CompletionItem::default()
 9638                },
 9639                lsp::CompletionItem {
 9640                    label: "single line label 2".to_string(),
 9641                    label_details: Some(lsp::CompletionItemLabelDetails {
 9642                        description: Some(multiline_description.to_string()),
 9643                        detail: None,
 9644                    }),
 9645                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9646                        range: lsp::Range {
 9647                            start: lsp::Position {
 9648                                line: params.text_document_position.position.line,
 9649                                character: params.text_document_position.position.character,
 9650                            },
 9651                            end: lsp::Position {
 9652                                line: params.text_document_position.position.line,
 9653                                character: params.text_document_position.position.character,
 9654                            },
 9655                        },
 9656                        new_text: "new_text_2".to_string(),
 9657                    })),
 9658                    ..lsp::CompletionItem::default()
 9659                },
 9660                lsp::CompletionItem {
 9661                    label: multiline_label_2.to_string(),
 9662                    detail: Some(multiline_detail_2.to_string()),
 9663                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9664                        range: lsp::Range {
 9665                            start: lsp::Position {
 9666                                line: params.text_document_position.position.line,
 9667                                character: params.text_document_position.position.character,
 9668                            },
 9669                            end: lsp::Position {
 9670                                line: params.text_document_position.position.line,
 9671                                character: params.text_document_position.position.character,
 9672                            },
 9673                        },
 9674                        new_text: "new_text_3".to_string(),
 9675                    })),
 9676                    ..lsp::CompletionItem::default()
 9677                },
 9678                lsp::CompletionItem {
 9679                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9680                    detail: Some(
 9681                        "Details with many     spaces and \t but without newlines".to_string(),
 9682                    ),
 9683                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9684                        range: lsp::Range {
 9685                            start: lsp::Position {
 9686                                line: params.text_document_position.position.line,
 9687                                character: params.text_document_position.position.character,
 9688                            },
 9689                            end: lsp::Position {
 9690                                line: params.text_document_position.position.line,
 9691                                character: params.text_document_position.position.character,
 9692                            },
 9693                        },
 9694                        new_text: "new_text_4".to_string(),
 9695                    })),
 9696                    ..lsp::CompletionItem::default()
 9697                },
 9698            ])))
 9699        });
 9700
 9701    editor.update_in(cx, |editor, window, cx| {
 9702        cx.focus_self(window);
 9703        editor.move_to_end(&MoveToEnd, window, cx);
 9704        editor.handle_input(".", window, cx);
 9705    });
 9706    cx.run_until_parked();
 9707    completion_handle.next().await.unwrap();
 9708
 9709    editor.update(cx, |editor, _| {
 9710        assert!(editor.context_menu_visible());
 9711        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9712        {
 9713            let completion_labels = menu
 9714                .completions
 9715                .borrow()
 9716                .iter()
 9717                .map(|c| c.label.text.clone())
 9718                .collect::<Vec<_>>();
 9719            assert_eq!(
 9720                completion_labels,
 9721                &[
 9722                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9723                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9724                    "single line label 2 d e f ",
 9725                    "a b c g h i ",
 9726                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9727                ],
 9728                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9729            );
 9730
 9731            for completion in menu
 9732                .completions
 9733                .borrow()
 9734                .iter() {
 9735                    assert_eq!(
 9736                        completion.label.filter_range,
 9737                        0..completion.label.text.len(),
 9738                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9739                    );
 9740                }
 9741
 9742        } else {
 9743            panic!("expected completion menu to be open");
 9744        }
 9745    });
 9746}
 9747
 9748#[gpui::test]
 9749async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9750    init_test(cx, |_| {});
 9751    let mut cx = EditorLspTestContext::new_rust(
 9752        lsp::ServerCapabilities {
 9753            completion_provider: Some(lsp::CompletionOptions {
 9754                trigger_characters: Some(vec![".".to_string()]),
 9755                ..Default::default()
 9756            }),
 9757            ..Default::default()
 9758        },
 9759        cx,
 9760    )
 9761    .await;
 9762    cx.lsp
 9763        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9764            Ok(Some(lsp::CompletionResponse::Array(vec![
 9765                lsp::CompletionItem {
 9766                    label: "first".into(),
 9767                    ..Default::default()
 9768                },
 9769                lsp::CompletionItem {
 9770                    label: "last".into(),
 9771                    ..Default::default()
 9772                },
 9773            ])))
 9774        });
 9775    cx.set_state("variableˇ");
 9776    cx.simulate_keystroke(".");
 9777    cx.executor().run_until_parked();
 9778
 9779    cx.update_editor(|editor, _, _| {
 9780        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9781        {
 9782            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9783        } else {
 9784            panic!("expected completion menu to be open");
 9785        }
 9786    });
 9787
 9788    cx.update_editor(|editor, window, cx| {
 9789        editor.move_page_down(&MovePageDown::default(), window, cx);
 9790        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9791        {
 9792            assert!(
 9793                menu.selected_item == 1,
 9794                "expected PageDown to select the last item from the context menu"
 9795            );
 9796        } else {
 9797            panic!("expected completion menu to stay open after PageDown");
 9798        }
 9799    });
 9800
 9801    cx.update_editor(|editor, window, cx| {
 9802        editor.move_page_up(&MovePageUp::default(), window, cx);
 9803        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9804        {
 9805            assert!(
 9806                menu.selected_item == 0,
 9807                "expected PageUp to select the first item from the context menu"
 9808            );
 9809        } else {
 9810            panic!("expected completion menu to stay open after PageUp");
 9811        }
 9812    });
 9813}
 9814
 9815#[gpui::test]
 9816async fn test_completion_sort(cx: &mut TestAppContext) {
 9817    init_test(cx, |_| {});
 9818    let mut cx = EditorLspTestContext::new_rust(
 9819        lsp::ServerCapabilities {
 9820            completion_provider: Some(lsp::CompletionOptions {
 9821                trigger_characters: Some(vec![".".to_string()]),
 9822                ..Default::default()
 9823            }),
 9824            ..Default::default()
 9825        },
 9826        cx,
 9827    )
 9828    .await;
 9829    cx.lsp
 9830        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9831            Ok(Some(lsp::CompletionResponse::Array(vec![
 9832                lsp::CompletionItem {
 9833                    label: "Range".into(),
 9834                    sort_text: Some("a".into()),
 9835                    ..Default::default()
 9836                },
 9837                lsp::CompletionItem {
 9838                    label: "r".into(),
 9839                    sort_text: Some("b".into()),
 9840                    ..Default::default()
 9841                },
 9842                lsp::CompletionItem {
 9843                    label: "ret".into(),
 9844                    sort_text: Some("c".into()),
 9845                    ..Default::default()
 9846                },
 9847                lsp::CompletionItem {
 9848                    label: "return".into(),
 9849                    sort_text: Some("d".into()),
 9850                    ..Default::default()
 9851                },
 9852                lsp::CompletionItem {
 9853                    label: "slice".into(),
 9854                    sort_text: Some("d".into()),
 9855                    ..Default::default()
 9856                },
 9857            ])))
 9858        });
 9859    cx.set_state("");
 9860    cx.executor().run_until_parked();
 9861    cx.update_editor(|editor, window, cx| {
 9862        editor.show_completions(
 9863            &ShowCompletions {
 9864                trigger: Some("r".into()),
 9865            },
 9866            window,
 9867            cx,
 9868        );
 9869    });
 9870    cx.executor().run_until_parked();
 9871
 9872    cx.update_editor(|editor, _, _| {
 9873        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9874        {
 9875            assert_eq!(
 9876                completion_menu_entries(&menu),
 9877                &["r", "ret", "Range", "return"]
 9878            );
 9879        } else {
 9880            panic!("expected completion menu to be open");
 9881        }
 9882    });
 9883}
 9884
 9885#[gpui::test]
 9886async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
 9887    init_test(cx, |_| {});
 9888
 9889    let mut cx = EditorLspTestContext::new_rust(
 9890        lsp::ServerCapabilities {
 9891            completion_provider: Some(lsp::CompletionOptions {
 9892                trigger_characters: Some(vec![".".to_string()]),
 9893                resolve_provider: Some(true),
 9894                ..Default::default()
 9895            }),
 9896            ..Default::default()
 9897        },
 9898        cx,
 9899    )
 9900    .await;
 9901
 9902    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
 9903    cx.simulate_keystroke(".");
 9904    let completion_item = lsp::CompletionItem {
 9905        label: "Some".into(),
 9906        kind: Some(lsp::CompletionItemKind::SNIPPET),
 9907        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
 9908        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
 9909            kind: lsp::MarkupKind::Markdown,
 9910            value: "```rust\nSome(2)\n```".to_string(),
 9911        })),
 9912        deprecated: Some(false),
 9913        sort_text: Some("Some".to_string()),
 9914        filter_text: Some("Some".to_string()),
 9915        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
 9916        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9917            range: lsp::Range {
 9918                start: lsp::Position {
 9919                    line: 0,
 9920                    character: 22,
 9921                },
 9922                end: lsp::Position {
 9923                    line: 0,
 9924                    character: 22,
 9925                },
 9926            },
 9927            new_text: "Some(2)".to_string(),
 9928        })),
 9929        additional_text_edits: Some(vec![lsp::TextEdit {
 9930            range: lsp::Range {
 9931                start: lsp::Position {
 9932                    line: 0,
 9933                    character: 20,
 9934                },
 9935                end: lsp::Position {
 9936                    line: 0,
 9937                    character: 22,
 9938                },
 9939            },
 9940            new_text: "".to_string(),
 9941        }]),
 9942        ..Default::default()
 9943    };
 9944
 9945    let closure_completion_item = completion_item.clone();
 9946    let counter = Arc::new(AtomicUsize::new(0));
 9947    let counter_clone = counter.clone();
 9948    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
 9949        let task_completion_item = closure_completion_item.clone();
 9950        counter_clone.fetch_add(1, atomic::Ordering::Release);
 9951        async move {
 9952            Ok(Some(lsp::CompletionResponse::Array(vec![
 9953                task_completion_item,
 9954            ])))
 9955        }
 9956    });
 9957
 9958    cx.condition(|editor, _| editor.context_menu_visible())
 9959        .await;
 9960    cx.assert_editor_state(indoc! {"fn main() { let a = 2.ˇ; }"});
 9961    assert!(request.next().await.is_some());
 9962    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9963
 9964    cx.simulate_keystroke("S");
 9965    cx.simulate_keystroke("o");
 9966    cx.simulate_keystroke("m");
 9967    cx.condition(|editor, _| editor.context_menu_visible())
 9968        .await;
 9969    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Somˇ; }"});
 9970    assert!(request.next().await.is_some());
 9971    assert!(request.next().await.is_some());
 9972    assert!(request.next().await.is_some());
 9973    request.close();
 9974    assert!(request.next().await.is_none());
 9975    assert_eq!(
 9976        counter.load(atomic::Ordering::Acquire),
 9977        4,
 9978        "With the completions menu open, only one LSP request should happen per input"
 9979    );
 9980}
 9981
 9982#[gpui::test]
 9983async fn test_toggle_comment(cx: &mut TestAppContext) {
 9984    init_test(cx, |_| {});
 9985    let mut cx = EditorTestContext::new(cx).await;
 9986    let language = Arc::new(Language::new(
 9987        LanguageConfig {
 9988            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
 9989            ..Default::default()
 9990        },
 9991        Some(tree_sitter_rust::LANGUAGE.into()),
 9992    ));
 9993    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 9994
 9995    // If multiple selections intersect a line, the line is only toggled once.
 9996    cx.set_state(indoc! {"
 9997        fn a() {
 9998            «//b();
 9999            ˇ»// «c();
10000            //ˇ»  d();
10001        }
10002    "});
10003
10004    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10005
10006    cx.assert_editor_state(indoc! {"
10007        fn a() {
10008            «b();
10009            c();
10010            ˇ» d();
10011        }
10012    "});
10013
10014    // The comment prefix is inserted at the same column for every line in a
10015    // selection.
10016    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10017
10018    cx.assert_editor_state(indoc! {"
10019        fn a() {
10020            // «b();
10021            // c();
10022            ˇ»//  d();
10023        }
10024    "});
10025
10026    // If a selection ends at the beginning of a line, that line is not toggled.
10027    cx.set_selections_state(indoc! {"
10028        fn a() {
10029            // b();
10030            «// c();
10031        ˇ»    //  d();
10032        }
10033    "});
10034
10035    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10036
10037    cx.assert_editor_state(indoc! {"
10038        fn a() {
10039            // b();
10040            «c();
10041        ˇ»    //  d();
10042        }
10043    "});
10044
10045    // If a selection span a single line and is empty, the line is toggled.
10046    cx.set_state(indoc! {"
10047        fn a() {
10048            a();
10049            b();
10050        ˇ
10051        }
10052    "});
10053
10054    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10055
10056    cx.assert_editor_state(indoc! {"
10057        fn a() {
10058            a();
10059            b();
10060        //•ˇ
10061        }
10062    "});
10063
10064    // If a selection span multiple lines, empty lines are not toggled.
10065    cx.set_state(indoc! {"
10066        fn a() {
10067            «a();
10068
10069            c();ˇ»
10070        }
10071    "});
10072
10073    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10074
10075    cx.assert_editor_state(indoc! {"
10076        fn a() {
10077            // «a();
10078
10079            // c();ˇ»
10080        }
10081    "});
10082
10083    // If a selection includes multiple comment prefixes, all lines are uncommented.
10084    cx.set_state(indoc! {"
10085        fn a() {
10086            «// a();
10087            /// b();
10088            //! c();ˇ»
10089        }
10090    "});
10091
10092    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10093
10094    cx.assert_editor_state(indoc! {"
10095        fn a() {
10096            «a();
10097            b();
10098            c();ˇ»
10099        }
10100    "});
10101}
10102
10103#[gpui::test]
10104async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10105    init_test(cx, |_| {});
10106    let mut cx = EditorTestContext::new(cx).await;
10107    let language = Arc::new(Language::new(
10108        LanguageConfig {
10109            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10110            ..Default::default()
10111        },
10112        Some(tree_sitter_rust::LANGUAGE.into()),
10113    ));
10114    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10115
10116    let toggle_comments = &ToggleComments {
10117        advance_downwards: false,
10118        ignore_indent: true,
10119    };
10120
10121    // If multiple selections intersect a line, the line is only toggled once.
10122    cx.set_state(indoc! {"
10123        fn a() {
10124        //    «b();
10125        //    c();
10126        //    ˇ» d();
10127        }
10128    "});
10129
10130    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10131
10132    cx.assert_editor_state(indoc! {"
10133        fn a() {
10134            «b();
10135            c();
10136            ˇ» d();
10137        }
10138    "});
10139
10140    // The comment prefix is inserted at the beginning of each line
10141    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10142
10143    cx.assert_editor_state(indoc! {"
10144        fn a() {
10145        //    «b();
10146        //    c();
10147        //    ˇ» d();
10148        }
10149    "});
10150
10151    // If a selection ends at the beginning of a line, that line is not toggled.
10152    cx.set_selections_state(indoc! {"
10153        fn a() {
10154        //    b();
10155        //    «c();
10156        ˇ»//     d();
10157        }
10158    "});
10159
10160    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10161
10162    cx.assert_editor_state(indoc! {"
10163        fn a() {
10164        //    b();
10165            «c();
10166        ˇ»//     d();
10167        }
10168    "});
10169
10170    // If a selection span a single line and is empty, the line is toggled.
10171    cx.set_state(indoc! {"
10172        fn a() {
10173            a();
10174            b();
10175        ˇ
10176        }
10177    "});
10178
10179    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10180
10181    cx.assert_editor_state(indoc! {"
10182        fn a() {
10183            a();
10184            b();
10185        //ˇ
10186        }
10187    "});
10188
10189    // If a selection span multiple lines, empty lines are not toggled.
10190    cx.set_state(indoc! {"
10191        fn a() {
10192            «a();
10193
10194            c();ˇ»
10195        }
10196    "});
10197
10198    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10199
10200    cx.assert_editor_state(indoc! {"
10201        fn a() {
10202        //    «a();
10203
10204        //    c();ˇ»
10205        }
10206    "});
10207
10208    // If a selection includes multiple comment prefixes, all lines are uncommented.
10209    cx.set_state(indoc! {"
10210        fn a() {
10211        //    «a();
10212        ///    b();
10213        //!    c();ˇ»
10214        }
10215    "});
10216
10217    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10218
10219    cx.assert_editor_state(indoc! {"
10220        fn a() {
10221            «a();
10222            b();
10223            c();ˇ»
10224        }
10225    "});
10226}
10227
10228#[gpui::test]
10229async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10230    init_test(cx, |_| {});
10231
10232    let language = Arc::new(Language::new(
10233        LanguageConfig {
10234            line_comments: vec!["// ".into()],
10235            ..Default::default()
10236        },
10237        Some(tree_sitter_rust::LANGUAGE.into()),
10238    ));
10239
10240    let mut cx = EditorTestContext::new(cx).await;
10241
10242    cx.language_registry().add(language.clone());
10243    cx.update_buffer(|buffer, cx| {
10244        buffer.set_language(Some(language), cx);
10245    });
10246
10247    let toggle_comments = &ToggleComments {
10248        advance_downwards: true,
10249        ignore_indent: false,
10250    };
10251
10252    // Single cursor on one line -> advance
10253    // Cursor moves horizontally 3 characters as well on non-blank line
10254    cx.set_state(indoc!(
10255        "fn a() {
10256             ˇdog();
10257             cat();
10258        }"
10259    ));
10260    cx.update_editor(|editor, window, cx| {
10261        editor.toggle_comments(toggle_comments, window, cx);
10262    });
10263    cx.assert_editor_state(indoc!(
10264        "fn a() {
10265             // dog();
10266             catˇ();
10267        }"
10268    ));
10269
10270    // Single selection on one line -> don't advance
10271    cx.set_state(indoc!(
10272        "fn a() {
10273             «dog()ˇ»;
10274             cat();
10275        }"
10276    ));
10277    cx.update_editor(|editor, window, cx| {
10278        editor.toggle_comments(toggle_comments, window, cx);
10279    });
10280    cx.assert_editor_state(indoc!(
10281        "fn a() {
10282             // «dog()ˇ»;
10283             cat();
10284        }"
10285    ));
10286
10287    // Multiple cursors on one line -> advance
10288    cx.set_state(indoc!(
10289        "fn a() {
10290             ˇdˇog();
10291             cat();
10292        }"
10293    ));
10294    cx.update_editor(|editor, window, cx| {
10295        editor.toggle_comments(toggle_comments, window, cx);
10296    });
10297    cx.assert_editor_state(indoc!(
10298        "fn a() {
10299             // dog();
10300             catˇ(ˇ);
10301        }"
10302    ));
10303
10304    // Multiple cursors on one line, with selection -> don't advance
10305    cx.set_state(indoc!(
10306        "fn a() {
10307             ˇdˇog«()ˇ»;
10308             cat();
10309        }"
10310    ));
10311    cx.update_editor(|editor, window, cx| {
10312        editor.toggle_comments(toggle_comments, window, cx);
10313    });
10314    cx.assert_editor_state(indoc!(
10315        "fn a() {
10316             // ˇdˇog«()ˇ»;
10317             cat();
10318        }"
10319    ));
10320
10321    // Single cursor on one line -> advance
10322    // Cursor moves to column 0 on blank line
10323    cx.set_state(indoc!(
10324        "fn a() {
10325             ˇdog();
10326
10327             cat();
10328        }"
10329    ));
10330    cx.update_editor(|editor, window, cx| {
10331        editor.toggle_comments(toggle_comments, window, cx);
10332    });
10333    cx.assert_editor_state(indoc!(
10334        "fn a() {
10335             // dog();
10336        ˇ
10337             cat();
10338        }"
10339    ));
10340
10341    // Single cursor on one line -> advance
10342    // Cursor starts and ends at column 0
10343    cx.set_state(indoc!(
10344        "fn a() {
10345         ˇ    dog();
10346             cat();
10347        }"
10348    ));
10349    cx.update_editor(|editor, window, cx| {
10350        editor.toggle_comments(toggle_comments, window, cx);
10351    });
10352    cx.assert_editor_state(indoc!(
10353        "fn a() {
10354             // dog();
10355         ˇ    cat();
10356        }"
10357    ));
10358}
10359
10360#[gpui::test]
10361async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10362    init_test(cx, |_| {});
10363
10364    let mut cx = EditorTestContext::new(cx).await;
10365
10366    let html_language = Arc::new(
10367        Language::new(
10368            LanguageConfig {
10369                name: "HTML".into(),
10370                block_comment: Some(("<!-- ".into(), " -->".into())),
10371                ..Default::default()
10372            },
10373            Some(tree_sitter_html::LANGUAGE.into()),
10374        )
10375        .with_injection_query(
10376            r#"
10377            (script_element
10378                (raw_text) @injection.content
10379                (#set! injection.language "javascript"))
10380            "#,
10381        )
10382        .unwrap(),
10383    );
10384
10385    let javascript_language = Arc::new(Language::new(
10386        LanguageConfig {
10387            name: "JavaScript".into(),
10388            line_comments: vec!["// ".into()],
10389            ..Default::default()
10390        },
10391        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10392    ));
10393
10394    cx.language_registry().add(html_language.clone());
10395    cx.language_registry().add(javascript_language.clone());
10396    cx.update_buffer(|buffer, cx| {
10397        buffer.set_language(Some(html_language), cx);
10398    });
10399
10400    // Toggle comments for empty selections
10401    cx.set_state(
10402        &r#"
10403            <p>A</p>ˇ
10404            <p>B</p>ˇ
10405            <p>C</p>ˇ
10406        "#
10407        .unindent(),
10408    );
10409    cx.update_editor(|editor, window, cx| {
10410        editor.toggle_comments(&ToggleComments::default(), window, cx)
10411    });
10412    cx.assert_editor_state(
10413        &r#"
10414            <!-- <p>A</p>ˇ -->
10415            <!-- <p>B</p>ˇ -->
10416            <!-- <p>C</p>ˇ -->
10417        "#
10418        .unindent(),
10419    );
10420    cx.update_editor(|editor, window, cx| {
10421        editor.toggle_comments(&ToggleComments::default(), window, cx)
10422    });
10423    cx.assert_editor_state(
10424        &r#"
10425            <p>A</p>ˇ
10426            <p>B</p>ˇ
10427            <p>C</p>ˇ
10428        "#
10429        .unindent(),
10430    );
10431
10432    // Toggle comments for mixture of empty and non-empty selections, where
10433    // multiple selections occupy a given line.
10434    cx.set_state(
10435        &r#"
10436            <p>A«</p>
10437            <p>ˇ»B</p>ˇ
10438            <p>C«</p>
10439            <p>ˇ»D</p>ˇ
10440        "#
10441        .unindent(),
10442    );
10443
10444    cx.update_editor(|editor, window, cx| {
10445        editor.toggle_comments(&ToggleComments::default(), window, cx)
10446    });
10447    cx.assert_editor_state(
10448        &r#"
10449            <!-- <p>A«</p>
10450            <p>ˇ»B</p>ˇ -->
10451            <!-- <p>C«</p>
10452            <p>ˇ»D</p>ˇ -->
10453        "#
10454        .unindent(),
10455    );
10456    cx.update_editor(|editor, window, cx| {
10457        editor.toggle_comments(&ToggleComments::default(), window, cx)
10458    });
10459    cx.assert_editor_state(
10460        &r#"
10461            <p>A«</p>
10462            <p>ˇ»B</p>ˇ
10463            <p>C«</p>
10464            <p>ˇ»D</p>ˇ
10465        "#
10466        .unindent(),
10467    );
10468
10469    // Toggle comments when different languages are active for different
10470    // selections.
10471    cx.set_state(
10472        &r#"
10473            ˇ<script>
10474                ˇvar x = new Y();
10475            ˇ</script>
10476        "#
10477        .unindent(),
10478    );
10479    cx.executor().run_until_parked();
10480    cx.update_editor(|editor, window, cx| {
10481        editor.toggle_comments(&ToggleComments::default(), window, cx)
10482    });
10483    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10484    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10485    cx.assert_editor_state(
10486        &r#"
10487            <!-- ˇ<script> -->
10488                // ˇvar x = new Y();
10489            <!-- ˇ</script> -->
10490        "#
10491        .unindent(),
10492    );
10493}
10494
10495#[gpui::test]
10496fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10497    init_test(cx, |_| {});
10498
10499    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10500    let multibuffer = cx.new(|cx| {
10501        let mut multibuffer = MultiBuffer::new(ReadWrite);
10502        multibuffer.push_excerpts(
10503            buffer.clone(),
10504            [
10505                ExcerptRange {
10506                    context: Point::new(0, 0)..Point::new(0, 4),
10507                    primary: None,
10508                },
10509                ExcerptRange {
10510                    context: Point::new(1, 0)..Point::new(1, 4),
10511                    primary: None,
10512                },
10513            ],
10514            cx,
10515        );
10516        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10517        multibuffer
10518    });
10519
10520    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10521    editor.update_in(cx, |editor, window, cx| {
10522        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10523        editor.change_selections(None, window, cx, |s| {
10524            s.select_ranges([
10525                Point::new(0, 0)..Point::new(0, 0),
10526                Point::new(1, 0)..Point::new(1, 0),
10527            ])
10528        });
10529
10530        editor.handle_input("X", window, cx);
10531        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10532        assert_eq!(
10533            editor.selections.ranges(cx),
10534            [
10535                Point::new(0, 1)..Point::new(0, 1),
10536                Point::new(1, 1)..Point::new(1, 1),
10537            ]
10538        );
10539
10540        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10541        editor.change_selections(None, window, cx, |s| {
10542            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10543        });
10544        editor.backspace(&Default::default(), window, cx);
10545        assert_eq!(editor.text(cx), "Xa\nbbb");
10546        assert_eq!(
10547            editor.selections.ranges(cx),
10548            [Point::new(1, 0)..Point::new(1, 0)]
10549        );
10550
10551        editor.change_selections(None, window, cx, |s| {
10552            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10553        });
10554        editor.backspace(&Default::default(), window, cx);
10555        assert_eq!(editor.text(cx), "X\nbb");
10556        assert_eq!(
10557            editor.selections.ranges(cx),
10558            [Point::new(0, 1)..Point::new(0, 1)]
10559        );
10560    });
10561}
10562
10563#[gpui::test]
10564fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10565    init_test(cx, |_| {});
10566
10567    let markers = vec![('[', ']').into(), ('(', ')').into()];
10568    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10569        indoc! {"
10570            [aaaa
10571            (bbbb]
10572            cccc)",
10573        },
10574        markers.clone(),
10575    );
10576    let excerpt_ranges = markers.into_iter().map(|marker| {
10577        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10578        ExcerptRange {
10579            context,
10580            primary: None,
10581        }
10582    });
10583    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10584    let multibuffer = cx.new(|cx| {
10585        let mut multibuffer = MultiBuffer::new(ReadWrite);
10586        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10587        multibuffer
10588    });
10589
10590    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10591    editor.update_in(cx, |editor, window, cx| {
10592        let (expected_text, selection_ranges) = marked_text_ranges(
10593            indoc! {"
10594                aaaa
10595                bˇbbb
10596                bˇbbˇb
10597                cccc"
10598            },
10599            true,
10600        );
10601        assert_eq!(editor.text(cx), expected_text);
10602        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10603
10604        editor.handle_input("X", window, cx);
10605
10606        let (expected_text, expected_selections) = marked_text_ranges(
10607            indoc! {"
10608                aaaa
10609                bXˇbbXb
10610                bXˇbbXˇb
10611                cccc"
10612            },
10613            false,
10614        );
10615        assert_eq!(editor.text(cx), expected_text);
10616        assert_eq!(editor.selections.ranges(cx), expected_selections);
10617
10618        editor.newline(&Newline, window, cx);
10619        let (expected_text, expected_selections) = marked_text_ranges(
10620            indoc! {"
10621                aaaa
10622                bX
10623                ˇbbX
10624                b
10625                bX
10626                ˇbbX
10627                ˇb
10628                cccc"
10629            },
10630            false,
10631        );
10632        assert_eq!(editor.text(cx), expected_text);
10633        assert_eq!(editor.selections.ranges(cx), expected_selections);
10634    });
10635}
10636
10637#[gpui::test]
10638fn test_refresh_selections(cx: &mut TestAppContext) {
10639    init_test(cx, |_| {});
10640
10641    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10642    let mut excerpt1_id = None;
10643    let multibuffer = cx.new(|cx| {
10644        let mut multibuffer = MultiBuffer::new(ReadWrite);
10645        excerpt1_id = multibuffer
10646            .push_excerpts(
10647                buffer.clone(),
10648                [
10649                    ExcerptRange {
10650                        context: Point::new(0, 0)..Point::new(1, 4),
10651                        primary: None,
10652                    },
10653                    ExcerptRange {
10654                        context: Point::new(1, 0)..Point::new(2, 4),
10655                        primary: None,
10656                    },
10657                ],
10658                cx,
10659            )
10660            .into_iter()
10661            .next();
10662        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10663        multibuffer
10664    });
10665
10666    let editor = cx.add_window(|window, cx| {
10667        let mut editor = build_editor(multibuffer.clone(), window, cx);
10668        let snapshot = editor.snapshot(window, cx);
10669        editor.change_selections(None, window, cx, |s| {
10670            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10671        });
10672        editor.begin_selection(
10673            Point::new(2, 1).to_display_point(&snapshot),
10674            true,
10675            1,
10676            window,
10677            cx,
10678        );
10679        assert_eq!(
10680            editor.selections.ranges(cx),
10681            [
10682                Point::new(1, 3)..Point::new(1, 3),
10683                Point::new(2, 1)..Point::new(2, 1),
10684            ]
10685        );
10686        editor
10687    });
10688
10689    // Refreshing selections is a no-op when excerpts haven't changed.
10690    _ = editor.update(cx, |editor, window, cx| {
10691        editor.change_selections(None, window, cx, |s| s.refresh());
10692        assert_eq!(
10693            editor.selections.ranges(cx),
10694            [
10695                Point::new(1, 3)..Point::new(1, 3),
10696                Point::new(2, 1)..Point::new(2, 1),
10697            ]
10698        );
10699    });
10700
10701    multibuffer.update(cx, |multibuffer, cx| {
10702        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10703    });
10704    _ = editor.update(cx, |editor, window, cx| {
10705        // Removing an excerpt causes the first selection to become degenerate.
10706        assert_eq!(
10707            editor.selections.ranges(cx),
10708            [
10709                Point::new(0, 0)..Point::new(0, 0),
10710                Point::new(0, 1)..Point::new(0, 1)
10711            ]
10712        );
10713
10714        // Refreshing selections will relocate the first selection to the original buffer
10715        // location.
10716        editor.change_selections(None, window, cx, |s| s.refresh());
10717        assert_eq!(
10718            editor.selections.ranges(cx),
10719            [
10720                Point::new(0, 1)..Point::new(0, 1),
10721                Point::new(0, 3)..Point::new(0, 3)
10722            ]
10723        );
10724        assert!(editor.selections.pending_anchor().is_some());
10725    });
10726}
10727
10728#[gpui::test]
10729fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10730    init_test(cx, |_| {});
10731
10732    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10733    let mut excerpt1_id = None;
10734    let multibuffer = cx.new(|cx| {
10735        let mut multibuffer = MultiBuffer::new(ReadWrite);
10736        excerpt1_id = multibuffer
10737            .push_excerpts(
10738                buffer.clone(),
10739                [
10740                    ExcerptRange {
10741                        context: Point::new(0, 0)..Point::new(1, 4),
10742                        primary: None,
10743                    },
10744                    ExcerptRange {
10745                        context: Point::new(1, 0)..Point::new(2, 4),
10746                        primary: None,
10747                    },
10748                ],
10749                cx,
10750            )
10751            .into_iter()
10752            .next();
10753        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10754        multibuffer
10755    });
10756
10757    let editor = cx.add_window(|window, cx| {
10758        let mut editor = build_editor(multibuffer.clone(), window, cx);
10759        let snapshot = editor.snapshot(window, cx);
10760        editor.begin_selection(
10761            Point::new(1, 3).to_display_point(&snapshot),
10762            false,
10763            1,
10764            window,
10765            cx,
10766        );
10767        assert_eq!(
10768            editor.selections.ranges(cx),
10769            [Point::new(1, 3)..Point::new(1, 3)]
10770        );
10771        editor
10772    });
10773
10774    multibuffer.update(cx, |multibuffer, cx| {
10775        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10776    });
10777    _ = editor.update(cx, |editor, window, cx| {
10778        assert_eq!(
10779            editor.selections.ranges(cx),
10780            [Point::new(0, 0)..Point::new(0, 0)]
10781        );
10782
10783        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10784        editor.change_selections(None, window, cx, |s| s.refresh());
10785        assert_eq!(
10786            editor.selections.ranges(cx),
10787            [Point::new(0, 3)..Point::new(0, 3)]
10788        );
10789        assert!(editor.selections.pending_anchor().is_some());
10790    });
10791}
10792
10793#[gpui::test]
10794async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10795    init_test(cx, |_| {});
10796
10797    let language = Arc::new(
10798        Language::new(
10799            LanguageConfig {
10800                brackets: BracketPairConfig {
10801                    pairs: vec![
10802                        BracketPair {
10803                            start: "{".to_string(),
10804                            end: "}".to_string(),
10805                            close: true,
10806                            surround: true,
10807                            newline: true,
10808                        },
10809                        BracketPair {
10810                            start: "/* ".to_string(),
10811                            end: " */".to_string(),
10812                            close: true,
10813                            surround: true,
10814                            newline: true,
10815                        },
10816                    ],
10817                    ..Default::default()
10818                },
10819                ..Default::default()
10820            },
10821            Some(tree_sitter_rust::LANGUAGE.into()),
10822        )
10823        .with_indents_query("")
10824        .unwrap(),
10825    );
10826
10827    let text = concat!(
10828        "{   }\n",     //
10829        "  x\n",       //
10830        "  /*   */\n", //
10831        "x\n",         //
10832        "{{} }\n",     //
10833    );
10834
10835    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10836    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10837    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10838    editor
10839        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10840        .await;
10841
10842    editor.update_in(cx, |editor, window, cx| {
10843        editor.change_selections(None, window, cx, |s| {
10844            s.select_display_ranges([
10845                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10846                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10847                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10848            ])
10849        });
10850        editor.newline(&Newline, window, cx);
10851
10852        assert_eq!(
10853            editor.buffer().read(cx).read(cx).text(),
10854            concat!(
10855                "{ \n",    // Suppress rustfmt
10856                "\n",      //
10857                "}\n",     //
10858                "  x\n",   //
10859                "  /* \n", //
10860                "  \n",    //
10861                "  */\n",  //
10862                "x\n",     //
10863                "{{} \n",  //
10864                "}\n",     //
10865            )
10866        );
10867    });
10868}
10869
10870#[gpui::test]
10871fn test_highlighted_ranges(cx: &mut TestAppContext) {
10872    init_test(cx, |_| {});
10873
10874    let editor = cx.add_window(|window, cx| {
10875        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10876        build_editor(buffer.clone(), window, cx)
10877    });
10878
10879    _ = editor.update(cx, |editor, window, cx| {
10880        struct Type1;
10881        struct Type2;
10882
10883        let buffer = editor.buffer.read(cx).snapshot(cx);
10884
10885        let anchor_range =
10886            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
10887
10888        editor.highlight_background::<Type1>(
10889            &[
10890                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10891                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10892                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10893                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10894            ],
10895            |_| Hsla::red(),
10896            cx,
10897        );
10898        editor.highlight_background::<Type2>(
10899            &[
10900                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10901                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10902                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10903                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10904            ],
10905            |_| Hsla::green(),
10906            cx,
10907        );
10908
10909        let snapshot = editor.snapshot(window, cx);
10910        let mut highlighted_ranges = editor.background_highlights_in_range(
10911            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10912            &snapshot,
10913            cx.theme().colors(),
10914        );
10915        // Enforce a consistent ordering based on color without relying on the ordering of the
10916        // highlight's `TypeId` which is non-executor.
10917        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10918        assert_eq!(
10919            highlighted_ranges,
10920            &[
10921                (
10922                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
10923                    Hsla::red(),
10924                ),
10925                (
10926                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10927                    Hsla::red(),
10928                ),
10929                (
10930                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
10931                    Hsla::green(),
10932                ),
10933                (
10934                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
10935                    Hsla::green(),
10936                ),
10937            ]
10938        );
10939        assert_eq!(
10940            editor.background_highlights_in_range(
10941                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10942                &snapshot,
10943                cx.theme().colors(),
10944            ),
10945            &[(
10946                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
10947                Hsla::red(),
10948            )]
10949        );
10950    });
10951}
10952
10953#[gpui::test]
10954async fn test_following(cx: &mut TestAppContext) {
10955    init_test(cx, |_| {});
10956
10957    let fs = FakeFs::new(cx.executor());
10958    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
10959
10960    let buffer = project.update(cx, |project, cx| {
10961        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
10962        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
10963    });
10964    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
10965    let follower = cx.update(|cx| {
10966        cx.open_window(
10967            WindowOptions {
10968                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
10969                    gpui::Point::new(px(0.), px(0.)),
10970                    gpui::Point::new(px(10.), px(80.)),
10971                ))),
10972                ..Default::default()
10973            },
10974            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
10975        )
10976        .unwrap()
10977    });
10978
10979    let is_still_following = Rc::new(RefCell::new(true));
10980    let follower_edit_event_count = Rc::new(RefCell::new(0));
10981    let pending_update = Rc::new(RefCell::new(None));
10982    let leader_entity = leader.root(cx).unwrap();
10983    let follower_entity = follower.root(cx).unwrap();
10984    _ = follower.update(cx, {
10985        let update = pending_update.clone();
10986        let is_still_following = is_still_following.clone();
10987        let follower_edit_event_count = follower_edit_event_count.clone();
10988        |_, window, cx| {
10989            cx.subscribe_in(
10990                &leader_entity,
10991                window,
10992                move |_, leader, event, window, cx| {
10993                    leader.read(cx).add_event_to_update_proto(
10994                        event,
10995                        &mut update.borrow_mut(),
10996                        window,
10997                        cx,
10998                    );
10999                },
11000            )
11001            .detach();
11002
11003            cx.subscribe_in(
11004                &follower_entity,
11005                window,
11006                move |_, _, event: &EditorEvent, _window, _cx| {
11007                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11008                        *is_still_following.borrow_mut() = false;
11009                    }
11010
11011                    if let EditorEvent::BufferEdited = event {
11012                        *follower_edit_event_count.borrow_mut() += 1;
11013                    }
11014                },
11015            )
11016            .detach();
11017        }
11018    });
11019
11020    // Update the selections only
11021    _ = leader.update(cx, |leader, window, cx| {
11022        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11023    });
11024    follower
11025        .update(cx, |follower, window, cx| {
11026            follower.apply_update_proto(
11027                &project,
11028                pending_update.borrow_mut().take().unwrap(),
11029                window,
11030                cx,
11031            )
11032        })
11033        .unwrap()
11034        .await
11035        .unwrap();
11036    _ = follower.update(cx, |follower, _, cx| {
11037        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11038    });
11039    assert!(*is_still_following.borrow());
11040    assert_eq!(*follower_edit_event_count.borrow(), 0);
11041
11042    // Update the scroll position only
11043    _ = leader.update(cx, |leader, window, cx| {
11044        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11045    });
11046    follower
11047        .update(cx, |follower, window, cx| {
11048            follower.apply_update_proto(
11049                &project,
11050                pending_update.borrow_mut().take().unwrap(),
11051                window,
11052                cx,
11053            )
11054        })
11055        .unwrap()
11056        .await
11057        .unwrap();
11058    assert_eq!(
11059        follower
11060            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11061            .unwrap(),
11062        gpui::Point::new(1.5, 3.5)
11063    );
11064    assert!(*is_still_following.borrow());
11065    assert_eq!(*follower_edit_event_count.borrow(), 0);
11066
11067    // Update the selections and scroll position. The follower's scroll position is updated
11068    // via autoscroll, not via the leader's exact scroll position.
11069    _ = leader.update(cx, |leader, window, cx| {
11070        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11071        leader.request_autoscroll(Autoscroll::newest(), cx);
11072        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11073    });
11074    follower
11075        .update(cx, |follower, window, cx| {
11076            follower.apply_update_proto(
11077                &project,
11078                pending_update.borrow_mut().take().unwrap(),
11079                window,
11080                cx,
11081            )
11082        })
11083        .unwrap()
11084        .await
11085        .unwrap();
11086    _ = follower.update(cx, |follower, _, cx| {
11087        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11088        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11089    });
11090    assert!(*is_still_following.borrow());
11091
11092    // Creating a pending selection that precedes another selection
11093    _ = leader.update(cx, |leader, window, cx| {
11094        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11095        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11096    });
11097    follower
11098        .update(cx, |follower, window, cx| {
11099            follower.apply_update_proto(
11100                &project,
11101                pending_update.borrow_mut().take().unwrap(),
11102                window,
11103                cx,
11104            )
11105        })
11106        .unwrap()
11107        .await
11108        .unwrap();
11109    _ = follower.update(cx, |follower, _, cx| {
11110        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11111    });
11112    assert!(*is_still_following.borrow());
11113
11114    // Extend the pending selection so that it surrounds another selection
11115    _ = leader.update(cx, |leader, window, cx| {
11116        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11117    });
11118    follower
11119        .update(cx, |follower, window, cx| {
11120            follower.apply_update_proto(
11121                &project,
11122                pending_update.borrow_mut().take().unwrap(),
11123                window,
11124                cx,
11125            )
11126        })
11127        .unwrap()
11128        .await
11129        .unwrap();
11130    _ = follower.update(cx, |follower, _, cx| {
11131        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11132    });
11133
11134    // Scrolling locally breaks the follow
11135    _ = follower.update(cx, |follower, window, cx| {
11136        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11137        follower.set_scroll_anchor(
11138            ScrollAnchor {
11139                anchor: top_anchor,
11140                offset: gpui::Point::new(0.0, 0.5),
11141            },
11142            window,
11143            cx,
11144        );
11145    });
11146    assert!(!(*is_still_following.borrow()));
11147}
11148
11149#[gpui::test]
11150async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11151    init_test(cx, |_| {});
11152
11153    let fs = FakeFs::new(cx.executor());
11154    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11155    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11156    let pane = workspace
11157        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11158        .unwrap();
11159
11160    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11161
11162    let leader = pane.update_in(cx, |_, window, cx| {
11163        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11164        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11165    });
11166
11167    // Start following the editor when it has no excerpts.
11168    let mut state_message =
11169        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11170    let workspace_entity = workspace.root(cx).unwrap();
11171    let follower_1 = cx
11172        .update_window(*workspace.deref(), |_, window, cx| {
11173            Editor::from_state_proto(
11174                workspace_entity,
11175                ViewId {
11176                    creator: Default::default(),
11177                    id: 0,
11178                },
11179                &mut state_message,
11180                window,
11181                cx,
11182            )
11183        })
11184        .unwrap()
11185        .unwrap()
11186        .await
11187        .unwrap();
11188
11189    let update_message = Rc::new(RefCell::new(None));
11190    follower_1.update_in(cx, {
11191        let update = update_message.clone();
11192        |_, window, cx| {
11193            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11194                leader.read(cx).add_event_to_update_proto(
11195                    event,
11196                    &mut update.borrow_mut(),
11197                    window,
11198                    cx,
11199                );
11200            })
11201            .detach();
11202        }
11203    });
11204
11205    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11206        (
11207            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11208            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11209        )
11210    });
11211
11212    // Insert some excerpts.
11213    leader.update(cx, |leader, cx| {
11214        leader.buffer.update(cx, |multibuffer, cx| {
11215            let excerpt_ids = multibuffer.push_excerpts(
11216                buffer_1.clone(),
11217                [
11218                    ExcerptRange {
11219                        context: 1..6,
11220                        primary: None,
11221                    },
11222                    ExcerptRange {
11223                        context: 12..15,
11224                        primary: None,
11225                    },
11226                    ExcerptRange {
11227                        context: 0..3,
11228                        primary: None,
11229                    },
11230                ],
11231                cx,
11232            );
11233            multibuffer.insert_excerpts_after(
11234                excerpt_ids[0],
11235                buffer_2.clone(),
11236                [
11237                    ExcerptRange {
11238                        context: 8..12,
11239                        primary: None,
11240                    },
11241                    ExcerptRange {
11242                        context: 0..6,
11243                        primary: None,
11244                    },
11245                ],
11246                cx,
11247            );
11248        });
11249    });
11250
11251    // Apply the update of adding the excerpts.
11252    follower_1
11253        .update_in(cx, |follower, window, cx| {
11254            follower.apply_update_proto(
11255                &project,
11256                update_message.borrow().clone().unwrap(),
11257                window,
11258                cx,
11259            )
11260        })
11261        .await
11262        .unwrap();
11263    assert_eq!(
11264        follower_1.update(cx, |editor, cx| editor.text(cx)),
11265        leader.update(cx, |editor, cx| editor.text(cx))
11266    );
11267    update_message.borrow_mut().take();
11268
11269    // Start following separately after it already has excerpts.
11270    let mut state_message =
11271        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11272    let workspace_entity = workspace.root(cx).unwrap();
11273    let follower_2 = cx
11274        .update_window(*workspace.deref(), |_, window, cx| {
11275            Editor::from_state_proto(
11276                workspace_entity,
11277                ViewId {
11278                    creator: Default::default(),
11279                    id: 0,
11280                },
11281                &mut state_message,
11282                window,
11283                cx,
11284            )
11285        })
11286        .unwrap()
11287        .unwrap()
11288        .await
11289        .unwrap();
11290    assert_eq!(
11291        follower_2.update(cx, |editor, cx| editor.text(cx)),
11292        leader.update(cx, |editor, cx| editor.text(cx))
11293    );
11294
11295    // Remove some excerpts.
11296    leader.update(cx, |leader, cx| {
11297        leader.buffer.update(cx, |multibuffer, cx| {
11298            let excerpt_ids = multibuffer.excerpt_ids();
11299            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11300            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11301        });
11302    });
11303
11304    // Apply the update of removing the excerpts.
11305    follower_1
11306        .update_in(cx, |follower, window, cx| {
11307            follower.apply_update_proto(
11308                &project,
11309                update_message.borrow().clone().unwrap(),
11310                window,
11311                cx,
11312            )
11313        })
11314        .await
11315        .unwrap();
11316    follower_2
11317        .update_in(cx, |follower, window, cx| {
11318            follower.apply_update_proto(
11319                &project,
11320                update_message.borrow().clone().unwrap(),
11321                window,
11322                cx,
11323            )
11324        })
11325        .await
11326        .unwrap();
11327    update_message.borrow_mut().take();
11328    assert_eq!(
11329        follower_1.update(cx, |editor, cx| editor.text(cx)),
11330        leader.update(cx, |editor, cx| editor.text(cx))
11331    );
11332}
11333
11334#[gpui::test]
11335async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11336    init_test(cx, |_| {});
11337
11338    let mut cx = EditorTestContext::new(cx).await;
11339    let lsp_store =
11340        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11341
11342    cx.set_state(indoc! {"
11343        ˇfn func(abc def: i32) -> u32 {
11344        }
11345    "});
11346
11347    cx.update(|_, cx| {
11348        lsp_store.update(cx, |lsp_store, cx| {
11349            lsp_store
11350                .update_diagnostics(
11351                    LanguageServerId(0),
11352                    lsp::PublishDiagnosticsParams {
11353                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11354                        version: None,
11355                        diagnostics: vec![
11356                            lsp::Diagnostic {
11357                                range: lsp::Range::new(
11358                                    lsp::Position::new(0, 11),
11359                                    lsp::Position::new(0, 12),
11360                                ),
11361                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11362                                ..Default::default()
11363                            },
11364                            lsp::Diagnostic {
11365                                range: lsp::Range::new(
11366                                    lsp::Position::new(0, 12),
11367                                    lsp::Position::new(0, 15),
11368                                ),
11369                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11370                                ..Default::default()
11371                            },
11372                            lsp::Diagnostic {
11373                                range: lsp::Range::new(
11374                                    lsp::Position::new(0, 25),
11375                                    lsp::Position::new(0, 28),
11376                                ),
11377                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11378                                ..Default::default()
11379                            },
11380                        ],
11381                    },
11382                    &[],
11383                    cx,
11384                )
11385                .unwrap()
11386        });
11387    });
11388
11389    executor.run_until_parked();
11390
11391    cx.update_editor(|editor, window, cx| {
11392        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11393    });
11394
11395    cx.assert_editor_state(indoc! {"
11396        fn func(abc def: i32) -> ˇu32 {
11397        }
11398    "});
11399
11400    cx.update_editor(|editor, window, cx| {
11401        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11402    });
11403
11404    cx.assert_editor_state(indoc! {"
11405        fn func(abc ˇdef: i32) -> u32 {
11406        }
11407    "});
11408
11409    cx.update_editor(|editor, window, cx| {
11410        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11411    });
11412
11413    cx.assert_editor_state(indoc! {"
11414        fn func(abcˇ def: i32) -> u32 {
11415        }
11416    "});
11417
11418    cx.update_editor(|editor, window, cx| {
11419        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11420    });
11421
11422    cx.assert_editor_state(indoc! {"
11423        fn func(abc def: i32) -> ˇu32 {
11424        }
11425    "});
11426}
11427
11428#[gpui::test]
11429async fn cycle_through_same_place_diagnostics(
11430    executor: BackgroundExecutor,
11431    cx: &mut TestAppContext,
11432) {
11433    init_test(cx, |_| {});
11434
11435    let mut cx = EditorTestContext::new(cx).await;
11436    let lsp_store =
11437        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11438
11439    cx.set_state(indoc! {"
11440        ˇfn func(abc def: i32) -> u32 {
11441        }
11442    "});
11443
11444    cx.update(|_, cx| {
11445        lsp_store.update(cx, |lsp_store, cx| {
11446            lsp_store
11447                .update_diagnostics(
11448                    LanguageServerId(0),
11449                    lsp::PublishDiagnosticsParams {
11450                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11451                        version: None,
11452                        diagnostics: vec![
11453                            lsp::Diagnostic {
11454                                range: lsp::Range::new(
11455                                    lsp::Position::new(0, 11),
11456                                    lsp::Position::new(0, 12),
11457                                ),
11458                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11459                                ..Default::default()
11460                            },
11461                            lsp::Diagnostic {
11462                                range: lsp::Range::new(
11463                                    lsp::Position::new(0, 12),
11464                                    lsp::Position::new(0, 15),
11465                                ),
11466                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11467                                ..Default::default()
11468                            },
11469                            lsp::Diagnostic {
11470                                range: lsp::Range::new(
11471                                    lsp::Position::new(0, 12),
11472                                    lsp::Position::new(0, 15),
11473                                ),
11474                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11475                                ..Default::default()
11476                            },
11477                            lsp::Diagnostic {
11478                                range: lsp::Range::new(
11479                                    lsp::Position::new(0, 25),
11480                                    lsp::Position::new(0, 28),
11481                                ),
11482                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11483                                ..Default::default()
11484                            },
11485                        ],
11486                    },
11487                    &[],
11488                    cx,
11489                )
11490                .unwrap()
11491        });
11492    });
11493    executor.run_until_parked();
11494
11495    //// Backward
11496
11497    // Fourth diagnostic
11498    cx.update_editor(|editor, window, cx| {
11499        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11500    });
11501    cx.assert_editor_state(indoc! {"
11502        fn func(abc def: i32) -> ˇu32 {
11503        }
11504    "});
11505
11506    // Third diagnostic
11507    cx.update_editor(|editor, window, cx| {
11508        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11509    });
11510    cx.assert_editor_state(indoc! {"
11511        fn func(abc ˇdef: i32) -> u32 {
11512        }
11513    "});
11514
11515    // Second diagnostic, same place
11516    cx.update_editor(|editor, window, cx| {
11517        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11518    });
11519    cx.assert_editor_state(indoc! {"
11520        fn func(abc ˇdef: i32) -> u32 {
11521        }
11522    "});
11523
11524    // First diagnostic
11525    cx.update_editor(|editor, window, cx| {
11526        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11527    });
11528    cx.assert_editor_state(indoc! {"
11529        fn func(abcˇ def: i32) -> u32 {
11530        }
11531    "});
11532
11533    // Wrapped over, fourth diagnostic
11534    cx.update_editor(|editor, window, cx| {
11535        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11536    });
11537    cx.assert_editor_state(indoc! {"
11538        fn func(abc def: i32) -> ˇu32 {
11539        }
11540    "});
11541
11542    cx.update_editor(|editor, window, cx| {
11543        editor.move_to_beginning(&MoveToBeginning, window, cx);
11544    });
11545    cx.assert_editor_state(indoc! {"
11546        ˇfn func(abc def: i32) -> u32 {
11547        }
11548    "});
11549
11550    //// Forward
11551
11552    // First diagnostic
11553    cx.update_editor(|editor, window, cx| {
11554        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11555    });
11556    cx.assert_editor_state(indoc! {"
11557        fn func(abcˇ def: i32) -> u32 {
11558        }
11559    "});
11560
11561    // Second diagnostic
11562    cx.update_editor(|editor, window, cx| {
11563        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11564    });
11565    cx.assert_editor_state(indoc! {"
11566        fn func(abc ˇdef: i32) -> u32 {
11567        }
11568    "});
11569
11570    // Third diagnostic, same place
11571    cx.update_editor(|editor, window, cx| {
11572        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11573    });
11574    cx.assert_editor_state(indoc! {"
11575        fn func(abc ˇdef: i32) -> u32 {
11576        }
11577    "});
11578
11579    // Fourth diagnostic
11580    cx.update_editor(|editor, window, cx| {
11581        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11582    });
11583    cx.assert_editor_state(indoc! {"
11584        fn func(abc def: i32) -> ˇu32 {
11585        }
11586    "});
11587
11588    // Wrapped around, first diagnostic
11589    cx.update_editor(|editor, window, cx| {
11590        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11591    });
11592    cx.assert_editor_state(indoc! {"
11593        fn func(abcˇ def: i32) -> u32 {
11594        }
11595    "});
11596}
11597
11598#[gpui::test]
11599async fn active_diagnostics_dismiss_after_invalidation(
11600    executor: BackgroundExecutor,
11601    cx: &mut TestAppContext,
11602) {
11603    init_test(cx, |_| {});
11604
11605    let mut cx = EditorTestContext::new(cx).await;
11606    let lsp_store =
11607        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11608
11609    cx.set_state(indoc! {"
11610        ˇfn func(abc def: i32) -> u32 {
11611        }
11612    "});
11613
11614    let message = "Something's wrong!";
11615    cx.update(|_, cx| {
11616        lsp_store.update(cx, |lsp_store, cx| {
11617            lsp_store
11618                .update_diagnostics(
11619                    LanguageServerId(0),
11620                    lsp::PublishDiagnosticsParams {
11621                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11622                        version: None,
11623                        diagnostics: vec![lsp::Diagnostic {
11624                            range: lsp::Range::new(
11625                                lsp::Position::new(0, 11),
11626                                lsp::Position::new(0, 12),
11627                            ),
11628                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11629                            message: message.to_string(),
11630                            ..Default::default()
11631                        }],
11632                    },
11633                    &[],
11634                    cx,
11635                )
11636                .unwrap()
11637        });
11638    });
11639    executor.run_until_parked();
11640
11641    cx.update_editor(|editor, window, cx| {
11642        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11643        assert_eq!(
11644            editor
11645                .active_diagnostics
11646                .as_ref()
11647                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11648            Some(message),
11649            "Should have a diagnostics group activated"
11650        );
11651    });
11652    cx.assert_editor_state(indoc! {"
11653        fn func(abcˇ def: i32) -> u32 {
11654        }
11655    "});
11656
11657    cx.update(|_, cx| {
11658        lsp_store.update(cx, |lsp_store, cx| {
11659            lsp_store
11660                .update_diagnostics(
11661                    LanguageServerId(0),
11662                    lsp::PublishDiagnosticsParams {
11663                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11664                        version: None,
11665                        diagnostics: Vec::new(),
11666                    },
11667                    &[],
11668                    cx,
11669                )
11670                .unwrap()
11671        });
11672    });
11673    executor.run_until_parked();
11674    cx.update_editor(|editor, _, _| {
11675        assert_eq!(
11676            editor.active_diagnostics, None,
11677            "After no diagnostics set to the editor, no diagnostics should be active"
11678        );
11679    });
11680    cx.assert_editor_state(indoc! {"
11681        fn func(abcˇ def: i32) -> u32 {
11682        }
11683    "});
11684
11685    cx.update_editor(|editor, window, cx| {
11686        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11687        assert_eq!(
11688            editor.active_diagnostics, None,
11689            "Should be no diagnostics to go to and activate"
11690        );
11691    });
11692    cx.assert_editor_state(indoc! {"
11693        fn func(abcˇ def: i32) -> u32 {
11694        }
11695    "});
11696}
11697
11698#[gpui::test]
11699async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11700    init_test(cx, |_| {});
11701
11702    let mut cx = EditorTestContext::new(cx).await;
11703
11704    cx.set_state(indoc! {"
11705        fn func(abˇc def: i32) -> u32 {
11706        }
11707    "});
11708    let lsp_store =
11709        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11710
11711    cx.update(|_, cx| {
11712        lsp_store.update(cx, |lsp_store, cx| {
11713            lsp_store.update_diagnostics(
11714                LanguageServerId(0),
11715                lsp::PublishDiagnosticsParams {
11716                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11717                    version: None,
11718                    diagnostics: vec![lsp::Diagnostic {
11719                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11720                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11721                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11722                        ..Default::default()
11723                    }],
11724                },
11725                &[],
11726                cx,
11727            )
11728        })
11729    }).unwrap();
11730    cx.run_until_parked();
11731    cx.update_editor(|editor, window, cx| {
11732        hover_popover::hover(editor, &Default::default(), window, cx)
11733    });
11734    cx.run_until_parked();
11735    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11736}
11737
11738#[gpui::test]
11739async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11740    init_test(cx, |_| {});
11741
11742    let mut cx = EditorTestContext::new(cx).await;
11743
11744    let diff_base = r#"
11745        use some::mod;
11746
11747        const A: u32 = 42;
11748
11749        fn main() {
11750            println!("hello");
11751
11752            println!("world");
11753        }
11754        "#
11755    .unindent();
11756
11757    // Edits are modified, removed, modified, added
11758    cx.set_state(
11759        &r#"
11760        use some::modified;
11761
11762        ˇ
11763        fn main() {
11764            println!("hello there");
11765
11766            println!("around the");
11767            println!("world");
11768        }
11769        "#
11770        .unindent(),
11771    );
11772
11773    cx.set_head_text(&diff_base);
11774    executor.run_until_parked();
11775
11776    cx.update_editor(|editor, window, cx| {
11777        //Wrap around the bottom of the buffer
11778        for _ in 0..3 {
11779            editor.go_to_next_hunk(&GoToHunk, window, cx);
11780        }
11781    });
11782
11783    cx.assert_editor_state(
11784        &r#"
11785        ˇuse some::modified;
11786
11787
11788        fn main() {
11789            println!("hello there");
11790
11791            println!("around the");
11792            println!("world");
11793        }
11794        "#
11795        .unindent(),
11796    );
11797
11798    cx.update_editor(|editor, window, cx| {
11799        //Wrap around the top of the buffer
11800        for _ in 0..2 {
11801            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11802        }
11803    });
11804
11805    cx.assert_editor_state(
11806        &r#"
11807        use some::modified;
11808
11809
11810        fn main() {
11811        ˇ    println!("hello there");
11812
11813            println!("around the");
11814            println!("world");
11815        }
11816        "#
11817        .unindent(),
11818    );
11819
11820    cx.update_editor(|editor, window, cx| {
11821        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11822    });
11823
11824    cx.assert_editor_state(
11825        &r#"
11826        use some::modified;
11827
11828        ˇ
11829        fn main() {
11830            println!("hello there");
11831
11832            println!("around the");
11833            println!("world");
11834        }
11835        "#
11836        .unindent(),
11837    );
11838
11839    cx.update_editor(|editor, window, cx| {
11840        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11841    });
11842
11843    cx.assert_editor_state(
11844        &r#"
11845        ˇuse some::modified;
11846
11847
11848        fn main() {
11849            println!("hello there");
11850
11851            println!("around the");
11852            println!("world");
11853        }
11854        "#
11855        .unindent(),
11856    );
11857
11858    cx.update_editor(|editor, window, cx| {
11859        for _ in 0..2 {
11860            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11861        }
11862    });
11863
11864    cx.assert_editor_state(
11865        &r#"
11866        use some::modified;
11867
11868
11869        fn main() {
11870        ˇ    println!("hello there");
11871
11872            println!("around the");
11873            println!("world");
11874        }
11875        "#
11876        .unindent(),
11877    );
11878
11879    cx.update_editor(|editor, window, cx| {
11880        editor.fold(&Fold, window, cx);
11881    });
11882
11883    cx.update_editor(|editor, window, cx| {
11884        editor.go_to_next_hunk(&GoToHunk, window, cx);
11885    });
11886
11887    cx.assert_editor_state(
11888        &r#"
11889        ˇuse some::modified;
11890
11891
11892        fn main() {
11893            println!("hello there");
11894
11895            println!("around the");
11896            println!("world");
11897        }
11898        "#
11899        .unindent(),
11900    );
11901}
11902
11903#[test]
11904fn test_split_words() {
11905    fn split(text: &str) -> Vec<&str> {
11906        split_words(text).collect()
11907    }
11908
11909    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
11910    assert_eq!(split("hello_world"), &["hello_", "world"]);
11911    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
11912    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
11913    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
11914    assert_eq!(split("helloworld"), &["helloworld"]);
11915
11916    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
11917}
11918
11919#[gpui::test]
11920async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
11921    init_test(cx, |_| {});
11922
11923    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
11924    let mut assert = |before, after| {
11925        let _state_context = cx.set_state(before);
11926        cx.run_until_parked();
11927        cx.update_editor(|editor, window, cx| {
11928            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
11929        });
11930        cx.run_until_parked();
11931        cx.assert_editor_state(after);
11932    };
11933
11934    // Outside bracket jumps to outside of matching bracket
11935    assert("console.logˇ(var);", "console.log(var)ˇ;");
11936    assert("console.log(var)ˇ;", "console.logˇ(var);");
11937
11938    // Inside bracket jumps to inside of matching bracket
11939    assert("console.log(ˇvar);", "console.log(varˇ);");
11940    assert("console.log(varˇ);", "console.log(ˇvar);");
11941
11942    // When outside a bracket and inside, favor jumping to the inside bracket
11943    assert(
11944        "console.log('foo', [1, 2, 3]ˇ);",
11945        "console.log(ˇ'foo', [1, 2, 3]);",
11946    );
11947    assert(
11948        "console.log(ˇ'foo', [1, 2, 3]);",
11949        "console.log('foo', [1, 2, 3]ˇ);",
11950    );
11951
11952    // Bias forward if two options are equally likely
11953    assert(
11954        "let result = curried_fun()ˇ();",
11955        "let result = curried_fun()()ˇ;",
11956    );
11957
11958    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
11959    assert(
11960        indoc! {"
11961            function test() {
11962                console.log('test')ˇ
11963            }"},
11964        indoc! {"
11965            function test() {
11966                console.logˇ('test')
11967            }"},
11968    );
11969}
11970
11971#[gpui::test]
11972async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
11973    init_test(cx, |_| {});
11974
11975    let fs = FakeFs::new(cx.executor());
11976    fs.insert_tree(
11977        path!("/a"),
11978        json!({
11979            "main.rs": "fn main() { let a = 5; }",
11980            "other.rs": "// Test file",
11981        }),
11982    )
11983    .await;
11984    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11985
11986    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11987    language_registry.add(Arc::new(Language::new(
11988        LanguageConfig {
11989            name: "Rust".into(),
11990            matcher: LanguageMatcher {
11991                path_suffixes: vec!["rs".to_string()],
11992                ..Default::default()
11993            },
11994            brackets: BracketPairConfig {
11995                pairs: vec![BracketPair {
11996                    start: "{".to_string(),
11997                    end: "}".to_string(),
11998                    close: true,
11999                    surround: true,
12000                    newline: true,
12001                }],
12002                disabled_scopes_by_bracket_ix: Vec::new(),
12003            },
12004            ..Default::default()
12005        },
12006        Some(tree_sitter_rust::LANGUAGE.into()),
12007    )));
12008    let mut fake_servers = language_registry.register_fake_lsp(
12009        "Rust",
12010        FakeLspAdapter {
12011            capabilities: lsp::ServerCapabilities {
12012                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12013                    first_trigger_character: "{".to_string(),
12014                    more_trigger_character: None,
12015                }),
12016                ..Default::default()
12017            },
12018            ..Default::default()
12019        },
12020    );
12021
12022    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12023
12024    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12025
12026    let worktree_id = workspace
12027        .update(cx, |workspace, _, cx| {
12028            workspace.project().update(cx, |project, cx| {
12029                project.worktrees(cx).next().unwrap().read(cx).id()
12030            })
12031        })
12032        .unwrap();
12033
12034    let buffer = project
12035        .update(cx, |project, cx| {
12036            project.open_local_buffer(path!("/a/main.rs"), cx)
12037        })
12038        .await
12039        .unwrap();
12040    let editor_handle = workspace
12041        .update(cx, |workspace, window, cx| {
12042            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12043        })
12044        .unwrap()
12045        .await
12046        .unwrap()
12047        .downcast::<Editor>()
12048        .unwrap();
12049
12050    cx.executor().start_waiting();
12051    let fake_server = fake_servers.next().await.unwrap();
12052
12053    fake_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
12054        assert_eq!(
12055            params.text_document_position.text_document.uri,
12056            lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12057        );
12058        assert_eq!(
12059            params.text_document_position.position,
12060            lsp::Position::new(0, 21),
12061        );
12062
12063        Ok(Some(vec![lsp::TextEdit {
12064            new_text: "]".to_string(),
12065            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12066        }]))
12067    });
12068
12069    editor_handle.update_in(cx, |editor, window, cx| {
12070        window.focus(&editor.focus_handle(cx));
12071        editor.change_selections(None, window, cx, |s| {
12072            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12073        });
12074        editor.handle_input("{", window, cx);
12075    });
12076
12077    cx.executor().run_until_parked();
12078
12079    buffer.update(cx, |buffer, _| {
12080        assert_eq!(
12081            buffer.text(),
12082            "fn main() { let a = {5}; }",
12083            "No extra braces from on type formatting should appear in the buffer"
12084        )
12085    });
12086}
12087
12088#[gpui::test]
12089async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12090    init_test(cx, |_| {});
12091
12092    let fs = FakeFs::new(cx.executor());
12093    fs.insert_tree(
12094        path!("/a"),
12095        json!({
12096            "main.rs": "fn main() { let a = 5; }",
12097            "other.rs": "// Test file",
12098        }),
12099    )
12100    .await;
12101
12102    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12103
12104    let server_restarts = Arc::new(AtomicUsize::new(0));
12105    let closure_restarts = Arc::clone(&server_restarts);
12106    let language_server_name = "test language server";
12107    let language_name: LanguageName = "Rust".into();
12108
12109    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12110    language_registry.add(Arc::new(Language::new(
12111        LanguageConfig {
12112            name: language_name.clone(),
12113            matcher: LanguageMatcher {
12114                path_suffixes: vec!["rs".to_string()],
12115                ..Default::default()
12116            },
12117            ..Default::default()
12118        },
12119        Some(tree_sitter_rust::LANGUAGE.into()),
12120    )));
12121    let mut fake_servers = language_registry.register_fake_lsp(
12122        "Rust",
12123        FakeLspAdapter {
12124            name: language_server_name,
12125            initialization_options: Some(json!({
12126                "testOptionValue": true
12127            })),
12128            initializer: Some(Box::new(move |fake_server| {
12129                let task_restarts = Arc::clone(&closure_restarts);
12130                fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
12131                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12132                    futures::future::ready(Ok(()))
12133                });
12134            })),
12135            ..Default::default()
12136        },
12137    );
12138
12139    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12140    let _buffer = project
12141        .update(cx, |project, cx| {
12142            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12143        })
12144        .await
12145        .unwrap();
12146    let _fake_server = fake_servers.next().await.unwrap();
12147    update_test_language_settings(cx, |language_settings| {
12148        language_settings.languages.insert(
12149            language_name.clone(),
12150            LanguageSettingsContent {
12151                tab_size: NonZeroU32::new(8),
12152                ..Default::default()
12153            },
12154        );
12155    });
12156    cx.executor().run_until_parked();
12157    assert_eq!(
12158        server_restarts.load(atomic::Ordering::Acquire),
12159        0,
12160        "Should not restart LSP server on an unrelated change"
12161    );
12162
12163    update_test_project_settings(cx, |project_settings| {
12164        project_settings.lsp.insert(
12165            "Some other server name".into(),
12166            LspSettings {
12167                binary: None,
12168                settings: None,
12169                initialization_options: Some(json!({
12170                    "some other init value": false
12171                })),
12172            },
12173        );
12174    });
12175    cx.executor().run_until_parked();
12176    assert_eq!(
12177        server_restarts.load(atomic::Ordering::Acquire),
12178        0,
12179        "Should not restart LSP server on an unrelated LSP settings change"
12180    );
12181
12182    update_test_project_settings(cx, |project_settings| {
12183        project_settings.lsp.insert(
12184            language_server_name.into(),
12185            LspSettings {
12186                binary: None,
12187                settings: None,
12188                initialization_options: Some(json!({
12189                    "anotherInitValue": false
12190                })),
12191            },
12192        );
12193    });
12194    cx.executor().run_until_parked();
12195    assert_eq!(
12196        server_restarts.load(atomic::Ordering::Acquire),
12197        1,
12198        "Should restart LSP server on a related LSP settings change"
12199    );
12200
12201    update_test_project_settings(cx, |project_settings| {
12202        project_settings.lsp.insert(
12203            language_server_name.into(),
12204            LspSettings {
12205                binary: None,
12206                settings: None,
12207                initialization_options: Some(json!({
12208                    "anotherInitValue": false
12209                })),
12210            },
12211        );
12212    });
12213    cx.executor().run_until_parked();
12214    assert_eq!(
12215        server_restarts.load(atomic::Ordering::Acquire),
12216        1,
12217        "Should not restart LSP server on a related LSP settings change that is the same"
12218    );
12219
12220    update_test_project_settings(cx, |project_settings| {
12221        project_settings.lsp.insert(
12222            language_server_name.into(),
12223            LspSettings {
12224                binary: None,
12225                settings: None,
12226                initialization_options: None,
12227            },
12228        );
12229    });
12230    cx.executor().run_until_parked();
12231    assert_eq!(
12232        server_restarts.load(atomic::Ordering::Acquire),
12233        2,
12234        "Should restart LSP server on another related LSP settings change"
12235    );
12236}
12237
12238#[gpui::test]
12239async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12240    init_test(cx, |_| {});
12241
12242    let mut cx = EditorLspTestContext::new_rust(
12243        lsp::ServerCapabilities {
12244            completion_provider: Some(lsp::CompletionOptions {
12245                trigger_characters: Some(vec![".".to_string()]),
12246                resolve_provider: Some(true),
12247                ..Default::default()
12248            }),
12249            ..Default::default()
12250        },
12251        cx,
12252    )
12253    .await;
12254
12255    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12256    cx.simulate_keystroke(".");
12257    let completion_item = lsp::CompletionItem {
12258        label: "some".into(),
12259        kind: Some(lsp::CompletionItemKind::SNIPPET),
12260        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12261        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12262            kind: lsp::MarkupKind::Markdown,
12263            value: "```rust\nSome(2)\n```".to_string(),
12264        })),
12265        deprecated: Some(false),
12266        sort_text: Some("fffffff2".to_string()),
12267        filter_text: Some("some".to_string()),
12268        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12269        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12270            range: lsp::Range {
12271                start: lsp::Position {
12272                    line: 0,
12273                    character: 22,
12274                },
12275                end: lsp::Position {
12276                    line: 0,
12277                    character: 22,
12278                },
12279            },
12280            new_text: "Some(2)".to_string(),
12281        })),
12282        additional_text_edits: Some(vec![lsp::TextEdit {
12283            range: lsp::Range {
12284                start: lsp::Position {
12285                    line: 0,
12286                    character: 20,
12287                },
12288                end: lsp::Position {
12289                    line: 0,
12290                    character: 22,
12291                },
12292            },
12293            new_text: "".to_string(),
12294        }]),
12295        ..Default::default()
12296    };
12297
12298    let closure_completion_item = completion_item.clone();
12299    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12300        let task_completion_item = closure_completion_item.clone();
12301        async move {
12302            Ok(Some(lsp::CompletionResponse::Array(vec![
12303                task_completion_item,
12304            ])))
12305        }
12306    });
12307
12308    request.next().await;
12309
12310    cx.condition(|editor, _| editor.context_menu_visible())
12311        .await;
12312    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12313        editor
12314            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12315            .unwrap()
12316    });
12317    cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
12318
12319    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12320        let task_completion_item = completion_item.clone();
12321        async move { Ok(task_completion_item) }
12322    })
12323    .next()
12324    .await
12325    .unwrap();
12326    apply_additional_edits.await.unwrap();
12327    cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
12328}
12329
12330#[gpui::test]
12331async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12332    init_test(cx, |_| {});
12333
12334    let mut cx = EditorLspTestContext::new_rust(
12335        lsp::ServerCapabilities {
12336            completion_provider: Some(lsp::CompletionOptions {
12337                trigger_characters: Some(vec![".".to_string()]),
12338                resolve_provider: Some(true),
12339                ..Default::default()
12340            }),
12341            ..Default::default()
12342        },
12343        cx,
12344    )
12345    .await;
12346
12347    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12348    cx.simulate_keystroke(".");
12349
12350    let item1 = lsp::CompletionItem {
12351        label: "method id()".to_string(),
12352        filter_text: Some("id".to_string()),
12353        detail: None,
12354        documentation: None,
12355        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12356            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12357            new_text: ".id".to_string(),
12358        })),
12359        ..lsp::CompletionItem::default()
12360    };
12361
12362    let item2 = lsp::CompletionItem {
12363        label: "other".to_string(),
12364        filter_text: Some("other".to_string()),
12365        detail: None,
12366        documentation: None,
12367        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12368            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12369            new_text: ".other".to_string(),
12370        })),
12371        ..lsp::CompletionItem::default()
12372    };
12373
12374    let item1 = item1.clone();
12375    cx.handle_request::<lsp::request::Completion, _, _>({
12376        let item1 = item1.clone();
12377        move |_, _, _| {
12378            let item1 = item1.clone();
12379            let item2 = item2.clone();
12380            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12381        }
12382    })
12383    .next()
12384    .await;
12385
12386    cx.condition(|editor, _| editor.context_menu_visible())
12387        .await;
12388    cx.update_editor(|editor, _, _| {
12389        let context_menu = editor.context_menu.borrow_mut();
12390        let context_menu = context_menu
12391            .as_ref()
12392            .expect("Should have the context menu deployed");
12393        match context_menu {
12394            CodeContextMenu::Completions(completions_menu) => {
12395                let completions = completions_menu.completions.borrow_mut();
12396                assert_eq!(
12397                    completions
12398                        .iter()
12399                        .map(|completion| &completion.label.text)
12400                        .collect::<Vec<_>>(),
12401                    vec!["method id()", "other"]
12402                )
12403            }
12404            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12405        }
12406    });
12407
12408    cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>({
12409        let item1 = item1.clone();
12410        move |_, item_to_resolve, _| {
12411            let item1 = item1.clone();
12412            async move {
12413                if item1 == item_to_resolve {
12414                    Ok(lsp::CompletionItem {
12415                        label: "method id()".to_string(),
12416                        filter_text: Some("id".to_string()),
12417                        detail: Some("Now resolved!".to_string()),
12418                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12419                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12420                            range: lsp::Range::new(
12421                                lsp::Position::new(0, 22),
12422                                lsp::Position::new(0, 22),
12423                            ),
12424                            new_text: ".id".to_string(),
12425                        })),
12426                        ..lsp::CompletionItem::default()
12427                    })
12428                } else {
12429                    Ok(item_to_resolve)
12430                }
12431            }
12432        }
12433    })
12434    .next()
12435    .await
12436    .unwrap();
12437    cx.run_until_parked();
12438
12439    cx.update_editor(|editor, window, cx| {
12440        editor.context_menu_next(&Default::default(), window, cx);
12441    });
12442
12443    cx.update_editor(|editor, _, _| {
12444        let context_menu = editor.context_menu.borrow_mut();
12445        let context_menu = context_menu
12446            .as_ref()
12447            .expect("Should have the context menu deployed");
12448        match context_menu {
12449            CodeContextMenu::Completions(completions_menu) => {
12450                let completions = completions_menu.completions.borrow_mut();
12451                assert_eq!(
12452                    completions
12453                        .iter()
12454                        .map(|completion| &completion.label.text)
12455                        .collect::<Vec<_>>(),
12456                    vec!["method id() Now resolved!", "other"],
12457                    "Should update first completion label, but not second as the filter text did not match."
12458                );
12459            }
12460            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12461        }
12462    });
12463}
12464
12465#[gpui::test]
12466async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12467    init_test(cx, |_| {});
12468
12469    let mut cx = EditorLspTestContext::new_rust(
12470        lsp::ServerCapabilities {
12471            completion_provider: Some(lsp::CompletionOptions {
12472                trigger_characters: Some(vec![".".to_string()]),
12473                resolve_provider: Some(true),
12474                ..Default::default()
12475            }),
12476            ..Default::default()
12477        },
12478        cx,
12479    )
12480    .await;
12481
12482    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12483    cx.simulate_keystroke(".");
12484
12485    let unresolved_item_1 = lsp::CompletionItem {
12486        label: "id".to_string(),
12487        filter_text: Some("id".to_string()),
12488        detail: None,
12489        documentation: None,
12490        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12491            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12492            new_text: ".id".to_string(),
12493        })),
12494        ..lsp::CompletionItem::default()
12495    };
12496    let resolved_item_1 = lsp::CompletionItem {
12497        additional_text_edits: Some(vec![lsp::TextEdit {
12498            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12499            new_text: "!!".to_string(),
12500        }]),
12501        ..unresolved_item_1.clone()
12502    };
12503    let unresolved_item_2 = lsp::CompletionItem {
12504        label: "other".to_string(),
12505        filter_text: Some("other".to_string()),
12506        detail: None,
12507        documentation: None,
12508        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12509            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12510            new_text: ".other".to_string(),
12511        })),
12512        ..lsp::CompletionItem::default()
12513    };
12514    let resolved_item_2 = lsp::CompletionItem {
12515        additional_text_edits: Some(vec![lsp::TextEdit {
12516            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12517            new_text: "??".to_string(),
12518        }]),
12519        ..unresolved_item_2.clone()
12520    };
12521
12522    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12523    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12524    cx.lsp
12525        .server
12526        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12527            let unresolved_item_1 = unresolved_item_1.clone();
12528            let resolved_item_1 = resolved_item_1.clone();
12529            let unresolved_item_2 = unresolved_item_2.clone();
12530            let resolved_item_2 = resolved_item_2.clone();
12531            let resolve_requests_1 = resolve_requests_1.clone();
12532            let resolve_requests_2 = resolve_requests_2.clone();
12533            move |unresolved_request, _| {
12534                let unresolved_item_1 = unresolved_item_1.clone();
12535                let resolved_item_1 = resolved_item_1.clone();
12536                let unresolved_item_2 = unresolved_item_2.clone();
12537                let resolved_item_2 = resolved_item_2.clone();
12538                let resolve_requests_1 = resolve_requests_1.clone();
12539                let resolve_requests_2 = resolve_requests_2.clone();
12540                async move {
12541                    if unresolved_request == unresolved_item_1 {
12542                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12543                        Ok(resolved_item_1.clone())
12544                    } else if unresolved_request == unresolved_item_2 {
12545                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12546                        Ok(resolved_item_2.clone())
12547                    } else {
12548                        panic!("Unexpected completion item {unresolved_request:?}")
12549                    }
12550                }
12551            }
12552        })
12553        .detach();
12554
12555    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12556        let unresolved_item_1 = unresolved_item_1.clone();
12557        let unresolved_item_2 = unresolved_item_2.clone();
12558        async move {
12559            Ok(Some(lsp::CompletionResponse::Array(vec![
12560                unresolved_item_1,
12561                unresolved_item_2,
12562            ])))
12563        }
12564    })
12565    .next()
12566    .await;
12567
12568    cx.condition(|editor, _| editor.context_menu_visible())
12569        .await;
12570    cx.update_editor(|editor, _, _| {
12571        let context_menu = editor.context_menu.borrow_mut();
12572        let context_menu = context_menu
12573            .as_ref()
12574            .expect("Should have the context menu deployed");
12575        match context_menu {
12576            CodeContextMenu::Completions(completions_menu) => {
12577                let completions = completions_menu.completions.borrow_mut();
12578                assert_eq!(
12579                    completions
12580                        .iter()
12581                        .map(|completion| &completion.label.text)
12582                        .collect::<Vec<_>>(),
12583                    vec!["id", "other"]
12584                )
12585            }
12586            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12587        }
12588    });
12589    cx.run_until_parked();
12590
12591    cx.update_editor(|editor, window, cx| {
12592        editor.context_menu_next(&ContextMenuNext, window, cx);
12593    });
12594    cx.run_until_parked();
12595    cx.update_editor(|editor, window, cx| {
12596        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12597    });
12598    cx.run_until_parked();
12599    cx.update_editor(|editor, window, cx| {
12600        editor.context_menu_next(&ContextMenuNext, window, cx);
12601    });
12602    cx.run_until_parked();
12603    cx.update_editor(|editor, window, cx| {
12604        editor
12605            .compose_completion(&ComposeCompletion::default(), window, cx)
12606            .expect("No task returned")
12607    })
12608    .await
12609    .expect("Completion failed");
12610    cx.run_until_parked();
12611
12612    cx.update_editor(|editor, _, cx| {
12613        assert_eq!(
12614            resolve_requests_1.load(atomic::Ordering::Acquire),
12615            1,
12616            "Should always resolve once despite multiple selections"
12617        );
12618        assert_eq!(
12619            resolve_requests_2.load(atomic::Ordering::Acquire),
12620            1,
12621            "Should always resolve once after multiple selections and applying the completion"
12622        );
12623        assert_eq!(
12624            editor.text(cx),
12625            "fn main() { let a = ??.other; }",
12626            "Should use resolved data when applying the completion"
12627        );
12628    });
12629}
12630
12631#[gpui::test]
12632async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12633    init_test(cx, |_| {});
12634
12635    let item_0 = lsp::CompletionItem {
12636        label: "abs".into(),
12637        insert_text: Some("abs".into()),
12638        data: Some(json!({ "very": "special"})),
12639        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12640        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12641            lsp::InsertReplaceEdit {
12642                new_text: "abs".to_string(),
12643                insert: lsp::Range::default(),
12644                replace: lsp::Range::default(),
12645            },
12646        )),
12647        ..lsp::CompletionItem::default()
12648    };
12649    let items = iter::once(item_0.clone())
12650        .chain((11..51).map(|i| lsp::CompletionItem {
12651            label: format!("item_{}", i),
12652            insert_text: Some(format!("item_{}", i)),
12653            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12654            ..lsp::CompletionItem::default()
12655        }))
12656        .collect::<Vec<_>>();
12657
12658    let default_commit_characters = vec!["?".to_string()];
12659    let default_data = json!({ "default": "data"});
12660    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12661    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12662    let default_edit_range = lsp::Range {
12663        start: lsp::Position {
12664            line: 0,
12665            character: 5,
12666        },
12667        end: lsp::Position {
12668            line: 0,
12669            character: 5,
12670        },
12671    };
12672
12673    let mut cx = EditorLspTestContext::new_rust(
12674        lsp::ServerCapabilities {
12675            completion_provider: Some(lsp::CompletionOptions {
12676                trigger_characters: Some(vec![".".to_string()]),
12677                resolve_provider: Some(true),
12678                ..Default::default()
12679            }),
12680            ..Default::default()
12681        },
12682        cx,
12683    )
12684    .await;
12685
12686    cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
12687    cx.simulate_keystroke(".");
12688
12689    let completion_data = default_data.clone();
12690    let completion_characters = default_commit_characters.clone();
12691    let completion_items = items.clone();
12692    cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
12693        let default_data = completion_data.clone();
12694        let default_commit_characters = completion_characters.clone();
12695        let items = completion_items.clone();
12696        async move {
12697            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12698                items,
12699                item_defaults: Some(lsp::CompletionListItemDefaults {
12700                    data: Some(default_data.clone()),
12701                    commit_characters: Some(default_commit_characters.clone()),
12702                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12703                        default_edit_range,
12704                    )),
12705                    insert_text_format: Some(default_insert_text_format),
12706                    insert_text_mode: Some(default_insert_text_mode),
12707                }),
12708                ..lsp::CompletionList::default()
12709            })))
12710        }
12711    })
12712    .next()
12713    .await;
12714
12715    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12716    cx.lsp
12717        .server
12718        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12719            let closure_resolved_items = resolved_items.clone();
12720            move |item_to_resolve, _| {
12721                let closure_resolved_items = closure_resolved_items.clone();
12722                async move {
12723                    closure_resolved_items.lock().push(item_to_resolve.clone());
12724                    Ok(item_to_resolve)
12725                }
12726            }
12727        })
12728        .detach();
12729
12730    cx.condition(|editor, _| editor.context_menu_visible())
12731        .await;
12732    cx.run_until_parked();
12733    cx.update_editor(|editor, _, _| {
12734        let menu = editor.context_menu.borrow_mut();
12735        match menu.as_ref().expect("should have the completions menu") {
12736            CodeContextMenu::Completions(completions_menu) => {
12737                assert_eq!(
12738                    completions_menu
12739                        .entries
12740                        .borrow()
12741                        .iter()
12742                        .map(|mat| mat.string.clone())
12743                        .collect::<Vec<String>>(),
12744                    items
12745                        .iter()
12746                        .map(|completion| completion.label.clone())
12747                        .collect::<Vec<String>>()
12748                );
12749            }
12750            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12751        }
12752    });
12753    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12754    // with 4 from the end.
12755    assert_eq!(
12756        *resolved_items.lock(),
12757        [&items[0..16], &items[items.len() - 4..items.len()]]
12758            .concat()
12759            .iter()
12760            .cloned()
12761            .map(|mut item| {
12762                if item.data.is_none() {
12763                    item.data = Some(default_data.clone());
12764                }
12765                item
12766            })
12767            .collect::<Vec<lsp::CompletionItem>>(),
12768        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12769    );
12770    resolved_items.lock().clear();
12771
12772    cx.update_editor(|editor, window, cx| {
12773        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12774    });
12775    cx.run_until_parked();
12776    // Completions that have already been resolved are skipped.
12777    assert_eq!(
12778        *resolved_items.lock(),
12779        items[items.len() - 16..items.len() - 4]
12780            .iter()
12781            .cloned()
12782            .map(|mut item| {
12783                if item.data.is_none() {
12784                    item.data = Some(default_data.clone());
12785                }
12786                item
12787            })
12788            .collect::<Vec<lsp::CompletionItem>>()
12789    );
12790    resolved_items.lock().clear();
12791}
12792
12793#[gpui::test]
12794async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12795    init_test(cx, |_| {});
12796
12797    let mut cx = EditorLspTestContext::new(
12798        Language::new(
12799            LanguageConfig {
12800                matcher: LanguageMatcher {
12801                    path_suffixes: vec!["jsx".into()],
12802                    ..Default::default()
12803                },
12804                overrides: [(
12805                    "element".into(),
12806                    LanguageConfigOverride {
12807                        word_characters: Override::Set(['-'].into_iter().collect()),
12808                        ..Default::default()
12809                    },
12810                )]
12811                .into_iter()
12812                .collect(),
12813                ..Default::default()
12814            },
12815            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12816        )
12817        .with_override_query("(jsx_self_closing_element) @element")
12818        .unwrap(),
12819        lsp::ServerCapabilities {
12820            completion_provider: Some(lsp::CompletionOptions {
12821                trigger_characters: Some(vec![":".to_string()]),
12822                ..Default::default()
12823            }),
12824            ..Default::default()
12825        },
12826        cx,
12827    )
12828    .await;
12829
12830    cx.lsp
12831        .handle_request::<lsp::request::Completion, _, _>(move |_, _| async move {
12832            Ok(Some(lsp::CompletionResponse::Array(vec![
12833                lsp::CompletionItem {
12834                    label: "bg-blue".into(),
12835                    ..Default::default()
12836                },
12837                lsp::CompletionItem {
12838                    label: "bg-red".into(),
12839                    ..Default::default()
12840                },
12841                lsp::CompletionItem {
12842                    label: "bg-yellow".into(),
12843                    ..Default::default()
12844                },
12845            ])))
12846        });
12847
12848    cx.set_state(r#"<p class="bgˇ" />"#);
12849
12850    // Trigger completion when typing a dash, because the dash is an extra
12851    // word character in the 'element' scope, which contains the cursor.
12852    cx.simulate_keystroke("-");
12853    cx.executor().run_until_parked();
12854    cx.update_editor(|editor, _, _| {
12855        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12856        {
12857            assert_eq!(
12858                completion_menu_entries(&menu),
12859                &["bg-red", "bg-blue", "bg-yellow"]
12860            );
12861        } else {
12862            panic!("expected completion menu to be open");
12863        }
12864    });
12865
12866    cx.simulate_keystroke("l");
12867    cx.executor().run_until_parked();
12868    cx.update_editor(|editor, _, _| {
12869        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12870        {
12871            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12872        } else {
12873            panic!("expected completion menu to be open");
12874        }
12875    });
12876
12877    // When filtering completions, consider the character after the '-' to
12878    // be the start of a subword.
12879    cx.set_state(r#"<p class="yelˇ" />"#);
12880    cx.simulate_keystroke("l");
12881    cx.executor().run_until_parked();
12882    cx.update_editor(|editor, _, _| {
12883        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12884        {
12885            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12886        } else {
12887            panic!("expected completion menu to be open");
12888        }
12889    });
12890}
12891
12892fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
12893    let entries = menu.entries.borrow();
12894    entries.iter().map(|mat| mat.string.clone()).collect()
12895}
12896
12897#[gpui::test]
12898async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
12899    init_test(cx, |settings| {
12900        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
12901            FormatterList(vec![Formatter::Prettier].into()),
12902        ))
12903    });
12904
12905    let fs = FakeFs::new(cx.executor());
12906    fs.insert_file(path!("/file.ts"), Default::default()).await;
12907
12908    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
12909    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12910
12911    language_registry.add(Arc::new(Language::new(
12912        LanguageConfig {
12913            name: "TypeScript".into(),
12914            matcher: LanguageMatcher {
12915                path_suffixes: vec!["ts".to_string()],
12916                ..Default::default()
12917            },
12918            ..Default::default()
12919        },
12920        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
12921    )));
12922    update_test_language_settings(cx, |settings| {
12923        settings.defaults.prettier = Some(PrettierSettings {
12924            allowed: true,
12925            ..PrettierSettings::default()
12926        });
12927    });
12928
12929    let test_plugin = "test_plugin";
12930    let _ = language_registry.register_fake_lsp(
12931        "TypeScript",
12932        FakeLspAdapter {
12933            prettier_plugins: vec![test_plugin],
12934            ..Default::default()
12935        },
12936    );
12937
12938    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
12939    let buffer = project
12940        .update(cx, |project, cx| {
12941            project.open_local_buffer(path!("/file.ts"), cx)
12942        })
12943        .await
12944        .unwrap();
12945
12946    let buffer_text = "one\ntwo\nthree\n";
12947    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12948    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12949    editor.update_in(cx, |editor, window, cx| {
12950        editor.set_text(buffer_text, window, cx)
12951    });
12952
12953    editor
12954        .update_in(cx, |editor, window, cx| {
12955            editor.perform_format(
12956                project.clone(),
12957                FormatTrigger::Manual,
12958                FormatTarget::Buffers,
12959                window,
12960                cx,
12961            )
12962        })
12963        .unwrap()
12964        .await;
12965    assert_eq!(
12966        editor.update(cx, |editor, cx| editor.text(cx)),
12967        buffer_text.to_string() + prettier_format_suffix,
12968        "Test prettier formatting was not applied to the original buffer text",
12969    );
12970
12971    update_test_language_settings(cx, |settings| {
12972        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
12973    });
12974    let format = editor.update_in(cx, |editor, window, cx| {
12975        editor.perform_format(
12976            project.clone(),
12977            FormatTrigger::Manual,
12978            FormatTarget::Buffers,
12979            window,
12980            cx,
12981        )
12982    });
12983    format.await.unwrap();
12984    assert_eq!(
12985        editor.update(cx, |editor, cx| editor.text(cx)),
12986        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
12987        "Autoformatting (via test prettier) was not applied to the original buffer text",
12988    );
12989}
12990
12991#[gpui::test]
12992async fn test_addition_reverts(cx: &mut TestAppContext) {
12993    init_test(cx, |_| {});
12994    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
12995    let base_text = indoc! {r#"
12996        struct Row;
12997        struct Row1;
12998        struct Row2;
12999
13000        struct Row4;
13001        struct Row5;
13002        struct Row6;
13003
13004        struct Row8;
13005        struct Row9;
13006        struct Row10;"#};
13007
13008    // When addition hunks are not adjacent to carets, no hunk revert is performed
13009    assert_hunk_revert(
13010        indoc! {r#"struct Row;
13011                   struct Row1;
13012                   struct Row1.1;
13013                   struct Row1.2;
13014                   struct Row2;ˇ
13015
13016                   struct Row4;
13017                   struct Row5;
13018                   struct Row6;
13019
13020                   struct Row8;
13021                   ˇstruct Row9;
13022                   struct Row9.1;
13023                   struct Row9.2;
13024                   struct Row9.3;
13025                   struct Row10;"#},
13026        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13027        indoc! {r#"struct Row;
13028                   struct Row1;
13029                   struct Row1.1;
13030                   struct Row1.2;
13031                   struct Row2;ˇ
13032
13033                   struct Row4;
13034                   struct Row5;
13035                   struct Row6;
13036
13037                   struct Row8;
13038                   ˇstruct Row9;
13039                   struct Row9.1;
13040                   struct Row9.2;
13041                   struct Row9.3;
13042                   struct Row10;"#},
13043        base_text,
13044        &mut cx,
13045    );
13046    // Same for selections
13047    assert_hunk_revert(
13048        indoc! {r#"struct Row;
13049                   struct Row1;
13050                   struct Row2;
13051                   struct Row2.1;
13052                   struct Row2.2;
13053                   «ˇ
13054                   struct Row4;
13055                   struct» Row5;
13056                   «struct Row6;
13057                   ˇ»
13058                   struct Row9.1;
13059                   struct Row9.2;
13060                   struct Row9.3;
13061                   struct Row8;
13062                   struct Row9;
13063                   struct Row10;"#},
13064        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13065        indoc! {r#"struct Row;
13066                   struct Row1;
13067                   struct Row2;
13068                   struct Row2.1;
13069                   struct Row2.2;
13070                   «ˇ
13071                   struct Row4;
13072                   struct» Row5;
13073                   «struct Row6;
13074                   ˇ»
13075                   struct Row9.1;
13076                   struct Row9.2;
13077                   struct Row9.3;
13078                   struct Row8;
13079                   struct Row9;
13080                   struct Row10;"#},
13081        base_text,
13082        &mut cx,
13083    );
13084
13085    // When carets and selections intersect the addition hunks, those are reverted.
13086    // Adjacent carets got merged.
13087    assert_hunk_revert(
13088        indoc! {r#"struct Row;
13089                   ˇ// something on the top
13090                   struct Row1;
13091                   struct Row2;
13092                   struct Roˇw3.1;
13093                   struct Row2.2;
13094                   struct Row2.3;ˇ
13095
13096                   struct Row4;
13097                   struct ˇRow5.1;
13098                   struct Row5.2;
13099                   struct «Rowˇ»5.3;
13100                   struct Row5;
13101                   struct Row6;
13102                   ˇ
13103                   struct Row9.1;
13104                   struct «Rowˇ»9.2;
13105                   struct «ˇRow»9.3;
13106                   struct Row8;
13107                   struct Row9;
13108                   «ˇ// something on bottom»
13109                   struct Row10;"#},
13110        vec![
13111            DiffHunkStatusKind::Added,
13112            DiffHunkStatusKind::Added,
13113            DiffHunkStatusKind::Added,
13114            DiffHunkStatusKind::Added,
13115            DiffHunkStatusKind::Added,
13116        ],
13117        indoc! {r#"struct Row;
13118                   ˇstruct Row1;
13119                   struct Row2;
13120                   ˇ
13121                   struct Row4;
13122                   ˇstruct Row5;
13123                   struct Row6;
13124                   ˇ
13125                   ˇstruct Row8;
13126                   struct Row9;
13127                   ˇstruct Row10;"#},
13128        base_text,
13129        &mut cx,
13130    );
13131}
13132
13133#[gpui::test]
13134async fn test_modification_reverts(cx: &mut TestAppContext) {
13135    init_test(cx, |_| {});
13136    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13137    let base_text = indoc! {r#"
13138        struct Row;
13139        struct Row1;
13140        struct Row2;
13141
13142        struct Row4;
13143        struct Row5;
13144        struct Row6;
13145
13146        struct Row8;
13147        struct Row9;
13148        struct Row10;"#};
13149
13150    // Modification hunks behave the same as the addition ones.
13151    assert_hunk_revert(
13152        indoc! {r#"struct Row;
13153                   struct Row1;
13154                   struct Row33;
13155                   ˇ
13156                   struct Row4;
13157                   struct Row5;
13158                   struct Row6;
13159                   ˇ
13160                   struct Row99;
13161                   struct Row9;
13162                   struct Row10;"#},
13163        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13164        indoc! {r#"struct Row;
13165                   struct Row1;
13166                   struct Row33;
13167                   ˇ
13168                   struct Row4;
13169                   struct Row5;
13170                   struct Row6;
13171                   ˇ
13172                   struct Row99;
13173                   struct Row9;
13174                   struct Row10;"#},
13175        base_text,
13176        &mut cx,
13177    );
13178    assert_hunk_revert(
13179        indoc! {r#"struct Row;
13180                   struct Row1;
13181                   struct Row33;
13182                   «ˇ
13183                   struct Row4;
13184                   struct» Row5;
13185                   «struct Row6;
13186                   ˇ»
13187                   struct Row99;
13188                   struct Row9;
13189                   struct Row10;"#},
13190        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13191        indoc! {r#"struct Row;
13192                   struct Row1;
13193                   struct Row33;
13194                   «ˇ
13195                   struct Row4;
13196                   struct» Row5;
13197                   «struct Row6;
13198                   ˇ»
13199                   struct Row99;
13200                   struct Row9;
13201                   struct Row10;"#},
13202        base_text,
13203        &mut cx,
13204    );
13205
13206    assert_hunk_revert(
13207        indoc! {r#"ˇstruct Row1.1;
13208                   struct Row1;
13209                   «ˇstr»uct Row22;
13210
13211                   struct ˇRow44;
13212                   struct Row5;
13213                   struct «Rˇ»ow66;ˇ
13214
13215                   «struˇ»ct Row88;
13216                   struct Row9;
13217                   struct Row1011;ˇ"#},
13218        vec![
13219            DiffHunkStatusKind::Modified,
13220            DiffHunkStatusKind::Modified,
13221            DiffHunkStatusKind::Modified,
13222            DiffHunkStatusKind::Modified,
13223            DiffHunkStatusKind::Modified,
13224            DiffHunkStatusKind::Modified,
13225        ],
13226        indoc! {r#"struct Row;
13227                   ˇstruct Row1;
13228                   struct Row2;
13229                   ˇ
13230                   struct Row4;
13231                   ˇstruct Row5;
13232                   struct Row6;
13233                   ˇ
13234                   struct Row8;
13235                   ˇstruct Row9;
13236                   struct Row10;ˇ"#},
13237        base_text,
13238        &mut cx,
13239    );
13240}
13241
13242#[gpui::test]
13243async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13244    init_test(cx, |_| {});
13245    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13246    let base_text = indoc! {r#"
13247        one
13248
13249        two
13250        three
13251        "#};
13252
13253    cx.set_head_text(base_text);
13254    cx.set_state("\nˇ\n");
13255    cx.executor().run_until_parked();
13256    cx.update_editor(|editor, _window, cx| {
13257        editor.expand_selected_diff_hunks(cx);
13258    });
13259    cx.executor().run_until_parked();
13260    cx.update_editor(|editor, window, cx| {
13261        editor.backspace(&Default::default(), window, cx);
13262    });
13263    cx.run_until_parked();
13264    cx.assert_state_with_diff(
13265        indoc! {r#"
13266
13267        - two
13268        - threeˇ
13269        +
13270        "#}
13271        .to_string(),
13272    );
13273}
13274
13275#[gpui::test]
13276async fn test_deletion_reverts(cx: &mut TestAppContext) {
13277    init_test(cx, |_| {});
13278    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13279    let base_text = indoc! {r#"struct Row;
13280struct Row1;
13281struct Row2;
13282
13283struct Row4;
13284struct Row5;
13285struct Row6;
13286
13287struct Row8;
13288struct Row9;
13289struct Row10;"#};
13290
13291    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13292    assert_hunk_revert(
13293        indoc! {r#"struct Row;
13294                   struct Row2;
13295
13296                   ˇstruct Row4;
13297                   struct Row5;
13298                   struct Row6;
13299                   ˇ
13300                   struct Row8;
13301                   struct Row10;"#},
13302        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13303        indoc! {r#"struct Row;
13304                   struct Row2;
13305
13306                   ˇstruct Row4;
13307                   struct Row5;
13308                   struct Row6;
13309                   ˇ
13310                   struct Row8;
13311                   struct Row10;"#},
13312        base_text,
13313        &mut cx,
13314    );
13315    assert_hunk_revert(
13316        indoc! {r#"struct Row;
13317                   struct Row2;
13318
13319                   «ˇstruct Row4;
13320                   struct» Row5;
13321                   «struct Row6;
13322                   ˇ»
13323                   struct Row8;
13324                   struct Row10;"#},
13325        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13326        indoc! {r#"struct Row;
13327                   struct Row2;
13328
13329                   «ˇstruct Row4;
13330                   struct» Row5;
13331                   «struct Row6;
13332                   ˇ»
13333                   struct Row8;
13334                   struct Row10;"#},
13335        base_text,
13336        &mut cx,
13337    );
13338
13339    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13340    assert_hunk_revert(
13341        indoc! {r#"struct Row;
13342                   ˇstruct Row2;
13343
13344                   struct Row4;
13345                   struct Row5;
13346                   struct Row6;
13347
13348                   struct Row8;ˇ
13349                   struct Row10;"#},
13350        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13351        indoc! {r#"struct Row;
13352                   struct Row1;
13353                   ˇstruct Row2;
13354
13355                   struct Row4;
13356                   struct Row5;
13357                   struct Row6;
13358
13359                   struct Row8;ˇ
13360                   struct Row9;
13361                   struct Row10;"#},
13362        base_text,
13363        &mut cx,
13364    );
13365    assert_hunk_revert(
13366        indoc! {r#"struct Row;
13367                   struct Row2«ˇ;
13368                   struct Row4;
13369                   struct» Row5;
13370                   «struct Row6;
13371
13372                   struct Row8;ˇ»
13373                   struct Row10;"#},
13374        vec![
13375            DiffHunkStatusKind::Deleted,
13376            DiffHunkStatusKind::Deleted,
13377            DiffHunkStatusKind::Deleted,
13378        ],
13379        indoc! {r#"struct Row;
13380                   struct Row1;
13381                   struct Row2«ˇ;
13382
13383                   struct Row4;
13384                   struct» Row5;
13385                   «struct Row6;
13386
13387                   struct Row8;ˇ»
13388                   struct Row9;
13389                   struct Row10;"#},
13390        base_text,
13391        &mut cx,
13392    );
13393}
13394
13395#[gpui::test]
13396async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13397    init_test(cx, |_| {});
13398
13399    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13400    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13401    let base_text_3 =
13402        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13403
13404    let text_1 = edit_first_char_of_every_line(base_text_1);
13405    let text_2 = edit_first_char_of_every_line(base_text_2);
13406    let text_3 = edit_first_char_of_every_line(base_text_3);
13407
13408    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13409    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13410    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13411
13412    let multibuffer = cx.new(|cx| {
13413        let mut multibuffer = MultiBuffer::new(ReadWrite);
13414        multibuffer.push_excerpts(
13415            buffer_1.clone(),
13416            [
13417                ExcerptRange {
13418                    context: Point::new(0, 0)..Point::new(3, 0),
13419                    primary: None,
13420                },
13421                ExcerptRange {
13422                    context: Point::new(5, 0)..Point::new(7, 0),
13423                    primary: None,
13424                },
13425                ExcerptRange {
13426                    context: Point::new(9, 0)..Point::new(10, 4),
13427                    primary: None,
13428                },
13429            ],
13430            cx,
13431        );
13432        multibuffer.push_excerpts(
13433            buffer_2.clone(),
13434            [
13435                ExcerptRange {
13436                    context: Point::new(0, 0)..Point::new(3, 0),
13437                    primary: None,
13438                },
13439                ExcerptRange {
13440                    context: Point::new(5, 0)..Point::new(7, 0),
13441                    primary: None,
13442                },
13443                ExcerptRange {
13444                    context: Point::new(9, 0)..Point::new(10, 4),
13445                    primary: None,
13446                },
13447            ],
13448            cx,
13449        );
13450        multibuffer.push_excerpts(
13451            buffer_3.clone(),
13452            [
13453                ExcerptRange {
13454                    context: Point::new(0, 0)..Point::new(3, 0),
13455                    primary: None,
13456                },
13457                ExcerptRange {
13458                    context: Point::new(5, 0)..Point::new(7, 0),
13459                    primary: None,
13460                },
13461                ExcerptRange {
13462                    context: Point::new(9, 0)..Point::new(10, 4),
13463                    primary: None,
13464                },
13465            ],
13466            cx,
13467        );
13468        multibuffer
13469    });
13470
13471    let fs = FakeFs::new(cx.executor());
13472    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13473    let (editor, cx) = cx
13474        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13475    editor.update_in(cx, |editor, _window, cx| {
13476        for (buffer, diff_base) in [
13477            (buffer_1.clone(), base_text_1),
13478            (buffer_2.clone(), base_text_2),
13479            (buffer_3.clone(), base_text_3),
13480        ] {
13481            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13482            editor
13483                .buffer
13484                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13485        }
13486    });
13487    cx.executor().run_until_parked();
13488
13489    editor.update_in(cx, |editor, window, cx| {
13490        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}");
13491        editor.select_all(&SelectAll, window, cx);
13492        editor.git_restore(&Default::default(), window, cx);
13493    });
13494    cx.executor().run_until_parked();
13495
13496    // When all ranges are selected, all buffer hunks are reverted.
13497    editor.update(cx, |editor, cx| {
13498        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");
13499    });
13500    buffer_1.update(cx, |buffer, _| {
13501        assert_eq!(buffer.text(), base_text_1);
13502    });
13503    buffer_2.update(cx, |buffer, _| {
13504        assert_eq!(buffer.text(), base_text_2);
13505    });
13506    buffer_3.update(cx, |buffer, _| {
13507        assert_eq!(buffer.text(), base_text_3);
13508    });
13509
13510    editor.update_in(cx, |editor, window, cx| {
13511        editor.undo(&Default::default(), window, cx);
13512    });
13513
13514    editor.update_in(cx, |editor, window, cx| {
13515        editor.change_selections(None, window, cx, |s| {
13516            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13517        });
13518        editor.git_restore(&Default::default(), window, cx);
13519    });
13520
13521    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13522    // but not affect buffer_2 and its related excerpts.
13523    editor.update(cx, |editor, cx| {
13524        assert_eq!(
13525            editor.text(cx),
13526            "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}"
13527        );
13528    });
13529    buffer_1.update(cx, |buffer, _| {
13530        assert_eq!(buffer.text(), base_text_1);
13531    });
13532    buffer_2.update(cx, |buffer, _| {
13533        assert_eq!(
13534            buffer.text(),
13535            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13536        );
13537    });
13538    buffer_3.update(cx, |buffer, _| {
13539        assert_eq!(
13540            buffer.text(),
13541            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13542        );
13543    });
13544
13545    fn edit_first_char_of_every_line(text: &str) -> String {
13546        text.split('\n')
13547            .map(|line| format!("X{}", &line[1..]))
13548            .collect::<Vec<_>>()
13549            .join("\n")
13550    }
13551}
13552
13553#[gpui::test]
13554async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13555    init_test(cx, |_| {});
13556
13557    let cols = 4;
13558    let rows = 10;
13559    let sample_text_1 = sample_text(rows, cols, 'a');
13560    assert_eq!(
13561        sample_text_1,
13562        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13563    );
13564    let sample_text_2 = sample_text(rows, cols, 'l');
13565    assert_eq!(
13566        sample_text_2,
13567        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13568    );
13569    let sample_text_3 = sample_text(rows, cols, 'v');
13570    assert_eq!(
13571        sample_text_3,
13572        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13573    );
13574
13575    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13576    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13577    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13578
13579    let multi_buffer = cx.new(|cx| {
13580        let mut multibuffer = MultiBuffer::new(ReadWrite);
13581        multibuffer.push_excerpts(
13582            buffer_1.clone(),
13583            [
13584                ExcerptRange {
13585                    context: Point::new(0, 0)..Point::new(3, 0),
13586                    primary: None,
13587                },
13588                ExcerptRange {
13589                    context: Point::new(5, 0)..Point::new(7, 0),
13590                    primary: None,
13591                },
13592                ExcerptRange {
13593                    context: Point::new(9, 0)..Point::new(10, 4),
13594                    primary: None,
13595                },
13596            ],
13597            cx,
13598        );
13599        multibuffer.push_excerpts(
13600            buffer_2.clone(),
13601            [
13602                ExcerptRange {
13603                    context: Point::new(0, 0)..Point::new(3, 0),
13604                    primary: None,
13605                },
13606                ExcerptRange {
13607                    context: Point::new(5, 0)..Point::new(7, 0),
13608                    primary: None,
13609                },
13610                ExcerptRange {
13611                    context: Point::new(9, 0)..Point::new(10, 4),
13612                    primary: None,
13613                },
13614            ],
13615            cx,
13616        );
13617        multibuffer.push_excerpts(
13618            buffer_3.clone(),
13619            [
13620                ExcerptRange {
13621                    context: Point::new(0, 0)..Point::new(3, 0),
13622                    primary: None,
13623                },
13624                ExcerptRange {
13625                    context: Point::new(5, 0)..Point::new(7, 0),
13626                    primary: None,
13627                },
13628                ExcerptRange {
13629                    context: Point::new(9, 0)..Point::new(10, 4),
13630                    primary: None,
13631                },
13632            ],
13633            cx,
13634        );
13635        multibuffer
13636    });
13637
13638    let fs = FakeFs::new(cx.executor());
13639    fs.insert_tree(
13640        "/a",
13641        json!({
13642            "main.rs": sample_text_1,
13643            "other.rs": sample_text_2,
13644            "lib.rs": sample_text_3,
13645        }),
13646    )
13647    .await;
13648    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13649    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13650    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13651    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13652        Editor::new(
13653            EditorMode::Full,
13654            multi_buffer,
13655            Some(project.clone()),
13656            window,
13657            cx,
13658        )
13659    });
13660    let multibuffer_item_id = workspace
13661        .update(cx, |workspace, window, cx| {
13662            assert!(
13663                workspace.active_item(cx).is_none(),
13664                "active item should be None before the first item is added"
13665            );
13666            workspace.add_item_to_active_pane(
13667                Box::new(multi_buffer_editor.clone()),
13668                None,
13669                true,
13670                window,
13671                cx,
13672            );
13673            let active_item = workspace
13674                .active_item(cx)
13675                .expect("should have an active item after adding the multi buffer");
13676            assert!(
13677                !active_item.is_singleton(cx),
13678                "A multi buffer was expected to active after adding"
13679            );
13680            active_item.item_id()
13681        })
13682        .unwrap();
13683    cx.executor().run_until_parked();
13684
13685    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13686        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13687            s.select_ranges(Some(1..2))
13688        });
13689        editor.open_excerpts(&OpenExcerpts, window, cx);
13690    });
13691    cx.executor().run_until_parked();
13692    let first_item_id = workspace
13693        .update(cx, |workspace, window, cx| {
13694            let active_item = workspace
13695                .active_item(cx)
13696                .expect("should have an active item after navigating into the 1st buffer");
13697            let first_item_id = active_item.item_id();
13698            assert_ne!(
13699                first_item_id, multibuffer_item_id,
13700                "Should navigate into the 1st buffer and activate it"
13701            );
13702            assert!(
13703                active_item.is_singleton(cx),
13704                "New active item should be a singleton buffer"
13705            );
13706            assert_eq!(
13707                active_item
13708                    .act_as::<Editor>(cx)
13709                    .expect("should have navigated into an editor for the 1st buffer")
13710                    .read(cx)
13711                    .text(cx),
13712                sample_text_1
13713            );
13714
13715            workspace
13716                .go_back(workspace.active_pane().downgrade(), window, cx)
13717                .detach_and_log_err(cx);
13718
13719            first_item_id
13720        })
13721        .unwrap();
13722    cx.executor().run_until_parked();
13723    workspace
13724        .update(cx, |workspace, _, cx| {
13725            let active_item = workspace
13726                .active_item(cx)
13727                .expect("should have an active item after navigating back");
13728            assert_eq!(
13729                active_item.item_id(),
13730                multibuffer_item_id,
13731                "Should navigate back to the multi buffer"
13732            );
13733            assert!(!active_item.is_singleton(cx));
13734        })
13735        .unwrap();
13736
13737    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13738        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13739            s.select_ranges(Some(39..40))
13740        });
13741        editor.open_excerpts(&OpenExcerpts, window, cx);
13742    });
13743    cx.executor().run_until_parked();
13744    let second_item_id = workspace
13745        .update(cx, |workspace, window, cx| {
13746            let active_item = workspace
13747                .active_item(cx)
13748                .expect("should have an active item after navigating into the 2nd buffer");
13749            let second_item_id = active_item.item_id();
13750            assert_ne!(
13751                second_item_id, multibuffer_item_id,
13752                "Should navigate away from the multibuffer"
13753            );
13754            assert_ne!(
13755                second_item_id, first_item_id,
13756                "Should navigate into the 2nd buffer and activate it"
13757            );
13758            assert!(
13759                active_item.is_singleton(cx),
13760                "New active item should be a singleton buffer"
13761            );
13762            assert_eq!(
13763                active_item
13764                    .act_as::<Editor>(cx)
13765                    .expect("should have navigated into an editor")
13766                    .read(cx)
13767                    .text(cx),
13768                sample_text_2
13769            );
13770
13771            workspace
13772                .go_back(workspace.active_pane().downgrade(), window, cx)
13773                .detach_and_log_err(cx);
13774
13775            second_item_id
13776        })
13777        .unwrap();
13778    cx.executor().run_until_parked();
13779    workspace
13780        .update(cx, |workspace, _, cx| {
13781            let active_item = workspace
13782                .active_item(cx)
13783                .expect("should have an active item after navigating back from the 2nd buffer");
13784            assert_eq!(
13785                active_item.item_id(),
13786                multibuffer_item_id,
13787                "Should navigate back from the 2nd buffer to the multi buffer"
13788            );
13789            assert!(!active_item.is_singleton(cx));
13790        })
13791        .unwrap();
13792
13793    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13794        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13795            s.select_ranges(Some(70..70))
13796        });
13797        editor.open_excerpts(&OpenExcerpts, window, cx);
13798    });
13799    cx.executor().run_until_parked();
13800    workspace
13801        .update(cx, |workspace, window, cx| {
13802            let active_item = workspace
13803                .active_item(cx)
13804                .expect("should have an active item after navigating into the 3rd buffer");
13805            let third_item_id = active_item.item_id();
13806            assert_ne!(
13807                third_item_id, multibuffer_item_id,
13808                "Should navigate into the 3rd buffer and activate it"
13809            );
13810            assert_ne!(third_item_id, first_item_id);
13811            assert_ne!(third_item_id, second_item_id);
13812            assert!(
13813                active_item.is_singleton(cx),
13814                "New active item should be a singleton buffer"
13815            );
13816            assert_eq!(
13817                active_item
13818                    .act_as::<Editor>(cx)
13819                    .expect("should have navigated into an editor")
13820                    .read(cx)
13821                    .text(cx),
13822                sample_text_3
13823            );
13824
13825            workspace
13826                .go_back(workspace.active_pane().downgrade(), window, cx)
13827                .detach_and_log_err(cx);
13828        })
13829        .unwrap();
13830    cx.executor().run_until_parked();
13831    workspace
13832        .update(cx, |workspace, _, cx| {
13833            let active_item = workspace
13834                .active_item(cx)
13835                .expect("should have an active item after navigating back from the 3rd buffer");
13836            assert_eq!(
13837                active_item.item_id(),
13838                multibuffer_item_id,
13839                "Should navigate back from the 3rd buffer to the multi buffer"
13840            );
13841            assert!(!active_item.is_singleton(cx));
13842        })
13843        .unwrap();
13844}
13845
13846#[gpui::test]
13847async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13848    init_test(cx, |_| {});
13849
13850    let mut cx = EditorTestContext::new(cx).await;
13851
13852    let diff_base = r#"
13853        use some::mod;
13854
13855        const A: u32 = 42;
13856
13857        fn main() {
13858            println!("hello");
13859
13860            println!("world");
13861        }
13862        "#
13863    .unindent();
13864
13865    cx.set_state(
13866        &r#"
13867        use some::modified;
13868
13869        ˇ
13870        fn main() {
13871            println!("hello there");
13872
13873            println!("around the");
13874            println!("world");
13875        }
13876        "#
13877        .unindent(),
13878    );
13879
13880    cx.set_head_text(&diff_base);
13881    executor.run_until_parked();
13882
13883    cx.update_editor(|editor, window, cx| {
13884        editor.go_to_next_hunk(&GoToHunk, window, cx);
13885        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13886    });
13887    executor.run_until_parked();
13888    cx.assert_state_with_diff(
13889        r#"
13890          use some::modified;
13891
13892
13893          fn main() {
13894        -     println!("hello");
13895        + ˇ    println!("hello there");
13896
13897              println!("around the");
13898              println!("world");
13899          }
13900        "#
13901        .unindent(),
13902    );
13903
13904    cx.update_editor(|editor, window, cx| {
13905        for _ in 0..2 {
13906            editor.go_to_next_hunk(&GoToHunk, window, cx);
13907            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13908        }
13909    });
13910    executor.run_until_parked();
13911    cx.assert_state_with_diff(
13912        r#"
13913        - use some::mod;
13914        + ˇuse some::modified;
13915
13916
13917          fn main() {
13918        -     println!("hello");
13919        +     println!("hello there");
13920
13921        +     println!("around the");
13922              println!("world");
13923          }
13924        "#
13925        .unindent(),
13926    );
13927
13928    cx.update_editor(|editor, window, cx| {
13929        editor.go_to_next_hunk(&GoToHunk, window, cx);
13930        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13931    });
13932    executor.run_until_parked();
13933    cx.assert_state_with_diff(
13934        r#"
13935        - use some::mod;
13936        + use some::modified;
13937
13938        - const A: u32 = 42;
13939          ˇ
13940          fn main() {
13941        -     println!("hello");
13942        +     println!("hello there");
13943
13944        +     println!("around the");
13945              println!("world");
13946          }
13947        "#
13948        .unindent(),
13949    );
13950
13951    cx.update_editor(|editor, window, cx| {
13952        editor.cancel(&Cancel, window, cx);
13953    });
13954
13955    cx.assert_state_with_diff(
13956        r#"
13957          use some::modified;
13958
13959          ˇ
13960          fn main() {
13961              println!("hello there");
13962
13963              println!("around the");
13964              println!("world");
13965          }
13966        "#
13967        .unindent(),
13968    );
13969}
13970
13971#[gpui::test]
13972async fn test_diff_base_change_with_expanded_diff_hunks(
13973    executor: BackgroundExecutor,
13974    cx: &mut TestAppContext,
13975) {
13976    init_test(cx, |_| {});
13977
13978    let mut cx = EditorTestContext::new(cx).await;
13979
13980    let diff_base = r#"
13981        use some::mod1;
13982        use some::mod2;
13983
13984        const A: u32 = 42;
13985        const B: u32 = 42;
13986        const C: u32 = 42;
13987
13988        fn main() {
13989            println!("hello");
13990
13991            println!("world");
13992        }
13993        "#
13994    .unindent();
13995
13996    cx.set_state(
13997        &r#"
13998        use some::mod2;
13999
14000        const A: u32 = 42;
14001        const C: u32 = 42;
14002
14003        fn main(ˇ) {
14004            //println!("hello");
14005
14006            println!("world");
14007            //
14008            //
14009        }
14010        "#
14011        .unindent(),
14012    );
14013
14014    cx.set_head_text(&diff_base);
14015    executor.run_until_parked();
14016
14017    cx.update_editor(|editor, window, cx| {
14018        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14019    });
14020    executor.run_until_parked();
14021    cx.assert_state_with_diff(
14022        r#"
14023        - use some::mod1;
14024          use some::mod2;
14025
14026          const A: u32 = 42;
14027        - const B: u32 = 42;
14028          const C: u32 = 42;
14029
14030          fn main(ˇ) {
14031        -     println!("hello");
14032        +     //println!("hello");
14033
14034              println!("world");
14035        +     //
14036        +     //
14037          }
14038        "#
14039        .unindent(),
14040    );
14041
14042    cx.set_head_text("new diff base!");
14043    executor.run_until_parked();
14044    cx.assert_state_with_diff(
14045        r#"
14046        - new diff base!
14047        + use some::mod2;
14048        +
14049        + const A: u32 = 42;
14050        + const C: u32 = 42;
14051        +
14052        + fn main(ˇ) {
14053        +     //println!("hello");
14054        +
14055        +     println!("world");
14056        +     //
14057        +     //
14058        + }
14059        "#
14060        .unindent(),
14061    );
14062}
14063
14064#[gpui::test]
14065async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14066    init_test(cx, |_| {});
14067
14068    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14069    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14070    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14071    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14072    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14073    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14074
14075    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14076    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14077    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14078
14079    let multi_buffer = cx.new(|cx| {
14080        let mut multibuffer = MultiBuffer::new(ReadWrite);
14081        multibuffer.push_excerpts(
14082            buffer_1.clone(),
14083            [
14084                ExcerptRange {
14085                    context: Point::new(0, 0)..Point::new(3, 0),
14086                    primary: None,
14087                },
14088                ExcerptRange {
14089                    context: Point::new(5, 0)..Point::new(7, 0),
14090                    primary: None,
14091                },
14092                ExcerptRange {
14093                    context: Point::new(9, 0)..Point::new(10, 3),
14094                    primary: None,
14095                },
14096            ],
14097            cx,
14098        );
14099        multibuffer.push_excerpts(
14100            buffer_2.clone(),
14101            [
14102                ExcerptRange {
14103                    context: Point::new(0, 0)..Point::new(3, 0),
14104                    primary: None,
14105                },
14106                ExcerptRange {
14107                    context: Point::new(5, 0)..Point::new(7, 0),
14108                    primary: None,
14109                },
14110                ExcerptRange {
14111                    context: Point::new(9, 0)..Point::new(10, 3),
14112                    primary: None,
14113                },
14114            ],
14115            cx,
14116        );
14117        multibuffer.push_excerpts(
14118            buffer_3.clone(),
14119            [
14120                ExcerptRange {
14121                    context: Point::new(0, 0)..Point::new(3, 0),
14122                    primary: None,
14123                },
14124                ExcerptRange {
14125                    context: Point::new(5, 0)..Point::new(7, 0),
14126                    primary: None,
14127                },
14128                ExcerptRange {
14129                    context: Point::new(9, 0)..Point::new(10, 3),
14130                    primary: None,
14131                },
14132            ],
14133            cx,
14134        );
14135        multibuffer
14136    });
14137
14138    let editor =
14139        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14140    editor
14141        .update(cx, |editor, _window, cx| {
14142            for (buffer, diff_base) in [
14143                (buffer_1.clone(), file_1_old),
14144                (buffer_2.clone(), file_2_old),
14145                (buffer_3.clone(), file_3_old),
14146            ] {
14147                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14148                editor
14149                    .buffer
14150                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14151            }
14152        })
14153        .unwrap();
14154
14155    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14156    cx.run_until_parked();
14157
14158    cx.assert_editor_state(
14159        &"
14160            ˇaaa
14161            ccc
14162            ddd
14163
14164            ggg
14165            hhh
14166
14167
14168            lll
14169            mmm
14170            NNN
14171
14172            qqq
14173            rrr
14174
14175            uuu
14176            111
14177            222
14178            333
14179
14180            666
14181            777
14182
14183            000
14184            !!!"
14185        .unindent(),
14186    );
14187
14188    cx.update_editor(|editor, window, cx| {
14189        editor.select_all(&SelectAll, window, cx);
14190        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14191    });
14192    cx.executor().run_until_parked();
14193
14194    cx.assert_state_with_diff(
14195        "
14196            «aaa
14197          - bbb
14198            ccc
14199            ddd
14200
14201            ggg
14202            hhh
14203
14204
14205            lll
14206            mmm
14207          - nnn
14208          + NNN
14209
14210            qqq
14211            rrr
14212
14213            uuu
14214            111
14215            222
14216            333
14217
14218          + 666
14219            777
14220
14221            000
14222            !!!ˇ»"
14223            .unindent(),
14224    );
14225}
14226
14227#[gpui::test]
14228async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14229    init_test(cx, |_| {});
14230
14231    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14232    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14233
14234    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14235    let multi_buffer = cx.new(|cx| {
14236        let mut multibuffer = MultiBuffer::new(ReadWrite);
14237        multibuffer.push_excerpts(
14238            buffer.clone(),
14239            [
14240                ExcerptRange {
14241                    context: Point::new(0, 0)..Point::new(2, 0),
14242                    primary: None,
14243                },
14244                ExcerptRange {
14245                    context: Point::new(4, 0)..Point::new(7, 0),
14246                    primary: None,
14247                },
14248                ExcerptRange {
14249                    context: Point::new(9, 0)..Point::new(10, 0),
14250                    primary: None,
14251                },
14252            ],
14253            cx,
14254        );
14255        multibuffer
14256    });
14257
14258    let editor =
14259        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14260    editor
14261        .update(cx, |editor, _window, cx| {
14262            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14263            editor
14264                .buffer
14265                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14266        })
14267        .unwrap();
14268
14269    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14270    cx.run_until_parked();
14271
14272    cx.update_editor(|editor, window, cx| {
14273        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14274    });
14275    cx.executor().run_until_parked();
14276
14277    // When the start of a hunk coincides with the start of its excerpt,
14278    // the hunk is expanded. When the start of a a hunk is earlier than
14279    // the start of its excerpt, the hunk is not expanded.
14280    cx.assert_state_with_diff(
14281        "
14282            ˇaaa
14283          - bbb
14284          + BBB
14285
14286          - ddd
14287          - eee
14288          + DDD
14289          + EEE
14290            fff
14291
14292            iii
14293        "
14294        .unindent(),
14295    );
14296}
14297
14298#[gpui::test]
14299async fn test_edits_around_expanded_insertion_hunks(
14300    executor: BackgroundExecutor,
14301    cx: &mut TestAppContext,
14302) {
14303    init_test(cx, |_| {});
14304
14305    let mut cx = EditorTestContext::new(cx).await;
14306
14307    let diff_base = r#"
14308        use some::mod1;
14309        use some::mod2;
14310
14311        const A: u32 = 42;
14312
14313        fn main() {
14314            println!("hello");
14315
14316            println!("world");
14317        }
14318        "#
14319    .unindent();
14320    executor.run_until_parked();
14321    cx.set_state(
14322        &r#"
14323        use some::mod1;
14324        use some::mod2;
14325
14326        const A: u32 = 42;
14327        const B: u32 = 42;
14328        const C: u32 = 42;
14329        ˇ
14330
14331        fn main() {
14332            println!("hello");
14333
14334            println!("world");
14335        }
14336        "#
14337        .unindent(),
14338    );
14339
14340    cx.set_head_text(&diff_base);
14341    executor.run_until_parked();
14342
14343    cx.update_editor(|editor, window, cx| {
14344        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14345    });
14346    executor.run_until_parked();
14347
14348    cx.assert_state_with_diff(
14349        r#"
14350        use some::mod1;
14351        use some::mod2;
14352
14353        const A: u32 = 42;
14354      + const B: u32 = 42;
14355      + const C: u32 = 42;
14356      + ˇ
14357
14358        fn main() {
14359            println!("hello");
14360
14361            println!("world");
14362        }
14363      "#
14364        .unindent(),
14365    );
14366
14367    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14368    executor.run_until_parked();
14369
14370    cx.assert_state_with_diff(
14371        r#"
14372        use some::mod1;
14373        use some::mod2;
14374
14375        const A: u32 = 42;
14376      + const B: u32 = 42;
14377      + const C: u32 = 42;
14378      + const D: u32 = 42;
14379      + ˇ
14380
14381        fn main() {
14382            println!("hello");
14383
14384            println!("world");
14385        }
14386      "#
14387        .unindent(),
14388    );
14389
14390    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14391    executor.run_until_parked();
14392
14393    cx.assert_state_with_diff(
14394        r#"
14395        use some::mod1;
14396        use some::mod2;
14397
14398        const A: u32 = 42;
14399      + const B: u32 = 42;
14400      + const C: u32 = 42;
14401      + const D: u32 = 42;
14402      + const E: u32 = 42;
14403      + ˇ
14404
14405        fn main() {
14406            println!("hello");
14407
14408            println!("world");
14409        }
14410      "#
14411        .unindent(),
14412    );
14413
14414    cx.update_editor(|editor, window, cx| {
14415        editor.delete_line(&DeleteLine, window, cx);
14416    });
14417    executor.run_until_parked();
14418
14419    cx.assert_state_with_diff(
14420        r#"
14421        use some::mod1;
14422        use some::mod2;
14423
14424        const A: u32 = 42;
14425      + const B: u32 = 42;
14426      + const C: u32 = 42;
14427      + const D: u32 = 42;
14428      + const E: u32 = 42;
14429        ˇ
14430        fn main() {
14431            println!("hello");
14432
14433            println!("world");
14434        }
14435      "#
14436        .unindent(),
14437    );
14438
14439    cx.update_editor(|editor, window, cx| {
14440        editor.move_up(&MoveUp, window, cx);
14441        editor.delete_line(&DeleteLine, window, cx);
14442        editor.move_up(&MoveUp, window, cx);
14443        editor.delete_line(&DeleteLine, window, cx);
14444        editor.move_up(&MoveUp, window, cx);
14445        editor.delete_line(&DeleteLine, window, cx);
14446    });
14447    executor.run_until_parked();
14448    cx.assert_state_with_diff(
14449        r#"
14450        use some::mod1;
14451        use some::mod2;
14452
14453        const A: u32 = 42;
14454      + const B: u32 = 42;
14455        ˇ
14456        fn main() {
14457            println!("hello");
14458
14459            println!("world");
14460        }
14461      "#
14462        .unindent(),
14463    );
14464
14465    cx.update_editor(|editor, window, cx| {
14466        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14467        editor.delete_line(&DeleteLine, window, cx);
14468    });
14469    executor.run_until_parked();
14470    cx.assert_state_with_diff(
14471        r#"
14472        ˇ
14473        fn main() {
14474            println!("hello");
14475
14476            println!("world");
14477        }
14478      "#
14479        .unindent(),
14480    );
14481}
14482
14483#[gpui::test]
14484async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14485    init_test(cx, |_| {});
14486
14487    let mut cx = EditorTestContext::new(cx).await;
14488    cx.set_head_text(indoc! { "
14489        one
14490        two
14491        three
14492        four
14493        five
14494        "
14495    });
14496    cx.set_state(indoc! { "
14497        one
14498        ˇthree
14499        five
14500    "});
14501    cx.run_until_parked();
14502    cx.update_editor(|editor, window, cx| {
14503        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14504    });
14505    cx.assert_state_with_diff(
14506        indoc! { "
14507        one
14508      - two
14509        ˇthree
14510      - four
14511        five
14512    "}
14513        .to_string(),
14514    );
14515    cx.update_editor(|editor, window, cx| {
14516        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14517    });
14518
14519    cx.assert_state_with_diff(
14520        indoc! { "
14521        one
14522        ˇthree
14523        five
14524    "}
14525        .to_string(),
14526    );
14527
14528    cx.set_state(indoc! { "
14529        one
14530        ˇTWO
14531        three
14532        four
14533        five
14534    "});
14535    cx.run_until_parked();
14536    cx.update_editor(|editor, window, cx| {
14537        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14538    });
14539
14540    cx.assert_state_with_diff(
14541        indoc! { "
14542            one
14543          - two
14544          + ˇTWO
14545            three
14546            four
14547            five
14548        "}
14549        .to_string(),
14550    );
14551    cx.update_editor(|editor, window, cx| {
14552        editor.move_up(&Default::default(), window, cx);
14553        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14554    });
14555    cx.assert_state_with_diff(
14556        indoc! { "
14557            one
14558            ˇTWO
14559            three
14560            four
14561            five
14562        "}
14563        .to_string(),
14564    );
14565}
14566
14567#[gpui::test]
14568async fn test_edits_around_expanded_deletion_hunks(
14569    executor: BackgroundExecutor,
14570    cx: &mut TestAppContext,
14571) {
14572    init_test(cx, |_| {});
14573
14574    let mut cx = EditorTestContext::new(cx).await;
14575
14576    let diff_base = r#"
14577        use some::mod1;
14578        use some::mod2;
14579
14580        const A: u32 = 42;
14581        const B: u32 = 42;
14582        const C: u32 = 42;
14583
14584
14585        fn main() {
14586            println!("hello");
14587
14588            println!("world");
14589        }
14590    "#
14591    .unindent();
14592    executor.run_until_parked();
14593    cx.set_state(
14594        &r#"
14595        use some::mod1;
14596        use some::mod2;
14597
14598        ˇconst B: u32 = 42;
14599        const C: u32 = 42;
14600
14601
14602        fn main() {
14603            println!("hello");
14604
14605            println!("world");
14606        }
14607        "#
14608        .unindent(),
14609    );
14610
14611    cx.set_head_text(&diff_base);
14612    executor.run_until_parked();
14613
14614    cx.update_editor(|editor, window, cx| {
14615        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14616    });
14617    executor.run_until_parked();
14618
14619    cx.assert_state_with_diff(
14620        r#"
14621        use some::mod1;
14622        use some::mod2;
14623
14624      - const A: u32 = 42;
14625        ˇconst B: u32 = 42;
14626        const C: u32 = 42;
14627
14628
14629        fn main() {
14630            println!("hello");
14631
14632            println!("world");
14633        }
14634      "#
14635        .unindent(),
14636    );
14637
14638    cx.update_editor(|editor, window, cx| {
14639        editor.delete_line(&DeleteLine, window, cx);
14640    });
14641    executor.run_until_parked();
14642    cx.assert_state_with_diff(
14643        r#"
14644        use some::mod1;
14645        use some::mod2;
14646
14647      - const A: u32 = 42;
14648      - const B: u32 = 42;
14649        ˇconst C: u32 = 42;
14650
14651
14652        fn main() {
14653            println!("hello");
14654
14655            println!("world");
14656        }
14657      "#
14658        .unindent(),
14659    );
14660
14661    cx.update_editor(|editor, window, cx| {
14662        editor.delete_line(&DeleteLine, window, cx);
14663    });
14664    executor.run_until_parked();
14665    cx.assert_state_with_diff(
14666        r#"
14667        use some::mod1;
14668        use some::mod2;
14669
14670      - const A: u32 = 42;
14671      - const B: u32 = 42;
14672      - const C: u32 = 42;
14673        ˇ
14674
14675        fn main() {
14676            println!("hello");
14677
14678            println!("world");
14679        }
14680      "#
14681        .unindent(),
14682    );
14683
14684    cx.update_editor(|editor, window, cx| {
14685        editor.handle_input("replacement", window, cx);
14686    });
14687    executor.run_until_parked();
14688    cx.assert_state_with_diff(
14689        r#"
14690        use some::mod1;
14691        use some::mod2;
14692
14693      - const A: u32 = 42;
14694      - const B: u32 = 42;
14695      - const C: u32 = 42;
14696      -
14697      + replacementˇ
14698
14699        fn main() {
14700            println!("hello");
14701
14702            println!("world");
14703        }
14704      "#
14705        .unindent(),
14706    );
14707}
14708
14709#[gpui::test]
14710async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14711    init_test(cx, |_| {});
14712
14713    let mut cx = EditorTestContext::new(cx).await;
14714
14715    let base_text = r#"
14716        one
14717        two
14718        three
14719        four
14720        five
14721    "#
14722    .unindent();
14723    executor.run_until_parked();
14724    cx.set_state(
14725        &r#"
14726        one
14727        two
14728        fˇour
14729        five
14730        "#
14731        .unindent(),
14732    );
14733
14734    cx.set_head_text(&base_text);
14735    executor.run_until_parked();
14736
14737    cx.update_editor(|editor, window, cx| {
14738        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14739    });
14740    executor.run_until_parked();
14741
14742    cx.assert_state_with_diff(
14743        r#"
14744          one
14745          two
14746        - three
14747          fˇour
14748          five
14749        "#
14750        .unindent(),
14751    );
14752
14753    cx.update_editor(|editor, window, cx| {
14754        editor.backspace(&Backspace, window, cx);
14755        editor.backspace(&Backspace, window, cx);
14756    });
14757    executor.run_until_parked();
14758    cx.assert_state_with_diff(
14759        r#"
14760          one
14761          two
14762        - threeˇ
14763        - four
14764        + our
14765          five
14766        "#
14767        .unindent(),
14768    );
14769}
14770
14771#[gpui::test]
14772async fn test_edit_after_expanded_modification_hunk(
14773    executor: BackgroundExecutor,
14774    cx: &mut TestAppContext,
14775) {
14776    init_test(cx, |_| {});
14777
14778    let mut cx = EditorTestContext::new(cx).await;
14779
14780    let diff_base = r#"
14781        use some::mod1;
14782        use some::mod2;
14783
14784        const A: u32 = 42;
14785        const B: u32 = 42;
14786        const C: u32 = 42;
14787        const D: u32 = 42;
14788
14789
14790        fn main() {
14791            println!("hello");
14792
14793            println!("world");
14794        }"#
14795    .unindent();
14796
14797    cx.set_state(
14798        &r#"
14799        use some::mod1;
14800        use some::mod2;
14801
14802        const A: u32 = 42;
14803        const B: u32 = 42;
14804        const C: u32 = 43ˇ
14805        const D: u32 = 42;
14806
14807
14808        fn main() {
14809            println!("hello");
14810
14811            println!("world");
14812        }"#
14813        .unindent(),
14814    );
14815
14816    cx.set_head_text(&diff_base);
14817    executor.run_until_parked();
14818    cx.update_editor(|editor, window, cx| {
14819        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14820    });
14821    executor.run_until_parked();
14822
14823    cx.assert_state_with_diff(
14824        r#"
14825        use some::mod1;
14826        use some::mod2;
14827
14828        const A: u32 = 42;
14829        const B: u32 = 42;
14830      - const C: u32 = 42;
14831      + const C: u32 = 43ˇ
14832        const D: u32 = 42;
14833
14834
14835        fn main() {
14836            println!("hello");
14837
14838            println!("world");
14839        }"#
14840        .unindent(),
14841    );
14842
14843    cx.update_editor(|editor, window, cx| {
14844        editor.handle_input("\nnew_line\n", window, cx);
14845    });
14846    executor.run_until_parked();
14847
14848    cx.assert_state_with_diff(
14849        r#"
14850        use some::mod1;
14851        use some::mod2;
14852
14853        const A: u32 = 42;
14854        const B: u32 = 42;
14855      - const C: u32 = 42;
14856      + const C: u32 = 43
14857      + new_line
14858      + ˇ
14859        const D: u32 = 42;
14860
14861
14862        fn main() {
14863            println!("hello");
14864
14865            println!("world");
14866        }"#
14867        .unindent(),
14868    );
14869}
14870
14871#[gpui::test]
14872async fn test_stage_and_unstage_added_file_hunk(
14873    executor: BackgroundExecutor,
14874    cx: &mut TestAppContext,
14875) {
14876    init_test(cx, |_| {});
14877
14878    let mut cx = EditorTestContext::new(cx).await;
14879    cx.update_editor(|editor, _, cx| {
14880        editor.set_expand_all_diff_hunks(cx);
14881    });
14882
14883    let working_copy = r#"
14884            ˇfn main() {
14885                println!("hello, world!");
14886            }
14887        "#
14888    .unindent();
14889
14890    cx.set_state(&working_copy);
14891    executor.run_until_parked();
14892
14893    cx.assert_state_with_diff(
14894        r#"
14895            + ˇfn main() {
14896            +     println!("hello, world!");
14897            + }
14898        "#
14899        .unindent(),
14900    );
14901    cx.assert_index_text(None);
14902
14903    cx.update_editor(|editor, window, cx| {
14904        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14905    });
14906    executor.run_until_parked();
14907    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14908    cx.assert_state_with_diff(
14909        r#"
14910            + ˇfn main() {
14911            +     println!("hello, world!");
14912            + }
14913        "#
14914        .unindent(),
14915    );
14916
14917    cx.update_editor(|editor, window, cx| {
14918        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14919    });
14920    executor.run_until_parked();
14921    cx.assert_index_text(None);
14922}
14923
14924async fn setup_indent_guides_editor(
14925    text: &str,
14926    cx: &mut TestAppContext,
14927) -> (BufferId, EditorTestContext) {
14928    init_test(cx, |_| {});
14929
14930    let mut cx = EditorTestContext::new(cx).await;
14931
14932    let buffer_id = cx.update_editor(|editor, window, cx| {
14933        editor.set_text(text, window, cx);
14934        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14935
14936        buffer_ids[0]
14937    });
14938
14939    (buffer_id, cx)
14940}
14941
14942fn assert_indent_guides(
14943    range: Range<u32>,
14944    expected: Vec<IndentGuide>,
14945    active_indices: Option<Vec<usize>>,
14946    cx: &mut EditorTestContext,
14947) {
14948    let indent_guides = cx.update_editor(|editor, window, cx| {
14949        let snapshot = editor.snapshot(window, cx).display_snapshot;
14950        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14951            editor,
14952            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14953            true,
14954            &snapshot,
14955            cx,
14956        );
14957
14958        indent_guides.sort_by(|a, b| {
14959            a.depth.cmp(&b.depth).then(
14960                a.start_row
14961                    .cmp(&b.start_row)
14962                    .then(a.end_row.cmp(&b.end_row)),
14963            )
14964        });
14965        indent_guides
14966    });
14967
14968    if let Some(expected) = active_indices {
14969        let active_indices = cx.update_editor(|editor, window, cx| {
14970            let snapshot = editor.snapshot(window, cx).display_snapshot;
14971            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14972        });
14973
14974        assert_eq!(
14975            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14976            expected,
14977            "Active indent guide indices do not match"
14978        );
14979    }
14980
14981    assert_eq!(indent_guides, expected, "Indent guides do not match");
14982}
14983
14984fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
14985    IndentGuide {
14986        buffer_id,
14987        start_row: MultiBufferRow(start_row),
14988        end_row: MultiBufferRow(end_row),
14989        depth,
14990        tab_size: 4,
14991        settings: IndentGuideSettings {
14992            enabled: true,
14993            line_width: 1,
14994            active_line_width: 1,
14995            ..Default::default()
14996        },
14997    }
14998}
14999
15000#[gpui::test]
15001async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15002    let (buffer_id, mut cx) = setup_indent_guides_editor(
15003        &"
15004    fn main() {
15005        let a = 1;
15006    }"
15007        .unindent(),
15008        cx,
15009    )
15010    .await;
15011
15012    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15013}
15014
15015#[gpui::test]
15016async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15017    let (buffer_id, mut cx) = setup_indent_guides_editor(
15018        &"
15019    fn main() {
15020        let a = 1;
15021        let b = 2;
15022    }"
15023        .unindent(),
15024        cx,
15025    )
15026    .await;
15027
15028    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15029}
15030
15031#[gpui::test]
15032async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15033    let (buffer_id, mut cx) = setup_indent_guides_editor(
15034        &"
15035    fn main() {
15036        let a = 1;
15037        if a == 3 {
15038            let b = 2;
15039        } else {
15040            let c = 3;
15041        }
15042    }"
15043        .unindent(),
15044        cx,
15045    )
15046    .await;
15047
15048    assert_indent_guides(
15049        0..8,
15050        vec![
15051            indent_guide(buffer_id, 1, 6, 0),
15052            indent_guide(buffer_id, 3, 3, 1),
15053            indent_guide(buffer_id, 5, 5, 1),
15054        ],
15055        None,
15056        &mut cx,
15057    );
15058}
15059
15060#[gpui::test]
15061async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15062    let (buffer_id, mut cx) = setup_indent_guides_editor(
15063        &"
15064    fn main() {
15065        let a = 1;
15066            let b = 2;
15067        let c = 3;
15068    }"
15069        .unindent(),
15070        cx,
15071    )
15072    .await;
15073
15074    assert_indent_guides(
15075        0..5,
15076        vec![
15077            indent_guide(buffer_id, 1, 3, 0),
15078            indent_guide(buffer_id, 2, 2, 1),
15079        ],
15080        None,
15081        &mut cx,
15082    );
15083}
15084
15085#[gpui::test]
15086async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15087    let (buffer_id, mut cx) = setup_indent_guides_editor(
15088        &"
15089        fn main() {
15090            let a = 1;
15091
15092            let c = 3;
15093        }"
15094        .unindent(),
15095        cx,
15096    )
15097    .await;
15098
15099    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15100}
15101
15102#[gpui::test]
15103async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15104    let (buffer_id, mut cx) = setup_indent_guides_editor(
15105        &"
15106        fn main() {
15107            let a = 1;
15108
15109            let c = 3;
15110
15111            if a == 3 {
15112                let b = 2;
15113            } else {
15114                let c = 3;
15115            }
15116        }"
15117        .unindent(),
15118        cx,
15119    )
15120    .await;
15121
15122    assert_indent_guides(
15123        0..11,
15124        vec![
15125            indent_guide(buffer_id, 1, 9, 0),
15126            indent_guide(buffer_id, 6, 6, 1),
15127            indent_guide(buffer_id, 8, 8, 1),
15128        ],
15129        None,
15130        &mut cx,
15131    );
15132}
15133
15134#[gpui::test]
15135async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15136    let (buffer_id, mut cx) = setup_indent_guides_editor(
15137        &"
15138        fn main() {
15139            let a = 1;
15140
15141            let c = 3;
15142
15143            if a == 3 {
15144                let b = 2;
15145            } else {
15146                let c = 3;
15147            }
15148        }"
15149        .unindent(),
15150        cx,
15151    )
15152    .await;
15153
15154    assert_indent_guides(
15155        1..11,
15156        vec![
15157            indent_guide(buffer_id, 1, 9, 0),
15158            indent_guide(buffer_id, 6, 6, 1),
15159            indent_guide(buffer_id, 8, 8, 1),
15160        ],
15161        None,
15162        &mut cx,
15163    );
15164}
15165
15166#[gpui::test]
15167async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15168    let (buffer_id, mut cx) = setup_indent_guides_editor(
15169        &"
15170        fn main() {
15171            let a = 1;
15172
15173            let c = 3;
15174
15175            if a == 3 {
15176                let b = 2;
15177            } else {
15178                let c = 3;
15179            }
15180        }"
15181        .unindent(),
15182        cx,
15183    )
15184    .await;
15185
15186    assert_indent_guides(
15187        1..10,
15188        vec![
15189            indent_guide(buffer_id, 1, 9, 0),
15190            indent_guide(buffer_id, 6, 6, 1),
15191            indent_guide(buffer_id, 8, 8, 1),
15192        ],
15193        None,
15194        &mut cx,
15195    );
15196}
15197
15198#[gpui::test]
15199async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15200    let (buffer_id, mut cx) = setup_indent_guides_editor(
15201        &"
15202        block1
15203            block2
15204                block3
15205                    block4
15206            block2
15207        block1
15208        block1"
15209            .unindent(),
15210        cx,
15211    )
15212    .await;
15213
15214    assert_indent_guides(
15215        1..10,
15216        vec![
15217            indent_guide(buffer_id, 1, 4, 0),
15218            indent_guide(buffer_id, 2, 3, 1),
15219            indent_guide(buffer_id, 3, 3, 2),
15220        ],
15221        None,
15222        &mut cx,
15223    );
15224}
15225
15226#[gpui::test]
15227async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15228    let (buffer_id, mut cx) = setup_indent_guides_editor(
15229        &"
15230        block1
15231            block2
15232                block3
15233
15234        block1
15235        block1"
15236            .unindent(),
15237        cx,
15238    )
15239    .await;
15240
15241    assert_indent_guides(
15242        0..6,
15243        vec![
15244            indent_guide(buffer_id, 1, 2, 0),
15245            indent_guide(buffer_id, 2, 2, 1),
15246        ],
15247        None,
15248        &mut cx,
15249    );
15250}
15251
15252#[gpui::test]
15253async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15254    let (buffer_id, mut cx) = setup_indent_guides_editor(
15255        &"
15256        block1
15257
15258
15259
15260            block2
15261        "
15262        .unindent(),
15263        cx,
15264    )
15265    .await;
15266
15267    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15268}
15269
15270#[gpui::test]
15271async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15272    let (buffer_id, mut cx) = setup_indent_guides_editor(
15273        &"
15274        def a:
15275        \tb = 3
15276        \tif True:
15277        \t\tc = 4
15278        \t\td = 5
15279        \tprint(b)
15280        "
15281        .unindent(),
15282        cx,
15283    )
15284    .await;
15285
15286    assert_indent_guides(
15287        0..6,
15288        vec![
15289            indent_guide(buffer_id, 1, 6, 0),
15290            indent_guide(buffer_id, 3, 4, 1),
15291        ],
15292        None,
15293        &mut cx,
15294    );
15295}
15296
15297#[gpui::test]
15298async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15299    let (buffer_id, mut cx) = setup_indent_guides_editor(
15300        &"
15301    fn main() {
15302        let a = 1;
15303    }"
15304        .unindent(),
15305        cx,
15306    )
15307    .await;
15308
15309    cx.update_editor(|editor, window, cx| {
15310        editor.change_selections(None, window, cx, |s| {
15311            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15312        });
15313    });
15314
15315    assert_indent_guides(
15316        0..3,
15317        vec![indent_guide(buffer_id, 1, 1, 0)],
15318        Some(vec![0]),
15319        &mut cx,
15320    );
15321}
15322
15323#[gpui::test]
15324async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15325    let (buffer_id, mut cx) = setup_indent_guides_editor(
15326        &"
15327    fn main() {
15328        if 1 == 2 {
15329            let a = 1;
15330        }
15331    }"
15332        .unindent(),
15333        cx,
15334    )
15335    .await;
15336
15337    cx.update_editor(|editor, window, cx| {
15338        editor.change_selections(None, window, cx, |s| {
15339            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15340        });
15341    });
15342
15343    assert_indent_guides(
15344        0..4,
15345        vec![
15346            indent_guide(buffer_id, 1, 3, 0),
15347            indent_guide(buffer_id, 2, 2, 1),
15348        ],
15349        Some(vec![1]),
15350        &mut cx,
15351    );
15352
15353    cx.update_editor(|editor, window, cx| {
15354        editor.change_selections(None, window, cx, |s| {
15355            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15356        });
15357    });
15358
15359    assert_indent_guides(
15360        0..4,
15361        vec![
15362            indent_guide(buffer_id, 1, 3, 0),
15363            indent_guide(buffer_id, 2, 2, 1),
15364        ],
15365        Some(vec![1]),
15366        &mut cx,
15367    );
15368
15369    cx.update_editor(|editor, window, cx| {
15370        editor.change_selections(None, window, cx, |s| {
15371            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15372        });
15373    });
15374
15375    assert_indent_guides(
15376        0..4,
15377        vec![
15378            indent_guide(buffer_id, 1, 3, 0),
15379            indent_guide(buffer_id, 2, 2, 1),
15380        ],
15381        Some(vec![0]),
15382        &mut cx,
15383    );
15384}
15385
15386#[gpui::test]
15387async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15388    let (buffer_id, mut cx) = setup_indent_guides_editor(
15389        &"
15390    fn main() {
15391        let a = 1;
15392
15393        let b = 2;
15394    }"
15395        .unindent(),
15396        cx,
15397    )
15398    .await;
15399
15400    cx.update_editor(|editor, window, cx| {
15401        editor.change_selections(None, window, cx, |s| {
15402            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15403        });
15404    });
15405
15406    assert_indent_guides(
15407        0..5,
15408        vec![indent_guide(buffer_id, 1, 3, 0)],
15409        Some(vec![0]),
15410        &mut cx,
15411    );
15412}
15413
15414#[gpui::test]
15415async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15416    let (buffer_id, mut cx) = setup_indent_guides_editor(
15417        &"
15418    def m:
15419        a = 1
15420        pass"
15421            .unindent(),
15422        cx,
15423    )
15424    .await;
15425
15426    cx.update_editor(|editor, window, cx| {
15427        editor.change_selections(None, window, cx, |s| {
15428            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15429        });
15430    });
15431
15432    assert_indent_guides(
15433        0..3,
15434        vec![indent_guide(buffer_id, 1, 2, 0)],
15435        Some(vec![0]),
15436        &mut cx,
15437    );
15438}
15439
15440#[gpui::test]
15441async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15442    init_test(cx, |_| {});
15443    let mut cx = EditorTestContext::new(cx).await;
15444    let text = indoc! {
15445        "
15446        impl A {
15447            fn b() {
15448                0;
15449                3;
15450                5;
15451                6;
15452                7;
15453            }
15454        }
15455        "
15456    };
15457    let base_text = indoc! {
15458        "
15459        impl A {
15460            fn b() {
15461                0;
15462                1;
15463                2;
15464                3;
15465                4;
15466            }
15467            fn c() {
15468                5;
15469                6;
15470                7;
15471            }
15472        }
15473        "
15474    };
15475
15476    cx.update_editor(|editor, window, cx| {
15477        editor.set_text(text, window, cx);
15478
15479        editor.buffer().update(cx, |multibuffer, cx| {
15480            let buffer = multibuffer.as_singleton().unwrap();
15481            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15482
15483            multibuffer.set_all_diff_hunks_expanded(cx);
15484            multibuffer.add_diff(diff, cx);
15485
15486            buffer.read(cx).remote_id()
15487        })
15488    });
15489    cx.run_until_parked();
15490
15491    cx.assert_state_with_diff(
15492        indoc! { "
15493          impl A {
15494              fn b() {
15495                  0;
15496        -         1;
15497        -         2;
15498                  3;
15499        -         4;
15500        -     }
15501        -     fn c() {
15502                  5;
15503                  6;
15504                  7;
15505              }
15506          }
15507          ˇ"
15508        }
15509        .to_string(),
15510    );
15511
15512    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15513        editor
15514            .snapshot(window, cx)
15515            .buffer_snapshot
15516            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15517            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15518            .collect::<Vec<_>>()
15519    });
15520    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15521    assert_eq!(
15522        actual_guides,
15523        vec![
15524            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15525            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15526            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15527        ]
15528    );
15529}
15530
15531#[gpui::test]
15532async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15533    init_test(cx, |_| {});
15534    let mut cx = EditorTestContext::new(cx).await;
15535
15536    let diff_base = r#"
15537        a
15538        b
15539        c
15540        "#
15541    .unindent();
15542
15543    cx.set_state(
15544        &r#"
15545        ˇA
15546        b
15547        C
15548        "#
15549        .unindent(),
15550    );
15551    cx.set_head_text(&diff_base);
15552    cx.update_editor(|editor, window, cx| {
15553        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15554    });
15555    executor.run_until_parked();
15556
15557    let both_hunks_expanded = r#"
15558        - a
15559        + ˇA
15560          b
15561        - c
15562        + C
15563        "#
15564    .unindent();
15565
15566    cx.assert_state_with_diff(both_hunks_expanded.clone());
15567
15568    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15569        let snapshot = editor.snapshot(window, cx);
15570        let hunks = editor
15571            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15572            .collect::<Vec<_>>();
15573        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15574        let buffer_id = hunks[0].buffer_id;
15575        hunks
15576            .into_iter()
15577            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15578            .collect::<Vec<_>>()
15579    });
15580    assert_eq!(hunk_ranges.len(), 2);
15581
15582    cx.update_editor(|editor, _, cx| {
15583        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15584    });
15585    executor.run_until_parked();
15586
15587    let second_hunk_expanded = r#"
15588          ˇA
15589          b
15590        - c
15591        + C
15592        "#
15593    .unindent();
15594
15595    cx.assert_state_with_diff(second_hunk_expanded);
15596
15597    cx.update_editor(|editor, _, cx| {
15598        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15599    });
15600    executor.run_until_parked();
15601
15602    cx.assert_state_with_diff(both_hunks_expanded.clone());
15603
15604    cx.update_editor(|editor, _, cx| {
15605        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15606    });
15607    executor.run_until_parked();
15608
15609    let first_hunk_expanded = r#"
15610        - a
15611        + ˇA
15612          b
15613          C
15614        "#
15615    .unindent();
15616
15617    cx.assert_state_with_diff(first_hunk_expanded);
15618
15619    cx.update_editor(|editor, _, cx| {
15620        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15621    });
15622    executor.run_until_parked();
15623
15624    cx.assert_state_with_diff(both_hunks_expanded);
15625
15626    cx.set_state(
15627        &r#"
15628        ˇA
15629        b
15630        "#
15631        .unindent(),
15632    );
15633    cx.run_until_parked();
15634
15635    // TODO this cursor position seems bad
15636    cx.assert_state_with_diff(
15637        r#"
15638        - ˇa
15639        + A
15640          b
15641        "#
15642        .unindent(),
15643    );
15644
15645    cx.update_editor(|editor, window, cx| {
15646        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15647    });
15648
15649    cx.assert_state_with_diff(
15650        r#"
15651            - ˇa
15652            + A
15653              b
15654            - c
15655            "#
15656        .unindent(),
15657    );
15658
15659    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15660        let snapshot = editor.snapshot(window, cx);
15661        let hunks = editor
15662            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15663            .collect::<Vec<_>>();
15664        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15665        let buffer_id = hunks[0].buffer_id;
15666        hunks
15667            .into_iter()
15668            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15669            .collect::<Vec<_>>()
15670    });
15671    assert_eq!(hunk_ranges.len(), 2);
15672
15673    cx.update_editor(|editor, _, cx| {
15674        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15675    });
15676    executor.run_until_parked();
15677
15678    cx.assert_state_with_diff(
15679        r#"
15680        - ˇa
15681        + A
15682          b
15683        "#
15684        .unindent(),
15685    );
15686}
15687
15688#[gpui::test]
15689async fn test_toggle_deletion_hunk_at_start_of_file(
15690    executor: BackgroundExecutor,
15691    cx: &mut TestAppContext,
15692) {
15693    init_test(cx, |_| {});
15694    let mut cx = EditorTestContext::new(cx).await;
15695
15696    let diff_base = r#"
15697        a
15698        b
15699        c
15700        "#
15701    .unindent();
15702
15703    cx.set_state(
15704        &r#"
15705        ˇb
15706        c
15707        "#
15708        .unindent(),
15709    );
15710    cx.set_head_text(&diff_base);
15711    cx.update_editor(|editor, window, cx| {
15712        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15713    });
15714    executor.run_until_parked();
15715
15716    let hunk_expanded = r#"
15717        - a
15718          ˇb
15719          c
15720        "#
15721    .unindent();
15722
15723    cx.assert_state_with_diff(hunk_expanded.clone());
15724
15725    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15726        let snapshot = editor.snapshot(window, cx);
15727        let hunks = editor
15728            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15729            .collect::<Vec<_>>();
15730        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15731        let buffer_id = hunks[0].buffer_id;
15732        hunks
15733            .into_iter()
15734            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15735            .collect::<Vec<_>>()
15736    });
15737    assert_eq!(hunk_ranges.len(), 1);
15738
15739    cx.update_editor(|editor, _, cx| {
15740        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15741    });
15742    executor.run_until_parked();
15743
15744    let hunk_collapsed = r#"
15745          ˇb
15746          c
15747        "#
15748    .unindent();
15749
15750    cx.assert_state_with_diff(hunk_collapsed);
15751
15752    cx.update_editor(|editor, _, cx| {
15753        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15754    });
15755    executor.run_until_parked();
15756
15757    cx.assert_state_with_diff(hunk_expanded.clone());
15758}
15759
15760#[gpui::test]
15761async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15762    init_test(cx, |_| {});
15763
15764    let fs = FakeFs::new(cx.executor());
15765    fs.insert_tree(
15766        path!("/test"),
15767        json!({
15768            ".git": {},
15769            "file-1": "ONE\n",
15770            "file-2": "TWO\n",
15771            "file-3": "THREE\n",
15772        }),
15773    )
15774    .await;
15775
15776    fs.set_head_for_repo(
15777        path!("/test/.git").as_ref(),
15778        &[
15779            ("file-1".into(), "one\n".into()),
15780            ("file-2".into(), "two\n".into()),
15781            ("file-3".into(), "three\n".into()),
15782        ],
15783    );
15784
15785    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15786    let mut buffers = vec![];
15787    for i in 1..=3 {
15788        let buffer = project
15789            .update(cx, |project, cx| {
15790                let path = format!(path!("/test/file-{}"), i);
15791                project.open_local_buffer(path, cx)
15792            })
15793            .await
15794            .unwrap();
15795        buffers.push(buffer);
15796    }
15797
15798    let multibuffer = cx.new(|cx| {
15799        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15800        multibuffer.set_all_diff_hunks_expanded(cx);
15801        for buffer in &buffers {
15802            let snapshot = buffer.read(cx).snapshot();
15803            multibuffer.set_excerpts_for_path(
15804                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15805                buffer.clone(),
15806                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15807                DEFAULT_MULTIBUFFER_CONTEXT,
15808                cx,
15809            );
15810        }
15811        multibuffer
15812    });
15813
15814    let editor = cx.add_window(|window, cx| {
15815        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15816    });
15817    cx.run_until_parked();
15818
15819    let snapshot = editor
15820        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15821        .unwrap();
15822    let hunks = snapshot
15823        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15824        .map(|hunk| match hunk {
15825            DisplayDiffHunk::Unfolded {
15826                display_row_range, ..
15827            } => display_row_range,
15828            DisplayDiffHunk::Folded { .. } => unreachable!(),
15829        })
15830        .collect::<Vec<_>>();
15831    assert_eq!(
15832        hunks,
15833        [
15834            DisplayRow(2)..DisplayRow(4),
15835            DisplayRow(7)..DisplayRow(9),
15836            DisplayRow(12)..DisplayRow(14),
15837        ]
15838    );
15839}
15840
15841#[gpui::test]
15842async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15843    init_test(cx, |_| {});
15844
15845    let mut cx = EditorTestContext::new(cx).await;
15846    cx.set_head_text(indoc! { "
15847        one
15848        two
15849        three
15850        four
15851        five
15852        "
15853    });
15854    cx.set_index_text(indoc! { "
15855        one
15856        two
15857        three
15858        four
15859        five
15860        "
15861    });
15862    cx.set_state(indoc! {"
15863        one
15864        TWO
15865        ˇTHREE
15866        FOUR
15867        five
15868    "});
15869    cx.run_until_parked();
15870    cx.update_editor(|editor, window, cx| {
15871        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15872    });
15873    cx.run_until_parked();
15874    cx.assert_index_text(Some(indoc! {"
15875        one
15876        TWO
15877        THREE
15878        FOUR
15879        five
15880    "}));
15881    cx.set_state(indoc! { "
15882        one
15883        TWO
15884        ˇTHREE-HUNDRED
15885        FOUR
15886        five
15887    "});
15888    cx.run_until_parked();
15889    cx.update_editor(|editor, window, cx| {
15890        let snapshot = editor.snapshot(window, cx);
15891        let hunks = editor
15892            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15893            .collect::<Vec<_>>();
15894        assert_eq!(hunks.len(), 1);
15895        assert_eq!(
15896            hunks[0].status(),
15897            DiffHunkStatus {
15898                kind: DiffHunkStatusKind::Modified,
15899                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15900            }
15901        );
15902
15903        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15904    });
15905    cx.run_until_parked();
15906    cx.assert_index_text(Some(indoc! {"
15907        one
15908        TWO
15909        THREE-HUNDRED
15910        FOUR
15911        five
15912    "}));
15913}
15914
15915#[gpui::test]
15916fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15917    init_test(cx, |_| {});
15918
15919    let editor = cx.add_window(|window, cx| {
15920        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15921        build_editor(buffer, window, cx)
15922    });
15923
15924    let render_args = Arc::new(Mutex::new(None));
15925    let snapshot = editor
15926        .update(cx, |editor, window, cx| {
15927            let snapshot = editor.buffer().read(cx).snapshot(cx);
15928            let range =
15929                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15930
15931            struct RenderArgs {
15932                row: MultiBufferRow,
15933                folded: bool,
15934                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15935            }
15936
15937            let crease = Crease::inline(
15938                range,
15939                FoldPlaceholder::test(),
15940                {
15941                    let toggle_callback = render_args.clone();
15942                    move |row, folded, callback, _window, _cx| {
15943                        *toggle_callback.lock() = Some(RenderArgs {
15944                            row,
15945                            folded,
15946                            callback,
15947                        });
15948                        div()
15949                    }
15950                },
15951                |_row, _folded, _window, _cx| div(),
15952            );
15953
15954            editor.insert_creases(Some(crease), cx);
15955            let snapshot = editor.snapshot(window, cx);
15956            let _div = snapshot.render_crease_toggle(
15957                MultiBufferRow(1),
15958                false,
15959                cx.entity().clone(),
15960                window,
15961                cx,
15962            );
15963            snapshot
15964        })
15965        .unwrap();
15966
15967    let render_args = render_args.lock().take().unwrap();
15968    assert_eq!(render_args.row, MultiBufferRow(1));
15969    assert!(!render_args.folded);
15970    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15971
15972    cx.update_window(*editor, |_, window, cx| {
15973        (render_args.callback)(true, window, cx)
15974    })
15975    .unwrap();
15976    let snapshot = editor
15977        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15978        .unwrap();
15979    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
15980
15981    cx.update_window(*editor, |_, window, cx| {
15982        (render_args.callback)(false, window, cx)
15983    })
15984    .unwrap();
15985    let snapshot = editor
15986        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15987        .unwrap();
15988    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15989}
15990
15991#[gpui::test]
15992async fn test_input_text(cx: &mut TestAppContext) {
15993    init_test(cx, |_| {});
15994    let mut cx = EditorTestContext::new(cx).await;
15995
15996    cx.set_state(
15997        &r#"ˇone
15998        two
15999
16000        three
16001        fourˇ
16002        five
16003
16004        siˇx"#
16005            .unindent(),
16006    );
16007
16008    cx.dispatch_action(HandleInput(String::new()));
16009    cx.assert_editor_state(
16010        &r#"ˇone
16011        two
16012
16013        three
16014        fourˇ
16015        five
16016
16017        siˇx"#
16018            .unindent(),
16019    );
16020
16021    cx.dispatch_action(HandleInput("AAAA".to_string()));
16022    cx.assert_editor_state(
16023        &r#"AAAAˇone
16024        two
16025
16026        three
16027        fourAAAAˇ
16028        five
16029
16030        siAAAAˇx"#
16031            .unindent(),
16032    );
16033}
16034
16035#[gpui::test]
16036async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16037    init_test(cx, |_| {});
16038
16039    let mut cx = EditorTestContext::new(cx).await;
16040    cx.set_state(
16041        r#"let foo = 1;
16042let foo = 2;
16043let foo = 3;
16044let fooˇ = 4;
16045let foo = 5;
16046let foo = 6;
16047let foo = 7;
16048let foo = 8;
16049let foo = 9;
16050let foo = 10;
16051let foo = 11;
16052let foo = 12;
16053let foo = 13;
16054let foo = 14;
16055let foo = 15;"#,
16056    );
16057
16058    cx.update_editor(|e, window, cx| {
16059        assert_eq!(
16060            e.next_scroll_position,
16061            NextScrollCursorCenterTopBottom::Center,
16062            "Default next scroll direction is center",
16063        );
16064
16065        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16066        assert_eq!(
16067            e.next_scroll_position,
16068            NextScrollCursorCenterTopBottom::Top,
16069            "After center, next scroll direction should be top",
16070        );
16071
16072        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16073        assert_eq!(
16074            e.next_scroll_position,
16075            NextScrollCursorCenterTopBottom::Bottom,
16076            "After top, next scroll direction should be bottom",
16077        );
16078
16079        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16080        assert_eq!(
16081            e.next_scroll_position,
16082            NextScrollCursorCenterTopBottom::Center,
16083            "After bottom, scrolling should start over",
16084        );
16085
16086        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16087        assert_eq!(
16088            e.next_scroll_position,
16089            NextScrollCursorCenterTopBottom::Top,
16090            "Scrolling continues if retriggered fast enough"
16091        );
16092    });
16093
16094    cx.executor()
16095        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16096    cx.executor().run_until_parked();
16097    cx.update_editor(|e, _, _| {
16098        assert_eq!(
16099            e.next_scroll_position,
16100            NextScrollCursorCenterTopBottom::Center,
16101            "If scrolling is not triggered fast enough, it should reset"
16102        );
16103    });
16104}
16105
16106#[gpui::test]
16107async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16108    init_test(cx, |_| {});
16109    let mut cx = EditorLspTestContext::new_rust(
16110        lsp::ServerCapabilities {
16111            definition_provider: Some(lsp::OneOf::Left(true)),
16112            references_provider: Some(lsp::OneOf::Left(true)),
16113            ..lsp::ServerCapabilities::default()
16114        },
16115        cx,
16116    )
16117    .await;
16118
16119    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16120        let go_to_definition = cx.lsp.handle_request::<lsp::request::GotoDefinition, _, _>(
16121            move |params, _| async move {
16122                if empty_go_to_definition {
16123                    Ok(None)
16124                } else {
16125                    Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16126                        uri: params.text_document_position_params.text_document.uri,
16127                        range: lsp::Range::new(lsp::Position::new(4, 3), lsp::Position::new(4, 6)),
16128                    })))
16129                }
16130            },
16131        );
16132        let references =
16133            cx.lsp
16134                .handle_request::<lsp::request::References, _, _>(move |params, _| async move {
16135                    Ok(Some(vec![lsp::Location {
16136                        uri: params.text_document_position.text_document.uri,
16137                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16138                    }]))
16139                });
16140        (go_to_definition, references)
16141    };
16142
16143    cx.set_state(
16144        &r#"fn one() {
16145            let mut a = ˇtwo();
16146        }
16147
16148        fn two() {}"#
16149            .unindent(),
16150    );
16151    set_up_lsp_handlers(false, &mut cx);
16152    let navigated = cx
16153        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16154        .await
16155        .expect("Failed to navigate to definition");
16156    assert_eq!(
16157        navigated,
16158        Navigated::Yes,
16159        "Should have navigated to definition from the GetDefinition response"
16160    );
16161    cx.assert_editor_state(
16162        &r#"fn one() {
16163            let mut a = two();
16164        }
16165
16166        fn «twoˇ»() {}"#
16167            .unindent(),
16168    );
16169
16170    let editors = cx.update_workspace(|workspace, _, cx| {
16171        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16172    });
16173    cx.update_editor(|_, _, test_editor_cx| {
16174        assert_eq!(
16175            editors.len(),
16176            1,
16177            "Initially, only one, test, editor should be open in the workspace"
16178        );
16179        assert_eq!(
16180            test_editor_cx.entity(),
16181            editors.last().expect("Asserted len is 1").clone()
16182        );
16183    });
16184
16185    set_up_lsp_handlers(true, &mut cx);
16186    let navigated = cx
16187        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16188        .await
16189        .expect("Failed to navigate to lookup references");
16190    assert_eq!(
16191        navigated,
16192        Navigated::Yes,
16193        "Should have navigated to references as a fallback after empty GoToDefinition response"
16194    );
16195    // We should not change the selections in the existing file,
16196    // if opening another milti buffer with the references
16197    cx.assert_editor_state(
16198        &r#"fn one() {
16199            let mut a = two();
16200        }
16201
16202        fn «twoˇ»() {}"#
16203            .unindent(),
16204    );
16205    let editors = cx.update_workspace(|workspace, _, cx| {
16206        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16207    });
16208    cx.update_editor(|_, _, test_editor_cx| {
16209        assert_eq!(
16210            editors.len(),
16211            2,
16212            "After falling back to references search, we open a new editor with the results"
16213        );
16214        let references_fallback_text = editors
16215            .into_iter()
16216            .find(|new_editor| *new_editor != test_editor_cx.entity())
16217            .expect("Should have one non-test editor now")
16218            .read(test_editor_cx)
16219            .text(test_editor_cx);
16220        assert_eq!(
16221            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16222            "Should use the range from the references response and not the GoToDefinition one"
16223        );
16224    });
16225}
16226
16227#[gpui::test]
16228async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16229    init_test(cx, |_| {});
16230
16231    let language = Arc::new(Language::new(
16232        LanguageConfig::default(),
16233        Some(tree_sitter_rust::LANGUAGE.into()),
16234    ));
16235
16236    let text = r#"
16237        #[cfg(test)]
16238        mod tests() {
16239            #[test]
16240            fn runnable_1() {
16241                let a = 1;
16242            }
16243
16244            #[test]
16245            fn runnable_2() {
16246                let a = 1;
16247                let b = 2;
16248            }
16249        }
16250    "#
16251    .unindent();
16252
16253    let fs = FakeFs::new(cx.executor());
16254    fs.insert_file("/file.rs", Default::default()).await;
16255
16256    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16257    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16258    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16259    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16260    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16261
16262    let editor = cx.new_window_entity(|window, cx| {
16263        Editor::new(
16264            EditorMode::Full,
16265            multi_buffer,
16266            Some(project.clone()),
16267            window,
16268            cx,
16269        )
16270    });
16271
16272    editor.update_in(cx, |editor, window, cx| {
16273        let snapshot = editor.buffer().read(cx).snapshot(cx);
16274        editor.tasks.insert(
16275            (buffer.read(cx).remote_id(), 3),
16276            RunnableTasks {
16277                templates: vec![],
16278                offset: snapshot.anchor_before(43),
16279                column: 0,
16280                extra_variables: HashMap::default(),
16281                context_range: BufferOffset(43)..BufferOffset(85),
16282            },
16283        );
16284        editor.tasks.insert(
16285            (buffer.read(cx).remote_id(), 8),
16286            RunnableTasks {
16287                templates: vec![],
16288                offset: snapshot.anchor_before(86),
16289                column: 0,
16290                extra_variables: HashMap::default(),
16291                context_range: BufferOffset(86)..BufferOffset(191),
16292            },
16293        );
16294
16295        // Test finding task when cursor is inside function body
16296        editor.change_selections(None, window, cx, |s| {
16297            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16298        });
16299        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16300        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16301
16302        // Test finding task when cursor is on function name
16303        editor.change_selections(None, window, cx, |s| {
16304            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16305        });
16306        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16307        assert_eq!(row, 8, "Should find task when cursor is on function name");
16308    });
16309}
16310
16311#[gpui::test]
16312async fn test_folding_buffers(cx: &mut TestAppContext) {
16313    init_test(cx, |_| {});
16314
16315    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16316    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16317    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16318
16319    let fs = FakeFs::new(cx.executor());
16320    fs.insert_tree(
16321        path!("/a"),
16322        json!({
16323            "first.rs": sample_text_1,
16324            "second.rs": sample_text_2,
16325            "third.rs": sample_text_3,
16326        }),
16327    )
16328    .await;
16329    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16330    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16331    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16332    let worktree = project.update(cx, |project, cx| {
16333        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16334        assert_eq!(worktrees.len(), 1);
16335        worktrees.pop().unwrap()
16336    });
16337    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16338
16339    let buffer_1 = project
16340        .update(cx, |project, cx| {
16341            project.open_buffer((worktree_id, "first.rs"), cx)
16342        })
16343        .await
16344        .unwrap();
16345    let buffer_2 = project
16346        .update(cx, |project, cx| {
16347            project.open_buffer((worktree_id, "second.rs"), cx)
16348        })
16349        .await
16350        .unwrap();
16351    let buffer_3 = project
16352        .update(cx, |project, cx| {
16353            project.open_buffer((worktree_id, "third.rs"), cx)
16354        })
16355        .await
16356        .unwrap();
16357
16358    let multi_buffer = cx.new(|cx| {
16359        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16360        multi_buffer.push_excerpts(
16361            buffer_1.clone(),
16362            [
16363                ExcerptRange {
16364                    context: Point::new(0, 0)..Point::new(3, 0),
16365                    primary: None,
16366                },
16367                ExcerptRange {
16368                    context: Point::new(5, 0)..Point::new(7, 0),
16369                    primary: None,
16370                },
16371                ExcerptRange {
16372                    context: Point::new(9, 0)..Point::new(10, 4),
16373                    primary: None,
16374                },
16375            ],
16376            cx,
16377        );
16378        multi_buffer.push_excerpts(
16379            buffer_2.clone(),
16380            [
16381                ExcerptRange {
16382                    context: Point::new(0, 0)..Point::new(3, 0),
16383                    primary: None,
16384                },
16385                ExcerptRange {
16386                    context: Point::new(5, 0)..Point::new(7, 0),
16387                    primary: None,
16388                },
16389                ExcerptRange {
16390                    context: Point::new(9, 0)..Point::new(10, 4),
16391                    primary: None,
16392                },
16393            ],
16394            cx,
16395        );
16396        multi_buffer.push_excerpts(
16397            buffer_3.clone(),
16398            [
16399                ExcerptRange {
16400                    context: Point::new(0, 0)..Point::new(3, 0),
16401                    primary: None,
16402                },
16403                ExcerptRange {
16404                    context: Point::new(5, 0)..Point::new(7, 0),
16405                    primary: None,
16406                },
16407                ExcerptRange {
16408                    context: Point::new(9, 0)..Point::new(10, 4),
16409                    primary: None,
16410                },
16411            ],
16412            cx,
16413        );
16414        multi_buffer
16415    });
16416    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16417        Editor::new(
16418            EditorMode::Full,
16419            multi_buffer.clone(),
16420            Some(project.clone()),
16421            window,
16422            cx,
16423        )
16424    });
16425
16426    assert_eq!(
16427        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16428        "\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",
16429    );
16430
16431    multi_buffer_editor.update(cx, |editor, cx| {
16432        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16433    });
16434    assert_eq!(
16435        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16436        "\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",
16437        "After folding the first buffer, its text should not be displayed"
16438    );
16439
16440    multi_buffer_editor.update(cx, |editor, cx| {
16441        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16442    });
16443    assert_eq!(
16444        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16445        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16446        "After folding the second buffer, its text should not be displayed"
16447    );
16448
16449    multi_buffer_editor.update(cx, |editor, cx| {
16450        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16451    });
16452    assert_eq!(
16453        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16454        "\n\n\n\n\n",
16455        "After folding the third buffer, its text should not be displayed"
16456    );
16457
16458    // Emulate selection inside the fold logic, that should work
16459    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16460        editor
16461            .snapshot(window, cx)
16462            .next_line_boundary(Point::new(0, 4));
16463    });
16464
16465    multi_buffer_editor.update(cx, |editor, cx| {
16466        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16467    });
16468    assert_eq!(
16469        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16470        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16471        "After unfolding the second buffer, its text should be displayed"
16472    );
16473
16474    // Typing inside of buffer 1 causes that buffer to be unfolded.
16475    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16476        assert_eq!(
16477            multi_buffer
16478                .read(cx)
16479                .snapshot(cx)
16480                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16481                .collect::<String>(),
16482            "bbbb"
16483        );
16484        editor.change_selections(None, window, cx, |selections| {
16485            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16486        });
16487        editor.handle_input("B", window, cx);
16488    });
16489
16490    assert_eq!(
16491        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16492        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16493        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16494    );
16495
16496    multi_buffer_editor.update(cx, |editor, cx| {
16497        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16498    });
16499    assert_eq!(
16500        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16501        "\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",
16502        "After unfolding the all buffers, all original text should be displayed"
16503    );
16504}
16505
16506#[gpui::test]
16507async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16508    init_test(cx, |_| {});
16509
16510    let sample_text_1 = "1111\n2222\n3333".to_string();
16511    let sample_text_2 = "4444\n5555\n6666".to_string();
16512    let sample_text_3 = "7777\n8888\n9999".to_string();
16513
16514    let fs = FakeFs::new(cx.executor());
16515    fs.insert_tree(
16516        path!("/a"),
16517        json!({
16518            "first.rs": sample_text_1,
16519            "second.rs": sample_text_2,
16520            "third.rs": sample_text_3,
16521        }),
16522    )
16523    .await;
16524    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16525    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16526    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16527    let worktree = project.update(cx, |project, cx| {
16528        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16529        assert_eq!(worktrees.len(), 1);
16530        worktrees.pop().unwrap()
16531    });
16532    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16533
16534    let buffer_1 = project
16535        .update(cx, |project, cx| {
16536            project.open_buffer((worktree_id, "first.rs"), cx)
16537        })
16538        .await
16539        .unwrap();
16540    let buffer_2 = project
16541        .update(cx, |project, cx| {
16542            project.open_buffer((worktree_id, "second.rs"), cx)
16543        })
16544        .await
16545        .unwrap();
16546    let buffer_3 = project
16547        .update(cx, |project, cx| {
16548            project.open_buffer((worktree_id, "third.rs"), cx)
16549        })
16550        .await
16551        .unwrap();
16552
16553    let multi_buffer = cx.new(|cx| {
16554        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16555        multi_buffer.push_excerpts(
16556            buffer_1.clone(),
16557            [ExcerptRange {
16558                context: Point::new(0, 0)..Point::new(3, 0),
16559                primary: None,
16560            }],
16561            cx,
16562        );
16563        multi_buffer.push_excerpts(
16564            buffer_2.clone(),
16565            [ExcerptRange {
16566                context: Point::new(0, 0)..Point::new(3, 0),
16567                primary: None,
16568            }],
16569            cx,
16570        );
16571        multi_buffer.push_excerpts(
16572            buffer_3.clone(),
16573            [ExcerptRange {
16574                context: Point::new(0, 0)..Point::new(3, 0),
16575                primary: None,
16576            }],
16577            cx,
16578        );
16579        multi_buffer
16580    });
16581
16582    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16583        Editor::new(
16584            EditorMode::Full,
16585            multi_buffer,
16586            Some(project.clone()),
16587            window,
16588            cx,
16589        )
16590    });
16591
16592    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16593    assert_eq!(
16594        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16595        full_text,
16596    );
16597
16598    multi_buffer_editor.update(cx, |editor, cx| {
16599        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16600    });
16601    assert_eq!(
16602        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16603        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16604        "After folding the first buffer, its text should not be displayed"
16605    );
16606
16607    multi_buffer_editor.update(cx, |editor, cx| {
16608        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16609    });
16610
16611    assert_eq!(
16612        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16613        "\n\n\n\n\n\n7777\n8888\n9999",
16614        "After folding the second buffer, its text should not be displayed"
16615    );
16616
16617    multi_buffer_editor.update(cx, |editor, cx| {
16618        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16619    });
16620    assert_eq!(
16621        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16622        "\n\n\n\n\n",
16623        "After folding the third buffer, its text should not be displayed"
16624    );
16625
16626    multi_buffer_editor.update(cx, |editor, cx| {
16627        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16628    });
16629    assert_eq!(
16630        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16631        "\n\n\n\n4444\n5555\n6666\n\n",
16632        "After unfolding the second buffer, its text should be displayed"
16633    );
16634
16635    multi_buffer_editor.update(cx, |editor, cx| {
16636        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16637    });
16638    assert_eq!(
16639        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16640        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16641        "After unfolding the first buffer, its text should be displayed"
16642    );
16643
16644    multi_buffer_editor.update(cx, |editor, cx| {
16645        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16646    });
16647    assert_eq!(
16648        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16649        full_text,
16650        "After unfolding all buffers, all original text should be displayed"
16651    );
16652}
16653
16654#[gpui::test]
16655async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16656    init_test(cx, |_| {});
16657
16658    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16659
16660    let fs = FakeFs::new(cx.executor());
16661    fs.insert_tree(
16662        path!("/a"),
16663        json!({
16664            "main.rs": sample_text,
16665        }),
16666    )
16667    .await;
16668    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16669    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16670    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16671    let worktree = project.update(cx, |project, cx| {
16672        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16673        assert_eq!(worktrees.len(), 1);
16674        worktrees.pop().unwrap()
16675    });
16676    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16677
16678    let buffer_1 = project
16679        .update(cx, |project, cx| {
16680            project.open_buffer((worktree_id, "main.rs"), cx)
16681        })
16682        .await
16683        .unwrap();
16684
16685    let multi_buffer = cx.new(|cx| {
16686        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16687        multi_buffer.push_excerpts(
16688            buffer_1.clone(),
16689            [ExcerptRange {
16690                context: Point::new(0, 0)
16691                    ..Point::new(
16692                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16693                        0,
16694                    ),
16695                primary: None,
16696            }],
16697            cx,
16698        );
16699        multi_buffer
16700    });
16701    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16702        Editor::new(
16703            EditorMode::Full,
16704            multi_buffer,
16705            Some(project.clone()),
16706            window,
16707            cx,
16708        )
16709    });
16710
16711    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16712    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16713        enum TestHighlight {}
16714        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16715        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16716        editor.highlight_text::<TestHighlight>(
16717            vec![highlight_range.clone()],
16718            HighlightStyle::color(Hsla::green()),
16719            cx,
16720        );
16721        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16722    });
16723
16724    let full_text = format!("\n\n{sample_text}");
16725    assert_eq!(
16726        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16727        full_text,
16728    );
16729}
16730
16731#[gpui::test]
16732async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16733    init_test(cx, |_| {});
16734    cx.update(|cx| {
16735        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16736            "keymaps/default-linux.json",
16737            cx,
16738        )
16739        .unwrap();
16740        cx.bind_keys(default_key_bindings);
16741    });
16742
16743    let (editor, cx) = cx.add_window_view(|window, cx| {
16744        let multi_buffer = MultiBuffer::build_multi(
16745            [
16746                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16747                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16748                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16749                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16750            ],
16751            cx,
16752        );
16753        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16754
16755        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16756        // fold all but the second buffer, so that we test navigating between two
16757        // adjacent folded buffers, as well as folded buffers at the start and
16758        // end the multibuffer
16759        editor.fold_buffer(buffer_ids[0], cx);
16760        editor.fold_buffer(buffer_ids[2], cx);
16761        editor.fold_buffer(buffer_ids[3], cx);
16762
16763        editor
16764    });
16765    cx.simulate_resize(size(px(1000.), px(1000.)));
16766
16767    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16768    cx.assert_excerpts_with_selections(indoc! {"
16769        [EXCERPT]
16770        ˇ[FOLDED]
16771        [EXCERPT]
16772        a1
16773        b1
16774        [EXCERPT]
16775        [FOLDED]
16776        [EXCERPT]
16777        [FOLDED]
16778        "
16779    });
16780    cx.simulate_keystroke("down");
16781    cx.assert_excerpts_with_selections(indoc! {"
16782        [EXCERPT]
16783        [FOLDED]
16784        [EXCERPT]
16785        ˇa1
16786        b1
16787        [EXCERPT]
16788        [FOLDED]
16789        [EXCERPT]
16790        [FOLDED]
16791        "
16792    });
16793    cx.simulate_keystroke("down");
16794    cx.assert_excerpts_with_selections(indoc! {"
16795        [EXCERPT]
16796        [FOLDED]
16797        [EXCERPT]
16798        a1
16799        ˇb1
16800        [EXCERPT]
16801        [FOLDED]
16802        [EXCERPT]
16803        [FOLDED]
16804        "
16805    });
16806    cx.simulate_keystroke("down");
16807    cx.assert_excerpts_with_selections(indoc! {"
16808        [EXCERPT]
16809        [FOLDED]
16810        [EXCERPT]
16811        a1
16812        b1
16813        ˇ[EXCERPT]
16814        [FOLDED]
16815        [EXCERPT]
16816        [FOLDED]
16817        "
16818    });
16819    cx.simulate_keystroke("down");
16820    cx.assert_excerpts_with_selections(indoc! {"
16821        [EXCERPT]
16822        [FOLDED]
16823        [EXCERPT]
16824        a1
16825        b1
16826        [EXCERPT]
16827        ˇ[FOLDED]
16828        [EXCERPT]
16829        [FOLDED]
16830        "
16831    });
16832    for _ in 0..5 {
16833        cx.simulate_keystroke("down");
16834        cx.assert_excerpts_with_selections(indoc! {"
16835            [EXCERPT]
16836            [FOLDED]
16837            [EXCERPT]
16838            a1
16839            b1
16840            [EXCERPT]
16841            [FOLDED]
16842            [EXCERPT]
16843            ˇ[FOLDED]
16844            "
16845        });
16846    }
16847
16848    cx.simulate_keystroke("up");
16849    cx.assert_excerpts_with_selections(indoc! {"
16850        [EXCERPT]
16851        [FOLDED]
16852        [EXCERPT]
16853        a1
16854        b1
16855        [EXCERPT]
16856        ˇ[FOLDED]
16857        [EXCERPT]
16858        [FOLDED]
16859        "
16860    });
16861    cx.simulate_keystroke("up");
16862    cx.assert_excerpts_with_selections(indoc! {"
16863        [EXCERPT]
16864        [FOLDED]
16865        [EXCERPT]
16866        a1
16867        b1
16868        ˇ[EXCERPT]
16869        [FOLDED]
16870        [EXCERPT]
16871        [FOLDED]
16872        "
16873    });
16874    cx.simulate_keystroke("up");
16875    cx.assert_excerpts_with_selections(indoc! {"
16876        [EXCERPT]
16877        [FOLDED]
16878        [EXCERPT]
16879        a1
16880        ˇb1
16881        [EXCERPT]
16882        [FOLDED]
16883        [EXCERPT]
16884        [FOLDED]
16885        "
16886    });
16887    cx.simulate_keystroke("up");
16888    cx.assert_excerpts_with_selections(indoc! {"
16889        [EXCERPT]
16890        [FOLDED]
16891        [EXCERPT]
16892        ˇa1
16893        b1
16894        [EXCERPT]
16895        [FOLDED]
16896        [EXCERPT]
16897        [FOLDED]
16898        "
16899    });
16900    for _ in 0..5 {
16901        cx.simulate_keystroke("up");
16902        cx.assert_excerpts_with_selections(indoc! {"
16903            [EXCERPT]
16904            ˇ[FOLDED]
16905            [EXCERPT]
16906            a1
16907            b1
16908            [EXCERPT]
16909            [FOLDED]
16910            [EXCERPT]
16911            [FOLDED]
16912            "
16913        });
16914    }
16915}
16916
16917#[gpui::test]
16918async fn test_inline_completion_text(cx: &mut TestAppContext) {
16919    init_test(cx, |_| {});
16920
16921    // Simple insertion
16922    assert_highlighted_edits(
16923        "Hello, world!",
16924        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16925        true,
16926        cx,
16927        |highlighted_edits, cx| {
16928            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16929            assert_eq!(highlighted_edits.highlights.len(), 1);
16930            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16931            assert_eq!(
16932                highlighted_edits.highlights[0].1.background_color,
16933                Some(cx.theme().status().created_background)
16934            );
16935        },
16936    )
16937    .await;
16938
16939    // Replacement
16940    assert_highlighted_edits(
16941        "This is a test.",
16942        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16943        false,
16944        cx,
16945        |highlighted_edits, cx| {
16946            assert_eq!(highlighted_edits.text, "That is a test.");
16947            assert_eq!(highlighted_edits.highlights.len(), 1);
16948            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
16949            assert_eq!(
16950                highlighted_edits.highlights[0].1.background_color,
16951                Some(cx.theme().status().created_background)
16952            );
16953        },
16954    )
16955    .await;
16956
16957    // Multiple edits
16958    assert_highlighted_edits(
16959        "Hello, world!",
16960        vec![
16961            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
16962            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
16963        ],
16964        false,
16965        cx,
16966        |highlighted_edits, cx| {
16967            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
16968            assert_eq!(highlighted_edits.highlights.len(), 2);
16969            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
16970            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
16971            assert_eq!(
16972                highlighted_edits.highlights[0].1.background_color,
16973                Some(cx.theme().status().created_background)
16974            );
16975            assert_eq!(
16976                highlighted_edits.highlights[1].1.background_color,
16977                Some(cx.theme().status().created_background)
16978            );
16979        },
16980    )
16981    .await;
16982
16983    // Multiple lines with edits
16984    assert_highlighted_edits(
16985        "First line\nSecond line\nThird line\nFourth line",
16986        vec![
16987            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
16988            (
16989                Point::new(2, 0)..Point::new(2, 10),
16990                "New third line".to_string(),
16991            ),
16992            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
16993        ],
16994        false,
16995        cx,
16996        |highlighted_edits, cx| {
16997            assert_eq!(
16998                highlighted_edits.text,
16999                "Second modified\nNew third line\nFourth updated line"
17000            );
17001            assert_eq!(highlighted_edits.highlights.len(), 3);
17002            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17003            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17004            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17005            for highlight in &highlighted_edits.highlights {
17006                assert_eq!(
17007                    highlight.1.background_color,
17008                    Some(cx.theme().status().created_background)
17009                );
17010            }
17011        },
17012    )
17013    .await;
17014}
17015
17016#[gpui::test]
17017async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17018    init_test(cx, |_| {});
17019
17020    // Deletion
17021    assert_highlighted_edits(
17022        "Hello, world!",
17023        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17024        true,
17025        cx,
17026        |highlighted_edits, cx| {
17027            assert_eq!(highlighted_edits.text, "Hello, world!");
17028            assert_eq!(highlighted_edits.highlights.len(), 1);
17029            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17030            assert_eq!(
17031                highlighted_edits.highlights[0].1.background_color,
17032                Some(cx.theme().status().deleted_background)
17033            );
17034        },
17035    )
17036    .await;
17037
17038    // Insertion
17039    assert_highlighted_edits(
17040        "Hello, world!",
17041        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17042        true,
17043        cx,
17044        |highlighted_edits, cx| {
17045            assert_eq!(highlighted_edits.highlights.len(), 1);
17046            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17047            assert_eq!(
17048                highlighted_edits.highlights[0].1.background_color,
17049                Some(cx.theme().status().created_background)
17050            );
17051        },
17052    )
17053    .await;
17054}
17055
17056async fn assert_highlighted_edits(
17057    text: &str,
17058    edits: Vec<(Range<Point>, String)>,
17059    include_deletions: bool,
17060    cx: &mut TestAppContext,
17061    assertion_fn: impl Fn(HighlightedText, &App),
17062) {
17063    let window = cx.add_window(|window, cx| {
17064        let buffer = MultiBuffer::build_simple(text, cx);
17065        Editor::new(EditorMode::Full, buffer, None, window, cx)
17066    });
17067    let cx = &mut VisualTestContext::from_window(*window, cx);
17068
17069    let (buffer, snapshot) = window
17070        .update(cx, |editor, _window, cx| {
17071            (
17072                editor.buffer().clone(),
17073                editor.buffer().read(cx).snapshot(cx),
17074            )
17075        })
17076        .unwrap();
17077
17078    let edits = edits
17079        .into_iter()
17080        .map(|(range, edit)| {
17081            (
17082                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17083                edit,
17084            )
17085        })
17086        .collect::<Vec<_>>();
17087
17088    let text_anchor_edits = edits
17089        .clone()
17090        .into_iter()
17091        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17092        .collect::<Vec<_>>();
17093
17094    let edit_preview = window
17095        .update(cx, |_, _window, cx| {
17096            buffer
17097                .read(cx)
17098                .as_singleton()
17099                .unwrap()
17100                .read(cx)
17101                .preview_edits(text_anchor_edits.into(), cx)
17102        })
17103        .unwrap()
17104        .await;
17105
17106    cx.update(|_window, cx| {
17107        let highlighted_edits = inline_completion_edit_text(
17108            &snapshot.as_singleton().unwrap().2,
17109            &edits,
17110            &edit_preview,
17111            include_deletions,
17112            cx,
17113        );
17114        assertion_fn(highlighted_edits, cx)
17115    });
17116}
17117
17118#[track_caller]
17119fn assert_breakpoint(
17120    breakpoints: &BTreeMap<Arc<Path>, Vec<SerializedBreakpoint>>,
17121    path: &Arc<Path>,
17122    expected: Vec<(u32, BreakpointKind)>,
17123) {
17124    if expected.len() == 0usize {
17125        assert!(!breakpoints.contains_key(path));
17126    } else {
17127        let mut breakpoint = breakpoints
17128            .get(path)
17129            .unwrap()
17130            .into_iter()
17131            .map(|breakpoint| (breakpoint.position, breakpoint.kind.clone()))
17132            .collect::<Vec<_>>();
17133
17134        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17135
17136        assert_eq!(expected, breakpoint);
17137    }
17138}
17139
17140fn add_log_breakpoint_at_cursor(
17141    editor: &mut Editor,
17142    log_message: &str,
17143    window: &mut Window,
17144    cx: &mut Context<Editor>,
17145) {
17146    let (anchor, bp) = editor
17147        .breakpoint_at_cursor_head(window, cx)
17148        .unwrap_or_else(|| {
17149            let cursor_position: Point = editor.selections.newest(cx).head();
17150
17151            let breakpoint_position = editor
17152                .snapshot(window, cx)
17153                .display_snapshot
17154                .buffer_snapshot
17155                .anchor_before(Point::new(cursor_position.row, 0));
17156
17157            let kind = BreakpointKind::Log(Arc::from(log_message));
17158
17159            (breakpoint_position, Breakpoint { kind })
17160        });
17161
17162    editor.edit_breakpoint_at_anchor(
17163        anchor,
17164        bp.kind,
17165        BreakpointEditAction::EditLogMessage(log_message.into()),
17166        cx,
17167    );
17168}
17169
17170#[gpui::test]
17171async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17172    init_test(cx, |_| {});
17173
17174    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17175    let fs = FakeFs::new(cx.executor());
17176    fs.insert_tree(
17177        path!("/a"),
17178        json!({
17179            "main.rs": sample_text,
17180        }),
17181    )
17182    .await;
17183    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17184    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17185    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17186
17187    let fs = FakeFs::new(cx.executor());
17188    fs.insert_tree(
17189        path!("/a"),
17190        json!({
17191            "main.rs": sample_text,
17192        }),
17193    )
17194    .await;
17195    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17196    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17197    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17198    let worktree_id = workspace
17199        .update(cx, |workspace, _window, cx| {
17200            workspace.project().update(cx, |project, cx| {
17201                project.worktrees(cx).next().unwrap().read(cx).id()
17202            })
17203        })
17204        .unwrap();
17205
17206    let buffer = project
17207        .update(cx, |project, cx| {
17208            project.open_buffer((worktree_id, "main.rs"), cx)
17209        })
17210        .await
17211        .unwrap();
17212
17213    let (editor, cx) = cx.add_window_view(|window, cx| {
17214        Editor::new(
17215            EditorMode::Full,
17216            MultiBuffer::build_from_buffer(buffer, cx),
17217            Some(project.clone()),
17218            window,
17219            cx,
17220        )
17221    });
17222
17223    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17224    let abs_path = project.read_with(cx, |project, cx| {
17225        project
17226            .absolute_path(&project_path, cx)
17227            .map(|path_buf| Arc::from(path_buf.to_owned()))
17228            .unwrap()
17229    });
17230
17231    // assert we can add breakpoint on the first line
17232    editor.update_in(cx, |editor, window, cx| {
17233        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17234        editor.move_to_end(&MoveToEnd, window, cx);
17235        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17236    });
17237
17238    let breakpoints = editor.update(cx, |editor, cx| {
17239        editor
17240            .breakpoint_store()
17241            .as_ref()
17242            .unwrap()
17243            .read(cx)
17244            .all_breakpoints(cx)
17245            .clone()
17246    });
17247
17248    assert_eq!(1, breakpoints.len());
17249    assert_breakpoint(
17250        &breakpoints,
17251        &abs_path,
17252        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17253    );
17254
17255    editor.update_in(cx, |editor, window, cx| {
17256        editor.move_to_beginning(&MoveToBeginning, window, cx);
17257        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17258    });
17259
17260    let breakpoints = editor.update(cx, |editor, cx| {
17261        editor
17262            .breakpoint_store()
17263            .as_ref()
17264            .unwrap()
17265            .read(cx)
17266            .all_breakpoints(cx)
17267            .clone()
17268    });
17269
17270    assert_eq!(1, breakpoints.len());
17271    assert_breakpoint(&breakpoints, &abs_path, vec![(3, BreakpointKind::Standard)]);
17272
17273    editor.update_in(cx, |editor, window, cx| {
17274        editor.move_to_end(&MoveToEnd, window, cx);
17275        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17276    });
17277
17278    let breakpoints = editor.update(cx, |editor, cx| {
17279        editor
17280            .breakpoint_store()
17281            .as_ref()
17282            .unwrap()
17283            .read(cx)
17284            .all_breakpoints(cx)
17285            .clone()
17286    });
17287
17288    assert_eq!(0, breakpoints.len());
17289    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17290}
17291
17292#[gpui::test]
17293async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17294    init_test(cx, |_| {});
17295
17296    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17297
17298    let fs = FakeFs::new(cx.executor());
17299    fs.insert_tree(
17300        path!("/a"),
17301        json!({
17302            "main.rs": sample_text,
17303        }),
17304    )
17305    .await;
17306    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17307    let (workspace, cx) =
17308        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17309
17310    let worktree_id = workspace.update(cx, |workspace, cx| {
17311        workspace.project().update(cx, |project, cx| {
17312            project.worktrees(cx).next().unwrap().read(cx).id()
17313        })
17314    });
17315
17316    let buffer = project
17317        .update(cx, |project, cx| {
17318            project.open_buffer((worktree_id, "main.rs"), cx)
17319        })
17320        .await
17321        .unwrap();
17322
17323    let (editor, cx) = cx.add_window_view(|window, cx| {
17324        Editor::new(
17325            EditorMode::Full,
17326            MultiBuffer::build_from_buffer(buffer, cx),
17327            Some(project.clone()),
17328            window,
17329            cx,
17330        )
17331    });
17332
17333    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17334    let abs_path = project.read_with(cx, |project, cx| {
17335        project
17336            .absolute_path(&project_path, cx)
17337            .map(|path_buf| Arc::from(path_buf.to_owned()))
17338            .unwrap()
17339    });
17340
17341    editor.update_in(cx, |editor, window, cx| {
17342        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17343    });
17344
17345    let breakpoints = editor.update(cx, |editor, cx| {
17346        editor
17347            .breakpoint_store()
17348            .as_ref()
17349            .unwrap()
17350            .read(cx)
17351            .all_breakpoints(cx)
17352            .clone()
17353    });
17354
17355    assert_breakpoint(
17356        &breakpoints,
17357        &abs_path,
17358        vec![(0, BreakpointKind::Log("hello world".into()))],
17359    );
17360
17361    // Removing a log message from a log breakpoint should remove it
17362    editor.update_in(cx, |editor, window, cx| {
17363        add_log_breakpoint_at_cursor(editor, "", window, cx);
17364    });
17365
17366    let breakpoints = editor.update(cx, |editor, cx| {
17367        editor
17368            .breakpoint_store()
17369            .as_ref()
17370            .unwrap()
17371            .read(cx)
17372            .all_breakpoints(cx)
17373            .clone()
17374    });
17375
17376    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17377
17378    editor.update_in(cx, |editor, window, cx| {
17379        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17380        editor.move_to_end(&MoveToEnd, window, cx);
17381        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17382        // Not adding a log message to a standard breakpoint shouldn't remove it
17383        add_log_breakpoint_at_cursor(editor, "", window, cx);
17384    });
17385
17386    let breakpoints = editor.update(cx, |editor, cx| {
17387        editor
17388            .breakpoint_store()
17389            .as_ref()
17390            .unwrap()
17391            .read(cx)
17392            .all_breakpoints(cx)
17393            .clone()
17394    });
17395
17396    assert_breakpoint(
17397        &breakpoints,
17398        &abs_path,
17399        vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
17400    );
17401
17402    editor.update_in(cx, |editor, window, cx| {
17403        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17404    });
17405
17406    let breakpoints = editor.update(cx, |editor, cx| {
17407        editor
17408            .breakpoint_store()
17409            .as_ref()
17410            .unwrap()
17411            .read(cx)
17412            .all_breakpoints(cx)
17413            .clone()
17414    });
17415
17416    assert_breakpoint(
17417        &breakpoints,
17418        &abs_path,
17419        vec![
17420            (0, BreakpointKind::Standard),
17421            (3, BreakpointKind::Log("hello world".into())),
17422        ],
17423    );
17424
17425    editor.update_in(cx, |editor, window, cx| {
17426        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17427    });
17428
17429    let breakpoints = editor.update(cx, |editor, cx| {
17430        editor
17431            .breakpoint_store()
17432            .as_ref()
17433            .unwrap()
17434            .read(cx)
17435            .all_breakpoints(cx)
17436            .clone()
17437    });
17438
17439    assert_breakpoint(
17440        &breakpoints,
17441        &abs_path,
17442        vec![
17443            (0, BreakpointKind::Standard),
17444            (3, BreakpointKind::Log("hello Earth !!".into())),
17445        ],
17446    );
17447}
17448
17449#[gpui::test]
17450async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17451    init_test(cx, |_| {});
17452    let capabilities = lsp::ServerCapabilities {
17453        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17454            prepare_provider: Some(true),
17455            work_done_progress_options: Default::default(),
17456        })),
17457        ..Default::default()
17458    };
17459    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17460
17461    cx.set_state(indoc! {"
17462        struct Fˇoo {}
17463    "});
17464
17465    cx.update_editor(|editor, _, cx| {
17466        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17467        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17468        editor.highlight_background::<DocumentHighlightRead>(
17469            &[highlight_range],
17470            |c| c.editor_document_highlight_read_background,
17471            cx,
17472        );
17473    });
17474
17475    let mut prepare_rename_handler =
17476        cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
17477            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17478                start: lsp::Position {
17479                    line: 0,
17480                    character: 7,
17481                },
17482                end: lsp::Position {
17483                    line: 0,
17484                    character: 10,
17485                },
17486            })))
17487        });
17488    let prepare_rename_task = cx
17489        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17490        .expect("Prepare rename was not started");
17491    prepare_rename_handler.next().await.unwrap();
17492    prepare_rename_task.await.expect("Prepare rename failed");
17493
17494    let mut rename_handler =
17495        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17496            let edit = lsp::TextEdit {
17497                range: lsp::Range {
17498                    start: lsp::Position {
17499                        line: 0,
17500                        character: 7,
17501                    },
17502                    end: lsp::Position {
17503                        line: 0,
17504                        character: 10,
17505                    },
17506                },
17507                new_text: "FooRenamed".to_string(),
17508            };
17509            Ok(Some(lsp::WorkspaceEdit::new(
17510                // Specify the same edit twice
17511                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17512            )))
17513        });
17514    let rename_task = cx
17515        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17516        .expect("Confirm rename was not started");
17517    rename_handler.next().await.unwrap();
17518    rename_task.await.expect("Confirm rename failed");
17519    cx.run_until_parked();
17520
17521    // Despite two edits, only one is actually applied as those are identical
17522    cx.assert_editor_state(indoc! {"
17523        struct FooRenamedˇ {}
17524    "});
17525}
17526
17527#[gpui::test]
17528async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17529    init_test(cx, |_| {});
17530    // These capabilities indicate that the server does not support prepare rename.
17531    let capabilities = lsp::ServerCapabilities {
17532        rename_provider: Some(lsp::OneOf::Left(true)),
17533        ..Default::default()
17534    };
17535    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17536
17537    cx.set_state(indoc! {"
17538        struct Fˇoo {}
17539    "});
17540
17541    cx.update_editor(|editor, _window, cx| {
17542        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17543        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17544        editor.highlight_background::<DocumentHighlightRead>(
17545            &[highlight_range],
17546            |c| c.editor_document_highlight_read_background,
17547            cx,
17548        );
17549    });
17550
17551    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17552        .expect("Prepare rename was not started")
17553        .await
17554        .expect("Prepare rename failed");
17555
17556    let mut rename_handler =
17557        cx.handle_request::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17558            let edit = lsp::TextEdit {
17559                range: lsp::Range {
17560                    start: lsp::Position {
17561                        line: 0,
17562                        character: 7,
17563                    },
17564                    end: lsp::Position {
17565                        line: 0,
17566                        character: 10,
17567                    },
17568                },
17569                new_text: "FooRenamed".to_string(),
17570            };
17571            Ok(Some(lsp::WorkspaceEdit::new(
17572                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17573            )))
17574        });
17575    let rename_task = cx
17576        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17577        .expect("Confirm rename was not started");
17578    rename_handler.next().await.unwrap();
17579    rename_task.await.expect("Confirm rename failed");
17580    cx.run_until_parked();
17581
17582    // Correct range is renamed, as `surrounding_word` is used to find it.
17583    cx.assert_editor_state(indoc! {"
17584        struct FooRenamedˇ {}
17585    "});
17586}
17587
17588#[gpui::test]
17589async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17590    init_test(cx, |_| {});
17591    let mut cx = EditorTestContext::new(cx).await;
17592
17593    let language = Arc::new(
17594        Language::new(
17595            LanguageConfig::default(),
17596            Some(tree_sitter_html::LANGUAGE.into()),
17597        )
17598        .with_brackets_query(
17599            r#"
17600            ("<" @open "/>" @close)
17601            ("</" @open ">" @close)
17602            ("<" @open ">" @close)
17603            ("\"" @open "\"" @close)
17604            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17605        "#,
17606        )
17607        .unwrap(),
17608    );
17609    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17610
17611    cx.set_state(indoc! {"
17612        <span>ˇ</span>
17613    "});
17614    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17615    cx.assert_editor_state(indoc! {"
17616        <span>
17617        ˇ
17618        </span>
17619    "});
17620
17621    cx.set_state(indoc! {"
17622        <span><span></span>ˇ</span>
17623    "});
17624    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17625    cx.assert_editor_state(indoc! {"
17626        <span><span></span>
17627        ˇ</span>
17628    "});
17629
17630    cx.set_state(indoc! {"
17631        <span>ˇ
17632        </span>
17633    "});
17634    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17635    cx.assert_editor_state(indoc! {"
17636        <span>
17637        ˇ
17638        </span>
17639    "});
17640}
17641
17642#[gpui::test(iterations = 10)]
17643async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17644    init_test(cx, |_| {});
17645
17646    let fs = FakeFs::new(cx.executor());
17647    fs.insert_tree(
17648        path!("/dir"),
17649        json!({
17650            "a.ts": "a",
17651        }),
17652    )
17653    .await;
17654
17655    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17656    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17657    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17658
17659    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17660    language_registry.add(Arc::new(Language::new(
17661        LanguageConfig {
17662            name: "TypeScript".into(),
17663            matcher: LanguageMatcher {
17664                path_suffixes: vec!["ts".to_string()],
17665                ..Default::default()
17666            },
17667            ..Default::default()
17668        },
17669        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17670    )));
17671    let mut fake_language_servers = language_registry.register_fake_lsp(
17672        "TypeScript",
17673        FakeLspAdapter {
17674            capabilities: lsp::ServerCapabilities {
17675                code_lens_provider: Some(lsp::CodeLensOptions {
17676                    resolve_provider: Some(true),
17677                }),
17678                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17679                    commands: vec!["_the/command".to_string()],
17680                    ..lsp::ExecuteCommandOptions::default()
17681                }),
17682                ..lsp::ServerCapabilities::default()
17683            },
17684            ..FakeLspAdapter::default()
17685        },
17686    );
17687
17688    let (buffer, _handle) = project
17689        .update(cx, |p, cx| {
17690            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17691        })
17692        .await
17693        .unwrap();
17694    cx.executor().run_until_parked();
17695
17696    let fake_server = fake_language_servers.next().await.unwrap();
17697
17698    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17699    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17700    drop(buffer_snapshot);
17701    let actions = cx
17702        .update_window(*workspace, |_, window, cx| {
17703            project.code_actions(&buffer, anchor..anchor, window, cx)
17704        })
17705        .unwrap();
17706
17707    fake_server
17708        .handle_request::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17709            Ok(Some(vec![
17710                lsp::CodeLens {
17711                    range: lsp::Range::default(),
17712                    command: Some(lsp::Command {
17713                        title: "Code lens command".to_owned(),
17714                        command: "_the/command".to_owned(),
17715                        arguments: None,
17716                    }),
17717                    data: None,
17718                },
17719                lsp::CodeLens {
17720                    range: lsp::Range::default(),
17721                    command: Some(lsp::Command {
17722                        title: "Command not in capabilities".to_owned(),
17723                        command: "not in capabilities".to_owned(),
17724                        arguments: None,
17725                    }),
17726                    data: None,
17727                },
17728                lsp::CodeLens {
17729                    range: lsp::Range {
17730                        start: lsp::Position {
17731                            line: 1,
17732                            character: 1,
17733                        },
17734                        end: lsp::Position {
17735                            line: 1,
17736                            character: 1,
17737                        },
17738                    },
17739                    command: Some(lsp::Command {
17740                        title: "Command not in range".to_owned(),
17741                        command: "_the/command".to_owned(),
17742                        arguments: None,
17743                    }),
17744                    data: None,
17745                },
17746            ]))
17747        })
17748        .next()
17749        .await;
17750
17751    let actions = actions.await.unwrap();
17752    assert_eq!(
17753        actions.len(),
17754        1,
17755        "Should have only one valid action for the 0..0 range"
17756    );
17757    let action = actions[0].clone();
17758    let apply = project.update(cx, |project, cx| {
17759        project.apply_code_action(buffer.clone(), action, true, cx)
17760    });
17761
17762    // Resolving the code action does not populate its edits. In absence of
17763    // edits, we must execute the given command.
17764    fake_server.handle_request::<lsp::request::CodeLensResolve, _, _>(|mut lens, _| async move {
17765        let lens_command = lens.command.as_mut().expect("should have a command");
17766        assert_eq!(lens_command.title, "Code lens command");
17767        lens_command.arguments = Some(vec![json!("the-argument")]);
17768        Ok(lens)
17769    });
17770
17771    // While executing the command, the language server sends the editor
17772    // a `workspaceEdit` request.
17773    fake_server
17774        .handle_request::<lsp::request::ExecuteCommand, _, _>({
17775            let fake = fake_server.clone();
17776            move |params, _| {
17777                assert_eq!(params.command, "_the/command");
17778                let fake = fake.clone();
17779                async move {
17780                    fake.server
17781                        .request::<lsp::request::ApplyWorkspaceEdit>(
17782                            lsp::ApplyWorkspaceEditParams {
17783                                label: None,
17784                                edit: lsp::WorkspaceEdit {
17785                                    changes: Some(
17786                                        [(
17787                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
17788                                            vec![lsp::TextEdit {
17789                                                range: lsp::Range::new(
17790                                                    lsp::Position::new(0, 0),
17791                                                    lsp::Position::new(0, 0),
17792                                                ),
17793                                                new_text: "X".into(),
17794                                            }],
17795                                        )]
17796                                        .into_iter()
17797                                        .collect(),
17798                                    ),
17799                                    ..Default::default()
17800                                },
17801                            },
17802                        )
17803                        .await
17804                        .unwrap();
17805                    Ok(Some(json!(null)))
17806                }
17807            }
17808        })
17809        .next()
17810        .await;
17811
17812    // Applying the code lens command returns a project transaction containing the edits
17813    // sent by the language server in its `workspaceEdit` request.
17814    let transaction = apply.await.unwrap();
17815    assert!(transaction.0.contains_key(&buffer));
17816    buffer.update(cx, |buffer, cx| {
17817        assert_eq!(buffer.text(), "Xa");
17818        buffer.undo(cx);
17819        assert_eq!(buffer.text(), "a");
17820    });
17821}
17822
17823mod autoclose_tags {
17824    use super::*;
17825    use language::language_settings::JsxTagAutoCloseSettings;
17826    use languages::language;
17827
17828    async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
17829        init_test(cx, |settings| {
17830            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17831        });
17832
17833        let mut cx = EditorTestContext::new(cx).await;
17834        cx.update_buffer(|buffer, cx| {
17835            let language = language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into());
17836
17837            buffer.set_language(Some(language), cx)
17838        });
17839
17840        cx
17841    }
17842
17843    macro_rules! check {
17844        ($name:ident, $initial:literal + $input:literal => $expected:expr) => {
17845            #[gpui::test]
17846            async fn $name(cx: &mut TestAppContext) {
17847                let mut cx = test_setup(cx).await;
17848                cx.set_state($initial);
17849                cx.run_until_parked();
17850
17851                cx.update_editor(|editor, window, cx| {
17852                    editor.handle_input($input, window, cx);
17853                });
17854                cx.run_until_parked();
17855                cx.assert_editor_state($expected);
17856            }
17857        };
17858    }
17859
17860    check!(
17861        test_basic,
17862        "<divˇ" + ">" => "<div>ˇ</div>"
17863    );
17864
17865    check!(
17866        test_basic_nested,
17867        "<div><divˇ</div>" + ">" => "<div><div>ˇ</div></div>"
17868    );
17869
17870    check!(
17871        test_basic_ignore_already_closed,
17872        "<div><divˇ</div></div>" + ">" => "<div><div>ˇ</div></div>"
17873    );
17874
17875    check!(
17876        test_doesnt_autoclose_closing_tag,
17877        "</divˇ" + ">" => "</div>ˇ"
17878    );
17879
17880    check!(
17881        test_jsx_attr,
17882        "<div attr={</div>}ˇ" + ">" => "<div attr={</div>}>ˇ</div>"
17883    );
17884
17885    check!(
17886        test_ignores_closing_tags_in_expr_block,
17887        "<div><divˇ{</div>}</div>" + ">" => "<div><div>ˇ</div>{</div>}</div>"
17888    );
17889
17890    check!(
17891        test_doesnt_autoclose_on_gt_in_expr,
17892        "<div attr={1 ˇ" + ">" => "<div attr={1 >ˇ"
17893    );
17894
17895    check!(
17896        test_ignores_closing_tags_with_different_tag_names,
17897        "<div><divˇ</div></span>" + ">" => "<div><div>ˇ</div></div></span>"
17898    );
17899
17900    check!(
17901        test_autocloses_in_jsx_expression,
17902        "<div>{<divˇ}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17903    );
17904
17905    check!(
17906        test_doesnt_autoclose_already_closed_in_jsx_expression,
17907        "<div>{<divˇ</div>}</div>" + ">" => "<div>{<div>ˇ</div>}</div>"
17908    );
17909
17910    check!(
17911        test_autocloses_fragment,
17912        "" + ">" => "<>ˇ</>"
17913    );
17914
17915    check!(
17916        test_does_not_include_type_argument_in_autoclose_tag_name,
17917        "<Component<T> attr={boolean_value}ˇ" + ">" => "<Component<T> attr={boolean_value}>ˇ</Component>"
17918    );
17919
17920    check!(
17921        test_does_not_autoclose_doctype,
17922        "<!DOCTYPE htmlˇ" + ">" => "<!DOCTYPE html>ˇ"
17923    );
17924
17925    check!(
17926        test_does_not_autoclose_comment,
17927        "<!-- comment --ˇ" + ">" => "<!-- comment -->ˇ"
17928    );
17929
17930    check!(
17931        test_multi_cursor_autoclose_same_tag,
17932        r#"
17933        <divˇ
17934        <divˇ
17935        "#
17936        + ">" =>
17937        r#"
17938        <div>ˇ</div>
17939        <div>ˇ</div>
17940        "#
17941    );
17942
17943    check!(
17944        test_multi_cursor_autoclose_different_tags,
17945        r#"
17946        <divˇ
17947        <spanˇ
17948        "#
17949        + ">" =>
17950        r#"
17951        <div>ˇ</div>
17952        <span>ˇ</span>
17953        "#
17954    );
17955
17956    check!(
17957        test_multi_cursor_autoclose_some_dont_autoclose_others,
17958        r#"
17959        <divˇ
17960        <div /ˇ
17961        <spanˇ</span>
17962        <!DOCTYPE htmlˇ
17963        </headˇ
17964        <Component<T>ˇ
17965        ˇ
17966        "#
17967        + ">" =>
17968        r#"
17969        <div>ˇ</div>
17970        <div />ˇ
17971        <span>ˇ</span>
17972        <!DOCTYPE html>ˇ
17973        </head>ˇ
17974        <Component<T>>ˇ</Component>
1797517976        "#
17977    );
17978
17979    check!(
17980        test_doesnt_mess_up_trailing_text,
17981        "<divˇfoobar" + ">" => "<div>ˇ</div>foobar"
17982    );
17983
17984    #[gpui::test]
17985    async fn test_multibuffer(cx: &mut TestAppContext) {
17986        init_test(cx, |settings| {
17987            settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true });
17988        });
17989
17990        let buffer_a = cx.new(|cx| {
17991            let mut buf = language::Buffer::local("<div", cx);
17992            buf.set_language(
17993                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
17994                cx,
17995            );
17996            buf
17997        });
17998        let buffer_b = cx.new(|cx| {
17999            let mut buf = language::Buffer::local("<pre", cx);
18000            buf.set_language(
18001                Some(language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into())),
18002                cx,
18003            );
18004            buf
18005        });
18006        let buffer_c = cx.new(|cx| {
18007            let buf = language::Buffer::local("<span", cx);
18008            buf
18009        });
18010        let buffer = cx.new(|cx| {
18011            let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
18012            buf.push_excerpts(
18013                buffer_a,
18014                [ExcerptRange {
18015                    context: text::Anchor::MIN..text::Anchor::MAX,
18016                    primary: None,
18017                }],
18018                cx,
18019            );
18020            buf.push_excerpts(
18021                buffer_b,
18022                [ExcerptRange {
18023                    context: text::Anchor::MIN..text::Anchor::MAX,
18024                    primary: None,
18025                }],
18026                cx,
18027            );
18028            buf.push_excerpts(
18029                buffer_c,
18030                [ExcerptRange {
18031                    context: text::Anchor::MIN..text::Anchor::MAX,
18032                    primary: None,
18033                }],
18034                cx,
18035            );
18036            buf
18037        });
18038        let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
18039
18040        let mut cx = EditorTestContext::for_editor(editor, cx).await;
18041
18042        cx.update_editor(|editor, window, cx| {
18043            editor.change_selections(None, window, cx, |selections| {
18044                selections.select(vec![
18045                    Selection::from_offset(4),
18046                    Selection::from_offset(9),
18047                    Selection::from_offset(15),
18048                ])
18049            })
18050        });
18051        cx.run_until_parked();
18052
18053        cx.update_editor(|editor, window, cx| {
18054            editor.handle_input(">", window, cx);
18055        });
18056        cx.run_until_parked();
18057
18058        cx.assert_editor_state("<div>ˇ</div>\n<pre>ˇ</pre>\n<span>ˇ");
18059    }
18060}
18061
18062fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18063    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18064    point..point
18065}
18066
18067fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18068    let (text, ranges) = marked_text_ranges(marked_text, true);
18069    assert_eq!(editor.text(cx), text);
18070    assert_eq!(
18071        editor.selections.ranges(cx),
18072        ranges,
18073        "Assert selections are {}",
18074        marked_text
18075    );
18076}
18077
18078pub fn handle_signature_help_request(
18079    cx: &mut EditorLspTestContext,
18080    mocked_response: lsp::SignatureHelp,
18081) -> impl Future<Output = ()> {
18082    let mut request =
18083        cx.handle_request::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18084            let mocked_response = mocked_response.clone();
18085            async move { Ok(Some(mocked_response)) }
18086        });
18087
18088    async move {
18089        request.next().await;
18090    }
18091}
18092
18093/// Handle completion request passing a marked string specifying where the completion
18094/// should be triggered from using '|' character, what range should be replaced, and what completions
18095/// should be returned using '<' and '>' to delimit the range
18096pub fn handle_completion_request(
18097    cx: &mut EditorLspTestContext,
18098    marked_string: &str,
18099    completions: Vec<&'static str>,
18100    counter: Arc<AtomicUsize>,
18101) -> impl Future<Output = ()> {
18102    let complete_from_marker: TextRangeMarker = '|'.into();
18103    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18104    let (_, mut marked_ranges) = marked_text_ranges_by(
18105        marked_string,
18106        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18107    );
18108
18109    let complete_from_position =
18110        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18111    let replace_range =
18112        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18113
18114    let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |url, params, _| {
18115        let completions = completions.clone();
18116        counter.fetch_add(1, atomic::Ordering::Release);
18117        async move {
18118            assert_eq!(params.text_document_position.text_document.uri, url.clone());
18119            assert_eq!(
18120                params.text_document_position.position,
18121                complete_from_position
18122            );
18123            Ok(Some(lsp::CompletionResponse::Array(
18124                completions
18125                    .iter()
18126                    .map(|completion_text| lsp::CompletionItem {
18127                        label: completion_text.to_string(),
18128                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18129                            range: replace_range,
18130                            new_text: completion_text.to_string(),
18131                        })),
18132                        ..Default::default()
18133                    })
18134                    .collect(),
18135            )))
18136        }
18137    });
18138
18139    async move {
18140        request.next().await;
18141    }
18142}
18143
18144fn handle_resolve_completion_request(
18145    cx: &mut EditorLspTestContext,
18146    edits: Option<Vec<(&'static str, &'static str)>>,
18147) -> impl Future<Output = ()> {
18148    let edits = edits.map(|edits| {
18149        edits
18150            .iter()
18151            .map(|(marked_string, new_text)| {
18152                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18153                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18154                lsp::TextEdit::new(replace_range, new_text.to_string())
18155            })
18156            .collect::<Vec<_>>()
18157    });
18158
18159    let mut request =
18160        cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18161            let edits = edits.clone();
18162            async move {
18163                Ok(lsp::CompletionItem {
18164                    additional_text_edits: edits,
18165                    ..Default::default()
18166                })
18167            }
18168        });
18169
18170    async move {
18171        request.next().await;
18172    }
18173}
18174
18175pub(crate) fn update_test_language_settings(
18176    cx: &mut TestAppContext,
18177    f: impl Fn(&mut AllLanguageSettingsContent),
18178) {
18179    cx.update(|cx| {
18180        SettingsStore::update_global(cx, |store, cx| {
18181            store.update_user_settings::<AllLanguageSettings>(cx, f);
18182        });
18183    });
18184}
18185
18186pub(crate) fn update_test_project_settings(
18187    cx: &mut TestAppContext,
18188    f: impl Fn(&mut ProjectSettings),
18189) {
18190    cx.update(|cx| {
18191        SettingsStore::update_global(cx, |store, cx| {
18192            store.update_user_settings::<ProjectSettings>(cx, f);
18193        });
18194    });
18195}
18196
18197pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18198    cx.update(|cx| {
18199        assets::Assets.load_test_fonts(cx);
18200        let store = SettingsStore::test(cx);
18201        cx.set_global(store);
18202        theme::init(theme::LoadThemes::JustBase, cx);
18203        release_channel::init(SemanticVersion::default(), cx);
18204        client::init_settings(cx);
18205        language::init(cx);
18206        Project::init_settings(cx);
18207        workspace::init_settings(cx);
18208        crate::init(cx);
18209    });
18210
18211    update_test_language_settings(cx, f);
18212}
18213
18214#[track_caller]
18215fn assert_hunk_revert(
18216    not_reverted_text_with_selections: &str,
18217    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18218    expected_reverted_text_with_selections: &str,
18219    base_text: &str,
18220    cx: &mut EditorLspTestContext,
18221) {
18222    cx.set_state(not_reverted_text_with_selections);
18223    cx.set_head_text(base_text);
18224    cx.executor().run_until_parked();
18225
18226    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18227        let snapshot = editor.snapshot(window, cx);
18228        let reverted_hunk_statuses = snapshot
18229            .buffer_snapshot
18230            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18231            .map(|hunk| hunk.status().kind)
18232            .collect::<Vec<_>>();
18233
18234        editor.git_restore(&Default::default(), window, cx);
18235        reverted_hunk_statuses
18236    });
18237    cx.executor().run_until_parked();
18238    cx.assert_editor_state(expected_reverted_text_with_selections);
18239    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18240}