editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   16    VisualTestContext, WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319#[gpui::test]
 1320fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1321    init_test(cx, |_| {});
 1322
 1323    let editor = cx.add_window(|window, cx| {
 1324        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1325        build_editor(buffer.clone(), window, cx)
 1326    });
 1327
 1328    assert_eq!('🟥'.len_utf8(), 4);
 1329    assert_eq!('α'.len_utf8(), 2);
 1330
 1331    _ = editor.update(cx, |editor, window, cx| {
 1332        editor.fold_creases(
 1333            vec![
 1334                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1337            ],
 1338            true,
 1339            window,
 1340            cx,
 1341        );
 1342        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1343
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧".len())]
 1353        );
 1354        editor.move_right(&MoveRight, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(0, "🟥🟧⋯".len())]
 1358        );
 1359
 1360        editor.move_down(&MoveDown, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯e".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "ab".len())]
 1374        );
 1375        editor.move_left(&MoveLeft, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(1, "a".len())]
 1379        );
 1380
 1381        editor.move_down(&MoveDown, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "α".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯".len())]
 1395        );
 1396        editor.move_right(&MoveRight, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(2, "αβ⋯ε".len())]
 1400        );
 1401
 1402        editor.move_up(&MoveUp, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(1, "ab⋯e".len())]
 1406        );
 1407        editor.move_down(&MoveDown, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβ⋯ε".len())]
 1411        );
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(1, "ab⋯e".len())]
 1416        );
 1417
 1418        editor.move_up(&MoveUp, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥🟧".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "🟥".len())]
 1427        );
 1428        editor.move_left(&MoveLeft, window, cx);
 1429        assert_eq!(
 1430            editor.selections.display_ranges(cx),
 1431            &[empty_range(0, "".len())]
 1432        );
 1433    });
 1434}
 1435
 1436#[gpui::test]
 1437fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1438    init_test(cx, |_| {});
 1439
 1440    let editor = cx.add_window(|window, cx| {
 1441        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1442        build_editor(buffer.clone(), window, cx)
 1443    });
 1444    _ = editor.update(cx, |editor, window, cx| {
 1445        editor.change_selections(None, window, cx, |s| {
 1446            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1447        });
 1448
 1449        // moving above start of document should move selection to start of document,
 1450        // but the next move down should still be at the original goal_x
 1451        editor.move_up(&MoveUp, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(0, "".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(1, "abcd".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(3, "abcd".len())]
 1473        );
 1474
 1475        editor.move_down(&MoveDown, window, cx);
 1476        assert_eq!(
 1477            editor.selections.display_ranges(cx),
 1478            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1479        );
 1480
 1481        // moving past end of document should not change goal_x
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_down(&MoveDown, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(5, "".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(3, "abcd".len())]
 1504        );
 1505
 1506        editor.move_up(&MoveUp, window, cx);
 1507        assert_eq!(
 1508            editor.selections.display_ranges(cx),
 1509            &[empty_range(2, "αβγ".len())]
 1510        );
 1511    });
 1512}
 1513
 1514#[gpui::test]
 1515fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1516    init_test(cx, |_| {});
 1517    let move_to_beg = MoveToBeginningOfLine {
 1518        stop_at_soft_wraps: true,
 1519        stop_at_indent: true,
 1520    };
 1521
 1522    let delete_to_beg = DeleteToBeginningOfLine {
 1523        stop_at_indent: false,
 1524    };
 1525
 1526    let move_to_end = MoveToEndOfLine {
 1527        stop_at_soft_wraps: true,
 1528    };
 1529
 1530    let editor = cx.add_window(|window, cx| {
 1531        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1532        build_editor(buffer, window, cx)
 1533    });
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.change_selections(None, window, cx, |s| {
 1536            s.select_display_ranges([
 1537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1538                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1539            ]);
 1540        });
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1571                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1572            ]
 1573        );
 1574    });
 1575
 1576    _ = editor.update(cx, |editor, window, cx| {
 1577        editor.move_to_end_of_line(&move_to_end, window, cx);
 1578        assert_eq!(
 1579            editor.selections.display_ranges(cx),
 1580            &[
 1581                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1582                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1583            ]
 1584        );
 1585    });
 1586
 1587    // Moving to the end of line again is a no-op.
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_to_end_of_line(&move_to_end, window, cx);
 1590        assert_eq!(
 1591            editor.selections.display_ranges(cx),
 1592            &[
 1593                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1594                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1595            ]
 1596        );
 1597    });
 1598
 1599    _ = editor.update(cx, |editor, window, cx| {
 1600        editor.move_left(&MoveLeft, window, cx);
 1601        editor.select_to_beginning_of_line(
 1602            &SelectToBeginningOfLine {
 1603                stop_at_soft_wraps: true,
 1604                stop_at_indent: true,
 1605            },
 1606            window,
 1607            cx,
 1608        );
 1609        assert_eq!(
 1610            editor.selections.display_ranges(cx),
 1611            &[
 1612                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1613                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1614            ]
 1615        );
 1616    });
 1617
 1618    _ = editor.update(cx, |editor, window, cx| {
 1619        editor.select_to_beginning_of_line(
 1620            &SelectToBeginningOfLine {
 1621                stop_at_soft_wraps: true,
 1622                stop_at_indent: true,
 1623            },
 1624            window,
 1625            cx,
 1626        );
 1627        assert_eq!(
 1628            editor.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635
 1636    _ = editor.update(cx, |editor, window, cx| {
 1637        editor.select_to_beginning_of_line(
 1638            &SelectToBeginningOfLine {
 1639                stop_at_soft_wraps: true,
 1640                stop_at_indent: true,
 1641            },
 1642            window,
 1643            cx,
 1644        );
 1645        assert_eq!(
 1646            editor.selections.display_ranges(cx),
 1647            &[
 1648                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1649                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1650            ]
 1651        );
 1652    });
 1653
 1654    _ = editor.update(cx, |editor, window, cx| {
 1655        editor.select_to_end_of_line(
 1656            &SelectToEndOfLine {
 1657                stop_at_soft_wraps: true,
 1658            },
 1659            window,
 1660            cx,
 1661        );
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "ab\n  de");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1678                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1679            ]
 1680        );
 1681    });
 1682
 1683    _ = editor.update(cx, |editor, window, cx| {
 1684        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1685        assert_eq!(editor.display_text(cx), "\n");
 1686        assert_eq!(
 1687            editor.selections.display_ranges(cx),
 1688            &[
 1689                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1691            ]
 1692        );
 1693    });
 1694}
 1695
 1696#[gpui::test]
 1697fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1698    init_test(cx, |_| {});
 1699    let move_to_beg = MoveToBeginningOfLine {
 1700        stop_at_soft_wraps: false,
 1701        stop_at_indent: false,
 1702    };
 1703
 1704    let move_to_end = MoveToEndOfLine {
 1705        stop_at_soft_wraps: false,
 1706    };
 1707
 1708    let editor = cx.add_window(|window, cx| {
 1709        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1710        build_editor(buffer, window, cx)
 1711    });
 1712
 1713    _ = editor.update(cx, |editor, window, cx| {
 1714        editor.set_wrap_width(Some(140.0.into()), cx);
 1715
 1716        // We expect the following lines after wrapping
 1717        // ```
 1718        // thequickbrownfox
 1719        // jumpedoverthelazydo
 1720        // gs
 1721        // ```
 1722        // The final `gs` was soft-wrapped onto a new line.
 1723        assert_eq!(
 1724            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1725            editor.display_text(cx),
 1726        );
 1727
 1728        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1729        // Start the cursor at the `k` on the first line
 1730        editor.change_selections(None, window, cx, |s| {
 1731            s.select_display_ranges([
 1732                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1733            ]);
 1734        });
 1735
 1736        // Moving to the beginning of the line should put us at the beginning of the line.
 1737        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1738        assert_eq!(
 1739            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1740            editor.selections.display_ranges(cx)
 1741        );
 1742
 1743        // Moving to the end of the line should put us at the end of the line.
 1744        editor.move_to_end_of_line(&move_to_end, window, cx);
 1745        assert_eq!(
 1746            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1747            editor.selections.display_ranges(cx)
 1748        );
 1749
 1750        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1751        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1752        editor.change_selections(None, window, cx, |s| {
 1753            s.select_display_ranges([
 1754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1755            ]);
 1756        });
 1757
 1758        // Moving to the beginning of the line should put us at the start of the second line of
 1759        // display text, i.e., the `j`.
 1760        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the beginning of the line again should be a no-op.
 1767        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772
 1773        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1774        // next display line.
 1775        editor.move_to_end_of_line(&move_to_end, window, cx);
 1776        assert_eq!(
 1777            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1778            editor.selections.display_ranges(cx)
 1779        );
 1780
 1781        // Moving to the end of the line again should be a no-op.
 1782        editor.move_to_end_of_line(&move_to_end, window, cx);
 1783        assert_eq!(
 1784            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1785            editor.selections.display_ranges(cx)
 1786        );
 1787    });
 1788}
 1789
 1790#[gpui::test]
 1791fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1792    init_test(cx, |_| {});
 1793
 1794    let move_to_beg = MoveToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let select_to_beg = SelectToBeginningOfLine {
 1800        stop_at_soft_wraps: true,
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let delete_to_beg = DeleteToBeginningOfLine {
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let move_to_end = MoveToEndOfLine {
 1809        stop_at_soft_wraps: false,
 1810    };
 1811
 1812    let editor = cx.add_window(|window, cx| {
 1813        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1814        build_editor(buffer, window, cx)
 1815    });
 1816
 1817    _ = editor.update(cx, |editor, window, cx| {
 1818        editor.change_selections(None, window, cx, |s| {
 1819            s.select_display_ranges([
 1820                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1821                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1822            ]);
 1823        });
 1824
 1825        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1826        // and the second cursor at the first non-whitespace character in the line.
 1827        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1828        assert_eq!(
 1829            editor.selections.display_ranges(cx),
 1830            &[
 1831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1833            ]
 1834        );
 1835
 1836        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1837        // and should move the second cursor to the beginning of the line.
 1838        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1839        assert_eq!(
 1840            editor.selections.display_ranges(cx),
 1841            &[
 1842                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1843                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1844            ]
 1845        );
 1846
 1847        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1848        // and should move the second cursor back to the first non-whitespace character in the line.
 1849        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1850        assert_eq!(
 1851            editor.selections.display_ranges(cx),
 1852            &[
 1853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1855            ]
 1856        );
 1857
 1858        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1859        // and to the first non-whitespace character in the line for the second cursor.
 1860        editor.move_to_end_of_line(&move_to_end, window, cx);
 1861        editor.move_left(&MoveLeft, window, cx);
 1862        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[
 1866                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1867                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1868            ]
 1869        );
 1870
 1871        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1872        // and should select to the beginning of the line for the second cursor.
 1873        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[
 1877                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1878                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1879            ]
 1880        );
 1881
 1882        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1883        // and should delete to the first non-whitespace character in the line for the second cursor.
 1884        editor.move_to_end_of_line(&move_to_end, window, cx);
 1885        editor.move_left(&MoveLeft, window, cx);
 1886        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1887        assert_eq!(editor.text(cx), "c\n  f");
 1888    });
 1889}
 1890
 1891#[gpui::test]
 1892fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1893    init_test(cx, |_| {});
 1894
 1895    let editor = cx.add_window(|window, cx| {
 1896        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1897        build_editor(buffer, window, cx)
 1898    });
 1899    _ = editor.update(cx, |editor, window, cx| {
 1900        editor.change_selections(None, window, cx, |s| {
 1901            s.select_display_ranges([
 1902                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1903                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1904            ])
 1905        });
 1906
 1907        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1908        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1923        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1930
 1931        editor.move_right(&MoveRight, window, cx);
 1932        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1933        assert_selection_ranges(
 1934            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1935            editor,
 1936            cx,
 1937        );
 1938
 1939        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1940        assert_selection_ranges(
 1941            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1942            editor,
 1943            cx,
 1944        );
 1945
 1946        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1947        assert_selection_ranges(
 1948            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1949            editor,
 1950            cx,
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1957    init_test(cx, |_| {});
 1958
 1959    let editor = cx.add_window(|window, cx| {
 1960        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1961        build_editor(buffer, window, cx)
 1962    });
 1963
 1964    _ = editor.update(cx, |editor, window, cx| {
 1965        editor.set_wrap_width(Some(140.0.into()), cx);
 1966        assert_eq!(
 1967            editor.display_text(cx),
 1968            "use one::{\n    two::three::\n    four::five\n};"
 1969        );
 1970
 1971        editor.change_selections(None, window, cx, |s| {
 1972            s.select_display_ranges([
 1973                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1974            ]);
 1975        });
 1976
 1977        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1978        assert_eq!(
 1979            editor.selections.display_ranges(cx),
 1980            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1981        );
 1982
 1983        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1984        assert_eq!(
 1985            editor.selections.display_ranges(cx),
 1986            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1987        );
 1988
 1989        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1990        assert_eq!(
 1991            editor.selections.display_ranges(cx),
 1992            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1993        );
 1994
 1995        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1996        assert_eq!(
 1997            editor.selections.display_ranges(cx),
 1998            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1999        );
 2000
 2001        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2002        assert_eq!(
 2003            editor.selections.display_ranges(cx),
 2004            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2005        );
 2006
 2007        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2008        assert_eq!(
 2009            editor.selections.display_ranges(cx),
 2010            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2011        );
 2012    });
 2013}
 2014
 2015#[gpui::test]
 2016async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2017    init_test(cx, |_| {});
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2028
 2029    cx.set_state(
 2030        &r#"ˇone
 2031        two
 2032
 2033        three
 2034        fourˇ
 2035        five
 2036
 2037        six"#
 2038            .unindent(),
 2039    );
 2040
 2041    cx.update_editor(|editor, window, cx| {
 2042        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2043    });
 2044    cx.assert_editor_state(
 2045        &r#"one
 2046        two
 2047        ˇ
 2048        three
 2049        four
 2050        five
 2051        ˇ
 2052        six"#
 2053            .unindent(),
 2054    );
 2055
 2056    cx.update_editor(|editor, window, cx| {
 2057        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"one
 2061        two
 2062
 2063        three
 2064        four
 2065        five
 2066        ˇ
 2067        sixˇ"#
 2068            .unindent(),
 2069    );
 2070
 2071    cx.update_editor(|editor, window, cx| {
 2072        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2073    });
 2074    cx.assert_editor_state(
 2075        &r#"one
 2076        two
 2077
 2078        three
 2079        four
 2080        five
 2081
 2082        sixˇ"#
 2083            .unindent(),
 2084    );
 2085
 2086    cx.update_editor(|editor, window, cx| {
 2087        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2088    });
 2089    cx.assert_editor_state(
 2090        &r#"one
 2091        two
 2092
 2093        three
 2094        four
 2095        five
 2096        ˇ
 2097        six"#
 2098            .unindent(),
 2099    );
 2100
 2101    cx.update_editor(|editor, window, cx| {
 2102        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2103    });
 2104    cx.assert_editor_state(
 2105        &r#"one
 2106        two
 2107        ˇ
 2108        three
 2109        four
 2110        five
 2111
 2112        six"#
 2113            .unindent(),
 2114    );
 2115
 2116    cx.update_editor(|editor, window, cx| {
 2117        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2118    });
 2119    cx.assert_editor_state(
 2120        &r#"ˇone
 2121        two
 2122
 2123        three
 2124        four
 2125        five
 2126
 2127        six"#
 2128            .unindent(),
 2129    );
 2130}
 2131
 2132#[gpui::test]
 2133async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135    let mut cx = EditorTestContext::new(cx).await;
 2136    let line_height = cx.editor(|editor, window, _| {
 2137        editor
 2138            .style()
 2139            .unwrap()
 2140            .text
 2141            .line_height_in_pixels(window.rem_size())
 2142    });
 2143    let window = cx.window;
 2144    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2145
 2146    cx.set_state(
 2147        r#"ˇone
 2148        two
 2149        three
 2150        four
 2151        five
 2152        six
 2153        seven
 2154        eight
 2155        nine
 2156        ten
 2157        "#,
 2158    );
 2159
 2160    cx.update_editor(|editor, window, cx| {
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 0.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 3.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 6.)
 2174        );
 2175        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2176        assert_eq!(
 2177            editor.snapshot(window, cx).scroll_position(),
 2178            gpui::Point::new(0., 3.)
 2179        );
 2180
 2181        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 1.)
 2185        );
 2186        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2187        assert_eq!(
 2188            editor.snapshot(window, cx).scroll_position(),
 2189            gpui::Point::new(0., 3.)
 2190        );
 2191    });
 2192}
 2193
 2194#[gpui::test]
 2195async fn test_autoscroll(cx: &mut TestAppContext) {
 2196    init_test(cx, |_| {});
 2197    let mut cx = EditorTestContext::new(cx).await;
 2198
 2199    let line_height = cx.update_editor(|editor, window, cx| {
 2200        editor.set_vertical_scroll_margin(2, cx);
 2201        editor
 2202            .style()
 2203            .unwrap()
 2204            .text
 2205            .line_height_in_pixels(window.rem_size())
 2206    });
 2207    let window = cx.window;
 2208    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2209
 2210    cx.set_state(
 2211        r#"ˇone
 2212            two
 2213            three
 2214            four
 2215            five
 2216            six
 2217            seven
 2218            eight
 2219            nine
 2220            ten
 2221        "#,
 2222    );
 2223    cx.update_editor(|editor, window, cx| {
 2224        assert_eq!(
 2225            editor.snapshot(window, cx).scroll_position(),
 2226            gpui::Point::new(0., 0.0)
 2227        );
 2228    });
 2229
 2230    // Add a cursor below the visible area. Since both cursors cannot fit
 2231    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2232    // allows the vertical scroll margin below that cursor.
 2233    cx.update_editor(|editor, window, cx| {
 2234        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2235            selections.select_ranges([
 2236                Point::new(0, 0)..Point::new(0, 0),
 2237                Point::new(6, 0)..Point::new(6, 0),
 2238            ]);
 2239        })
 2240    });
 2241    cx.update_editor(|editor, window, cx| {
 2242        assert_eq!(
 2243            editor.snapshot(window, cx).scroll_position(),
 2244            gpui::Point::new(0., 3.0)
 2245        );
 2246    });
 2247
 2248    // Move down. The editor cursor scrolls down to track the newest cursor.
 2249    cx.update_editor(|editor, window, cx| {
 2250        editor.move_down(&Default::default(), window, cx);
 2251    });
 2252    cx.update_editor(|editor, window, cx| {
 2253        assert_eq!(
 2254            editor.snapshot(window, cx).scroll_position(),
 2255            gpui::Point::new(0., 4.0)
 2256        );
 2257    });
 2258
 2259    // Add a cursor above the visible area. Since both cursors fit on screen,
 2260    // the editor scrolls to show both.
 2261    cx.update_editor(|editor, window, cx| {
 2262        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2263            selections.select_ranges([
 2264                Point::new(1, 0)..Point::new(1, 0),
 2265                Point::new(6, 0)..Point::new(6, 0),
 2266            ]);
 2267        })
 2268    });
 2269    cx.update_editor(|editor, window, cx| {
 2270        assert_eq!(
 2271            editor.snapshot(window, cx).scroll_position(),
 2272            gpui::Point::new(0., 1.0)
 2273        );
 2274    });
 2275}
 2276
 2277#[gpui::test]
 2278async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2279    init_test(cx, |_| {});
 2280    let mut cx = EditorTestContext::new(cx).await;
 2281
 2282    let line_height = cx.editor(|editor, window, _cx| {
 2283        editor
 2284            .style()
 2285            .unwrap()
 2286            .text
 2287            .line_height_in_pixels(window.rem_size())
 2288    });
 2289    let window = cx.window;
 2290    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2291    cx.set_state(
 2292        &r#"
 2293        ˇone
 2294        two
 2295        threeˇ
 2296        four
 2297        five
 2298        six
 2299        seven
 2300        eight
 2301        nine
 2302        ten
 2303        "#
 2304        .unindent(),
 2305    );
 2306
 2307    cx.update_editor(|editor, window, cx| {
 2308        editor.move_page_down(&MovePageDown::default(), window, cx)
 2309    });
 2310    cx.assert_editor_state(
 2311        &r#"
 2312        one
 2313        two
 2314        three
 2315        ˇfour
 2316        five
 2317        sixˇ
 2318        seven
 2319        eight
 2320        nine
 2321        ten
 2322        "#
 2323        .unindent(),
 2324    );
 2325
 2326    cx.update_editor(|editor, window, cx| {
 2327        editor.move_page_down(&MovePageDown::default(), window, cx)
 2328    });
 2329    cx.assert_editor_state(
 2330        &r#"
 2331        one
 2332        two
 2333        three
 2334        four
 2335        five
 2336        six
 2337        ˇseven
 2338        eight
 2339        nineˇ
 2340        ten
 2341        "#
 2342        .unindent(),
 2343    );
 2344
 2345    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2346    cx.assert_editor_state(
 2347        &r#"
 2348        one
 2349        two
 2350        three
 2351        ˇfour
 2352        five
 2353        sixˇ
 2354        seven
 2355        eight
 2356        nine
 2357        ten
 2358        "#
 2359        .unindent(),
 2360    );
 2361
 2362    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2363    cx.assert_editor_state(
 2364        &r#"
 2365        ˇone
 2366        two
 2367        threeˇ
 2368        four
 2369        five
 2370        six
 2371        seven
 2372        eight
 2373        nine
 2374        ten
 2375        "#
 2376        .unindent(),
 2377    );
 2378
 2379    // Test select collapsing
 2380    cx.update_editor(|editor, window, cx| {
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384    });
 2385    cx.assert_editor_state(
 2386        &r#"
 2387        one
 2388        two
 2389        three
 2390        four
 2391        five
 2392        six
 2393        seven
 2394        eight
 2395        nine
 2396        ˇten
 2397        ˇ"#
 2398        .unindent(),
 2399    );
 2400}
 2401
 2402#[gpui::test]
 2403async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2404    init_test(cx, |_| {});
 2405    let mut cx = EditorTestContext::new(cx).await;
 2406    cx.set_state("one «two threeˇ» four");
 2407    cx.update_editor(|editor, window, cx| {
 2408        editor.delete_to_beginning_of_line(
 2409            &DeleteToBeginningOfLine {
 2410                stop_at_indent: false,
 2411            },
 2412            window,
 2413            cx,
 2414        );
 2415        assert_eq!(editor.text(cx), " four");
 2416    });
 2417}
 2418
 2419#[gpui::test]
 2420fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2421    init_test(cx, |_| {});
 2422
 2423    let editor = cx.add_window(|window, cx| {
 2424        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2425        build_editor(buffer.clone(), window, cx)
 2426    });
 2427
 2428    _ = editor.update(cx, |editor, window, cx| {
 2429        editor.change_selections(None, window, cx, |s| {
 2430            s.select_display_ranges([
 2431                // an empty selection - the preceding word fragment is deleted
 2432                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2433                // characters selected - they are deleted
 2434                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2435            ])
 2436        });
 2437        editor.delete_to_previous_word_start(
 2438            &DeleteToPreviousWordStart {
 2439                ignore_newlines: false,
 2440            },
 2441            window,
 2442            cx,
 2443        );
 2444        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2445    });
 2446
 2447    _ = editor.update(cx, |editor, window, cx| {
 2448        editor.change_selections(None, window, cx, |s| {
 2449            s.select_display_ranges([
 2450                // an empty selection - the following word fragment is deleted
 2451                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2452                // characters selected - they are deleted
 2453                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2454            ])
 2455        });
 2456        editor.delete_to_next_word_end(
 2457            &DeleteToNextWordEnd {
 2458                ignore_newlines: false,
 2459            },
 2460            window,
 2461            cx,
 2462        );
 2463        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2464    });
 2465}
 2466
 2467#[gpui::test]
 2468fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2469    init_test(cx, |_| {});
 2470
 2471    let editor = cx.add_window(|window, cx| {
 2472        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2473        build_editor(buffer.clone(), window, cx)
 2474    });
 2475    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2476        ignore_newlines: false,
 2477    };
 2478    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2479        ignore_newlines: true,
 2480    };
 2481
 2482    _ = editor.update(cx, |editor, window, cx| {
 2483        editor.change_selections(None, window, cx, |s| {
 2484            s.select_display_ranges([
 2485                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2486            ])
 2487        });
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2500    });
 2501}
 2502
 2503#[gpui::test]
 2504fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2505    init_test(cx, |_| {});
 2506
 2507    let editor = cx.add_window(|window, cx| {
 2508        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2509        build_editor(buffer.clone(), window, cx)
 2510    });
 2511    let del_to_next_word_end = DeleteToNextWordEnd {
 2512        ignore_newlines: false,
 2513    };
 2514    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2515        ignore_newlines: true,
 2516    };
 2517
 2518    _ = editor.update(cx, |editor, window, cx| {
 2519        editor.change_selections(None, window, cx, |s| {
 2520            s.select_display_ranges([
 2521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2522            ])
 2523        });
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "one\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "\n   two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(
 2536            editor.buffer.read(cx).read(cx).text(),
 2537            "two\nthree\n   four"
 2538        );
 2539        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2541        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2545    });
 2546}
 2547
 2548#[gpui::test]
 2549fn test_newline(cx: &mut TestAppContext) {
 2550    init_test(cx, |_| {});
 2551
 2552    let editor = cx.add_window(|window, cx| {
 2553        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2554        build_editor(buffer.clone(), window, cx)
 2555    });
 2556
 2557    _ = editor.update(cx, |editor, window, cx| {
 2558        editor.change_selections(None, window, cx, |s| {
 2559            s.select_display_ranges([
 2560                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2562                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2563            ])
 2564        });
 2565
 2566        editor.newline(&Newline, window, cx);
 2567        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2568    });
 2569}
 2570
 2571#[gpui::test]
 2572fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|window, cx| {
 2576        let buffer = MultiBuffer::build_simple(
 2577            "
 2578                a
 2579                b(
 2580                    X
 2581                )
 2582                c(
 2583                    X
 2584                )
 2585            "
 2586            .unindent()
 2587            .as_str(),
 2588            cx,
 2589        );
 2590        let mut editor = build_editor(buffer.clone(), window, cx);
 2591        editor.change_selections(None, window, cx, |s| {
 2592            s.select_ranges([
 2593                Point::new(2, 4)..Point::new(2, 5),
 2594                Point::new(5, 4)..Point::new(5, 5),
 2595            ])
 2596        });
 2597        editor
 2598    });
 2599
 2600    _ = editor.update(cx, |editor, window, cx| {
 2601        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2602        editor.buffer.update(cx, |buffer, cx| {
 2603            buffer.edit(
 2604                [
 2605                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2606                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2607                ],
 2608                None,
 2609                cx,
 2610            );
 2611            assert_eq!(
 2612                buffer.read(cx).text(),
 2613                "
 2614                    a
 2615                    b()
 2616                    c()
 2617                "
 2618                .unindent()
 2619            );
 2620        });
 2621        assert_eq!(
 2622            editor.selections.ranges(cx),
 2623            &[
 2624                Point::new(1, 2)..Point::new(1, 2),
 2625                Point::new(2, 2)..Point::new(2, 2),
 2626            ],
 2627        );
 2628
 2629        editor.newline(&Newline, window, cx);
 2630        assert_eq!(
 2631            editor.text(cx),
 2632            "
 2633                a
 2634                b(
 2635                )
 2636                c(
 2637                )
 2638            "
 2639            .unindent()
 2640        );
 2641
 2642        // The selections are moved after the inserted newlines
 2643        assert_eq!(
 2644            editor.selections.ranges(cx),
 2645            &[
 2646                Point::new(2, 0)..Point::new(2, 0),
 2647                Point::new(4, 0)..Point::new(4, 0),
 2648            ],
 2649        );
 2650    });
 2651}
 2652
 2653#[gpui::test]
 2654async fn test_newline_above(cx: &mut TestAppContext) {
 2655    init_test(cx, |settings| {
 2656        settings.defaults.tab_size = NonZeroU32::new(4)
 2657    });
 2658
 2659    let language = Arc::new(
 2660        Language::new(
 2661            LanguageConfig::default(),
 2662            Some(tree_sitter_rust::LANGUAGE.into()),
 2663        )
 2664        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2665        .unwrap(),
 2666    );
 2667
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670    cx.set_state(indoc! {"
 2671        const a: ˇA = (
 2672 2673                «const_functionˇ»(ˇ),
 2674                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2675 2676        ˇ);ˇ
 2677    "});
 2678
 2679    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        ˇ
 2682        const a: A = (
 2683            ˇ
 2684            (
 2685                ˇ
 2686                ˇ
 2687                const_function(),
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                something_else,
 2693                ˇ
 2694            )
 2695            ˇ
 2696            ˇ
 2697        );
 2698    "});
 2699}
 2700
 2701#[gpui::test]
 2702async fn test_newline_below(cx: &mut TestAppContext) {
 2703    init_test(cx, |settings| {
 2704        settings.defaults.tab_size = NonZeroU32::new(4)
 2705    });
 2706
 2707    let language = Arc::new(
 2708        Language::new(
 2709            LanguageConfig::default(),
 2710            Some(tree_sitter_rust::LANGUAGE.into()),
 2711        )
 2712        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2713        .unwrap(),
 2714    );
 2715
 2716    let mut cx = EditorTestContext::new(cx).await;
 2717    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2718    cx.set_state(indoc! {"
 2719        const a: ˇA = (
 2720 2721                «const_functionˇ»(ˇ),
 2722                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2723 2724        ˇ);ˇ
 2725    "});
 2726
 2727    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2728    cx.assert_editor_state(indoc! {"
 2729        const a: A = (
 2730            ˇ
 2731            (
 2732                ˇ
 2733                const_function(),
 2734                ˇ
 2735                ˇ
 2736                something_else,
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741            )
 2742            ˇ
 2743        );
 2744        ˇ
 2745        ˇ
 2746    "});
 2747}
 2748
 2749#[gpui::test]
 2750async fn test_newline_comments(cx: &mut TestAppContext) {
 2751    init_test(cx, |settings| {
 2752        settings.defaults.tab_size = NonZeroU32::new(4)
 2753    });
 2754
 2755    let language = Arc::new(Language::new(
 2756        LanguageConfig {
 2757            line_comments: vec!["//".into()],
 2758            ..LanguageConfig::default()
 2759        },
 2760        None,
 2761    ));
 2762    {
 2763        let mut cx = EditorTestContext::new(cx).await;
 2764        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2765        cx.set_state(indoc! {"
 2766        // Fooˇ
 2767    "});
 2768
 2769        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2770        cx.assert_editor_state(indoc! {"
 2771        // Foo
 2772        //ˇ
 2773    "});
 2774        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2775        cx.set_state(indoc! {"
 2776        ˇ// Foo
 2777    "});
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(indoc! {"
 2780
 2781        ˇ// Foo
 2782    "});
 2783    }
 2784    // Ensure that comment continuations can be disabled.
 2785    update_test_language_settings(cx, |settings| {
 2786        settings.defaults.extend_comment_on_newline = Some(false);
 2787    });
 2788    let mut cx = EditorTestContext::new(cx).await;
 2789    cx.set_state(indoc! {"
 2790        // Fooˇ
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        ˇ
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2801    init_test(cx, |_| {});
 2802
 2803    let editor = cx.add_window(|window, cx| {
 2804        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2805        let mut editor = build_editor(buffer.clone(), window, cx);
 2806        editor.change_selections(None, window, cx, |s| {
 2807            s.select_ranges([3..4, 11..12, 19..20])
 2808        });
 2809        editor
 2810    });
 2811
 2812    _ = editor.update(cx, |editor, window, cx| {
 2813        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2814        editor.buffer.update(cx, |buffer, cx| {
 2815            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2816            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2817        });
 2818        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2819
 2820        editor.insert("Z", window, cx);
 2821        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2822
 2823        // The selections are moved after the inserted characters
 2824        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2825    });
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_tab(cx: &mut TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.tab_size = NonZeroU32::new(3)
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835    cx.set_state(indoc! {"
 2836        ˇabˇc
 2837        ˇ🏀ˇ🏀ˇefg
 2838 2839    "});
 2840    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2841    cx.assert_editor_state(indoc! {"
 2842           ˇab ˇc
 2843           ˇ🏀  ˇ🏀  ˇefg
 2844        d  ˇ
 2845    "});
 2846
 2847    cx.set_state(indoc! {"
 2848        a
 2849        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        a
 2854           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2855    "});
 2856}
 2857
 2858#[gpui::test]
 2859async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2860    init_test(cx, |_| {});
 2861
 2862    let mut cx = EditorTestContext::new(cx).await;
 2863    let language = Arc::new(
 2864        Language::new(
 2865            LanguageConfig::default(),
 2866            Some(tree_sitter_rust::LANGUAGE.into()),
 2867        )
 2868        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2869        .unwrap(),
 2870    );
 2871    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2872
 2873    // cursors that are already at the suggested indent level insert
 2874    // a soft tab. cursors that are to the left of the suggested indent
 2875    // auto-indent their line.
 2876    cx.set_state(indoc! {"
 2877        ˇ
 2878        const a: B = (
 2879            c(
 2880                d(
 2881        ˇ
 2882                )
 2883        ˇ
 2884        ˇ    )
 2885        );
 2886    "});
 2887    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889            ˇ
 2890        const a: B = (
 2891            c(
 2892                d(
 2893                    ˇ
 2894                )
 2895                ˇ
 2896            ˇ)
 2897        );
 2898    "});
 2899
 2900    // handle auto-indent when there are multiple cursors on the same line
 2901    cx.set_state(indoc! {"
 2902        const a: B = (
 2903            c(
 2904        ˇ    ˇ
 2905        ˇ    )
 2906        );
 2907    "});
 2908    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        const a: B = (
 2911            c(
 2912                ˇ
 2913            ˇ)
 2914        );
 2915    "});
 2916}
 2917
 2918#[gpui::test]
 2919async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2920    init_test(cx, |settings| {
 2921        settings.defaults.tab_size = NonZeroU32::new(3)
 2922    });
 2923
 2924    let mut cx = EditorTestContext::new(cx).await;
 2925    cx.set_state(indoc! {"
 2926         ˇ
 2927        \t ˇ
 2928        \t  ˇ
 2929        \t   ˇ
 2930         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2931    "});
 2932
 2933    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2934    cx.assert_editor_state(indoc! {"
 2935           ˇ
 2936        \t   ˇ
 2937        \t   ˇ
 2938        \t      ˇ
 2939         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2940    "});
 2941}
 2942
 2943#[gpui::test]
 2944async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2945    init_test(cx, |settings| {
 2946        settings.defaults.tab_size = NonZeroU32::new(4)
 2947    });
 2948
 2949    let language = Arc::new(
 2950        Language::new(
 2951            LanguageConfig::default(),
 2952            Some(tree_sitter_rust::LANGUAGE.into()),
 2953        )
 2954        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2955        .unwrap(),
 2956    );
 2957
 2958    let mut cx = EditorTestContext::new(cx).await;
 2959    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2960    cx.set_state(indoc! {"
 2961        fn a() {
 2962            if b {
 2963        \t ˇc
 2964            }
 2965        }
 2966    "});
 2967
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        fn a() {
 2971            if b {
 2972                ˇc
 2973            }
 2974        }
 2975    "});
 2976}
 2977
 2978#[gpui::test]
 2979async fn test_indent_outdent(cx: &mut TestAppContext) {
 2980    init_test(cx, |settings| {
 2981        settings.defaults.tab_size = NonZeroU32::new(4);
 2982    });
 2983
 2984    let mut cx = EditorTestContext::new(cx).await;
 2985
 2986    cx.set_state(indoc! {"
 2987          «oneˇ» «twoˇ»
 2988        three
 2989         four
 2990    "});
 2991    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2992    cx.assert_editor_state(indoc! {"
 2993            «oneˇ» «twoˇ»
 2994        three
 2995         four
 2996    "});
 2997
 2998    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        «oneˇ» «twoˇ»
 3001        three
 3002         four
 3003    "});
 3004
 3005    // select across line ending
 3006    cx.set_state(indoc! {"
 3007        one two
 3008        t«hree
 3009        ˇ» four
 3010    "});
 3011    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3012    cx.assert_editor_state(indoc! {"
 3013        one two
 3014            t«hree
 3015        ˇ» four
 3016    "});
 3017
 3018    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        one two
 3021        t«hree
 3022        ˇ» four
 3023    "});
 3024
 3025    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3026    cx.set_state(indoc! {"
 3027        one two
 3028        ˇthree
 3029            four
 3030    "});
 3031    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3032    cx.assert_editor_state(indoc! {"
 3033        one two
 3034            ˇthree
 3035            four
 3036    "});
 3037
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        ˇ    three
 3041            four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046        ˇthree
 3047            four
 3048    "});
 3049}
 3050
 3051#[gpui::test]
 3052async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3053    init_test(cx, |settings| {
 3054        settings.defaults.hard_tabs = Some(true);
 3055    });
 3056
 3057    let mut cx = EditorTestContext::new(cx).await;
 3058
 3059    // select two ranges on one line
 3060    cx.set_state(indoc! {"
 3061        «oneˇ» «twoˇ»
 3062        three
 3063        four
 3064    "});
 3065    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        \t«oneˇ» «twoˇ»
 3068        three
 3069        four
 3070    "});
 3071    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3072    cx.assert_editor_state(indoc! {"
 3073        \t\t«oneˇ» «twoˇ»
 3074        three
 3075        four
 3076    "});
 3077    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        \t«oneˇ» «twoˇ»
 3080        three
 3081        four
 3082    "});
 3083    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3084    cx.assert_editor_state(indoc! {"
 3085        «oneˇ» «twoˇ»
 3086        three
 3087        four
 3088    "});
 3089
 3090    // select across a line ending
 3091    cx.set_state(indoc! {"
 3092        one two
 3093        t«hree
 3094        ˇ»four
 3095    "});
 3096    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3097    cx.assert_editor_state(indoc! {"
 3098        one two
 3099        \tt«hree
 3100        ˇ»four
 3101    "});
 3102    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3103    cx.assert_editor_state(indoc! {"
 3104        one two
 3105        \t\tt«hree
 3106        ˇ»four
 3107    "});
 3108    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        one two
 3111        \tt«hree
 3112        ˇ»four
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        one two
 3117        t«hree
 3118        ˇ»four
 3119    "});
 3120
 3121    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3122    cx.set_state(indoc! {"
 3123        one two
 3124        ˇthree
 3125        four
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        one two
 3130        ˇthree
 3131        four
 3132    "});
 3133    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3134    cx.assert_editor_state(indoc! {"
 3135        one two
 3136        \tˇthree
 3137        four
 3138    "});
 3139    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3140    cx.assert_editor_state(indoc! {"
 3141        one two
 3142        ˇthree
 3143        four
 3144    "});
 3145}
 3146
 3147#[gpui::test]
 3148fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3149    init_test(cx, |settings| {
 3150        settings.languages.extend([
 3151            (
 3152                "TOML".into(),
 3153                LanguageSettingsContent {
 3154                    tab_size: NonZeroU32::new(2),
 3155                    ..Default::default()
 3156                },
 3157            ),
 3158            (
 3159                "Rust".into(),
 3160                LanguageSettingsContent {
 3161                    tab_size: NonZeroU32::new(4),
 3162                    ..Default::default()
 3163                },
 3164            ),
 3165        ]);
 3166    });
 3167
 3168    let toml_language = Arc::new(Language::new(
 3169        LanguageConfig {
 3170            name: "TOML".into(),
 3171            ..Default::default()
 3172        },
 3173        None,
 3174    ));
 3175    let rust_language = Arc::new(Language::new(
 3176        LanguageConfig {
 3177            name: "Rust".into(),
 3178            ..Default::default()
 3179        },
 3180        None,
 3181    ));
 3182
 3183    let toml_buffer =
 3184        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3185    let rust_buffer =
 3186        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3187    let multibuffer = cx.new(|cx| {
 3188        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3189        multibuffer.push_excerpts(
 3190            toml_buffer.clone(),
 3191            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3192            cx,
 3193        );
 3194        multibuffer.push_excerpts(
 3195            rust_buffer.clone(),
 3196            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3197            cx,
 3198        );
 3199        multibuffer
 3200    });
 3201
 3202    cx.add_window(|window, cx| {
 3203        let mut editor = build_editor(multibuffer, window, cx);
 3204
 3205        assert_eq!(
 3206            editor.text(cx),
 3207            indoc! {"
 3208                a = 1
 3209                b = 2
 3210
 3211                const c: usize = 3;
 3212            "}
 3213        );
 3214
 3215        select_ranges(
 3216            &mut editor,
 3217            indoc! {"
 3218                «aˇ» = 1
 3219                b = 2
 3220
 3221                «const c:ˇ» usize = 3;
 3222            "},
 3223            window,
 3224            cx,
 3225        );
 3226
 3227        editor.tab(&Tab, window, cx);
 3228        assert_text_with_selections(
 3229            &mut editor,
 3230            indoc! {"
 3231                  «aˇ» = 1
 3232                b = 2
 3233
 3234                    «const c:ˇ» usize = 3;
 3235            "},
 3236            cx,
 3237        );
 3238        editor.backtab(&Backtab, window, cx);
 3239        assert_text_with_selections(
 3240            &mut editor,
 3241            indoc! {"
 3242                «aˇ» = 1
 3243                b = 2
 3244
 3245                «const c:ˇ» usize = 3;
 3246            "},
 3247            cx,
 3248        );
 3249
 3250        editor
 3251    });
 3252}
 3253
 3254#[gpui::test]
 3255async fn test_backspace(cx: &mut TestAppContext) {
 3256    init_test(cx, |_| {});
 3257
 3258    let mut cx = EditorTestContext::new(cx).await;
 3259
 3260    // Basic backspace
 3261    cx.set_state(indoc! {"
 3262        onˇe two three
 3263        fou«rˇ» five six
 3264        seven «ˇeight nine
 3265        »ten
 3266    "});
 3267    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        oˇe two three
 3270        fouˇ five six
 3271        seven ˇten
 3272    "});
 3273
 3274    // Test backspace inside and around indents
 3275    cx.set_state(indoc! {"
 3276        zero
 3277            ˇone
 3278                ˇtwo
 3279            ˇ ˇ ˇ  three
 3280        ˇ  ˇ  four
 3281    "});
 3282    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3283    cx.assert_editor_state(indoc! {"
 3284        zero
 3285        ˇone
 3286            ˇtwo
 3287        ˇ  threeˇ  four
 3288    "});
 3289}
 3290
 3291#[gpui::test]
 3292async fn test_delete(cx: &mut TestAppContext) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296    cx.set_state(indoc! {"
 3297        onˇe two three
 3298        fou«rˇ» five six
 3299        seven «ˇeight nine
 3300        »ten
 3301    "});
 3302    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3303    cx.assert_editor_state(indoc! {"
 3304        onˇ two three
 3305        fouˇ five six
 3306        seven ˇten
 3307    "});
 3308}
 3309
 3310#[gpui::test]
 3311fn test_delete_line(cx: &mut TestAppContext) {
 3312    init_test(cx, |_| {});
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3322                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3323                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3324            ])
 3325        });
 3326        editor.delete_line(&DeleteLine, window, cx);
 3327        assert_eq!(editor.display_text(cx), "ghi");
 3328        assert_eq!(
 3329            editor.selections.display_ranges(cx),
 3330            vec![
 3331                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3332                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3333            ]
 3334        );
 3335    });
 3336
 3337    let editor = cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3339        build_editor(buffer, window, cx)
 3340    });
 3341    _ = editor.update(cx, |editor, window, cx| {
 3342        editor.change_selections(None, window, cx, |s| {
 3343            s.select_display_ranges([
 3344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3345            ])
 3346        });
 3347        editor.delete_line(&DeleteLine, window, cx);
 3348        assert_eq!(editor.display_text(cx), "ghi\n");
 3349        assert_eq!(
 3350            editor.selections.display_ranges(cx),
 3351            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3352        );
 3353    });
 3354}
 3355
 3356#[gpui::test]
 3357fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3358    init_test(cx, |_| {});
 3359
 3360    cx.add_window(|window, cx| {
 3361        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3362        let mut editor = build_editor(buffer.clone(), window, cx);
 3363        let buffer = buffer.read(cx).as_singleton().unwrap();
 3364
 3365        assert_eq!(
 3366            editor.selections.ranges::<Point>(cx),
 3367            &[Point::new(0, 0)..Point::new(0, 0)]
 3368        );
 3369
 3370        // When on single line, replace newline at end by space
 3371        editor.join_lines(&JoinLines, window, cx);
 3372        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3373        assert_eq!(
 3374            editor.selections.ranges::<Point>(cx),
 3375            &[Point::new(0, 3)..Point::new(0, 3)]
 3376        );
 3377
 3378        // When multiple lines are selected, remove newlines that are spanned by the selection
 3379        editor.change_selections(None, window, cx, |s| {
 3380            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3381        });
 3382        editor.join_lines(&JoinLines, window, cx);
 3383        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3384        assert_eq!(
 3385            editor.selections.ranges::<Point>(cx),
 3386            &[Point::new(0, 11)..Point::new(0, 11)]
 3387        );
 3388
 3389        // Undo should be transactional
 3390        editor.undo(&Undo, window, cx);
 3391        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3392        assert_eq!(
 3393            editor.selections.ranges::<Point>(cx),
 3394            &[Point::new(0, 5)..Point::new(2, 2)]
 3395        );
 3396
 3397        // When joining an empty line don't insert a space
 3398        editor.change_selections(None, window, cx, |s| {
 3399            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3400        });
 3401        editor.join_lines(&JoinLines, window, cx);
 3402        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3403        assert_eq!(
 3404            editor.selections.ranges::<Point>(cx),
 3405            [Point::new(2, 3)..Point::new(2, 3)]
 3406        );
 3407
 3408        // We can remove trailing newlines
 3409        editor.join_lines(&JoinLines, window, cx);
 3410        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3411        assert_eq!(
 3412            editor.selections.ranges::<Point>(cx),
 3413            [Point::new(2, 3)..Point::new(2, 3)]
 3414        );
 3415
 3416        // We don't blow up on the last line
 3417        editor.join_lines(&JoinLines, window, cx);
 3418        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3419        assert_eq!(
 3420            editor.selections.ranges::<Point>(cx),
 3421            [Point::new(2, 3)..Point::new(2, 3)]
 3422        );
 3423
 3424        // reset to test indentation
 3425        editor.buffer.update(cx, |buffer, cx| {
 3426            buffer.edit(
 3427                [
 3428                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3429                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3430                ],
 3431                None,
 3432                cx,
 3433            )
 3434        });
 3435
 3436        // We remove any leading spaces
 3437        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3438        editor.change_selections(None, window, cx, |s| {
 3439            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3440        });
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3443
 3444        // We don't insert a space for a line containing only spaces
 3445        editor.join_lines(&JoinLines, window, cx);
 3446        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3447
 3448        // We ignore any leading tabs
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3451
 3452        editor
 3453    });
 3454}
 3455
 3456#[gpui::test]
 3457fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    cx.add_window(|window, cx| {
 3461        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3462        let mut editor = build_editor(buffer.clone(), window, cx);
 3463        let buffer = buffer.read(cx).as_singleton().unwrap();
 3464
 3465        editor.change_selections(None, window, cx, |s| {
 3466            s.select_ranges([
 3467                Point::new(0, 2)..Point::new(1, 1),
 3468                Point::new(1, 2)..Point::new(1, 2),
 3469                Point::new(3, 1)..Point::new(3, 2),
 3470            ])
 3471        });
 3472
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3475
 3476        assert_eq!(
 3477            editor.selections.ranges::<Point>(cx),
 3478            [
 3479                Point::new(0, 7)..Point::new(0, 7),
 3480                Point::new(1, 3)..Point::new(1, 3)
 3481            ]
 3482        );
 3483        editor
 3484    });
 3485}
 3486
 3487#[gpui::test]
 3488async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3489    init_test(cx, |_| {});
 3490
 3491    let mut cx = EditorTestContext::new(cx).await;
 3492
 3493    let diff_base = r#"
 3494        Line 0
 3495        Line 1
 3496        Line 2
 3497        Line 3
 3498        "#
 3499    .unindent();
 3500
 3501    cx.set_state(
 3502        &r#"
 3503        ˇLine 0
 3504        Line 1
 3505        Line 2
 3506        Line 3
 3507        "#
 3508        .unindent(),
 3509    );
 3510
 3511    cx.set_head_text(&diff_base);
 3512    executor.run_until_parked();
 3513
 3514    // Join lines
 3515    cx.update_editor(|editor, window, cx| {
 3516        editor.join_lines(&JoinLines, window, cx);
 3517    });
 3518    executor.run_until_parked();
 3519
 3520    cx.assert_editor_state(
 3521        &r#"
 3522        Line 0ˇ Line 1
 3523        Line 2
 3524        Line 3
 3525        "#
 3526        .unindent(),
 3527    );
 3528    // Join again
 3529    cx.update_editor(|editor, window, cx| {
 3530        editor.join_lines(&JoinLines, window, cx);
 3531    });
 3532    executor.run_until_parked();
 3533
 3534    cx.assert_editor_state(
 3535        &r#"
 3536        Line 0 Line 1ˇ Line 2
 3537        Line 3
 3538        "#
 3539        .unindent(),
 3540    );
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_custom_newlines_cause_no_false_positive_diffs(
 3545    executor: BackgroundExecutor,
 3546    cx: &mut TestAppContext,
 3547) {
 3548    init_test(cx, |_| {});
 3549    let mut cx = EditorTestContext::new(cx).await;
 3550    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3551    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3552    executor.run_until_parked();
 3553
 3554    cx.update_editor(|editor, window, cx| {
 3555        let snapshot = editor.snapshot(window, cx);
 3556        assert_eq!(
 3557            snapshot
 3558                .buffer_snapshot
 3559                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3560                .collect::<Vec<_>>(),
 3561            Vec::new(),
 3562            "Should not have any diffs for files with custom newlines"
 3563        );
 3564    });
 3565}
 3566
 3567#[gpui::test]
 3568async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3569    init_test(cx, |_| {});
 3570
 3571    let mut cx = EditorTestContext::new(cx).await;
 3572
 3573    // Test sort_lines_case_insensitive()
 3574    cx.set_state(indoc! {"
 3575        «z
 3576        y
 3577        x
 3578        Z
 3579        Y
 3580        Xˇ»
 3581    "});
 3582    cx.update_editor(|e, window, cx| {
 3583        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «x
 3587        X
 3588        y
 3589        Y
 3590        z
 3591        Zˇ»
 3592    "});
 3593
 3594    // Test reverse_lines()
 3595    cx.set_state(indoc! {"
 3596        «5
 3597        4
 3598        3
 3599        2
 3600        1ˇ»
 3601    "});
 3602    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «1
 3605        2
 3606        3
 3607        4
 3608        5ˇ»
 3609    "});
 3610
 3611    // Skip testing shuffle_line()
 3612
 3613    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3614    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3615
 3616    // Don't manipulate when cursor is on single line, but expand the selection
 3617    cx.set_state(indoc! {"
 3618        ddˇdd
 3619        ccc
 3620        bb
 3621        a
 3622    "});
 3623    cx.update_editor(|e, window, cx| {
 3624        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3625    });
 3626    cx.assert_editor_state(indoc! {"
 3627        «ddddˇ»
 3628        ccc
 3629        bb
 3630        a
 3631    "});
 3632
 3633    // Basic manipulate case
 3634    // Start selection moves to column 0
 3635    // End of selection shrinks to fit shorter line
 3636    cx.set_state(indoc! {"
 3637        dd«d
 3638        ccc
 3639        bb
 3640        aaaaaˇ»
 3641    "});
 3642    cx.update_editor(|e, window, cx| {
 3643        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «aaaaa
 3647        bb
 3648        ccc
 3649        dddˇ»
 3650    "});
 3651
 3652    // Manipulate case with newlines
 3653    cx.set_state(indoc! {"
 3654        dd«d
 3655        ccc
 3656
 3657        bb
 3658        aaaaa
 3659
 3660        ˇ»
 3661    "});
 3662    cx.update_editor(|e, window, cx| {
 3663        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3664    });
 3665    cx.assert_editor_state(indoc! {"
 3666        «
 3667
 3668        aaaaa
 3669        bb
 3670        ccc
 3671        dddˇ»
 3672
 3673    "});
 3674
 3675    // Adding new line
 3676    cx.set_state(indoc! {"
 3677        aa«a
 3678        bbˇ»b
 3679    "});
 3680    cx.update_editor(|e, window, cx| {
 3681        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3682    });
 3683    cx.assert_editor_state(indoc! {"
 3684        «aaa
 3685        bbb
 3686        added_lineˇ»
 3687    "});
 3688
 3689    // Removing line
 3690    cx.set_state(indoc! {"
 3691        aa«a
 3692        bbbˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.manipulate_lines(window, cx, |lines| {
 3696            lines.pop();
 3697        })
 3698    });
 3699    cx.assert_editor_state(indoc! {"
 3700        «aaaˇ»
 3701    "});
 3702
 3703    // Removing all lines
 3704    cx.set_state(indoc! {"
 3705        aa«a
 3706        bbbˇ»
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.manipulate_lines(window, cx, |lines| {
 3710            lines.drain(..);
 3711        })
 3712    });
 3713    cx.assert_editor_state(indoc! {"
 3714        ˇ
 3715    "});
 3716}
 3717
 3718#[gpui::test]
 3719async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3720    init_test(cx, |_| {});
 3721
 3722    let mut cx = EditorTestContext::new(cx).await;
 3723
 3724    // Consider continuous selection as single selection
 3725    cx.set_state(indoc! {"
 3726        Aaa«aa
 3727        cˇ»c«c
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «Aaaaa
 3736        ccc
 3737        bb
 3738        aaaaaˇ»
 3739    "});
 3740
 3741    cx.set_state(indoc! {"
 3742        Aaa«aa
 3743        cˇ»c«c
 3744        bb
 3745        aaaˇ»aa
 3746    "});
 3747    cx.update_editor(|e, window, cx| {
 3748        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3749    });
 3750    cx.assert_editor_state(indoc! {"
 3751        «Aaaaa
 3752        ccc
 3753        bbˇ»
 3754    "});
 3755
 3756    // Consider non continuous selection as distinct dedup operations
 3757    cx.set_state(indoc! {"
 3758        «aaaaa
 3759        bb
 3760        aaaaa
 3761        aaaaaˇ»
 3762
 3763        aaa«aaˇ»
 3764    "});
 3765    cx.update_editor(|e, window, cx| {
 3766        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3767    });
 3768    cx.assert_editor_state(indoc! {"
 3769        «aaaaa
 3770        bbˇ»
 3771
 3772        «aaaaaˇ»
 3773    "});
 3774}
 3775
 3776#[gpui::test]
 3777async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3778    init_test(cx, |_| {});
 3779
 3780    let mut cx = EditorTestContext::new(cx).await;
 3781
 3782    cx.set_state(indoc! {"
 3783        «Aaa
 3784        aAa
 3785        Aaaˇ»
 3786    "});
 3787    cx.update_editor(|e, window, cx| {
 3788        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3789    });
 3790    cx.assert_editor_state(indoc! {"
 3791        «Aaa
 3792        aAaˇ»
 3793    "});
 3794
 3795    cx.set_state(indoc! {"
 3796        «Aaa
 3797        aAa
 3798        aaAˇ»
 3799    "});
 3800    cx.update_editor(|e, window, cx| {
 3801        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3802    });
 3803    cx.assert_editor_state(indoc! {"
 3804        «Aaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    // Manipulate with multiple selections on a single line
 3815    cx.set_state(indoc! {"
 3816        dd«dd
 3817        cˇ»c«c
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «aaaaa
 3826        bb
 3827        ccc
 3828        ddddˇ»
 3829    "});
 3830
 3831    // Manipulate with multiple disjoin selections
 3832    cx.set_state(indoc! {"
 3833 3834        4
 3835        3
 3836        2
 3837        1ˇ»
 3838
 3839        dd«dd
 3840        ccc
 3841        bb
 3842        aaaˇ»aa
 3843    "});
 3844    cx.update_editor(|e, window, cx| {
 3845        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3846    });
 3847    cx.assert_editor_state(indoc! {"
 3848        «1
 3849        2
 3850        3
 3851        4
 3852        5ˇ»
 3853
 3854        «aaaaa
 3855        bb
 3856        ccc
 3857        ddddˇ»
 3858    "});
 3859
 3860    // Adding lines on each selection
 3861    cx.set_state(indoc! {"
 3862 3863        1ˇ»
 3864
 3865        bb«bb
 3866        aaaˇ»aa
 3867    "});
 3868    cx.update_editor(|e, window, cx| {
 3869        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2
 3873        1
 3874        added lineˇ»
 3875
 3876        «bbbb
 3877        aaaaa
 3878        added lineˇ»
 3879    "});
 3880
 3881    // Removing lines on each selection
 3882    cx.set_state(indoc! {"
 3883 3884        1ˇ»
 3885
 3886        bb«bb
 3887        aaaˇ»aa
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.manipulate_lines(window, cx, |lines| {
 3891            lines.pop();
 3892        })
 3893    });
 3894    cx.assert_editor_state(indoc! {"
 3895        «2ˇ»
 3896
 3897        «bbbbˇ»
 3898    "});
 3899}
 3900
 3901#[gpui::test]
 3902async fn test_toggle_case(cx: &mut TestAppContext) {
 3903    init_test(cx, |_| {});
 3904
 3905    let mut cx = EditorTestContext::new(cx).await;
 3906
 3907    // If all lower case -> upper case
 3908    cx.set_state(indoc! {"
 3909        «hello worldˇ»
 3910    "});
 3911    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3912    cx.assert_editor_state(indoc! {"
 3913        «HELLO WORLDˇ»
 3914    "});
 3915
 3916    // If all upper case -> lower case
 3917    cx.set_state(indoc! {"
 3918        «HELLO WORLDˇ»
 3919    "});
 3920    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3921    cx.assert_editor_state(indoc! {"
 3922        «hello worldˇ»
 3923    "});
 3924
 3925    // If any upper case characters are identified -> lower case
 3926    // This matches JetBrains IDEs
 3927    cx.set_state(indoc! {"
 3928        «hEllo worldˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «hello worldˇ»
 3933    "});
 3934}
 3935
 3936#[gpui::test]
 3937async fn test_manipulate_text(cx: &mut TestAppContext) {
 3938    init_test(cx, |_| {});
 3939
 3940    let mut cx = EditorTestContext::new(cx).await;
 3941
 3942    // Test convert_to_upper_case()
 3943    cx.set_state(indoc! {"
 3944        «hello worldˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3947    cx.assert_editor_state(indoc! {"
 3948        «HELLO WORLDˇ»
 3949    "});
 3950
 3951    // Test convert_to_lower_case()
 3952    cx.set_state(indoc! {"
 3953        «HELLO WORLDˇ»
 3954    "});
 3955    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3956    cx.assert_editor_state(indoc! {"
 3957        «hello worldˇ»
 3958    "});
 3959
 3960    // Test multiple line, single selection case
 3961    cx.set_state(indoc! {"
 3962        «The quick brown
 3963        fox jumps over
 3964        the lazy dogˇ»
 3965    "});
 3966    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3967    cx.assert_editor_state(indoc! {"
 3968        «The Quick Brown
 3969        Fox Jumps Over
 3970        The Lazy Dogˇ»
 3971    "});
 3972
 3973    // Test multiple line, single selection case
 3974    cx.set_state(indoc! {"
 3975        «The quick brown
 3976        fox jumps over
 3977        the lazy dogˇ»
 3978    "});
 3979    cx.update_editor(|e, window, cx| {
 3980        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3981    });
 3982    cx.assert_editor_state(indoc! {"
 3983        «TheQuickBrown
 3984        FoxJumpsOver
 3985        TheLazyDogˇ»
 3986    "});
 3987
 3988    // From here on out, test more complex cases of manipulate_text()
 3989
 3990    // Test no selection case - should affect words cursors are in
 3991    // Cursor at beginning, middle, and end of word
 3992    cx.set_state(indoc! {"
 3993        ˇhello big beauˇtiful worldˇ
 3994    "});
 3995    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3996    cx.assert_editor_state(indoc! {"
 3997        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3998    "});
 3999
 4000    // Test multiple selections on a single line and across multiple lines
 4001    cx.set_state(indoc! {"
 4002        «Theˇ» quick «brown
 4003        foxˇ» jumps «overˇ»
 4004        the «lazyˇ» dog
 4005    "});
 4006    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4007    cx.assert_editor_state(indoc! {"
 4008        «THEˇ» quick «BROWN
 4009        FOXˇ» jumps «OVERˇ»
 4010        the «LAZYˇ» dog
 4011    "});
 4012
 4013    // Test case where text length grows
 4014    cx.set_state(indoc! {"
 4015        «tschüߡ»
 4016    "});
 4017    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4018    cx.assert_editor_state(indoc! {"
 4019        «TSCHÜSSˇ»
 4020    "});
 4021
 4022    // Test to make sure we don't crash when text shrinks
 4023    cx.set_state(indoc! {"
 4024        aaa_bbbˇ
 4025    "});
 4026    cx.update_editor(|e, window, cx| {
 4027        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4028    });
 4029    cx.assert_editor_state(indoc! {"
 4030        «aaaBbbˇ»
 4031    "});
 4032
 4033    // Test to make sure we all aware of the fact that each word can grow and shrink
 4034    // Final selections should be aware of this fact
 4035    cx.set_state(indoc! {"
 4036        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4037    "});
 4038    cx.update_editor(|e, window, cx| {
 4039        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4040    });
 4041    cx.assert_editor_state(indoc! {"
 4042        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4043    "});
 4044
 4045    cx.set_state(indoc! {"
 4046        «hElLo, WoRld!ˇ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| {
 4049        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4050    });
 4051    cx.assert_editor_state(indoc! {"
 4052        «HeLlO, wOrLD!ˇ»
 4053    "});
 4054}
 4055
 4056#[gpui::test]
 4057fn test_duplicate_line(cx: &mut TestAppContext) {
 4058    init_test(cx, |_| {});
 4059
 4060    let editor = cx.add_window(|window, cx| {
 4061        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4062        build_editor(buffer, window, cx)
 4063    });
 4064    _ = editor.update(cx, |editor, window, cx| {
 4065        editor.change_selections(None, window, cx, |s| {
 4066            s.select_display_ranges([
 4067                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4068                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4069                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4070                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4071            ])
 4072        });
 4073        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4074        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4075        assert_eq!(
 4076            editor.selections.display_ranges(cx),
 4077            vec![
 4078                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4079                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4080                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4081                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4082            ]
 4083        );
 4084    });
 4085
 4086    let editor = cx.add_window(|window, cx| {
 4087        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4088        build_editor(buffer, window, cx)
 4089    });
 4090    _ = editor.update(cx, |editor, window, cx| {
 4091        editor.change_selections(None, window, cx, |s| {
 4092            s.select_display_ranges([
 4093                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4094                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4095            ])
 4096        });
 4097        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4098        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4099        assert_eq!(
 4100            editor.selections.display_ranges(cx),
 4101            vec![
 4102                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4103                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4104            ]
 4105        );
 4106    });
 4107
 4108    // With `move_upwards` the selections stay in place, except for
 4109    // the lines inserted above them
 4110    let editor = cx.add_window(|window, cx| {
 4111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4112        build_editor(buffer, window, cx)
 4113    });
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.change_selections(None, window, cx, |s| {
 4116            s.select_display_ranges([
 4117                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4118                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4119                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4120                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4121            ])
 4122        });
 4123        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4124        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4125        assert_eq!(
 4126            editor.selections.display_ranges(cx),
 4127            vec![
 4128                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4129                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4130                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        editor.change_selections(None, window, cx, |s| {
 4142            s.select_display_ranges([
 4143                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4144                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4145            ])
 4146        });
 4147        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4148        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4149        assert_eq!(
 4150            editor.selections.display_ranges(cx),
 4151            vec![
 4152                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4153                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4154            ]
 4155        );
 4156    });
 4157
 4158    let editor = cx.add_window(|window, cx| {
 4159        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4160        build_editor(buffer, window, cx)
 4161    });
 4162    _ = editor.update(cx, |editor, window, cx| {
 4163        editor.change_selections(None, window, cx, |s| {
 4164            s.select_display_ranges([
 4165                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4166                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4167            ])
 4168        });
 4169        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4170        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4171        assert_eq!(
 4172            editor.selections.display_ranges(cx),
 4173            vec![
 4174                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4175                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4176            ]
 4177        );
 4178    });
 4179}
 4180
 4181#[gpui::test]
 4182fn test_move_line_up_down(cx: &mut TestAppContext) {
 4183    init_test(cx, |_| {});
 4184
 4185    let editor = cx.add_window(|window, cx| {
 4186        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4187        build_editor(buffer, window, cx)
 4188    });
 4189    _ = editor.update(cx, |editor, window, cx| {
 4190        editor.fold_creases(
 4191            vec![
 4192                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4193                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4194                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4195            ],
 4196            true,
 4197            window,
 4198            cx,
 4199        );
 4200        editor.change_selections(None, window, cx, |s| {
 4201            s.select_display_ranges([
 4202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4206            ])
 4207        });
 4208        assert_eq!(
 4209            editor.display_text(cx),
 4210            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4211        );
 4212
 4213        editor.move_line_up(&MoveLineUp, window, cx);
 4214        assert_eq!(
 4215            editor.display_text(cx),
 4216            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4217        );
 4218        assert_eq!(
 4219            editor.selections.display_ranges(cx),
 4220            vec![
 4221                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4222                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4223                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4224                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4225            ]
 4226        );
 4227    });
 4228
 4229    _ = editor.update(cx, |editor, window, cx| {
 4230        editor.move_line_down(&MoveLineDown, window, cx);
 4231        assert_eq!(
 4232            editor.display_text(cx),
 4233            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4234        );
 4235        assert_eq!(
 4236            editor.selections.display_ranges(cx),
 4237            vec![
 4238                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4239                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4240                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4241                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4242            ]
 4243        );
 4244    });
 4245
 4246    _ = editor.update(cx, |editor, window, cx| {
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248        assert_eq!(
 4249            editor.display_text(cx),
 4250            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4251        );
 4252        assert_eq!(
 4253            editor.selections.display_ranges(cx),
 4254            vec![
 4255                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4257                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4258                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4259            ]
 4260        );
 4261    });
 4262
 4263    _ = editor.update(cx, |editor, window, cx| {
 4264        editor.move_line_up(&MoveLineUp, window, cx);
 4265        assert_eq!(
 4266            editor.display_text(cx),
 4267            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4268        );
 4269        assert_eq!(
 4270            editor.selections.display_ranges(cx),
 4271            vec![
 4272                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4273                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4274                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4275                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4276            ]
 4277        );
 4278    });
 4279}
 4280
 4281#[gpui::test]
 4282fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4283    init_test(cx, |_| {});
 4284
 4285    let editor = cx.add_window(|window, cx| {
 4286        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4287        build_editor(buffer, window, cx)
 4288    });
 4289    _ = editor.update(cx, |editor, window, cx| {
 4290        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4291        editor.insert_blocks(
 4292            [BlockProperties {
 4293                style: BlockStyle::Fixed,
 4294                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4295                height: Some(1),
 4296                render: Arc::new(|_| div().into_any()),
 4297                priority: 0,
 4298            }],
 4299            Some(Autoscroll::fit()),
 4300            cx,
 4301        );
 4302        editor.change_selections(None, window, cx, |s| {
 4303            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4304        });
 4305        editor.move_line_down(&MoveLineDown, window, cx);
 4306    });
 4307}
 4308
 4309#[gpui::test]
 4310async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4311    init_test(cx, |_| {});
 4312
 4313    let mut cx = EditorTestContext::new(cx).await;
 4314    cx.set_state(
 4315        &"
 4316            ˇzero
 4317            one
 4318            two
 4319            three
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Create a four-line block that replaces three lines of text.
 4327    cx.update_editor(|editor, window, cx| {
 4328        let snapshot = editor.snapshot(window, cx);
 4329        let snapshot = &snapshot.buffer_snapshot;
 4330        let placement = BlockPlacement::Replace(
 4331            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4332        );
 4333        editor.insert_blocks(
 4334            [BlockProperties {
 4335                placement,
 4336                height: Some(4),
 4337                style: BlockStyle::Sticky,
 4338                render: Arc::new(|_| gpui::div().into_any_element()),
 4339                priority: 0,
 4340            }],
 4341            None,
 4342            cx,
 4343        );
 4344    });
 4345
 4346    // Move down so that the cursor touches the block.
 4347    cx.update_editor(|editor, window, cx| {
 4348        editor.move_down(&Default::default(), window, cx);
 4349    });
 4350    cx.assert_editor_state(
 4351        &"
 4352            zero
 4353            «one
 4354            two
 4355            threeˇ»
 4356            four
 4357            five
 4358        "
 4359        .unindent(),
 4360    );
 4361
 4362    // Move down past the block.
 4363    cx.update_editor(|editor, window, cx| {
 4364        editor.move_down(&Default::default(), window, cx);
 4365    });
 4366    cx.assert_editor_state(
 4367        &"
 4368            zero
 4369            one
 4370            two
 4371            three
 4372            ˇfour
 4373            five
 4374        "
 4375        .unindent(),
 4376    );
 4377}
 4378
 4379#[gpui::test]
 4380fn test_transpose(cx: &mut TestAppContext) {
 4381    init_test(cx, |_| {});
 4382
 4383    _ = cx.add_window(|window, cx| {
 4384        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4385        editor.set_style(EditorStyle::default(), window, cx);
 4386        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4387        editor.transpose(&Default::default(), window, cx);
 4388        assert_eq!(editor.text(cx), "bac");
 4389        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4390
 4391        editor.transpose(&Default::default(), window, cx);
 4392        assert_eq!(editor.text(cx), "bca");
 4393        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4394
 4395        editor.transpose(&Default::default(), window, cx);
 4396        assert_eq!(editor.text(cx), "bac");
 4397        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4398
 4399        editor
 4400    });
 4401
 4402    _ = cx.add_window(|window, cx| {
 4403        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4404        editor.set_style(EditorStyle::default(), window, cx);
 4405        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "acb\nde");
 4408        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4409
 4410        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "acbd\ne");
 4413        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "acbde\n");
 4417        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4418
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "acbd\ne");
 4421        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4422
 4423        editor
 4424    });
 4425
 4426    _ = cx.add_window(|window, cx| {
 4427        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4428        editor.set_style(EditorStyle::default(), window, cx);
 4429        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4430        editor.transpose(&Default::default(), window, cx);
 4431        assert_eq!(editor.text(cx), "bacd\ne");
 4432        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4433
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "bcade\n");
 4436        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4437
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "bcda\ne");
 4440        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4441
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "bcade\n");
 4444        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4445
 4446        editor.transpose(&Default::default(), window, cx);
 4447        assert_eq!(editor.text(cx), "bcaed\n");
 4448        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4449
 4450        editor
 4451    });
 4452
 4453    _ = cx.add_window(|window, cx| {
 4454        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4455        editor.set_style(EditorStyle::default(), window, cx);
 4456        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4457        editor.transpose(&Default::default(), window, cx);
 4458        assert_eq!(editor.text(cx), "🏀🍐✋");
 4459        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4460
 4461        editor.transpose(&Default::default(), window, cx);
 4462        assert_eq!(editor.text(cx), "🏀✋🍐");
 4463        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4464
 4465        editor.transpose(&Default::default(), window, cx);
 4466        assert_eq!(editor.text(cx), "🏀🍐✋");
 4467        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4468
 4469        editor
 4470    });
 4471}
 4472
 4473#[gpui::test]
 4474async fn test_rewrap(cx: &mut TestAppContext) {
 4475    init_test(cx, |settings| {
 4476        settings.languages.extend([
 4477            (
 4478                "Markdown".into(),
 4479                LanguageSettingsContent {
 4480                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4481                    ..Default::default()
 4482                },
 4483            ),
 4484            (
 4485                "Plain Text".into(),
 4486                LanguageSettingsContent {
 4487                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4488                    ..Default::default()
 4489                },
 4490            ),
 4491        ])
 4492    });
 4493
 4494    let mut cx = EditorTestContext::new(cx).await;
 4495
 4496    let language_with_c_comments = Arc::new(Language::new(
 4497        LanguageConfig {
 4498            line_comments: vec!["// ".into()],
 4499            ..LanguageConfig::default()
 4500        },
 4501        None,
 4502    ));
 4503    let language_with_pound_comments = Arc::new(Language::new(
 4504        LanguageConfig {
 4505            line_comments: vec!["# ".into()],
 4506            ..LanguageConfig::default()
 4507        },
 4508        None,
 4509    ));
 4510    let markdown_language = Arc::new(Language::new(
 4511        LanguageConfig {
 4512            name: "Markdown".into(),
 4513            ..LanguageConfig::default()
 4514        },
 4515        None,
 4516    ));
 4517    let language_with_doc_comments = Arc::new(Language::new(
 4518        LanguageConfig {
 4519            line_comments: vec!["// ".into(), "/// ".into()],
 4520            ..LanguageConfig::default()
 4521        },
 4522        Some(tree_sitter_rust::LANGUAGE.into()),
 4523    ));
 4524
 4525    let plaintext_language = Arc::new(Language::new(
 4526        LanguageConfig {
 4527            name: "Plain Text".into(),
 4528            ..LanguageConfig::default()
 4529        },
 4530        None,
 4531    ));
 4532
 4533    assert_rewrap(
 4534        indoc! {"
 4535            // ˇ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.
 4536        "},
 4537        indoc! {"
 4538            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4539            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4540            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4541            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4542            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4543            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4544            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4545            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4546            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4547            // porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        language_with_c_comments.clone(),
 4550        &mut cx,
 4551    );
 4552
 4553    // Test that rewrapping works inside of a selection
 4554    assert_rewrap(
 4555        indoc! {"
 4556            «// 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.ˇ»
 4557        "},
 4558        indoc! {"
 4559            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4560            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4561            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4562            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4563            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4564            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4565            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4566            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4567            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4568            // porttitor id. Aliquam id accumsan eros.ˇ»
 4569        "},
 4570        language_with_c_comments.clone(),
 4571        &mut cx,
 4572    );
 4573
 4574    // Test that cursors that expand to the same region are collapsed.
 4575    assert_rewrap(
 4576        indoc! {"
 4577            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4578            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4579            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4580            // ˇ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.
 4581        "},
 4582        indoc! {"
 4583            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4584            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4585            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4586            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4587            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4588            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4589            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4590            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4591            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4592            // porttitor id. Aliquam id accumsan eros.
 4593        "},
 4594        language_with_c_comments.clone(),
 4595        &mut cx,
 4596    );
 4597
 4598    // Test that non-contiguous selections are treated separately.
 4599    assert_rewrap(
 4600        indoc! {"
 4601            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4602            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4603            //
 4604            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4605            // ˇ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.
 4606        "},
 4607        indoc! {"
 4608            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4609            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4610            // auctor, eu lacinia sapien scelerisque.
 4611            //
 4612            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4613            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4614            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4615            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4616            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4617            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4618            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4619        "},
 4620        language_with_c_comments.clone(),
 4621        &mut cx,
 4622    );
 4623
 4624    // Test that different comment prefixes are supported.
 4625    assert_rewrap(
 4626        indoc! {"
 4627            # ˇ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.
 4628        "},
 4629        indoc! {"
 4630            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4631            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4632            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4633            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4634            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4635            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4636            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4637            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4638            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4639            # accumsan eros.
 4640        "},
 4641        language_with_pound_comments.clone(),
 4642        &mut cx,
 4643    );
 4644
 4645    // Test that rewrapping is ignored outside of comments in most languages.
 4646    assert_rewrap(
 4647        indoc! {"
 4648            /// Adds two numbers.
 4649            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4650            fn add(a: u32, b: u32) -> u32 {
 4651                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ˇ
 4652            }
 4653        "},
 4654        indoc! {"
 4655            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4656            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4657            fn add(a: u32, b: u32) -> u32 {
 4658                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ˇ
 4659            }
 4660        "},
 4661        language_with_doc_comments.clone(),
 4662        &mut cx,
 4663    );
 4664
 4665    // Test that rewrapping works in Markdown and Plain Text languages.
 4666    assert_rewrap(
 4667        indoc! {"
 4668            # Hello
 4669
 4670            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.
 4671        "},
 4672        indoc! {"
 4673            # Hello
 4674
 4675            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4676            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4677            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4678            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4679            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4680            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4681            Integer sit amet scelerisque nisi.
 4682        "},
 4683        markdown_language,
 4684        &mut cx,
 4685    );
 4686
 4687    assert_rewrap(
 4688        indoc! {"
 4689            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.
 4690        "},
 4691        indoc! {"
 4692            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4693            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4694            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4695            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4696            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4697            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4698            Integer sit amet scelerisque nisi.
 4699        "},
 4700        plaintext_language,
 4701        &mut cx,
 4702    );
 4703
 4704    // Test rewrapping unaligned comments in a selection.
 4705    assert_rewrap(
 4706        indoc! {"
 4707            fn foo() {
 4708                if true {
 4709            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4710            // Praesent semper egestas tellus id dignissim.ˇ»
 4711                    do_something();
 4712                } else {
 4713                    //
 4714                }
 4715            }
 4716        "},
 4717        indoc! {"
 4718            fn foo() {
 4719                if true {
 4720            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4721                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4722                    // egestas tellus id dignissim.ˇ»
 4723                    do_something();
 4724                } else {
 4725                    //
 4726                }
 4727            }
 4728        "},
 4729        language_with_doc_comments.clone(),
 4730        &mut cx,
 4731    );
 4732
 4733    assert_rewrap(
 4734        indoc! {"
 4735            fn foo() {
 4736                if true {
 4737            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4738            // Praesent semper egestas tellus id dignissim.»
 4739                    do_something();
 4740                } else {
 4741                    //
 4742                }
 4743
 4744            }
 4745        "},
 4746        indoc! {"
 4747            fn foo() {
 4748                if true {
 4749            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4750                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4751                    // egestas tellus id dignissim.»
 4752                    do_something();
 4753                } else {
 4754                    //
 4755                }
 4756
 4757            }
 4758        "},
 4759        language_with_doc_comments.clone(),
 4760        &mut cx,
 4761    );
 4762
 4763    #[track_caller]
 4764    fn assert_rewrap(
 4765        unwrapped_text: &str,
 4766        wrapped_text: &str,
 4767        language: Arc<Language>,
 4768        cx: &mut EditorTestContext,
 4769    ) {
 4770        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4771        cx.set_state(unwrapped_text);
 4772        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4773        cx.assert_editor_state(wrapped_text);
 4774    }
 4775}
 4776
 4777#[gpui::test]
 4778async fn test_hard_wrap(cx: &mut TestAppContext) {
 4779    init_test(cx, |_| {});
 4780    let mut cx = EditorTestContext::new(cx).await;
 4781
 4782    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4783    cx.update_editor(|editor, _, cx| {
 4784        editor.set_hard_wrap(Some(14), cx);
 4785    });
 4786
 4787    cx.set_state(indoc!(
 4788        "
 4789        one two three ˇ
 4790        "
 4791    ));
 4792    cx.simulate_input("four");
 4793    cx.run_until_parked();
 4794
 4795    cx.assert_editor_state(indoc!(
 4796        "
 4797        one two three
 4798        fourˇ
 4799        "
 4800    ));
 4801
 4802    cx.update_editor(|editor, window, cx| {
 4803        editor.newline(&Default::default(), window, cx);
 4804    });
 4805    cx.run_until_parked();
 4806    cx.assert_editor_state(indoc!(
 4807        "
 4808        one two three
 4809        four
 4810        ˇ
 4811        "
 4812    ));
 4813
 4814    cx.simulate_input("five");
 4815    cx.run_until_parked();
 4816    cx.assert_editor_state(indoc!(
 4817        "
 4818        one two three
 4819        four
 4820        fiveˇ
 4821        "
 4822    ));
 4823
 4824    cx.update_editor(|editor, window, cx| {
 4825        editor.newline(&Default::default(), window, cx);
 4826    });
 4827    cx.run_until_parked();
 4828    cx.simulate_input("# ");
 4829    cx.run_until_parked();
 4830    cx.assert_editor_state(indoc!(
 4831        "
 4832        one two three
 4833        four
 4834        five
 4835        # ˇ
 4836        "
 4837    ));
 4838
 4839    cx.update_editor(|editor, window, cx| {
 4840        editor.newline(&Default::default(), window, cx);
 4841    });
 4842    cx.run_until_parked();
 4843    cx.assert_editor_state(indoc!(
 4844        "
 4845        one two three
 4846        four
 4847        five
 4848        #\x20
 4849 4850        "
 4851    ));
 4852
 4853    cx.simulate_input(" 6");
 4854    cx.run_until_parked();
 4855    cx.assert_editor_state(indoc!(
 4856        "
 4857        one two three
 4858        four
 4859        five
 4860        #
 4861        # 6ˇ
 4862        "
 4863    ));
 4864}
 4865
 4866#[gpui::test]
 4867async fn test_clipboard(cx: &mut TestAppContext) {
 4868    init_test(cx, |_| {});
 4869
 4870    let mut cx = EditorTestContext::new(cx).await;
 4871
 4872    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4873    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4874    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4875
 4876    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4877    cx.set_state("two ˇfour ˇsix ˇ");
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4880
 4881    // Paste again but with only two cursors. Since the number of cursors doesn't
 4882    // match the number of slices in the clipboard, the entire clipboard text
 4883    // is pasted at each cursor.
 4884    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4885    cx.update_editor(|e, window, cx| {
 4886        e.handle_input("( ", window, cx);
 4887        e.paste(&Paste, window, cx);
 4888        e.handle_input(") ", window, cx);
 4889    });
 4890    cx.assert_editor_state(
 4891        &([
 4892            "( one✅ ",
 4893            "three ",
 4894            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4895            "three ",
 4896            "five ) ˇ",
 4897        ]
 4898        .join("\n")),
 4899    );
 4900
 4901    // Cut with three selections, one of which is full-line.
 4902    cx.set_state(indoc! {"
 4903        1«2ˇ»3
 4904        4ˇ567
 4905        «8ˇ»9"});
 4906    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4907    cx.assert_editor_state(indoc! {"
 4908        1ˇ3
 4909        ˇ9"});
 4910
 4911    // Paste with three selections, noticing how the copied selection that was full-line
 4912    // gets inserted before the second cursor.
 4913    cx.set_state(indoc! {"
 4914        1ˇ3
 4915 4916        «oˇ»ne"});
 4917    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        12ˇ3
 4920        4567
 4921 4922        8ˇne"});
 4923
 4924    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4925    cx.set_state(indoc! {"
 4926        The quick brown
 4927        fox juˇmps over
 4928        the lazy dog"});
 4929    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4930    assert_eq!(
 4931        cx.read_from_clipboard()
 4932            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4933        Some("fox jumps over\n".to_string())
 4934    );
 4935
 4936    // Paste with three selections, noticing how the copied full-line selection is inserted
 4937    // before the empty selections but replaces the selection that is non-empty.
 4938    cx.set_state(indoc! {"
 4939        Tˇhe quick brown
 4940        «foˇ»x jumps over
 4941        tˇhe lazy dog"});
 4942    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4943    cx.assert_editor_state(indoc! {"
 4944        fox jumps over
 4945        Tˇhe quick brown
 4946        fox jumps over
 4947        ˇx jumps over
 4948        fox jumps over
 4949        tˇhe lazy dog"});
 4950}
 4951
 4952#[gpui::test]
 4953async fn test_copy_trim(cx: &mut TestAppContext) {
 4954    init_test(cx, |_| {});
 4955
 4956    let mut cx = EditorTestContext::new(cx).await;
 4957    cx.set_state(
 4958        r#"            «for selection in selections.iter() {
 4959            let mut start = selection.start;
 4960            let mut end = selection.end;
 4961            let is_entire_line = selection.is_empty();
 4962            if is_entire_line {
 4963                start = Point::new(start.row, 0);ˇ»
 4964                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4965            }
 4966        "#,
 4967    );
 4968    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4969    assert_eq!(
 4970        cx.read_from_clipboard()
 4971            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4972        Some(
 4973            "for selection in selections.iter() {
 4974            let mut start = selection.start;
 4975            let mut end = selection.end;
 4976            let is_entire_line = selection.is_empty();
 4977            if is_entire_line {
 4978                start = Point::new(start.row, 0);"
 4979                .to_string()
 4980        ),
 4981        "Regular copying preserves all indentation selected",
 4982    );
 4983    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4984    assert_eq!(
 4985        cx.read_from_clipboard()
 4986            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4987        Some(
 4988            "for selection in selections.iter() {
 4989let mut start = selection.start;
 4990let mut end = selection.end;
 4991let is_entire_line = selection.is_empty();
 4992if is_entire_line {
 4993    start = Point::new(start.row, 0);"
 4994                .to_string()
 4995        ),
 4996        "Copying with stripping should strip all leading whitespaces"
 4997    );
 4998
 4999    cx.set_state(
 5000        r#"       «     for selection in selections.iter() {
 5001            let mut start = selection.start;
 5002            let mut end = selection.end;
 5003            let is_entire_line = selection.is_empty();
 5004            if is_entire_line {
 5005                start = Point::new(start.row, 0);ˇ»
 5006                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5007            }
 5008        "#,
 5009    );
 5010    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5011    assert_eq!(
 5012        cx.read_from_clipboard()
 5013            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5014        Some(
 5015            "     for selection in selections.iter() {
 5016            let mut start = selection.start;
 5017            let mut end = selection.end;
 5018            let is_entire_line = selection.is_empty();
 5019            if is_entire_line {
 5020                start = Point::new(start.row, 0);"
 5021                .to_string()
 5022        ),
 5023        "Regular copying preserves all indentation selected",
 5024    );
 5025    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5026    assert_eq!(
 5027        cx.read_from_clipboard()
 5028            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5029        Some(
 5030            "for selection in selections.iter() {
 5031let mut start = selection.start;
 5032let mut end = selection.end;
 5033let is_entire_line = selection.is_empty();
 5034if is_entire_line {
 5035    start = Point::new(start.row, 0);"
 5036                .to_string()
 5037        ),
 5038        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5039    );
 5040
 5041    cx.set_state(
 5042        r#"       «ˇ     for selection in selections.iter() {
 5043            let mut start = selection.start;
 5044            let mut end = selection.end;
 5045            let is_entire_line = selection.is_empty();
 5046            if is_entire_line {
 5047                start = Point::new(start.row, 0);»
 5048                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5049            }
 5050        "#,
 5051    );
 5052    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5053    assert_eq!(
 5054        cx.read_from_clipboard()
 5055            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5056        Some(
 5057            "     for selection in selections.iter() {
 5058            let mut start = selection.start;
 5059            let mut end = selection.end;
 5060            let is_entire_line = selection.is_empty();
 5061            if is_entire_line {
 5062                start = Point::new(start.row, 0);"
 5063                .to_string()
 5064        ),
 5065        "Regular copying for reverse selection works the same",
 5066    );
 5067    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5068    assert_eq!(
 5069        cx.read_from_clipboard()
 5070            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5071        Some(
 5072            "for selection in selections.iter() {
 5073let mut start = selection.start;
 5074let mut end = selection.end;
 5075let is_entire_line = selection.is_empty();
 5076if is_entire_line {
 5077    start = Point::new(start.row, 0);"
 5078                .to_string()
 5079        ),
 5080        "Copying with stripping for reverse selection works the same"
 5081    );
 5082
 5083    cx.set_state(
 5084        r#"            for selection «in selections.iter() {
 5085            let mut start = selection.start;
 5086            let mut end = selection.end;
 5087            let is_entire_line = selection.is_empty();
 5088            if is_entire_line {
 5089                start = Point::new(start.row, 0);ˇ»
 5090                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5091            }
 5092        "#,
 5093    );
 5094    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5095    assert_eq!(
 5096        cx.read_from_clipboard()
 5097            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5098        Some(
 5099            "in selections.iter() {
 5100            let mut start = selection.start;
 5101            let mut end = selection.end;
 5102            let is_entire_line = selection.is_empty();
 5103            if is_entire_line {
 5104                start = Point::new(start.row, 0);"
 5105                .to_string()
 5106        ),
 5107        "When selecting past the indent, the copying works as usual",
 5108    );
 5109    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5110    assert_eq!(
 5111        cx.read_from_clipboard()
 5112            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5113        Some(
 5114            "in selections.iter() {
 5115            let mut start = selection.start;
 5116            let mut end = selection.end;
 5117            let is_entire_line = selection.is_empty();
 5118            if is_entire_line {
 5119                start = Point::new(start.row, 0);"
 5120                .to_string()
 5121        ),
 5122        "When selecting past the indent, nothing is trimmed"
 5123    );
 5124
 5125    cx.set_state(
 5126        r#"            «for selection in selections.iter() {
 5127            let mut start = selection.start;
 5128
 5129            let mut end = selection.end;
 5130            let is_entire_line = selection.is_empty();
 5131            if is_entire_line {
 5132                start = Point::new(start.row, 0);
 5133ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5134            }
 5135        "#,
 5136    );
 5137    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5138    assert_eq!(
 5139        cx.read_from_clipboard()
 5140            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5141        Some(
 5142            "for selection in selections.iter() {
 5143let mut start = selection.start;
 5144
 5145let mut end = selection.end;
 5146let is_entire_line = selection.is_empty();
 5147if is_entire_line {
 5148    start = Point::new(start.row, 0);
 5149"
 5150            .to_string()
 5151        ),
 5152        "Copying with stripping should ignore empty lines"
 5153    );
 5154}
 5155
 5156#[gpui::test]
 5157async fn test_paste_multiline(cx: &mut TestAppContext) {
 5158    init_test(cx, |_| {});
 5159
 5160    let mut cx = EditorTestContext::new(cx).await;
 5161    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5162
 5163    // Cut an indented block, without the leading whitespace.
 5164    cx.set_state(indoc! {"
 5165        const a: B = (
 5166            c(),
 5167            «d(
 5168                e,
 5169                f
 5170            )ˇ»
 5171        );
 5172    "});
 5173    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5174    cx.assert_editor_state(indoc! {"
 5175        const a: B = (
 5176            c(),
 5177            ˇ
 5178        );
 5179    "});
 5180
 5181    // Paste it at the same position.
 5182    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5183    cx.assert_editor_state(indoc! {"
 5184        const a: B = (
 5185            c(),
 5186            d(
 5187                e,
 5188                f
 5189 5190        );
 5191    "});
 5192
 5193    // Paste it at a line with a lower indent level.
 5194    cx.set_state(indoc! {"
 5195        ˇ
 5196        const a: B = (
 5197            c(),
 5198        );
 5199    "});
 5200    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5201    cx.assert_editor_state(indoc! {"
 5202        d(
 5203            e,
 5204            f
 5205 5206        const a: B = (
 5207            c(),
 5208        );
 5209    "});
 5210
 5211    // Cut an indented block, with the leading whitespace.
 5212    cx.set_state(indoc! {"
 5213        const a: B = (
 5214            c(),
 5215        «    d(
 5216                e,
 5217                f
 5218            )
 5219        ˇ»);
 5220    "});
 5221    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5222    cx.assert_editor_state(indoc! {"
 5223        const a: B = (
 5224            c(),
 5225        ˇ);
 5226    "});
 5227
 5228    // Paste it at the same position.
 5229    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5230    cx.assert_editor_state(indoc! {"
 5231        const a: B = (
 5232            c(),
 5233            d(
 5234                e,
 5235                f
 5236            )
 5237        ˇ);
 5238    "});
 5239
 5240    // Paste it at a line with a higher indent level.
 5241    cx.set_state(indoc! {"
 5242        const a: B = (
 5243            c(),
 5244            d(
 5245                e,
 5246 5247            )
 5248        );
 5249    "});
 5250    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5251    cx.assert_editor_state(indoc! {"
 5252        const a: B = (
 5253            c(),
 5254            d(
 5255                e,
 5256                f    d(
 5257                    e,
 5258                    f
 5259                )
 5260        ˇ
 5261            )
 5262        );
 5263    "});
 5264
 5265    // Copy an indented block, starting mid-line
 5266    cx.set_state(indoc! {"
 5267        const a: B = (
 5268            c(),
 5269            somethin«g(
 5270                e,
 5271                f
 5272            )ˇ»
 5273        );
 5274    "});
 5275    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5276
 5277    // Paste it on a line with a lower indent level
 5278    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5279    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5280    cx.assert_editor_state(indoc! {"
 5281        const a: B = (
 5282            c(),
 5283            something(
 5284                e,
 5285                f
 5286            )
 5287        );
 5288        g(
 5289            e,
 5290            f
 5291"});
 5292}
 5293
 5294#[gpui::test]
 5295async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5296    init_test(cx, |_| {});
 5297
 5298    cx.write_to_clipboard(ClipboardItem::new_string(
 5299        "    d(\n        e\n    );\n".into(),
 5300    ));
 5301
 5302    let mut cx = EditorTestContext::new(cx).await;
 5303    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5304
 5305    cx.set_state(indoc! {"
 5306        fn a() {
 5307            b();
 5308            if c() {
 5309                ˇ
 5310            }
 5311        }
 5312    "});
 5313
 5314    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5315    cx.assert_editor_state(indoc! {"
 5316        fn a() {
 5317            b();
 5318            if c() {
 5319                d(
 5320                    e
 5321                );
 5322        ˇ
 5323            }
 5324        }
 5325    "});
 5326
 5327    cx.set_state(indoc! {"
 5328        fn a() {
 5329            b();
 5330            ˇ
 5331        }
 5332    "});
 5333
 5334    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5335    cx.assert_editor_state(indoc! {"
 5336        fn a() {
 5337            b();
 5338            d(
 5339                e
 5340            );
 5341        ˇ
 5342        }
 5343    "});
 5344}
 5345
 5346#[gpui::test]
 5347fn test_select_all(cx: &mut TestAppContext) {
 5348    init_test(cx, |_| {});
 5349
 5350    let editor = cx.add_window(|window, cx| {
 5351        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5352        build_editor(buffer, window, cx)
 5353    });
 5354    _ = editor.update(cx, |editor, window, cx| {
 5355        editor.select_all(&SelectAll, window, cx);
 5356        assert_eq!(
 5357            editor.selections.display_ranges(cx),
 5358            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5359        );
 5360    });
 5361}
 5362
 5363#[gpui::test]
 5364fn test_select_line(cx: &mut TestAppContext) {
 5365    init_test(cx, |_| {});
 5366
 5367    let editor = cx.add_window(|window, cx| {
 5368        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5369        build_editor(buffer, window, cx)
 5370    });
 5371    _ = editor.update(cx, |editor, window, cx| {
 5372        editor.change_selections(None, window, cx, |s| {
 5373            s.select_display_ranges([
 5374                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5375                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5376                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5377                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5378            ])
 5379        });
 5380        editor.select_line(&SelectLine, window, cx);
 5381        assert_eq!(
 5382            editor.selections.display_ranges(cx),
 5383            vec![
 5384                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5385                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5386            ]
 5387        );
 5388    });
 5389
 5390    _ = editor.update(cx, |editor, window, cx| {
 5391        editor.select_line(&SelectLine, window, cx);
 5392        assert_eq!(
 5393            editor.selections.display_ranges(cx),
 5394            vec![
 5395                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5396                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5397            ]
 5398        );
 5399    });
 5400
 5401    _ = editor.update(cx, |editor, window, cx| {
 5402        editor.select_line(&SelectLine, window, cx);
 5403        assert_eq!(
 5404            editor.selections.display_ranges(cx),
 5405            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5406        );
 5407    });
 5408}
 5409
 5410#[gpui::test]
 5411async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5412    init_test(cx, |_| {});
 5413    let mut cx = EditorTestContext::new(cx).await;
 5414
 5415    #[track_caller]
 5416    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5417        cx.set_state(initial_state);
 5418        cx.update_editor(|e, window, cx| {
 5419            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5420        });
 5421        cx.assert_editor_state(expected_state);
 5422    }
 5423
 5424    // Selection starts and ends at the middle of lines, left-to-right
 5425    test(
 5426        &mut cx,
 5427        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5428        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5429    );
 5430    // Same thing, right-to-left
 5431    test(
 5432        &mut cx,
 5433        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5434        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5435    );
 5436
 5437    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5438    test(
 5439        &mut cx,
 5440        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5441        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5442    );
 5443    // Same thing, right-to-left
 5444    test(
 5445        &mut cx,
 5446        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5447        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5448    );
 5449
 5450    // Whole buffer, left-to-right, last line ends with newline
 5451    test(
 5452        &mut cx,
 5453        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5454        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5455    );
 5456    // Same thing, right-to-left
 5457    test(
 5458        &mut cx,
 5459        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5460        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5461    );
 5462
 5463    // Starts at the end of a line, ends at the start of another
 5464    test(
 5465        &mut cx,
 5466        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5467        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5468    );
 5469}
 5470
 5471#[gpui::test]
 5472async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5473    init_test(cx, |_| {});
 5474
 5475    let editor = cx.add_window(|window, cx| {
 5476        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5477        build_editor(buffer, window, cx)
 5478    });
 5479
 5480    // setup
 5481    _ = editor.update(cx, |editor, window, cx| {
 5482        editor.fold_creases(
 5483            vec![
 5484                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5485                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5486                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5487            ],
 5488            true,
 5489            window,
 5490            cx,
 5491        );
 5492        assert_eq!(
 5493            editor.display_text(cx),
 5494            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5495        );
 5496    });
 5497
 5498    _ = editor.update(cx, |editor, window, cx| {
 5499        editor.change_selections(None, window, cx, |s| {
 5500            s.select_display_ranges([
 5501                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5502                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5503                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5504                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5505            ])
 5506        });
 5507        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5508        assert_eq!(
 5509            editor.display_text(cx),
 5510            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5511        );
 5512    });
 5513    EditorTestContext::for_editor(editor, cx)
 5514        .await
 5515        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5516
 5517    _ = editor.update(cx, |editor, window, cx| {
 5518        editor.change_selections(None, window, cx, |s| {
 5519            s.select_display_ranges([
 5520                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5521            ])
 5522        });
 5523        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5524        assert_eq!(
 5525            editor.display_text(cx),
 5526            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5527        );
 5528        assert_eq!(
 5529            editor.selections.display_ranges(cx),
 5530            [
 5531                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5532                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5533                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5534                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5535                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5536                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5537                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5538            ]
 5539        );
 5540    });
 5541    EditorTestContext::for_editor(editor, cx)
 5542        .await
 5543        .assert_editor_state(
 5544            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5545        );
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    cx.set_state(indoc!(
 5555        r#"abc
 5556           defˇghi
 5557
 5558           jk
 5559           nlmo
 5560           "#
 5561    ));
 5562
 5563    cx.update_editor(|editor, window, cx| {
 5564        editor.add_selection_above(&Default::default(), window, cx);
 5565    });
 5566
 5567    cx.assert_editor_state(indoc!(
 5568        r#"abcˇ
 5569           defˇghi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_above(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abcˇ
 5582            defˇghi
 5583
 5584            jk
 5585            nlmo
 5586            "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           defˇghi
 5596
 5597           jk
 5598           nlmo
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.undo_selection(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abcˇ
 5608           defˇghi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.redo_selection(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           defˇghi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    cx.update_editor(|editor, window, cx| {
 5629        editor.add_selection_below(&Default::default(), window, cx);
 5630    });
 5631
 5632    cx.assert_editor_state(indoc!(
 5633        r#"abc
 5634           defˇghi
 5635
 5636           jk
 5637           nlmˇo
 5638           "#
 5639    ));
 5640
 5641    cx.update_editor(|editor, window, cx| {
 5642        editor.add_selection_below(&Default::default(), window, cx);
 5643    });
 5644
 5645    cx.assert_editor_state(indoc!(
 5646        r#"abc
 5647           defˇghi
 5648
 5649           jk
 5650           nlmˇo
 5651           "#
 5652    ));
 5653
 5654    // change selections
 5655    cx.set_state(indoc!(
 5656        r#"abc
 5657           def«ˇg»hi
 5658
 5659           jk
 5660           nlmo
 5661           "#
 5662    ));
 5663
 5664    cx.update_editor(|editor, window, cx| {
 5665        editor.add_selection_below(&Default::default(), window, cx);
 5666    });
 5667
 5668    cx.assert_editor_state(indoc!(
 5669        r#"abc
 5670           def«ˇg»hi
 5671
 5672           jk
 5673           nlm«ˇo»
 5674           "#
 5675    ));
 5676
 5677    cx.update_editor(|editor, window, cx| {
 5678        editor.add_selection_below(&Default::default(), window, cx);
 5679    });
 5680
 5681    cx.assert_editor_state(indoc!(
 5682        r#"abc
 5683           def«ˇg»hi
 5684
 5685           jk
 5686           nlm«ˇo»
 5687           "#
 5688    ));
 5689
 5690    cx.update_editor(|editor, window, cx| {
 5691        editor.add_selection_above(&Default::default(), window, cx);
 5692    });
 5693
 5694    cx.assert_editor_state(indoc!(
 5695        r#"abc
 5696           def«ˇg»hi
 5697
 5698           jk
 5699           nlmo
 5700           "#
 5701    ));
 5702
 5703    cx.update_editor(|editor, window, cx| {
 5704        editor.add_selection_above(&Default::default(), window, cx);
 5705    });
 5706
 5707    cx.assert_editor_state(indoc!(
 5708        r#"abc
 5709           def«ˇg»hi
 5710
 5711           jk
 5712           nlmo
 5713           "#
 5714    ));
 5715
 5716    // Change selections again
 5717    cx.set_state(indoc!(
 5718        r#"a«bc
 5719           defgˇ»hi
 5720
 5721           jk
 5722           nlmo
 5723           "#
 5724    ));
 5725
 5726    cx.update_editor(|editor, window, cx| {
 5727        editor.add_selection_below(&Default::default(), window, cx);
 5728    });
 5729
 5730    cx.assert_editor_state(indoc!(
 5731        r#"a«bcˇ»
 5732           d«efgˇ»hi
 5733
 5734           j«kˇ»
 5735           nlmo
 5736           "#
 5737    ));
 5738
 5739    cx.update_editor(|editor, window, cx| {
 5740        editor.add_selection_below(&Default::default(), window, cx);
 5741    });
 5742    cx.assert_editor_state(indoc!(
 5743        r#"a«bcˇ»
 5744           d«efgˇ»hi
 5745
 5746           j«kˇ»
 5747           n«lmoˇ»
 5748           "#
 5749    ));
 5750    cx.update_editor(|editor, window, cx| {
 5751        editor.add_selection_above(&Default::default(), window, cx);
 5752    });
 5753
 5754    cx.assert_editor_state(indoc!(
 5755        r#"a«bcˇ»
 5756           d«efgˇ»hi
 5757
 5758           j«kˇ»
 5759           nlmo
 5760           "#
 5761    ));
 5762
 5763    // Change selections again
 5764    cx.set_state(indoc!(
 5765        r#"abc
 5766           d«ˇefghi
 5767
 5768           jk
 5769           nlm»o
 5770           "#
 5771    ));
 5772
 5773    cx.update_editor(|editor, window, cx| {
 5774        editor.add_selection_above(&Default::default(), window, cx);
 5775    });
 5776
 5777    cx.assert_editor_state(indoc!(
 5778        r#"a«ˇbc»
 5779           d«ˇef»ghi
 5780
 5781           j«ˇk»
 5782           n«ˇlm»o
 5783           "#
 5784    ));
 5785
 5786    cx.update_editor(|editor, window, cx| {
 5787        editor.add_selection_below(&Default::default(), window, cx);
 5788    });
 5789
 5790    cx.assert_editor_state(indoc!(
 5791        r#"abc
 5792           d«ˇef»ghi
 5793
 5794           j«ˇk»
 5795           n«ˇlm»o
 5796           "#
 5797    ));
 5798}
 5799
 5800#[gpui::test]
 5801async fn test_select_next(cx: &mut TestAppContext) {
 5802    init_test(cx, |_| {});
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5806
 5807    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5808        .unwrap();
 5809    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5810
 5811    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5812        .unwrap();
 5813    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5814
 5815    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5816    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5817
 5818    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5819    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5820
 5821    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5822        .unwrap();
 5823    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5824
 5825    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5826        .unwrap();
 5827    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5828}
 5829
 5830#[gpui::test]
 5831async fn test_select_all_matches(cx: &mut TestAppContext) {
 5832    init_test(cx, |_| {});
 5833
 5834    let mut cx = EditorTestContext::new(cx).await;
 5835
 5836    // Test caret-only selections
 5837    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5838    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5839        .unwrap();
 5840    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5841
 5842    // Test left-to-right selections
 5843    cx.set_state("abc\n«abcˇ»\nabc");
 5844    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5845        .unwrap();
 5846    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5847
 5848    // Test right-to-left selections
 5849    cx.set_state("abc\n«ˇabc»\nabc");
 5850    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5851        .unwrap();
 5852    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5853
 5854    // Test selecting whitespace with caret selection
 5855    cx.set_state("abc\nˇ   abc\nabc");
 5856    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5857        .unwrap();
 5858    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5859
 5860    // Test selecting whitespace with left-to-right selection
 5861    cx.set_state("abc\n«ˇ  »abc\nabc");
 5862    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5863        .unwrap();
 5864    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5865
 5866    // Test no matches with right-to-left selection
 5867    cx.set_state("abc\n«  ˇ»abc\nabc");
 5868    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5869        .unwrap();
 5870    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5871}
 5872
 5873#[gpui::test]
 5874async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5875    init_test(cx, |_| {});
 5876
 5877    let mut cx = EditorTestContext::new(cx).await;
 5878
 5879    let large_body_1 = "\nd".repeat(200);
 5880    let large_body_2 = "\ne".repeat(200);
 5881
 5882    cx.set_state(&format!(
 5883        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5884    ));
 5885    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5886        let scroll_position = editor.scroll_position(cx);
 5887        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5888        scroll_position
 5889    });
 5890
 5891    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5892        .unwrap();
 5893    cx.assert_editor_state(&format!(
 5894        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5895    ));
 5896    let scroll_position_after_selection =
 5897        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5898    assert_eq!(
 5899        initial_scroll_position, scroll_position_after_selection,
 5900        "Scroll position should not change after selecting all matches"
 5901    );
 5902}
 5903
 5904#[gpui::test]
 5905async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5906    init_test(cx, |_| {});
 5907
 5908    let mut cx = EditorLspTestContext::new_rust(
 5909        lsp::ServerCapabilities {
 5910            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5911            ..Default::default()
 5912        },
 5913        cx,
 5914    )
 5915    .await;
 5916
 5917    cx.set_state(indoc! {"
 5918        line 1
 5919        line 2
 5920        linˇe 3
 5921        line 4
 5922        line 5
 5923    "});
 5924
 5925    // Make an edit
 5926    cx.update_editor(|editor, window, cx| {
 5927        editor.handle_input("X", window, cx);
 5928    });
 5929
 5930    // Move cursor to a different position
 5931    cx.update_editor(|editor, window, cx| {
 5932        editor.change_selections(None, window, cx, |s| {
 5933            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5934        });
 5935    });
 5936
 5937    cx.assert_editor_state(indoc! {"
 5938        line 1
 5939        line 2
 5940        linXe 3
 5941        line 4
 5942        liˇne 5
 5943    "});
 5944
 5945    cx.lsp
 5946        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5947            Ok(Some(vec![lsp::TextEdit::new(
 5948                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5949                "PREFIX ".to_string(),
 5950            )]))
 5951        });
 5952
 5953    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5954        .unwrap()
 5955        .await
 5956        .unwrap();
 5957
 5958    cx.assert_editor_state(indoc! {"
 5959        PREFIX line 1
 5960        line 2
 5961        linXe 3
 5962        line 4
 5963        liˇne 5
 5964    "});
 5965
 5966    // Undo formatting
 5967    cx.update_editor(|editor, window, cx| {
 5968        editor.undo(&Default::default(), window, cx);
 5969    });
 5970
 5971    // Verify cursor moved back to position after edit
 5972    cx.assert_editor_state(indoc! {"
 5973        line 1
 5974        line 2
 5975        linXˇe 3
 5976        line 4
 5977        line 5
 5978    "});
 5979}
 5980
 5981#[gpui::test]
 5982async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5983    init_test(cx, |_| {});
 5984
 5985    let mut cx = EditorTestContext::new(cx).await;
 5986    cx.set_state(
 5987        r#"let foo = 2;
 5988lˇet foo = 2;
 5989let fooˇ = 2;
 5990let foo = 2;
 5991let foo = ˇ2;"#,
 5992    );
 5993
 5994    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5995        .unwrap();
 5996    cx.assert_editor_state(
 5997        r#"let foo = 2;
 5998«letˇ» foo = 2;
 5999let «fooˇ» = 2;
 6000let foo = 2;
 6001let foo = «2ˇ»;"#,
 6002    );
 6003
 6004    // noop for multiple selections with different contents
 6005    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6006        .unwrap();
 6007    cx.assert_editor_state(
 6008        r#"let foo = 2;
 6009«letˇ» foo = 2;
 6010let «fooˇ» = 2;
 6011let foo = 2;
 6012let foo = «2ˇ»;"#,
 6013    );
 6014}
 6015
 6016#[gpui::test]
 6017async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6018    init_test(cx, |_| {});
 6019
 6020    let mut cx =
 6021        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6022
 6023    cx.assert_editor_state(indoc! {"
 6024        ˇbbb
 6025        ccc
 6026
 6027        bbb
 6028        ccc
 6029        "});
 6030    cx.dispatch_action(SelectPrevious::default());
 6031    cx.assert_editor_state(indoc! {"
 6032                «bbbˇ»
 6033                ccc
 6034
 6035                bbb
 6036                ccc
 6037                "});
 6038    cx.dispatch_action(SelectPrevious::default());
 6039    cx.assert_editor_state(indoc! {"
 6040                «bbbˇ»
 6041                ccc
 6042
 6043                «bbbˇ»
 6044                ccc
 6045                "});
 6046}
 6047
 6048#[gpui::test]
 6049async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6050    init_test(cx, |_| {});
 6051
 6052    let mut cx = EditorTestContext::new(cx).await;
 6053    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6054
 6055    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6056        .unwrap();
 6057    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6058
 6059    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6060        .unwrap();
 6061    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6062
 6063    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6064    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6065
 6066    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6067    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6068
 6069    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6070        .unwrap();
 6071    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6072
 6073    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6074        .unwrap();
 6075    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 6076
 6077    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6078        .unwrap();
 6079    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6080}
 6081
 6082#[gpui::test]
 6083async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6084    init_test(cx, |_| {});
 6085
 6086    let mut cx = EditorTestContext::new(cx).await;
 6087    cx.set_state("");
 6088
 6089    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6090        .unwrap();
 6091    cx.assert_editor_state("«aˇ»");
 6092    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6093        .unwrap();
 6094    cx.assert_editor_state("«aˇ»");
 6095}
 6096
 6097#[gpui::test]
 6098async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6099    init_test(cx, |_| {});
 6100
 6101    let mut cx = EditorTestContext::new(cx).await;
 6102    cx.set_state(
 6103        r#"let foo = 2;
 6104lˇet foo = 2;
 6105let fooˇ = 2;
 6106let foo = 2;
 6107let foo = ˇ2;"#,
 6108    );
 6109
 6110    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6111        .unwrap();
 6112    cx.assert_editor_state(
 6113        r#"let foo = 2;
 6114«letˇ» foo = 2;
 6115let «fooˇ» = 2;
 6116let foo = 2;
 6117let foo = «2ˇ»;"#,
 6118    );
 6119
 6120    // noop for multiple selections with different contents
 6121    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6122        .unwrap();
 6123    cx.assert_editor_state(
 6124        r#"let foo = 2;
 6125«letˇ» foo = 2;
 6126let «fooˇ» = 2;
 6127let foo = 2;
 6128let foo = «2ˇ»;"#,
 6129    );
 6130}
 6131
 6132#[gpui::test]
 6133async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6134    init_test(cx, |_| {});
 6135
 6136    let mut cx = EditorTestContext::new(cx).await;
 6137    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6138
 6139    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6140        .unwrap();
 6141    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6142
 6143    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6144        .unwrap();
 6145    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6146
 6147    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6148    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6149
 6150    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6151    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6152
 6153    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6154        .unwrap();
 6155    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 6156
 6157    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6158        .unwrap();
 6159    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6160}
 6161
 6162#[gpui::test]
 6163async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6164    init_test(cx, |_| {});
 6165
 6166    let language = Arc::new(Language::new(
 6167        LanguageConfig::default(),
 6168        Some(tree_sitter_rust::LANGUAGE.into()),
 6169    ));
 6170
 6171    let text = r#"
 6172        use mod1::mod2::{mod3, mod4};
 6173
 6174        fn fn_1(param1: bool, param2: &str) {
 6175            let var1 = "text";
 6176        }
 6177    "#
 6178    .unindent();
 6179
 6180    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6181    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6182    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6183
 6184    editor
 6185        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6186        .await;
 6187
 6188    editor.update_in(cx, |editor, window, cx| {
 6189        editor.change_selections(None, window, cx, |s| {
 6190            s.select_display_ranges([
 6191                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6192                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6193                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6194            ]);
 6195        });
 6196        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6197    });
 6198    editor.update(cx, |editor, cx| {
 6199        assert_text_with_selections(
 6200            editor,
 6201            indoc! {r#"
 6202                use mod1::mod2::{mod3, «mod4ˇ»};
 6203
 6204                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6205                    let var1 = "«ˇtext»";
 6206                }
 6207            "#},
 6208            cx,
 6209        );
 6210    });
 6211
 6212    editor.update_in(cx, |editor, window, cx| {
 6213        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6214    });
 6215    editor.update(cx, |editor, cx| {
 6216        assert_text_with_selections(
 6217            editor,
 6218            indoc! {r#"
 6219                use mod1::mod2::«{mod3, mod4}ˇ»;
 6220
 6221                «ˇfn fn_1(param1: bool, param2: &str) {
 6222                    let var1 = "text";
 6223 6224            "#},
 6225            cx,
 6226        );
 6227    });
 6228
 6229    editor.update_in(cx, |editor, window, cx| {
 6230        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6231    });
 6232    assert_eq!(
 6233        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6234        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6235    );
 6236
 6237    // Trying to expand the selected syntax node one more time has no effect.
 6238    editor.update_in(cx, |editor, window, cx| {
 6239        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6240    });
 6241    assert_eq!(
 6242        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6243        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6244    );
 6245
 6246    editor.update_in(cx, |editor, window, cx| {
 6247        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6248    });
 6249    editor.update(cx, |editor, cx| {
 6250        assert_text_with_selections(
 6251            editor,
 6252            indoc! {r#"
 6253                use mod1::mod2::«{mod3, mod4}ˇ»;
 6254
 6255                «ˇfn fn_1(param1: bool, param2: &str) {
 6256                    let var1 = "text";
 6257 6258            "#},
 6259            cx,
 6260        );
 6261    });
 6262
 6263    editor.update_in(cx, |editor, window, cx| {
 6264        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6265    });
 6266    editor.update(cx, |editor, cx| {
 6267        assert_text_with_selections(
 6268            editor,
 6269            indoc! {r#"
 6270                use mod1::mod2::{mod3, «mod4ˇ»};
 6271
 6272                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6273                    let var1 = "«ˇtext»";
 6274                }
 6275            "#},
 6276            cx,
 6277        );
 6278    });
 6279
 6280    editor.update_in(cx, |editor, window, cx| {
 6281        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6282    });
 6283    editor.update(cx, |editor, cx| {
 6284        assert_text_with_selections(
 6285            editor,
 6286            indoc! {r#"
 6287                use mod1::mod2::{mod3, mo«ˇ»d4};
 6288
 6289                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6290                    let var1 = "te«ˇ»xt";
 6291                }
 6292            "#},
 6293            cx,
 6294        );
 6295    });
 6296
 6297    // Trying to shrink the selected syntax node one more time has no effect.
 6298    editor.update_in(cx, |editor, window, cx| {
 6299        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6300    });
 6301    editor.update_in(cx, |editor, _, cx| {
 6302        assert_text_with_selections(
 6303            editor,
 6304            indoc! {r#"
 6305                use mod1::mod2::{mod3, mo«ˇ»d4};
 6306
 6307                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6308                    let var1 = "te«ˇ»xt";
 6309                }
 6310            "#},
 6311            cx,
 6312        );
 6313    });
 6314
 6315    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6316    // a fold.
 6317    editor.update_in(cx, |editor, window, cx| {
 6318        editor.fold_creases(
 6319            vec![
 6320                Crease::simple(
 6321                    Point::new(0, 21)..Point::new(0, 24),
 6322                    FoldPlaceholder::test(),
 6323                ),
 6324                Crease::simple(
 6325                    Point::new(3, 20)..Point::new(3, 22),
 6326                    FoldPlaceholder::test(),
 6327                ),
 6328            ],
 6329            true,
 6330            window,
 6331            cx,
 6332        );
 6333        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6334    });
 6335    editor.update(cx, |editor, cx| {
 6336        assert_text_with_selections(
 6337            editor,
 6338            indoc! {r#"
 6339                use mod1::mod2::«{mod3, mod4}ˇ»;
 6340
 6341                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6342                    let var1 = "«ˇtext»";
 6343                }
 6344            "#},
 6345            cx,
 6346        );
 6347    });
 6348}
 6349
 6350#[gpui::test]
 6351async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6352    init_test(cx, |_| {});
 6353
 6354    let language = Arc::new(Language::new(
 6355        LanguageConfig::default(),
 6356        Some(tree_sitter_rust::LANGUAGE.into()),
 6357    ));
 6358
 6359    let text = r#"
 6360        use mod1::mod2::{mod3, mod4};
 6361
 6362        fn fn_1(param1: bool, param2: &str) {
 6363            let var1 = "hello world";
 6364        }
 6365    "#
 6366    .unindent();
 6367
 6368    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6369    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6370    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6371
 6372    editor
 6373        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6374        .await;
 6375
 6376    // Test 1: Cursor on a letter of a string word
 6377    editor.update_in(cx, |editor, window, cx| {
 6378        editor.change_selections(None, window, cx, |s| {
 6379            s.select_display_ranges([
 6380                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6381            ]);
 6382        });
 6383    });
 6384    editor.update_in(cx, |editor, window, cx| {
 6385        assert_text_with_selections(
 6386            editor,
 6387            indoc! {r#"
 6388                use mod1::mod2::{mod3, mod4};
 6389
 6390                fn fn_1(param1: bool, param2: &str) {
 6391                    let var1 = "hˇello world";
 6392                }
 6393            "#},
 6394            cx,
 6395        );
 6396        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6397        assert_text_with_selections(
 6398            editor,
 6399            indoc! {r#"
 6400                use mod1::mod2::{mod3, mod4};
 6401
 6402                fn fn_1(param1: bool, param2: &str) {
 6403                    let var1 = "«ˇhello» world";
 6404                }
 6405            "#},
 6406            cx,
 6407        );
 6408    });
 6409
 6410    // Test 2: Partial selection within a word
 6411    editor.update_in(cx, |editor, window, cx| {
 6412        editor.change_selections(None, window, cx, |s| {
 6413            s.select_display_ranges([
 6414                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6415            ]);
 6416        });
 6417    });
 6418    editor.update_in(cx, |editor, window, cx| {
 6419        assert_text_with_selections(
 6420            editor,
 6421            indoc! {r#"
 6422                use mod1::mod2::{mod3, mod4};
 6423
 6424                fn fn_1(param1: bool, param2: &str) {
 6425                    let var1 = "h«elˇ»lo world";
 6426                }
 6427            "#},
 6428            cx,
 6429        );
 6430        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6431        assert_text_with_selections(
 6432            editor,
 6433            indoc! {r#"
 6434                use mod1::mod2::{mod3, mod4};
 6435
 6436                fn fn_1(param1: bool, param2: &str) {
 6437                    let var1 = "«ˇhello» world";
 6438                }
 6439            "#},
 6440            cx,
 6441        );
 6442    });
 6443
 6444    // Test 3: Complete word already selected
 6445    editor.update_in(cx, |editor, window, cx| {
 6446        editor.change_selections(None, window, cx, |s| {
 6447            s.select_display_ranges([
 6448                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6449            ]);
 6450        });
 6451    });
 6452    editor.update_in(cx, |editor, window, cx| {
 6453        assert_text_with_selections(
 6454            editor,
 6455            indoc! {r#"
 6456                use mod1::mod2::{mod3, mod4};
 6457
 6458                fn fn_1(param1: bool, param2: &str) {
 6459                    let var1 = "«helloˇ» world";
 6460                }
 6461            "#},
 6462            cx,
 6463        );
 6464        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6465        assert_text_with_selections(
 6466            editor,
 6467            indoc! {r#"
 6468                use mod1::mod2::{mod3, mod4};
 6469
 6470                fn fn_1(param1: bool, param2: &str) {
 6471                    let var1 = "«hello worldˇ»";
 6472                }
 6473            "#},
 6474            cx,
 6475        );
 6476    });
 6477
 6478    // Test 4: Selection spanning across words
 6479    editor.update_in(cx, |editor, window, cx| {
 6480        editor.change_selections(None, window, cx, |s| {
 6481            s.select_display_ranges([
 6482                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6483            ]);
 6484        });
 6485    });
 6486    editor.update_in(cx, |editor, window, cx| {
 6487        assert_text_with_selections(
 6488            editor,
 6489            indoc! {r#"
 6490                use mod1::mod2::{mod3, mod4};
 6491
 6492                fn fn_1(param1: bool, param2: &str) {
 6493                    let var1 = "hel«lo woˇ»rld";
 6494                }
 6495            "#},
 6496            cx,
 6497        );
 6498        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6499        assert_text_with_selections(
 6500            editor,
 6501            indoc! {r#"
 6502                use mod1::mod2::{mod3, mod4};
 6503
 6504                fn fn_1(param1: bool, param2: &str) {
 6505                    let var1 = "«ˇhello world»";
 6506                }
 6507            "#},
 6508            cx,
 6509        );
 6510    });
 6511
 6512    // Test 5: Expansion beyond string
 6513    editor.update_in(cx, |editor, window, cx| {
 6514        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6515        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6516        assert_text_with_selections(
 6517            editor,
 6518            indoc! {r#"
 6519                use mod1::mod2::{mod3, mod4};
 6520
 6521                fn fn_1(param1: bool, param2: &str) {
 6522                    «ˇlet var1 = "hello world";»
 6523                }
 6524            "#},
 6525            cx,
 6526        );
 6527    });
 6528}
 6529
 6530#[gpui::test]
 6531async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6532    init_test(cx, |_| {});
 6533
 6534    let base_text = r#"
 6535        impl A {
 6536            // this is an uncommitted comment
 6537
 6538            fn b() {
 6539                c();
 6540            }
 6541
 6542            // this is another uncommitted comment
 6543
 6544            fn d() {
 6545                // e
 6546                // f
 6547            }
 6548        }
 6549
 6550        fn g() {
 6551            // h
 6552        }
 6553    "#
 6554    .unindent();
 6555
 6556    let text = r#"
 6557        ˇimpl A {
 6558
 6559            fn b() {
 6560                c();
 6561            }
 6562
 6563            fn d() {
 6564                // e
 6565                // f
 6566            }
 6567        }
 6568
 6569        fn g() {
 6570            // h
 6571        }
 6572    "#
 6573    .unindent();
 6574
 6575    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6576    cx.set_state(&text);
 6577    cx.set_head_text(&base_text);
 6578    cx.update_editor(|editor, window, cx| {
 6579        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6580    });
 6581
 6582    cx.assert_state_with_diff(
 6583        "
 6584        ˇimpl A {
 6585      -     // this is an uncommitted comment
 6586
 6587            fn b() {
 6588                c();
 6589            }
 6590
 6591      -     // this is another uncommitted comment
 6592      -
 6593            fn d() {
 6594                // e
 6595                // f
 6596            }
 6597        }
 6598
 6599        fn g() {
 6600            // h
 6601        }
 6602    "
 6603        .unindent(),
 6604    );
 6605
 6606    let expected_display_text = "
 6607        impl A {
 6608            // this is an uncommitted comment
 6609
 6610            fn b() {
 6611 6612            }
 6613
 6614            // this is another uncommitted comment
 6615
 6616            fn d() {
 6617 6618            }
 6619        }
 6620
 6621        fn g() {
 6622 6623        }
 6624        "
 6625    .unindent();
 6626
 6627    cx.update_editor(|editor, window, cx| {
 6628        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6629        assert_eq!(editor.display_text(cx), expected_display_text);
 6630    });
 6631}
 6632
 6633#[gpui::test]
 6634async fn test_autoindent(cx: &mut TestAppContext) {
 6635    init_test(cx, |_| {});
 6636
 6637    let language = Arc::new(
 6638        Language::new(
 6639            LanguageConfig {
 6640                brackets: BracketPairConfig {
 6641                    pairs: vec![
 6642                        BracketPair {
 6643                            start: "{".to_string(),
 6644                            end: "}".to_string(),
 6645                            close: false,
 6646                            surround: false,
 6647                            newline: true,
 6648                        },
 6649                        BracketPair {
 6650                            start: "(".to_string(),
 6651                            end: ")".to_string(),
 6652                            close: false,
 6653                            surround: false,
 6654                            newline: true,
 6655                        },
 6656                    ],
 6657                    ..Default::default()
 6658                },
 6659                ..Default::default()
 6660            },
 6661            Some(tree_sitter_rust::LANGUAGE.into()),
 6662        )
 6663        .with_indents_query(
 6664            r#"
 6665                (_ "(" ")" @end) @indent
 6666                (_ "{" "}" @end) @indent
 6667            "#,
 6668        )
 6669        .unwrap(),
 6670    );
 6671
 6672    let text = "fn a() {}";
 6673
 6674    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6675    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6676    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6677    editor
 6678        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6679        .await;
 6680
 6681    editor.update_in(cx, |editor, window, cx| {
 6682        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6683        editor.newline(&Newline, window, cx);
 6684        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6685        assert_eq!(
 6686            editor.selections.ranges(cx),
 6687            &[
 6688                Point::new(1, 4)..Point::new(1, 4),
 6689                Point::new(3, 4)..Point::new(3, 4),
 6690                Point::new(5, 0)..Point::new(5, 0)
 6691            ]
 6692        );
 6693    });
 6694}
 6695
 6696#[gpui::test]
 6697async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6698    init_test(cx, |_| {});
 6699
 6700    {
 6701        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6702        cx.set_state(indoc! {"
 6703            impl A {
 6704
 6705                fn b() {}
 6706
 6707            «fn c() {
 6708
 6709            }ˇ»
 6710            }
 6711        "});
 6712
 6713        cx.update_editor(|editor, window, cx| {
 6714            editor.autoindent(&Default::default(), window, cx);
 6715        });
 6716
 6717        cx.assert_editor_state(indoc! {"
 6718            impl A {
 6719
 6720                fn b() {}
 6721
 6722                «fn c() {
 6723
 6724                }ˇ»
 6725            }
 6726        "});
 6727    }
 6728
 6729    {
 6730        let mut cx = EditorTestContext::new_multibuffer(
 6731            cx,
 6732            [indoc! { "
 6733                impl A {
 6734                «
 6735                // a
 6736                fn b(){}
 6737                »
 6738                «
 6739                    }
 6740                    fn c(){}
 6741                »
 6742            "}],
 6743        );
 6744
 6745        let buffer = cx.update_editor(|editor, _, cx| {
 6746            let buffer = editor.buffer().update(cx, |buffer, _| {
 6747                buffer.all_buffers().iter().next().unwrap().clone()
 6748            });
 6749            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6750            buffer
 6751        });
 6752
 6753        cx.run_until_parked();
 6754        cx.update_editor(|editor, window, cx| {
 6755            editor.select_all(&Default::default(), window, cx);
 6756            editor.autoindent(&Default::default(), window, cx)
 6757        });
 6758        cx.run_until_parked();
 6759
 6760        cx.update(|_, cx| {
 6761            assert_eq!(
 6762                buffer.read(cx).text(),
 6763                indoc! { "
 6764                    impl A {
 6765
 6766                        // a
 6767                        fn b(){}
 6768
 6769
 6770                    }
 6771                    fn c(){}
 6772
 6773                " }
 6774            )
 6775        });
 6776    }
 6777}
 6778
 6779#[gpui::test]
 6780async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6781    init_test(cx, |_| {});
 6782
 6783    let mut cx = EditorTestContext::new(cx).await;
 6784
 6785    let language = Arc::new(Language::new(
 6786        LanguageConfig {
 6787            brackets: BracketPairConfig {
 6788                pairs: vec![
 6789                    BracketPair {
 6790                        start: "{".to_string(),
 6791                        end: "}".to_string(),
 6792                        close: true,
 6793                        surround: true,
 6794                        newline: true,
 6795                    },
 6796                    BracketPair {
 6797                        start: "(".to_string(),
 6798                        end: ")".to_string(),
 6799                        close: true,
 6800                        surround: true,
 6801                        newline: true,
 6802                    },
 6803                    BracketPair {
 6804                        start: "/*".to_string(),
 6805                        end: " */".to_string(),
 6806                        close: true,
 6807                        surround: true,
 6808                        newline: true,
 6809                    },
 6810                    BracketPair {
 6811                        start: "[".to_string(),
 6812                        end: "]".to_string(),
 6813                        close: false,
 6814                        surround: false,
 6815                        newline: true,
 6816                    },
 6817                    BracketPair {
 6818                        start: "\"".to_string(),
 6819                        end: "\"".to_string(),
 6820                        close: true,
 6821                        surround: true,
 6822                        newline: false,
 6823                    },
 6824                    BracketPair {
 6825                        start: "<".to_string(),
 6826                        end: ">".to_string(),
 6827                        close: false,
 6828                        surround: true,
 6829                        newline: true,
 6830                    },
 6831                ],
 6832                ..Default::default()
 6833            },
 6834            autoclose_before: "})]".to_string(),
 6835            ..Default::default()
 6836        },
 6837        Some(tree_sitter_rust::LANGUAGE.into()),
 6838    ));
 6839
 6840    cx.language_registry().add(language.clone());
 6841    cx.update_buffer(|buffer, cx| {
 6842        buffer.set_language(Some(language), cx);
 6843    });
 6844
 6845    cx.set_state(
 6846        &r#"
 6847            🏀ˇ
 6848            εˇ
 6849            ❤️ˇ
 6850        "#
 6851        .unindent(),
 6852    );
 6853
 6854    // autoclose multiple nested brackets at multiple cursors
 6855    cx.update_editor(|editor, window, cx| {
 6856        editor.handle_input("{", window, cx);
 6857        editor.handle_input("{", window, cx);
 6858        editor.handle_input("{", window, cx);
 6859    });
 6860    cx.assert_editor_state(
 6861        &"
 6862            🏀{{{ˇ}}}
 6863            ε{{{ˇ}}}
 6864            ❤️{{{ˇ}}}
 6865        "
 6866        .unindent(),
 6867    );
 6868
 6869    // insert a different closing bracket
 6870    cx.update_editor(|editor, window, cx| {
 6871        editor.handle_input(")", window, cx);
 6872    });
 6873    cx.assert_editor_state(
 6874        &"
 6875            🏀{{{)ˇ}}}
 6876            ε{{{)ˇ}}}
 6877            ❤️{{{)ˇ}}}
 6878        "
 6879        .unindent(),
 6880    );
 6881
 6882    // skip over the auto-closed brackets when typing a closing bracket
 6883    cx.update_editor(|editor, window, cx| {
 6884        editor.move_right(&MoveRight, window, cx);
 6885        editor.handle_input("}", window, cx);
 6886        editor.handle_input("}", window, cx);
 6887        editor.handle_input("}", window, cx);
 6888    });
 6889    cx.assert_editor_state(
 6890        &"
 6891            🏀{{{)}}}}ˇ
 6892            ε{{{)}}}}ˇ
 6893            ❤️{{{)}}}}ˇ
 6894        "
 6895        .unindent(),
 6896    );
 6897
 6898    // autoclose multi-character pairs
 6899    cx.set_state(
 6900        &"
 6901            ˇ
 6902            ˇ
 6903        "
 6904        .unindent(),
 6905    );
 6906    cx.update_editor(|editor, window, cx| {
 6907        editor.handle_input("/", window, cx);
 6908        editor.handle_input("*", window, cx);
 6909    });
 6910    cx.assert_editor_state(
 6911        &"
 6912            /*ˇ */
 6913            /*ˇ */
 6914        "
 6915        .unindent(),
 6916    );
 6917
 6918    // one cursor autocloses a multi-character pair, one cursor
 6919    // does not autoclose.
 6920    cx.set_state(
 6921        &"
 6922 6923            ˇ
 6924        "
 6925        .unindent(),
 6926    );
 6927    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6928    cx.assert_editor_state(
 6929        &"
 6930            /*ˇ */
 6931 6932        "
 6933        .unindent(),
 6934    );
 6935
 6936    // Don't autoclose if the next character isn't whitespace and isn't
 6937    // listed in the language's "autoclose_before" section.
 6938    cx.set_state("ˇa b");
 6939    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6940    cx.assert_editor_state("{ˇa b");
 6941
 6942    // Don't autoclose if `close` is false for the bracket pair
 6943    cx.set_state("ˇ");
 6944    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6945    cx.assert_editor_state("");
 6946
 6947    // Surround with brackets if text is selected
 6948    cx.set_state("«aˇ» b");
 6949    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6950    cx.assert_editor_state("{«aˇ»} b");
 6951
 6952    // Autoclose when not immediately after a word character
 6953    cx.set_state("a ˇ");
 6954    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6955    cx.assert_editor_state("a \"ˇ\"");
 6956
 6957    // Autoclose pair where the start and end characters are the same
 6958    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6959    cx.assert_editor_state("a \"\"ˇ");
 6960
 6961    // Don't autoclose when immediately after a word character
 6962    cx.set_state("");
 6963    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6964    cx.assert_editor_state("a\"ˇ");
 6965
 6966    // Do autoclose when after a non-word character
 6967    cx.set_state("");
 6968    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6969    cx.assert_editor_state("{\"ˇ\"");
 6970
 6971    // Non identical pairs autoclose regardless of preceding character
 6972    cx.set_state("");
 6973    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6974    cx.assert_editor_state("a{ˇ}");
 6975
 6976    // Don't autoclose pair if autoclose is disabled
 6977    cx.set_state("ˇ");
 6978    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6979    cx.assert_editor_state("");
 6980
 6981    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6982    cx.set_state("«aˇ» b");
 6983    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6984    cx.assert_editor_state("<«aˇ»> b");
 6985}
 6986
 6987#[gpui::test]
 6988async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6989    init_test(cx, |settings| {
 6990        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6991    });
 6992
 6993    let mut cx = EditorTestContext::new(cx).await;
 6994
 6995    let language = Arc::new(Language::new(
 6996        LanguageConfig {
 6997            brackets: BracketPairConfig {
 6998                pairs: vec![
 6999                    BracketPair {
 7000                        start: "{".to_string(),
 7001                        end: "}".to_string(),
 7002                        close: true,
 7003                        surround: true,
 7004                        newline: true,
 7005                    },
 7006                    BracketPair {
 7007                        start: "(".to_string(),
 7008                        end: ")".to_string(),
 7009                        close: true,
 7010                        surround: true,
 7011                        newline: true,
 7012                    },
 7013                    BracketPair {
 7014                        start: "[".to_string(),
 7015                        end: "]".to_string(),
 7016                        close: false,
 7017                        surround: false,
 7018                        newline: true,
 7019                    },
 7020                ],
 7021                ..Default::default()
 7022            },
 7023            autoclose_before: "})]".to_string(),
 7024            ..Default::default()
 7025        },
 7026        Some(tree_sitter_rust::LANGUAGE.into()),
 7027    ));
 7028
 7029    cx.language_registry().add(language.clone());
 7030    cx.update_buffer(|buffer, cx| {
 7031        buffer.set_language(Some(language), cx);
 7032    });
 7033
 7034    cx.set_state(
 7035        &"
 7036            ˇ
 7037            ˇ
 7038            ˇ
 7039        "
 7040        .unindent(),
 7041    );
 7042
 7043    // ensure only matching closing brackets are skipped over
 7044    cx.update_editor(|editor, window, cx| {
 7045        editor.handle_input("}", window, cx);
 7046        editor.move_left(&MoveLeft, window, cx);
 7047        editor.handle_input(")", window, cx);
 7048        editor.move_left(&MoveLeft, window, cx);
 7049    });
 7050    cx.assert_editor_state(
 7051        &"
 7052            ˇ)}
 7053            ˇ)}
 7054            ˇ)}
 7055        "
 7056        .unindent(),
 7057    );
 7058
 7059    // skip-over closing brackets at multiple cursors
 7060    cx.update_editor(|editor, window, cx| {
 7061        editor.handle_input(")", window, cx);
 7062        editor.handle_input("}", window, cx);
 7063    });
 7064    cx.assert_editor_state(
 7065        &"
 7066            )}ˇ
 7067            )}ˇ
 7068            )}ˇ
 7069        "
 7070        .unindent(),
 7071    );
 7072
 7073    // ignore non-close brackets
 7074    cx.update_editor(|editor, window, cx| {
 7075        editor.handle_input("]", window, cx);
 7076        editor.move_left(&MoveLeft, window, cx);
 7077        editor.handle_input("]", window, cx);
 7078    });
 7079    cx.assert_editor_state(
 7080        &"
 7081            )}]ˇ]
 7082            )}]ˇ]
 7083            )}]ˇ]
 7084        "
 7085        .unindent(),
 7086    );
 7087}
 7088
 7089#[gpui::test]
 7090async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7091    init_test(cx, |_| {});
 7092
 7093    let mut cx = EditorTestContext::new(cx).await;
 7094
 7095    let html_language = Arc::new(
 7096        Language::new(
 7097            LanguageConfig {
 7098                name: "HTML".into(),
 7099                brackets: BracketPairConfig {
 7100                    pairs: vec![
 7101                        BracketPair {
 7102                            start: "<".into(),
 7103                            end: ">".into(),
 7104                            close: true,
 7105                            ..Default::default()
 7106                        },
 7107                        BracketPair {
 7108                            start: "{".into(),
 7109                            end: "}".into(),
 7110                            close: true,
 7111                            ..Default::default()
 7112                        },
 7113                        BracketPair {
 7114                            start: "(".into(),
 7115                            end: ")".into(),
 7116                            close: true,
 7117                            ..Default::default()
 7118                        },
 7119                    ],
 7120                    ..Default::default()
 7121                },
 7122                autoclose_before: "})]>".into(),
 7123                ..Default::default()
 7124            },
 7125            Some(tree_sitter_html::LANGUAGE.into()),
 7126        )
 7127        .with_injection_query(
 7128            r#"
 7129            (script_element
 7130                (raw_text) @injection.content
 7131                (#set! injection.language "javascript"))
 7132            "#,
 7133        )
 7134        .unwrap(),
 7135    );
 7136
 7137    let javascript_language = Arc::new(Language::new(
 7138        LanguageConfig {
 7139            name: "JavaScript".into(),
 7140            brackets: BracketPairConfig {
 7141                pairs: vec![
 7142                    BracketPair {
 7143                        start: "/*".into(),
 7144                        end: " */".into(),
 7145                        close: true,
 7146                        ..Default::default()
 7147                    },
 7148                    BracketPair {
 7149                        start: "{".into(),
 7150                        end: "}".into(),
 7151                        close: true,
 7152                        ..Default::default()
 7153                    },
 7154                    BracketPair {
 7155                        start: "(".into(),
 7156                        end: ")".into(),
 7157                        close: true,
 7158                        ..Default::default()
 7159                    },
 7160                ],
 7161                ..Default::default()
 7162            },
 7163            autoclose_before: "})]>".into(),
 7164            ..Default::default()
 7165        },
 7166        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7167    ));
 7168
 7169    cx.language_registry().add(html_language.clone());
 7170    cx.language_registry().add(javascript_language.clone());
 7171
 7172    cx.update_buffer(|buffer, cx| {
 7173        buffer.set_language(Some(html_language), cx);
 7174    });
 7175
 7176    cx.set_state(
 7177        &r#"
 7178            <body>ˇ
 7179                <script>
 7180                    var x = 1;ˇ
 7181                </script>
 7182            </body>ˇ
 7183        "#
 7184        .unindent(),
 7185    );
 7186
 7187    // Precondition: different languages are active at different locations.
 7188    cx.update_editor(|editor, window, cx| {
 7189        let snapshot = editor.snapshot(window, cx);
 7190        let cursors = editor.selections.ranges::<usize>(cx);
 7191        let languages = cursors
 7192            .iter()
 7193            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7194            .collect::<Vec<_>>();
 7195        assert_eq!(
 7196            languages,
 7197            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7198        );
 7199    });
 7200
 7201    // Angle brackets autoclose in HTML, but not JavaScript.
 7202    cx.update_editor(|editor, window, cx| {
 7203        editor.handle_input("<", window, cx);
 7204        editor.handle_input("a", window, cx);
 7205    });
 7206    cx.assert_editor_state(
 7207        &r#"
 7208            <body><aˇ>
 7209                <script>
 7210                    var x = 1;<aˇ
 7211                </script>
 7212            </body><aˇ>
 7213        "#
 7214        .unindent(),
 7215    );
 7216
 7217    // Curly braces and parens autoclose in both HTML and JavaScript.
 7218    cx.update_editor(|editor, window, cx| {
 7219        editor.handle_input(" b=", window, cx);
 7220        editor.handle_input("{", window, cx);
 7221        editor.handle_input("c", window, cx);
 7222        editor.handle_input("(", window, cx);
 7223    });
 7224    cx.assert_editor_state(
 7225        &r#"
 7226            <body><a b={c(ˇ)}>
 7227                <script>
 7228                    var x = 1;<a b={c(ˇ)}
 7229                </script>
 7230            </body><a b={c(ˇ)}>
 7231        "#
 7232        .unindent(),
 7233    );
 7234
 7235    // Brackets that were already autoclosed are skipped.
 7236    cx.update_editor(|editor, window, cx| {
 7237        editor.handle_input(")", window, cx);
 7238        editor.handle_input("d", window, cx);
 7239        editor.handle_input("}", window, cx);
 7240    });
 7241    cx.assert_editor_state(
 7242        &r#"
 7243            <body><a b={c()d}ˇ>
 7244                <script>
 7245                    var x = 1;<a b={c()d}ˇ
 7246                </script>
 7247            </body><a b={c()d}ˇ>
 7248        "#
 7249        .unindent(),
 7250    );
 7251    cx.update_editor(|editor, window, cx| {
 7252        editor.handle_input(">", window, cx);
 7253    });
 7254    cx.assert_editor_state(
 7255        &r#"
 7256            <body><a b={c()d}>ˇ
 7257                <script>
 7258                    var x = 1;<a b={c()d}>ˇ
 7259                </script>
 7260            </body><a b={c()d}>ˇ
 7261        "#
 7262        .unindent(),
 7263    );
 7264
 7265    // Reset
 7266    cx.set_state(
 7267        &r#"
 7268            <body>ˇ
 7269                <script>
 7270                    var x = 1;ˇ
 7271                </script>
 7272            </body>ˇ
 7273        "#
 7274        .unindent(),
 7275    );
 7276
 7277    cx.update_editor(|editor, window, cx| {
 7278        editor.handle_input("<", window, cx);
 7279    });
 7280    cx.assert_editor_state(
 7281        &r#"
 7282            <body><ˇ>
 7283                <script>
 7284                    var x = 1;<ˇ
 7285                </script>
 7286            </body><ˇ>
 7287        "#
 7288        .unindent(),
 7289    );
 7290
 7291    // When backspacing, the closing angle brackets are removed.
 7292    cx.update_editor(|editor, window, cx| {
 7293        editor.backspace(&Backspace, window, cx);
 7294    });
 7295    cx.assert_editor_state(
 7296        &r#"
 7297            <body>ˇ
 7298                <script>
 7299                    var x = 1;ˇ
 7300                </script>
 7301            </body>ˇ
 7302        "#
 7303        .unindent(),
 7304    );
 7305
 7306    // Block comments autoclose in JavaScript, but not HTML.
 7307    cx.update_editor(|editor, window, cx| {
 7308        editor.handle_input("/", window, cx);
 7309        editor.handle_input("*", window, cx);
 7310    });
 7311    cx.assert_editor_state(
 7312        &r#"
 7313            <body>/*ˇ
 7314                <script>
 7315                    var x = 1;/*ˇ */
 7316                </script>
 7317            </body>/*ˇ
 7318        "#
 7319        .unindent(),
 7320    );
 7321}
 7322
 7323#[gpui::test]
 7324async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7325    init_test(cx, |_| {});
 7326
 7327    let mut cx = EditorTestContext::new(cx).await;
 7328
 7329    let rust_language = Arc::new(
 7330        Language::new(
 7331            LanguageConfig {
 7332                name: "Rust".into(),
 7333                brackets: serde_json::from_value(json!([
 7334                    { "start": "{", "end": "}", "close": true, "newline": true },
 7335                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7336                ]))
 7337                .unwrap(),
 7338                autoclose_before: "})]>".into(),
 7339                ..Default::default()
 7340            },
 7341            Some(tree_sitter_rust::LANGUAGE.into()),
 7342        )
 7343        .with_override_query("(string_literal) @string")
 7344        .unwrap(),
 7345    );
 7346
 7347    cx.language_registry().add(rust_language.clone());
 7348    cx.update_buffer(|buffer, cx| {
 7349        buffer.set_language(Some(rust_language), cx);
 7350    });
 7351
 7352    cx.set_state(
 7353        &r#"
 7354            let x = ˇ
 7355        "#
 7356        .unindent(),
 7357    );
 7358
 7359    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7360    cx.update_editor(|editor, window, cx| {
 7361        editor.handle_input("\"", window, cx);
 7362    });
 7363    cx.assert_editor_state(
 7364        &r#"
 7365            let x = "ˇ"
 7366        "#
 7367        .unindent(),
 7368    );
 7369
 7370    // Inserting another quotation mark. The cursor moves across the existing
 7371    // automatically-inserted quotation mark.
 7372    cx.update_editor(|editor, window, cx| {
 7373        editor.handle_input("\"", window, cx);
 7374    });
 7375    cx.assert_editor_state(
 7376        &r#"
 7377            let x = ""ˇ
 7378        "#
 7379        .unindent(),
 7380    );
 7381
 7382    // Reset
 7383    cx.set_state(
 7384        &r#"
 7385            let x = ˇ
 7386        "#
 7387        .unindent(),
 7388    );
 7389
 7390    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7391    cx.update_editor(|editor, window, cx| {
 7392        editor.handle_input("\"", window, cx);
 7393        editor.handle_input(" ", window, cx);
 7394        editor.move_left(&Default::default(), window, cx);
 7395        editor.handle_input("\\", window, cx);
 7396        editor.handle_input("\"", window, cx);
 7397    });
 7398    cx.assert_editor_state(
 7399        &r#"
 7400            let x = "\"ˇ "
 7401        "#
 7402        .unindent(),
 7403    );
 7404
 7405    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7406    // mark. Nothing is inserted.
 7407    cx.update_editor(|editor, window, cx| {
 7408        editor.move_right(&Default::default(), window, cx);
 7409        editor.handle_input("\"", window, cx);
 7410    });
 7411    cx.assert_editor_state(
 7412        &r#"
 7413            let x = "\" "ˇ
 7414        "#
 7415        .unindent(),
 7416    );
 7417}
 7418
 7419#[gpui::test]
 7420async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7421    init_test(cx, |_| {});
 7422
 7423    let language = Arc::new(Language::new(
 7424        LanguageConfig {
 7425            brackets: BracketPairConfig {
 7426                pairs: vec![
 7427                    BracketPair {
 7428                        start: "{".to_string(),
 7429                        end: "}".to_string(),
 7430                        close: true,
 7431                        surround: true,
 7432                        newline: true,
 7433                    },
 7434                    BracketPair {
 7435                        start: "/* ".to_string(),
 7436                        end: "*/".to_string(),
 7437                        close: true,
 7438                        surround: true,
 7439                        ..Default::default()
 7440                    },
 7441                ],
 7442                ..Default::default()
 7443            },
 7444            ..Default::default()
 7445        },
 7446        Some(tree_sitter_rust::LANGUAGE.into()),
 7447    ));
 7448
 7449    let text = r#"
 7450        a
 7451        b
 7452        c
 7453    "#
 7454    .unindent();
 7455
 7456    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7457    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7458    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7459    editor
 7460        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7461        .await;
 7462
 7463    editor.update_in(cx, |editor, window, cx| {
 7464        editor.change_selections(None, window, cx, |s| {
 7465            s.select_display_ranges([
 7466                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7467                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7468                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7469            ])
 7470        });
 7471
 7472        editor.handle_input("{", window, cx);
 7473        editor.handle_input("{", window, cx);
 7474        editor.handle_input("{", window, cx);
 7475        assert_eq!(
 7476            editor.text(cx),
 7477            "
 7478                {{{a}}}
 7479                {{{b}}}
 7480                {{{c}}}
 7481            "
 7482            .unindent()
 7483        );
 7484        assert_eq!(
 7485            editor.selections.display_ranges(cx),
 7486            [
 7487                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7488                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7489                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7490            ]
 7491        );
 7492
 7493        editor.undo(&Undo, window, cx);
 7494        editor.undo(&Undo, window, cx);
 7495        editor.undo(&Undo, window, cx);
 7496        assert_eq!(
 7497            editor.text(cx),
 7498            "
 7499                a
 7500                b
 7501                c
 7502            "
 7503            .unindent()
 7504        );
 7505        assert_eq!(
 7506            editor.selections.display_ranges(cx),
 7507            [
 7508                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7509                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7510                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7511            ]
 7512        );
 7513
 7514        // Ensure inserting the first character of a multi-byte bracket pair
 7515        // doesn't surround the selections with the bracket.
 7516        editor.handle_input("/", window, cx);
 7517        assert_eq!(
 7518            editor.text(cx),
 7519            "
 7520                /
 7521                /
 7522                /
 7523            "
 7524            .unindent()
 7525        );
 7526        assert_eq!(
 7527            editor.selections.display_ranges(cx),
 7528            [
 7529                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7530                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7531                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7532            ]
 7533        );
 7534
 7535        editor.undo(&Undo, window, cx);
 7536        assert_eq!(
 7537            editor.text(cx),
 7538            "
 7539                a
 7540                b
 7541                c
 7542            "
 7543            .unindent()
 7544        );
 7545        assert_eq!(
 7546            editor.selections.display_ranges(cx),
 7547            [
 7548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7549                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7550                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7551            ]
 7552        );
 7553
 7554        // Ensure inserting the last character of a multi-byte bracket pair
 7555        // doesn't surround the selections with the bracket.
 7556        editor.handle_input("*", window, cx);
 7557        assert_eq!(
 7558            editor.text(cx),
 7559            "
 7560                *
 7561                *
 7562                *
 7563            "
 7564            .unindent()
 7565        );
 7566        assert_eq!(
 7567            editor.selections.display_ranges(cx),
 7568            [
 7569                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7570                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7571                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7572            ]
 7573        );
 7574    });
 7575}
 7576
 7577#[gpui::test]
 7578async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7579    init_test(cx, |_| {});
 7580
 7581    let language = Arc::new(Language::new(
 7582        LanguageConfig {
 7583            brackets: BracketPairConfig {
 7584                pairs: vec![BracketPair {
 7585                    start: "{".to_string(),
 7586                    end: "}".to_string(),
 7587                    close: true,
 7588                    surround: true,
 7589                    newline: true,
 7590                }],
 7591                ..Default::default()
 7592            },
 7593            autoclose_before: "}".to_string(),
 7594            ..Default::default()
 7595        },
 7596        Some(tree_sitter_rust::LANGUAGE.into()),
 7597    ));
 7598
 7599    let text = r#"
 7600        a
 7601        b
 7602        c
 7603    "#
 7604    .unindent();
 7605
 7606    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7607    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7608    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7609    editor
 7610        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7611        .await;
 7612
 7613    editor.update_in(cx, |editor, window, cx| {
 7614        editor.change_selections(None, window, cx, |s| {
 7615            s.select_ranges([
 7616                Point::new(0, 1)..Point::new(0, 1),
 7617                Point::new(1, 1)..Point::new(1, 1),
 7618                Point::new(2, 1)..Point::new(2, 1),
 7619            ])
 7620        });
 7621
 7622        editor.handle_input("{", window, cx);
 7623        editor.handle_input("{", window, cx);
 7624        editor.handle_input("_", window, cx);
 7625        assert_eq!(
 7626            editor.text(cx),
 7627            "
 7628                a{{_}}
 7629                b{{_}}
 7630                c{{_}}
 7631            "
 7632            .unindent()
 7633        );
 7634        assert_eq!(
 7635            editor.selections.ranges::<Point>(cx),
 7636            [
 7637                Point::new(0, 4)..Point::new(0, 4),
 7638                Point::new(1, 4)..Point::new(1, 4),
 7639                Point::new(2, 4)..Point::new(2, 4)
 7640            ]
 7641        );
 7642
 7643        editor.backspace(&Default::default(), window, cx);
 7644        editor.backspace(&Default::default(), window, cx);
 7645        assert_eq!(
 7646            editor.text(cx),
 7647            "
 7648                a{}
 7649                b{}
 7650                c{}
 7651            "
 7652            .unindent()
 7653        );
 7654        assert_eq!(
 7655            editor.selections.ranges::<Point>(cx),
 7656            [
 7657                Point::new(0, 2)..Point::new(0, 2),
 7658                Point::new(1, 2)..Point::new(1, 2),
 7659                Point::new(2, 2)..Point::new(2, 2)
 7660            ]
 7661        );
 7662
 7663        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7664        assert_eq!(
 7665            editor.text(cx),
 7666            "
 7667                a
 7668                b
 7669                c
 7670            "
 7671            .unindent()
 7672        );
 7673        assert_eq!(
 7674            editor.selections.ranges::<Point>(cx),
 7675            [
 7676                Point::new(0, 1)..Point::new(0, 1),
 7677                Point::new(1, 1)..Point::new(1, 1),
 7678                Point::new(2, 1)..Point::new(2, 1)
 7679            ]
 7680        );
 7681    });
 7682}
 7683
 7684#[gpui::test]
 7685async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7686    init_test(cx, |settings| {
 7687        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7688    });
 7689
 7690    let mut cx = EditorTestContext::new(cx).await;
 7691
 7692    let language = Arc::new(Language::new(
 7693        LanguageConfig {
 7694            brackets: BracketPairConfig {
 7695                pairs: vec![
 7696                    BracketPair {
 7697                        start: "{".to_string(),
 7698                        end: "}".to_string(),
 7699                        close: true,
 7700                        surround: true,
 7701                        newline: true,
 7702                    },
 7703                    BracketPair {
 7704                        start: "(".to_string(),
 7705                        end: ")".to_string(),
 7706                        close: true,
 7707                        surround: true,
 7708                        newline: true,
 7709                    },
 7710                    BracketPair {
 7711                        start: "[".to_string(),
 7712                        end: "]".to_string(),
 7713                        close: false,
 7714                        surround: true,
 7715                        newline: true,
 7716                    },
 7717                ],
 7718                ..Default::default()
 7719            },
 7720            autoclose_before: "})]".to_string(),
 7721            ..Default::default()
 7722        },
 7723        Some(tree_sitter_rust::LANGUAGE.into()),
 7724    ));
 7725
 7726    cx.language_registry().add(language.clone());
 7727    cx.update_buffer(|buffer, cx| {
 7728        buffer.set_language(Some(language), cx);
 7729    });
 7730
 7731    cx.set_state(
 7732        &"
 7733            {(ˇ)}
 7734            [[ˇ]]
 7735            {(ˇ)}
 7736        "
 7737        .unindent(),
 7738    );
 7739
 7740    cx.update_editor(|editor, window, cx| {
 7741        editor.backspace(&Default::default(), window, cx);
 7742        editor.backspace(&Default::default(), window, cx);
 7743    });
 7744
 7745    cx.assert_editor_state(
 7746        &"
 7747            ˇ
 7748            ˇ]]
 7749            ˇ
 7750        "
 7751        .unindent(),
 7752    );
 7753
 7754    cx.update_editor(|editor, window, cx| {
 7755        editor.handle_input("{", window, cx);
 7756        editor.handle_input("{", window, cx);
 7757        editor.move_right(&MoveRight, window, cx);
 7758        editor.move_right(&MoveRight, window, cx);
 7759        editor.move_left(&MoveLeft, window, cx);
 7760        editor.move_left(&MoveLeft, window, cx);
 7761        editor.backspace(&Default::default(), window, cx);
 7762    });
 7763
 7764    cx.assert_editor_state(
 7765        &"
 7766            {ˇ}
 7767            {ˇ}]]
 7768            {ˇ}
 7769        "
 7770        .unindent(),
 7771    );
 7772
 7773    cx.update_editor(|editor, window, cx| {
 7774        editor.backspace(&Default::default(), window, cx);
 7775    });
 7776
 7777    cx.assert_editor_state(
 7778        &"
 7779            ˇ
 7780            ˇ]]
 7781            ˇ
 7782        "
 7783        .unindent(),
 7784    );
 7785}
 7786
 7787#[gpui::test]
 7788async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7789    init_test(cx, |_| {});
 7790
 7791    let language = Arc::new(Language::new(
 7792        LanguageConfig::default(),
 7793        Some(tree_sitter_rust::LANGUAGE.into()),
 7794    ));
 7795
 7796    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7797    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7798    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7799    editor
 7800        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7801        .await;
 7802
 7803    editor.update_in(cx, |editor, window, cx| {
 7804        editor.set_auto_replace_emoji_shortcode(true);
 7805
 7806        editor.handle_input("Hello ", window, cx);
 7807        editor.handle_input(":wave", window, cx);
 7808        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7809
 7810        editor.handle_input(":", window, cx);
 7811        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7812
 7813        editor.handle_input(" :smile", window, cx);
 7814        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7815
 7816        editor.handle_input(":", window, cx);
 7817        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7818
 7819        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7820        editor.handle_input(":wave", window, cx);
 7821        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7822
 7823        editor.handle_input(":", window, cx);
 7824        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7825
 7826        editor.handle_input(":1", window, cx);
 7827        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7828
 7829        editor.handle_input(":", window, cx);
 7830        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7831
 7832        // Ensure shortcode does not get replaced when it is part of a word
 7833        editor.handle_input(" Test:wave", window, cx);
 7834        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7835
 7836        editor.handle_input(":", window, cx);
 7837        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7838
 7839        editor.set_auto_replace_emoji_shortcode(false);
 7840
 7841        // Ensure shortcode does not get replaced when auto replace is off
 7842        editor.handle_input(" :wave", window, cx);
 7843        assert_eq!(
 7844            editor.text(cx),
 7845            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7846        );
 7847
 7848        editor.handle_input(":", window, cx);
 7849        assert_eq!(
 7850            editor.text(cx),
 7851            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7852        );
 7853    });
 7854}
 7855
 7856#[gpui::test]
 7857async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7858    init_test(cx, |_| {});
 7859
 7860    let (text, insertion_ranges) = marked_text_ranges(
 7861        indoc! {"
 7862            ˇ
 7863        "},
 7864        false,
 7865    );
 7866
 7867    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7868    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7869
 7870    _ = editor.update_in(cx, |editor, window, cx| {
 7871        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7872
 7873        editor
 7874            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7875            .unwrap();
 7876
 7877        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7878            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7879            assert_eq!(editor.text(cx), expected_text);
 7880            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7881        }
 7882
 7883        assert(
 7884            editor,
 7885            cx,
 7886            indoc! {"
 7887            type «» =•
 7888            "},
 7889        );
 7890
 7891        assert!(editor.context_menu_visible(), "There should be a matches");
 7892    });
 7893}
 7894
 7895#[gpui::test]
 7896async fn test_snippets(cx: &mut TestAppContext) {
 7897    init_test(cx, |_| {});
 7898
 7899    let (text, insertion_ranges) = marked_text_ranges(
 7900        indoc! {"
 7901            a.ˇ b
 7902            a.ˇ b
 7903            a.ˇ b
 7904        "},
 7905        false,
 7906    );
 7907
 7908    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7909    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7910
 7911    editor.update_in(cx, |editor, window, cx| {
 7912        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7913
 7914        editor
 7915            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7916            .unwrap();
 7917
 7918        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7919            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7920            assert_eq!(editor.text(cx), expected_text);
 7921            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7922        }
 7923
 7924        assert(
 7925            editor,
 7926            cx,
 7927            indoc! {"
 7928                a.f(«one», two, «three») b
 7929                a.f(«one», two, «three») b
 7930                a.f(«one», two, «three») b
 7931            "},
 7932        );
 7933
 7934        // Can't move earlier than the first tab stop
 7935        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7936        assert(
 7937            editor,
 7938            cx,
 7939            indoc! {"
 7940                a.f(«one», two, «three») b
 7941                a.f(«one», two, «three») b
 7942                a.f(«one», two, «three») b
 7943            "},
 7944        );
 7945
 7946        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7947        assert(
 7948            editor,
 7949            cx,
 7950            indoc! {"
 7951                a.f(one, «two», three) b
 7952                a.f(one, «two», three) b
 7953                a.f(one, «two», three) b
 7954            "},
 7955        );
 7956
 7957        editor.move_to_prev_snippet_tabstop(window, cx);
 7958        assert(
 7959            editor,
 7960            cx,
 7961            indoc! {"
 7962                a.f(«one», two, «three») b
 7963                a.f(«one», two, «three») b
 7964                a.f(«one», two, «three») b
 7965            "},
 7966        );
 7967
 7968        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7969        assert(
 7970            editor,
 7971            cx,
 7972            indoc! {"
 7973                a.f(one, «two», three) b
 7974                a.f(one, «two», three) b
 7975                a.f(one, «two», three) b
 7976            "},
 7977        );
 7978        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7979        assert(
 7980            editor,
 7981            cx,
 7982            indoc! {"
 7983                a.f(one, two, three)ˇ b
 7984                a.f(one, two, three)ˇ b
 7985                a.f(one, two, three)ˇ b
 7986            "},
 7987        );
 7988
 7989        // As soon as the last tab stop is reached, snippet state is gone
 7990        editor.move_to_prev_snippet_tabstop(window, cx);
 7991        assert(
 7992            editor,
 7993            cx,
 7994            indoc! {"
 7995                a.f(one, two, three)ˇ b
 7996                a.f(one, two, three)ˇ b
 7997                a.f(one, two, three)ˇ b
 7998            "},
 7999        );
 8000    });
 8001}
 8002
 8003#[gpui::test]
 8004async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8005    init_test(cx, |_| {});
 8006
 8007    let fs = FakeFs::new(cx.executor());
 8008    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8009
 8010    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8011
 8012    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8013    language_registry.add(rust_lang());
 8014    let mut fake_servers = language_registry.register_fake_lsp(
 8015        "Rust",
 8016        FakeLspAdapter {
 8017            capabilities: lsp::ServerCapabilities {
 8018                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8019                ..Default::default()
 8020            },
 8021            ..Default::default()
 8022        },
 8023    );
 8024
 8025    let buffer = project
 8026        .update(cx, |project, cx| {
 8027            project.open_local_buffer(path!("/file.rs"), cx)
 8028        })
 8029        .await
 8030        .unwrap();
 8031
 8032    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8033    let (editor, cx) = cx.add_window_view(|window, cx| {
 8034        build_editor_with_project(project.clone(), buffer, window, cx)
 8035    });
 8036    editor.update_in(cx, |editor, window, cx| {
 8037        editor.set_text("one\ntwo\nthree\n", window, cx)
 8038    });
 8039    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8040
 8041    cx.executor().start_waiting();
 8042    let fake_server = fake_servers.next().await.unwrap();
 8043
 8044    {
 8045        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8046            move |params, _| async move {
 8047                assert_eq!(
 8048                    params.text_document.uri,
 8049                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8050                );
 8051                assert_eq!(params.options.tab_size, 4);
 8052                Ok(Some(vec![lsp::TextEdit::new(
 8053                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8054                    ", ".to_string(),
 8055                )]))
 8056            },
 8057        );
 8058        let save = editor
 8059            .update_in(cx, |editor, window, cx| {
 8060                editor.save(true, project.clone(), window, cx)
 8061            })
 8062            .unwrap();
 8063        cx.executor().start_waiting();
 8064        save.await;
 8065
 8066        assert_eq!(
 8067            editor.update(cx, |editor, cx| editor.text(cx)),
 8068            "one, two\nthree\n"
 8069        );
 8070        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8071    }
 8072
 8073    {
 8074        editor.update_in(cx, |editor, window, cx| {
 8075            editor.set_text("one\ntwo\nthree\n", window, cx)
 8076        });
 8077        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8078
 8079        // Ensure we can still save even if formatting hangs.
 8080        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8081            move |params, _| async move {
 8082                assert_eq!(
 8083                    params.text_document.uri,
 8084                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8085                );
 8086                futures::future::pending::<()>().await;
 8087                unreachable!()
 8088            },
 8089        );
 8090        let save = editor
 8091            .update_in(cx, |editor, window, cx| {
 8092                editor.save(true, project.clone(), window, cx)
 8093            })
 8094            .unwrap();
 8095        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8096        cx.executor().start_waiting();
 8097        save.await;
 8098        assert_eq!(
 8099            editor.update(cx, |editor, cx| editor.text(cx)),
 8100            "one\ntwo\nthree\n"
 8101        );
 8102    }
 8103
 8104    // For non-dirty buffer, no formatting request should be sent
 8105    {
 8106        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8107
 8108        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8109            panic!("Should not be invoked on non-dirty buffer");
 8110        });
 8111        let save = editor
 8112            .update_in(cx, |editor, window, cx| {
 8113                editor.save(true, project.clone(), window, cx)
 8114            })
 8115            .unwrap();
 8116        cx.executor().start_waiting();
 8117        save.await;
 8118    }
 8119
 8120    // Set rust language override and assert overridden tabsize is sent to language server
 8121    update_test_language_settings(cx, |settings| {
 8122        settings.languages.insert(
 8123            "Rust".into(),
 8124            LanguageSettingsContent {
 8125                tab_size: NonZeroU32::new(8),
 8126                ..Default::default()
 8127            },
 8128        );
 8129    });
 8130
 8131    {
 8132        editor.update_in(cx, |editor, window, cx| {
 8133            editor.set_text("somehting_new\n", window, cx)
 8134        });
 8135        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8136        let _formatting_request_signal = fake_server
 8137            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8138                assert_eq!(
 8139                    params.text_document.uri,
 8140                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8141                );
 8142                assert_eq!(params.options.tab_size, 8);
 8143                Ok(Some(vec![]))
 8144            });
 8145        let save = editor
 8146            .update_in(cx, |editor, window, cx| {
 8147                editor.save(true, project.clone(), window, cx)
 8148            })
 8149            .unwrap();
 8150        cx.executor().start_waiting();
 8151        save.await;
 8152    }
 8153}
 8154
 8155#[gpui::test]
 8156async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8157    init_test(cx, |_| {});
 8158
 8159    let cols = 4;
 8160    let rows = 10;
 8161    let sample_text_1 = sample_text(rows, cols, 'a');
 8162    assert_eq!(
 8163        sample_text_1,
 8164        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8165    );
 8166    let sample_text_2 = sample_text(rows, cols, 'l');
 8167    assert_eq!(
 8168        sample_text_2,
 8169        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8170    );
 8171    let sample_text_3 = sample_text(rows, cols, 'v');
 8172    assert_eq!(
 8173        sample_text_3,
 8174        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8175    );
 8176
 8177    let fs = FakeFs::new(cx.executor());
 8178    fs.insert_tree(
 8179        path!("/a"),
 8180        json!({
 8181            "main.rs": sample_text_1,
 8182            "other.rs": sample_text_2,
 8183            "lib.rs": sample_text_3,
 8184        }),
 8185    )
 8186    .await;
 8187
 8188    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8189    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8190    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8191
 8192    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8193    language_registry.add(rust_lang());
 8194    let mut fake_servers = language_registry.register_fake_lsp(
 8195        "Rust",
 8196        FakeLspAdapter {
 8197            capabilities: lsp::ServerCapabilities {
 8198                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8199                ..Default::default()
 8200            },
 8201            ..Default::default()
 8202        },
 8203    );
 8204
 8205    let worktree = project.update(cx, |project, cx| {
 8206        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8207        assert_eq!(worktrees.len(), 1);
 8208        worktrees.pop().unwrap()
 8209    });
 8210    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8211
 8212    let buffer_1 = project
 8213        .update(cx, |project, cx| {
 8214            project.open_buffer((worktree_id, "main.rs"), cx)
 8215        })
 8216        .await
 8217        .unwrap();
 8218    let buffer_2 = project
 8219        .update(cx, |project, cx| {
 8220            project.open_buffer((worktree_id, "other.rs"), cx)
 8221        })
 8222        .await
 8223        .unwrap();
 8224    let buffer_3 = project
 8225        .update(cx, |project, cx| {
 8226            project.open_buffer((worktree_id, "lib.rs"), cx)
 8227        })
 8228        .await
 8229        .unwrap();
 8230
 8231    let multi_buffer = cx.new(|cx| {
 8232        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8233        multi_buffer.push_excerpts(
 8234            buffer_1.clone(),
 8235            [
 8236                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8237                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8238                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8239            ],
 8240            cx,
 8241        );
 8242        multi_buffer.push_excerpts(
 8243            buffer_2.clone(),
 8244            [
 8245                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8246                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8247                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8248            ],
 8249            cx,
 8250        );
 8251        multi_buffer.push_excerpts(
 8252            buffer_3.clone(),
 8253            [
 8254                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8255                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8256                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8257            ],
 8258            cx,
 8259        );
 8260        multi_buffer
 8261    });
 8262    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8263        Editor::new(
 8264            EditorMode::full(),
 8265            multi_buffer,
 8266            Some(project.clone()),
 8267            window,
 8268            cx,
 8269        )
 8270    });
 8271
 8272    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8273        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8274            s.select_ranges(Some(1..2))
 8275        });
 8276        editor.insert("|one|two|three|", window, cx);
 8277    });
 8278    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8279    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8280        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8281            s.select_ranges(Some(60..70))
 8282        });
 8283        editor.insert("|four|five|six|", window, cx);
 8284    });
 8285    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8286
 8287    // First two buffers should be edited, but not the third one.
 8288    assert_eq!(
 8289        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8290        "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}",
 8291    );
 8292    buffer_1.update(cx, |buffer, _| {
 8293        assert!(buffer.is_dirty());
 8294        assert_eq!(
 8295            buffer.text(),
 8296            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8297        )
 8298    });
 8299    buffer_2.update(cx, |buffer, _| {
 8300        assert!(buffer.is_dirty());
 8301        assert_eq!(
 8302            buffer.text(),
 8303            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8304        )
 8305    });
 8306    buffer_3.update(cx, |buffer, _| {
 8307        assert!(!buffer.is_dirty());
 8308        assert_eq!(buffer.text(), sample_text_3,)
 8309    });
 8310    cx.executor().run_until_parked();
 8311
 8312    cx.executor().start_waiting();
 8313    let save = multi_buffer_editor
 8314        .update_in(cx, |editor, window, cx| {
 8315            editor.save(true, project.clone(), window, cx)
 8316        })
 8317        .unwrap();
 8318
 8319    let fake_server = fake_servers.next().await.unwrap();
 8320    fake_server
 8321        .server
 8322        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8323            Ok(Some(vec![lsp::TextEdit::new(
 8324                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8325                format!("[{} formatted]", params.text_document.uri),
 8326            )]))
 8327        })
 8328        .detach();
 8329    save.await;
 8330
 8331    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8332    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8333    assert_eq!(
 8334        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8335        uri!(
 8336            "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}"
 8337        ),
 8338    );
 8339    buffer_1.update(cx, |buffer, _| {
 8340        assert!(!buffer.is_dirty());
 8341        assert_eq!(
 8342            buffer.text(),
 8343            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8344        )
 8345    });
 8346    buffer_2.update(cx, |buffer, _| {
 8347        assert!(!buffer.is_dirty());
 8348        assert_eq!(
 8349            buffer.text(),
 8350            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8351        )
 8352    });
 8353    buffer_3.update(cx, |buffer, _| {
 8354        assert!(!buffer.is_dirty());
 8355        assert_eq!(buffer.text(), sample_text_3,)
 8356    });
 8357}
 8358
 8359#[gpui::test]
 8360async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8361    init_test(cx, |_| {});
 8362
 8363    let fs = FakeFs::new(cx.executor());
 8364    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8365
 8366    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8367
 8368    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8369    language_registry.add(rust_lang());
 8370    let mut fake_servers = language_registry.register_fake_lsp(
 8371        "Rust",
 8372        FakeLspAdapter {
 8373            capabilities: lsp::ServerCapabilities {
 8374                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8375                ..Default::default()
 8376            },
 8377            ..Default::default()
 8378        },
 8379    );
 8380
 8381    let buffer = project
 8382        .update(cx, |project, cx| {
 8383            project.open_local_buffer(path!("/file.rs"), cx)
 8384        })
 8385        .await
 8386        .unwrap();
 8387
 8388    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8389    let (editor, cx) = cx.add_window_view(|window, cx| {
 8390        build_editor_with_project(project.clone(), buffer, window, cx)
 8391    });
 8392    editor.update_in(cx, |editor, window, cx| {
 8393        editor.set_text("one\ntwo\nthree\n", window, cx)
 8394    });
 8395    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8396
 8397    cx.executor().start_waiting();
 8398    let fake_server = fake_servers.next().await.unwrap();
 8399
 8400    let save = editor
 8401        .update_in(cx, |editor, window, cx| {
 8402            editor.save(true, project.clone(), window, cx)
 8403        })
 8404        .unwrap();
 8405    fake_server
 8406        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8407            assert_eq!(
 8408                params.text_document.uri,
 8409                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8410            );
 8411            assert_eq!(params.options.tab_size, 4);
 8412            Ok(Some(vec![lsp::TextEdit::new(
 8413                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8414                ", ".to_string(),
 8415            )]))
 8416        })
 8417        .next()
 8418        .await;
 8419    cx.executor().start_waiting();
 8420    save.await;
 8421    assert_eq!(
 8422        editor.update(cx, |editor, cx| editor.text(cx)),
 8423        "one, two\nthree\n"
 8424    );
 8425    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8426
 8427    editor.update_in(cx, |editor, window, cx| {
 8428        editor.set_text("one\ntwo\nthree\n", window, cx)
 8429    });
 8430    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8431
 8432    // Ensure we can still save even if formatting hangs.
 8433    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8434        move |params, _| async move {
 8435            assert_eq!(
 8436                params.text_document.uri,
 8437                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8438            );
 8439            futures::future::pending::<()>().await;
 8440            unreachable!()
 8441        },
 8442    );
 8443    let save = editor
 8444        .update_in(cx, |editor, window, cx| {
 8445            editor.save(true, project.clone(), window, cx)
 8446        })
 8447        .unwrap();
 8448    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8449    cx.executor().start_waiting();
 8450    save.await;
 8451    assert_eq!(
 8452        editor.update(cx, |editor, cx| editor.text(cx)),
 8453        "one\ntwo\nthree\n"
 8454    );
 8455    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8456
 8457    // For non-dirty buffer, no formatting request should be sent
 8458    let save = editor
 8459        .update_in(cx, |editor, window, cx| {
 8460            editor.save(true, project.clone(), window, cx)
 8461        })
 8462        .unwrap();
 8463    let _pending_format_request = fake_server
 8464        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8465            panic!("Should not be invoked on non-dirty buffer");
 8466        })
 8467        .next();
 8468    cx.executor().start_waiting();
 8469    save.await;
 8470
 8471    // Set Rust language override and assert overridden tabsize is sent to language server
 8472    update_test_language_settings(cx, |settings| {
 8473        settings.languages.insert(
 8474            "Rust".into(),
 8475            LanguageSettingsContent {
 8476                tab_size: NonZeroU32::new(8),
 8477                ..Default::default()
 8478            },
 8479        );
 8480    });
 8481
 8482    editor.update_in(cx, |editor, window, cx| {
 8483        editor.set_text("somehting_new\n", window, cx)
 8484    });
 8485    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8486    let save = editor
 8487        .update_in(cx, |editor, window, cx| {
 8488            editor.save(true, project.clone(), window, cx)
 8489        })
 8490        .unwrap();
 8491    fake_server
 8492        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8493            assert_eq!(
 8494                params.text_document.uri,
 8495                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8496            );
 8497            assert_eq!(params.options.tab_size, 8);
 8498            Ok(Some(vec![]))
 8499        })
 8500        .next()
 8501        .await;
 8502    cx.executor().start_waiting();
 8503    save.await;
 8504}
 8505
 8506#[gpui::test]
 8507async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8508    init_test(cx, |settings| {
 8509        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8510            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8511        ))
 8512    });
 8513
 8514    let fs = FakeFs::new(cx.executor());
 8515    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8516
 8517    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8518
 8519    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8520    language_registry.add(Arc::new(Language::new(
 8521        LanguageConfig {
 8522            name: "Rust".into(),
 8523            matcher: LanguageMatcher {
 8524                path_suffixes: vec!["rs".to_string()],
 8525                ..Default::default()
 8526            },
 8527            ..LanguageConfig::default()
 8528        },
 8529        Some(tree_sitter_rust::LANGUAGE.into()),
 8530    )));
 8531    update_test_language_settings(cx, |settings| {
 8532        // Enable Prettier formatting for the same buffer, and ensure
 8533        // LSP is called instead of Prettier.
 8534        settings.defaults.prettier = Some(PrettierSettings {
 8535            allowed: true,
 8536            ..PrettierSettings::default()
 8537        });
 8538    });
 8539    let mut fake_servers = language_registry.register_fake_lsp(
 8540        "Rust",
 8541        FakeLspAdapter {
 8542            capabilities: lsp::ServerCapabilities {
 8543                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8544                ..Default::default()
 8545            },
 8546            ..Default::default()
 8547        },
 8548    );
 8549
 8550    let buffer = project
 8551        .update(cx, |project, cx| {
 8552            project.open_local_buffer(path!("/file.rs"), cx)
 8553        })
 8554        .await
 8555        .unwrap();
 8556
 8557    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8558    let (editor, cx) = cx.add_window_view(|window, cx| {
 8559        build_editor_with_project(project.clone(), buffer, window, cx)
 8560    });
 8561    editor.update_in(cx, |editor, window, cx| {
 8562        editor.set_text("one\ntwo\nthree\n", window, cx)
 8563    });
 8564
 8565    cx.executor().start_waiting();
 8566    let fake_server = fake_servers.next().await.unwrap();
 8567
 8568    let format = editor
 8569        .update_in(cx, |editor, window, cx| {
 8570            editor.perform_format(
 8571                project.clone(),
 8572                FormatTrigger::Manual,
 8573                FormatTarget::Buffers,
 8574                window,
 8575                cx,
 8576            )
 8577        })
 8578        .unwrap();
 8579    fake_server
 8580        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8581            assert_eq!(
 8582                params.text_document.uri,
 8583                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8584            );
 8585            assert_eq!(params.options.tab_size, 4);
 8586            Ok(Some(vec![lsp::TextEdit::new(
 8587                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8588                ", ".to_string(),
 8589            )]))
 8590        })
 8591        .next()
 8592        .await;
 8593    cx.executor().start_waiting();
 8594    format.await;
 8595    assert_eq!(
 8596        editor.update(cx, |editor, cx| editor.text(cx)),
 8597        "one, two\nthree\n"
 8598    );
 8599
 8600    editor.update_in(cx, |editor, window, cx| {
 8601        editor.set_text("one\ntwo\nthree\n", window, cx)
 8602    });
 8603    // Ensure we don't lock if formatting hangs.
 8604    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8605        move |params, _| async move {
 8606            assert_eq!(
 8607                params.text_document.uri,
 8608                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8609            );
 8610            futures::future::pending::<()>().await;
 8611            unreachable!()
 8612        },
 8613    );
 8614    let format = editor
 8615        .update_in(cx, |editor, window, cx| {
 8616            editor.perform_format(
 8617                project,
 8618                FormatTrigger::Manual,
 8619                FormatTarget::Buffers,
 8620                window,
 8621                cx,
 8622            )
 8623        })
 8624        .unwrap();
 8625    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8626    cx.executor().start_waiting();
 8627    format.await;
 8628    assert_eq!(
 8629        editor.update(cx, |editor, cx| editor.text(cx)),
 8630        "one\ntwo\nthree\n"
 8631    );
 8632}
 8633
 8634#[gpui::test]
 8635async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8636    init_test(cx, |settings| {
 8637        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8638        settings.defaults.formatter =
 8639            Some(language_settings::SelectedFormatter::List(FormatterList(
 8640                vec![
 8641                    Formatter::LanguageServer { name: None },
 8642                    Formatter::CodeActions(
 8643                        [
 8644                            ("code-action-1".into(), true),
 8645                            ("code-action-2".into(), true),
 8646                        ]
 8647                        .into_iter()
 8648                        .collect(),
 8649                    ),
 8650                ]
 8651                .into(),
 8652            )))
 8653    });
 8654
 8655    let fs = FakeFs::new(cx.executor());
 8656    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8657        .await;
 8658
 8659    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8660    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8661    language_registry.add(rust_lang());
 8662
 8663    let mut fake_servers = language_registry.register_fake_lsp(
 8664        "Rust",
 8665        FakeLspAdapter {
 8666            capabilities: lsp::ServerCapabilities {
 8667                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8668                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8669                    commands: vec!["the-command-for-code-action-1".into()],
 8670                    ..Default::default()
 8671                }),
 8672                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8673                ..Default::default()
 8674            },
 8675            ..Default::default()
 8676        },
 8677    );
 8678
 8679    let buffer = project
 8680        .update(cx, |project, cx| {
 8681            project.open_local_buffer(path!("/file.rs"), cx)
 8682        })
 8683        .await
 8684        .unwrap();
 8685
 8686    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8687    let (editor, cx) = cx.add_window_view(|window, cx| {
 8688        build_editor_with_project(project.clone(), buffer, window, cx)
 8689    });
 8690
 8691    cx.executor().start_waiting();
 8692
 8693    let fake_server = fake_servers.next().await.unwrap();
 8694    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8695        move |_params, _| async move {
 8696            Ok(Some(vec![lsp::TextEdit::new(
 8697                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8698                "applied-formatting\n".to_string(),
 8699            )]))
 8700        },
 8701    );
 8702    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8703        move |params, _| async move {
 8704            assert_eq!(
 8705                params.context.only,
 8706                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8707            );
 8708            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8709            Ok(Some(vec![
 8710                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8711                    kind: Some("code-action-1".into()),
 8712                    edit: Some(lsp::WorkspaceEdit::new(
 8713                        [(
 8714                            uri.clone(),
 8715                            vec![lsp::TextEdit::new(
 8716                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8717                                "applied-code-action-1-edit\n".to_string(),
 8718                            )],
 8719                        )]
 8720                        .into_iter()
 8721                        .collect(),
 8722                    )),
 8723                    command: Some(lsp::Command {
 8724                        command: "the-command-for-code-action-1".into(),
 8725                        ..Default::default()
 8726                    }),
 8727                    ..Default::default()
 8728                }),
 8729                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8730                    kind: Some("code-action-2".into()),
 8731                    edit: Some(lsp::WorkspaceEdit::new(
 8732                        [(
 8733                            uri.clone(),
 8734                            vec![lsp::TextEdit::new(
 8735                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8736                                "applied-code-action-2-edit\n".to_string(),
 8737                            )],
 8738                        )]
 8739                        .into_iter()
 8740                        .collect(),
 8741                    )),
 8742                    ..Default::default()
 8743                }),
 8744            ]))
 8745        },
 8746    );
 8747
 8748    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8749        move |params, _| async move { Ok(params) }
 8750    });
 8751
 8752    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8753    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8754        let fake = fake_server.clone();
 8755        let lock = command_lock.clone();
 8756        move |params, _| {
 8757            assert_eq!(params.command, "the-command-for-code-action-1");
 8758            let fake = fake.clone();
 8759            let lock = lock.clone();
 8760            async move {
 8761                lock.lock().await;
 8762                fake.server
 8763                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8764                        label: None,
 8765                        edit: lsp::WorkspaceEdit {
 8766                            changes: Some(
 8767                                [(
 8768                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8769                                    vec![lsp::TextEdit {
 8770                                        range: lsp::Range::new(
 8771                                            lsp::Position::new(0, 0),
 8772                                            lsp::Position::new(0, 0),
 8773                                        ),
 8774                                        new_text: "applied-code-action-1-command\n".into(),
 8775                                    }],
 8776                                )]
 8777                                .into_iter()
 8778                                .collect(),
 8779                            ),
 8780                            ..Default::default()
 8781                        },
 8782                    })
 8783                    .await
 8784                    .unwrap();
 8785                Ok(Some(json!(null)))
 8786            }
 8787        }
 8788    });
 8789
 8790    cx.executor().start_waiting();
 8791    editor
 8792        .update_in(cx, |editor, window, cx| {
 8793            editor.perform_format(
 8794                project.clone(),
 8795                FormatTrigger::Manual,
 8796                FormatTarget::Buffers,
 8797                window,
 8798                cx,
 8799            )
 8800        })
 8801        .unwrap()
 8802        .await;
 8803    editor.update(cx, |editor, cx| {
 8804        assert_eq!(
 8805            editor.text(cx),
 8806            r#"
 8807                applied-code-action-2-edit
 8808                applied-code-action-1-command
 8809                applied-code-action-1-edit
 8810                applied-formatting
 8811                one
 8812                two
 8813                three
 8814            "#
 8815            .unindent()
 8816        );
 8817    });
 8818
 8819    editor.update_in(cx, |editor, window, cx| {
 8820        editor.undo(&Default::default(), window, cx);
 8821        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8822    });
 8823
 8824    // Perform a manual edit while waiting for an LSP command
 8825    // that's being run as part of a formatting code action.
 8826    let lock_guard = command_lock.lock().await;
 8827    let format = editor
 8828        .update_in(cx, |editor, window, cx| {
 8829            editor.perform_format(
 8830                project.clone(),
 8831                FormatTrigger::Manual,
 8832                FormatTarget::Buffers,
 8833                window,
 8834                cx,
 8835            )
 8836        })
 8837        .unwrap();
 8838    cx.run_until_parked();
 8839    editor.update(cx, |editor, cx| {
 8840        assert_eq!(
 8841            editor.text(cx),
 8842            r#"
 8843                applied-code-action-1-edit
 8844                applied-formatting
 8845                one
 8846                two
 8847                three
 8848            "#
 8849            .unindent()
 8850        );
 8851
 8852        editor.buffer.update(cx, |buffer, cx| {
 8853            let ix = buffer.len(cx);
 8854            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8855        });
 8856    });
 8857
 8858    // Allow the LSP command to proceed. Because the buffer was edited,
 8859    // the second code action will not be run.
 8860    drop(lock_guard);
 8861    format.await;
 8862    editor.update_in(cx, |editor, window, cx| {
 8863        assert_eq!(
 8864            editor.text(cx),
 8865            r#"
 8866                applied-code-action-1-command
 8867                applied-code-action-1-edit
 8868                applied-formatting
 8869                one
 8870                two
 8871                three
 8872                edited
 8873            "#
 8874            .unindent()
 8875        );
 8876
 8877        // The manual edit is undone first, because it is the last thing the user did
 8878        // (even though the command completed afterwards).
 8879        editor.undo(&Default::default(), window, cx);
 8880        assert_eq!(
 8881            editor.text(cx),
 8882            r#"
 8883                applied-code-action-1-command
 8884                applied-code-action-1-edit
 8885                applied-formatting
 8886                one
 8887                two
 8888                three
 8889            "#
 8890            .unindent()
 8891        );
 8892
 8893        // All the formatting (including the command, which completed after the manual edit)
 8894        // is undone together.
 8895        editor.undo(&Default::default(), window, cx);
 8896        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8897    });
 8898}
 8899
 8900#[gpui::test]
 8901async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8902    init_test(cx, |settings| {
 8903        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8904            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8905        ))
 8906    });
 8907
 8908    let fs = FakeFs::new(cx.executor());
 8909    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8910
 8911    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8912
 8913    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8914    language_registry.add(Arc::new(Language::new(
 8915        LanguageConfig {
 8916            name: "TypeScript".into(),
 8917            matcher: LanguageMatcher {
 8918                path_suffixes: vec!["ts".to_string()],
 8919                ..Default::default()
 8920            },
 8921            ..LanguageConfig::default()
 8922        },
 8923        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8924    )));
 8925    update_test_language_settings(cx, |settings| {
 8926        settings.defaults.prettier = Some(PrettierSettings {
 8927            allowed: true,
 8928            ..PrettierSettings::default()
 8929        });
 8930    });
 8931    let mut fake_servers = language_registry.register_fake_lsp(
 8932        "TypeScript",
 8933        FakeLspAdapter {
 8934            capabilities: lsp::ServerCapabilities {
 8935                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8936                ..Default::default()
 8937            },
 8938            ..Default::default()
 8939        },
 8940    );
 8941
 8942    let buffer = project
 8943        .update(cx, |project, cx| {
 8944            project.open_local_buffer(path!("/file.ts"), cx)
 8945        })
 8946        .await
 8947        .unwrap();
 8948
 8949    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8950    let (editor, cx) = cx.add_window_view(|window, cx| {
 8951        build_editor_with_project(project.clone(), buffer, window, cx)
 8952    });
 8953    editor.update_in(cx, |editor, window, cx| {
 8954        editor.set_text(
 8955            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8956            window,
 8957            cx,
 8958        )
 8959    });
 8960
 8961    cx.executor().start_waiting();
 8962    let fake_server = fake_servers.next().await.unwrap();
 8963
 8964    let format = editor
 8965        .update_in(cx, |editor, window, cx| {
 8966            editor.perform_code_action_kind(
 8967                project.clone(),
 8968                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8969                window,
 8970                cx,
 8971            )
 8972        })
 8973        .unwrap();
 8974    fake_server
 8975        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8976            assert_eq!(
 8977                params.text_document.uri,
 8978                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8979            );
 8980            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8981                lsp::CodeAction {
 8982                    title: "Organize Imports".to_string(),
 8983                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8984                    edit: Some(lsp::WorkspaceEdit {
 8985                        changes: Some(
 8986                            [(
 8987                                params.text_document.uri.clone(),
 8988                                vec![lsp::TextEdit::new(
 8989                                    lsp::Range::new(
 8990                                        lsp::Position::new(1, 0),
 8991                                        lsp::Position::new(2, 0),
 8992                                    ),
 8993                                    "".to_string(),
 8994                                )],
 8995                            )]
 8996                            .into_iter()
 8997                            .collect(),
 8998                        ),
 8999                        ..Default::default()
 9000                    }),
 9001                    ..Default::default()
 9002                },
 9003            )]))
 9004        })
 9005        .next()
 9006        .await;
 9007    cx.executor().start_waiting();
 9008    format.await;
 9009    assert_eq!(
 9010        editor.update(cx, |editor, cx| editor.text(cx)),
 9011        "import { a } from 'module';\n\nconst x = a;\n"
 9012    );
 9013
 9014    editor.update_in(cx, |editor, window, cx| {
 9015        editor.set_text(
 9016            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9017            window,
 9018            cx,
 9019        )
 9020    });
 9021    // Ensure we don't lock if code action hangs.
 9022    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9023        move |params, _| async move {
 9024            assert_eq!(
 9025                params.text_document.uri,
 9026                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9027            );
 9028            futures::future::pending::<()>().await;
 9029            unreachable!()
 9030        },
 9031    );
 9032    let format = editor
 9033        .update_in(cx, |editor, window, cx| {
 9034            editor.perform_code_action_kind(
 9035                project,
 9036                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9037                window,
 9038                cx,
 9039            )
 9040        })
 9041        .unwrap();
 9042    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9043    cx.executor().start_waiting();
 9044    format.await;
 9045    assert_eq!(
 9046        editor.update(cx, |editor, cx| editor.text(cx)),
 9047        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9048    );
 9049}
 9050
 9051#[gpui::test]
 9052async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9053    init_test(cx, |_| {});
 9054
 9055    let mut cx = EditorLspTestContext::new_rust(
 9056        lsp::ServerCapabilities {
 9057            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9058            ..Default::default()
 9059        },
 9060        cx,
 9061    )
 9062    .await;
 9063
 9064    cx.set_state(indoc! {"
 9065        one.twoˇ
 9066    "});
 9067
 9068    // The format request takes a long time. When it completes, it inserts
 9069    // a newline and an indent before the `.`
 9070    cx.lsp
 9071        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9072            let executor = cx.background_executor().clone();
 9073            async move {
 9074                executor.timer(Duration::from_millis(100)).await;
 9075                Ok(Some(vec![lsp::TextEdit {
 9076                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9077                    new_text: "\n    ".into(),
 9078                }]))
 9079            }
 9080        });
 9081
 9082    // Submit a format request.
 9083    let format_1 = cx
 9084        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9085        .unwrap();
 9086    cx.executor().run_until_parked();
 9087
 9088    // Submit a second format request.
 9089    let format_2 = cx
 9090        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9091        .unwrap();
 9092    cx.executor().run_until_parked();
 9093
 9094    // Wait for both format requests to complete
 9095    cx.executor().advance_clock(Duration::from_millis(200));
 9096    cx.executor().start_waiting();
 9097    format_1.await.unwrap();
 9098    cx.executor().start_waiting();
 9099    format_2.await.unwrap();
 9100
 9101    // The formatting edits only happens once.
 9102    cx.assert_editor_state(indoc! {"
 9103        one
 9104            .twoˇ
 9105    "});
 9106}
 9107
 9108#[gpui::test]
 9109async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9110    init_test(cx, |settings| {
 9111        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9112    });
 9113
 9114    let mut cx = EditorLspTestContext::new_rust(
 9115        lsp::ServerCapabilities {
 9116            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9117            ..Default::default()
 9118        },
 9119        cx,
 9120    )
 9121    .await;
 9122
 9123    // Set up a buffer white some trailing whitespace and no trailing newline.
 9124    cx.set_state(
 9125        &[
 9126            "one ",   //
 9127            "twoˇ",   //
 9128            "three ", //
 9129            "four",   //
 9130        ]
 9131        .join("\n"),
 9132    );
 9133
 9134    // Submit a format request.
 9135    let format = cx
 9136        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9137        .unwrap();
 9138
 9139    // Record which buffer changes have been sent to the language server
 9140    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9141    cx.lsp
 9142        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9143            let buffer_changes = buffer_changes.clone();
 9144            move |params, _| {
 9145                buffer_changes.lock().extend(
 9146                    params
 9147                        .content_changes
 9148                        .into_iter()
 9149                        .map(|e| (e.range.unwrap(), e.text)),
 9150                );
 9151            }
 9152        });
 9153
 9154    // Handle formatting requests to the language server.
 9155    cx.lsp
 9156        .set_request_handler::<lsp::request::Formatting, _, _>({
 9157            let buffer_changes = buffer_changes.clone();
 9158            move |_, _| {
 9159                // When formatting is requested, trailing whitespace has already been stripped,
 9160                // and the trailing newline has already been added.
 9161                assert_eq!(
 9162                    &buffer_changes.lock()[1..],
 9163                    &[
 9164                        (
 9165                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9166                            "".into()
 9167                        ),
 9168                        (
 9169                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9170                            "".into()
 9171                        ),
 9172                        (
 9173                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9174                            "\n".into()
 9175                        ),
 9176                    ]
 9177                );
 9178
 9179                // Insert blank lines between each line of the buffer.
 9180                async move {
 9181                    Ok(Some(vec![
 9182                        lsp::TextEdit {
 9183                            range: lsp::Range::new(
 9184                                lsp::Position::new(1, 0),
 9185                                lsp::Position::new(1, 0),
 9186                            ),
 9187                            new_text: "\n".into(),
 9188                        },
 9189                        lsp::TextEdit {
 9190                            range: lsp::Range::new(
 9191                                lsp::Position::new(2, 0),
 9192                                lsp::Position::new(2, 0),
 9193                            ),
 9194                            new_text: "\n".into(),
 9195                        },
 9196                    ]))
 9197                }
 9198            }
 9199        });
 9200
 9201    // After formatting the buffer, the trailing whitespace is stripped,
 9202    // a newline is appended, and the edits provided by the language server
 9203    // have been applied.
 9204    format.await.unwrap();
 9205    cx.assert_editor_state(
 9206        &[
 9207            "one",   //
 9208            "",      //
 9209            "twoˇ",  //
 9210            "",      //
 9211            "three", //
 9212            "four",  //
 9213            "",      //
 9214        ]
 9215        .join("\n"),
 9216    );
 9217
 9218    // Undoing the formatting undoes the trailing whitespace removal, the
 9219    // trailing newline, and the LSP edits.
 9220    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9221    cx.assert_editor_state(
 9222        &[
 9223            "one ",   //
 9224            "twoˇ",   //
 9225            "three ", //
 9226            "four",   //
 9227        ]
 9228        .join("\n"),
 9229    );
 9230}
 9231
 9232#[gpui::test]
 9233async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9234    cx: &mut TestAppContext,
 9235) {
 9236    init_test(cx, |_| {});
 9237
 9238    cx.update(|cx| {
 9239        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9240            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9241                settings.auto_signature_help = Some(true);
 9242            });
 9243        });
 9244    });
 9245
 9246    let mut cx = EditorLspTestContext::new_rust(
 9247        lsp::ServerCapabilities {
 9248            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9249                ..Default::default()
 9250            }),
 9251            ..Default::default()
 9252        },
 9253        cx,
 9254    )
 9255    .await;
 9256
 9257    let language = Language::new(
 9258        LanguageConfig {
 9259            name: "Rust".into(),
 9260            brackets: BracketPairConfig {
 9261                pairs: vec![
 9262                    BracketPair {
 9263                        start: "{".to_string(),
 9264                        end: "}".to_string(),
 9265                        close: true,
 9266                        surround: true,
 9267                        newline: true,
 9268                    },
 9269                    BracketPair {
 9270                        start: "(".to_string(),
 9271                        end: ")".to_string(),
 9272                        close: true,
 9273                        surround: true,
 9274                        newline: true,
 9275                    },
 9276                    BracketPair {
 9277                        start: "/*".to_string(),
 9278                        end: " */".to_string(),
 9279                        close: true,
 9280                        surround: true,
 9281                        newline: true,
 9282                    },
 9283                    BracketPair {
 9284                        start: "[".to_string(),
 9285                        end: "]".to_string(),
 9286                        close: false,
 9287                        surround: false,
 9288                        newline: true,
 9289                    },
 9290                    BracketPair {
 9291                        start: "\"".to_string(),
 9292                        end: "\"".to_string(),
 9293                        close: true,
 9294                        surround: true,
 9295                        newline: false,
 9296                    },
 9297                    BracketPair {
 9298                        start: "<".to_string(),
 9299                        end: ">".to_string(),
 9300                        close: false,
 9301                        surround: true,
 9302                        newline: true,
 9303                    },
 9304                ],
 9305                ..Default::default()
 9306            },
 9307            autoclose_before: "})]".to_string(),
 9308            ..Default::default()
 9309        },
 9310        Some(tree_sitter_rust::LANGUAGE.into()),
 9311    );
 9312    let language = Arc::new(language);
 9313
 9314    cx.language_registry().add(language.clone());
 9315    cx.update_buffer(|buffer, cx| {
 9316        buffer.set_language(Some(language), cx);
 9317    });
 9318
 9319    cx.set_state(
 9320        &r#"
 9321            fn main() {
 9322                sampleˇ
 9323            }
 9324        "#
 9325        .unindent(),
 9326    );
 9327
 9328    cx.update_editor(|editor, window, cx| {
 9329        editor.handle_input("(", window, cx);
 9330    });
 9331    cx.assert_editor_state(
 9332        &"
 9333            fn main() {
 9334                sample(ˇ)
 9335            }
 9336        "
 9337        .unindent(),
 9338    );
 9339
 9340    let mocked_response = lsp::SignatureHelp {
 9341        signatures: vec![lsp::SignatureInformation {
 9342            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9343            documentation: None,
 9344            parameters: Some(vec![
 9345                lsp::ParameterInformation {
 9346                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9347                    documentation: None,
 9348                },
 9349                lsp::ParameterInformation {
 9350                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9351                    documentation: None,
 9352                },
 9353            ]),
 9354            active_parameter: None,
 9355        }],
 9356        active_signature: Some(0),
 9357        active_parameter: Some(0),
 9358    };
 9359    handle_signature_help_request(&mut cx, mocked_response).await;
 9360
 9361    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9362        .await;
 9363
 9364    cx.editor(|editor, _, _| {
 9365        let signature_help_state = editor.signature_help_state.popover().cloned();
 9366        assert_eq!(
 9367            signature_help_state.unwrap().label,
 9368            "param1: u8, param2: u8"
 9369        );
 9370    });
 9371}
 9372
 9373#[gpui::test]
 9374async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9375    init_test(cx, |_| {});
 9376
 9377    cx.update(|cx| {
 9378        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9379            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9380                settings.auto_signature_help = Some(false);
 9381                settings.show_signature_help_after_edits = Some(false);
 9382            });
 9383        });
 9384    });
 9385
 9386    let mut cx = EditorLspTestContext::new_rust(
 9387        lsp::ServerCapabilities {
 9388            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9389                ..Default::default()
 9390            }),
 9391            ..Default::default()
 9392        },
 9393        cx,
 9394    )
 9395    .await;
 9396
 9397    let language = Language::new(
 9398        LanguageConfig {
 9399            name: "Rust".into(),
 9400            brackets: BracketPairConfig {
 9401                pairs: vec![
 9402                    BracketPair {
 9403                        start: "{".to_string(),
 9404                        end: "}".to_string(),
 9405                        close: true,
 9406                        surround: true,
 9407                        newline: true,
 9408                    },
 9409                    BracketPair {
 9410                        start: "(".to_string(),
 9411                        end: ")".to_string(),
 9412                        close: true,
 9413                        surround: true,
 9414                        newline: true,
 9415                    },
 9416                    BracketPair {
 9417                        start: "/*".to_string(),
 9418                        end: " */".to_string(),
 9419                        close: true,
 9420                        surround: true,
 9421                        newline: true,
 9422                    },
 9423                    BracketPair {
 9424                        start: "[".to_string(),
 9425                        end: "]".to_string(),
 9426                        close: false,
 9427                        surround: false,
 9428                        newline: true,
 9429                    },
 9430                    BracketPair {
 9431                        start: "\"".to_string(),
 9432                        end: "\"".to_string(),
 9433                        close: true,
 9434                        surround: true,
 9435                        newline: false,
 9436                    },
 9437                    BracketPair {
 9438                        start: "<".to_string(),
 9439                        end: ">".to_string(),
 9440                        close: false,
 9441                        surround: true,
 9442                        newline: true,
 9443                    },
 9444                ],
 9445                ..Default::default()
 9446            },
 9447            autoclose_before: "})]".to_string(),
 9448            ..Default::default()
 9449        },
 9450        Some(tree_sitter_rust::LANGUAGE.into()),
 9451    );
 9452    let language = Arc::new(language);
 9453
 9454    cx.language_registry().add(language.clone());
 9455    cx.update_buffer(|buffer, cx| {
 9456        buffer.set_language(Some(language), cx);
 9457    });
 9458
 9459    // Ensure that signature_help is not called when no signature help is enabled.
 9460    cx.set_state(
 9461        &r#"
 9462            fn main() {
 9463                sampleˇ
 9464            }
 9465        "#
 9466        .unindent(),
 9467    );
 9468    cx.update_editor(|editor, window, cx| {
 9469        editor.handle_input("(", window, cx);
 9470    });
 9471    cx.assert_editor_state(
 9472        &"
 9473            fn main() {
 9474                sample(ˇ)
 9475            }
 9476        "
 9477        .unindent(),
 9478    );
 9479    cx.editor(|editor, _, _| {
 9480        assert!(editor.signature_help_state.task().is_none());
 9481    });
 9482
 9483    let mocked_response = lsp::SignatureHelp {
 9484        signatures: vec![lsp::SignatureInformation {
 9485            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9486            documentation: None,
 9487            parameters: Some(vec![
 9488                lsp::ParameterInformation {
 9489                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9490                    documentation: None,
 9491                },
 9492                lsp::ParameterInformation {
 9493                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9494                    documentation: None,
 9495                },
 9496            ]),
 9497            active_parameter: None,
 9498        }],
 9499        active_signature: Some(0),
 9500        active_parameter: Some(0),
 9501    };
 9502
 9503    // Ensure that signature_help is called when enabled afte edits
 9504    cx.update(|_, cx| {
 9505        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9506            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9507                settings.auto_signature_help = Some(false);
 9508                settings.show_signature_help_after_edits = Some(true);
 9509            });
 9510        });
 9511    });
 9512    cx.set_state(
 9513        &r#"
 9514            fn main() {
 9515                sampleˇ
 9516            }
 9517        "#
 9518        .unindent(),
 9519    );
 9520    cx.update_editor(|editor, window, cx| {
 9521        editor.handle_input("(", window, cx);
 9522    });
 9523    cx.assert_editor_state(
 9524        &"
 9525            fn main() {
 9526                sample(ˇ)
 9527            }
 9528        "
 9529        .unindent(),
 9530    );
 9531    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9532    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9533        .await;
 9534    cx.update_editor(|editor, _, _| {
 9535        let signature_help_state = editor.signature_help_state.popover().cloned();
 9536        assert!(signature_help_state.is_some());
 9537        assert_eq!(
 9538            signature_help_state.unwrap().label,
 9539            "param1: u8, param2: u8"
 9540        );
 9541        editor.signature_help_state = SignatureHelpState::default();
 9542    });
 9543
 9544    // Ensure that signature_help is called when auto signature help override is enabled
 9545    cx.update(|_, cx| {
 9546        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9547            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9548                settings.auto_signature_help = Some(true);
 9549                settings.show_signature_help_after_edits = Some(false);
 9550            });
 9551        });
 9552    });
 9553    cx.set_state(
 9554        &r#"
 9555            fn main() {
 9556                sampleˇ
 9557            }
 9558        "#
 9559        .unindent(),
 9560    );
 9561    cx.update_editor(|editor, window, cx| {
 9562        editor.handle_input("(", window, cx);
 9563    });
 9564    cx.assert_editor_state(
 9565        &"
 9566            fn main() {
 9567                sample(ˇ)
 9568            }
 9569        "
 9570        .unindent(),
 9571    );
 9572    handle_signature_help_request(&mut cx, mocked_response).await;
 9573    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9574        .await;
 9575    cx.editor(|editor, _, _| {
 9576        let signature_help_state = editor.signature_help_state.popover().cloned();
 9577        assert!(signature_help_state.is_some());
 9578        assert_eq!(
 9579            signature_help_state.unwrap().label,
 9580            "param1: u8, param2: u8"
 9581        );
 9582    });
 9583}
 9584
 9585#[gpui::test]
 9586async fn test_signature_help(cx: &mut TestAppContext) {
 9587    init_test(cx, |_| {});
 9588    cx.update(|cx| {
 9589        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9590            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9591                settings.auto_signature_help = Some(true);
 9592            });
 9593        });
 9594    });
 9595
 9596    let mut cx = EditorLspTestContext::new_rust(
 9597        lsp::ServerCapabilities {
 9598            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9599                ..Default::default()
 9600            }),
 9601            ..Default::default()
 9602        },
 9603        cx,
 9604    )
 9605    .await;
 9606
 9607    // A test that directly calls `show_signature_help`
 9608    cx.update_editor(|editor, window, cx| {
 9609        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9610    });
 9611
 9612    let mocked_response = lsp::SignatureHelp {
 9613        signatures: vec![lsp::SignatureInformation {
 9614            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9615            documentation: None,
 9616            parameters: Some(vec![
 9617                lsp::ParameterInformation {
 9618                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9619                    documentation: None,
 9620                },
 9621                lsp::ParameterInformation {
 9622                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9623                    documentation: None,
 9624                },
 9625            ]),
 9626            active_parameter: None,
 9627        }],
 9628        active_signature: Some(0),
 9629        active_parameter: Some(0),
 9630    };
 9631    handle_signature_help_request(&mut cx, mocked_response).await;
 9632
 9633    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9634        .await;
 9635
 9636    cx.editor(|editor, _, _| {
 9637        let signature_help_state = editor.signature_help_state.popover().cloned();
 9638        assert!(signature_help_state.is_some());
 9639        assert_eq!(
 9640            signature_help_state.unwrap().label,
 9641            "param1: u8, param2: u8"
 9642        );
 9643    });
 9644
 9645    // When exiting outside from inside the brackets, `signature_help` is closed.
 9646    cx.set_state(indoc! {"
 9647        fn main() {
 9648            sample(ˇ);
 9649        }
 9650
 9651        fn sample(param1: u8, param2: u8) {}
 9652    "});
 9653
 9654    cx.update_editor(|editor, window, cx| {
 9655        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9656    });
 9657
 9658    let mocked_response = lsp::SignatureHelp {
 9659        signatures: Vec::new(),
 9660        active_signature: None,
 9661        active_parameter: None,
 9662    };
 9663    handle_signature_help_request(&mut cx, mocked_response).await;
 9664
 9665    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9666        .await;
 9667
 9668    cx.editor(|editor, _, _| {
 9669        assert!(!editor.signature_help_state.is_shown());
 9670    });
 9671
 9672    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9673    cx.set_state(indoc! {"
 9674        fn main() {
 9675            sample(ˇ);
 9676        }
 9677
 9678        fn sample(param1: u8, param2: u8) {}
 9679    "});
 9680
 9681    let mocked_response = lsp::SignatureHelp {
 9682        signatures: vec![lsp::SignatureInformation {
 9683            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9684            documentation: None,
 9685            parameters: Some(vec![
 9686                lsp::ParameterInformation {
 9687                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9688                    documentation: None,
 9689                },
 9690                lsp::ParameterInformation {
 9691                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9692                    documentation: None,
 9693                },
 9694            ]),
 9695            active_parameter: None,
 9696        }],
 9697        active_signature: Some(0),
 9698        active_parameter: Some(0),
 9699    };
 9700    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9701    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9702        .await;
 9703    cx.editor(|editor, _, _| {
 9704        assert!(editor.signature_help_state.is_shown());
 9705    });
 9706
 9707    // Restore the popover with more parameter input
 9708    cx.set_state(indoc! {"
 9709        fn main() {
 9710            sample(param1, param2ˇ);
 9711        }
 9712
 9713        fn sample(param1: u8, param2: u8) {}
 9714    "});
 9715
 9716    let mocked_response = lsp::SignatureHelp {
 9717        signatures: vec![lsp::SignatureInformation {
 9718            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9719            documentation: None,
 9720            parameters: Some(vec![
 9721                lsp::ParameterInformation {
 9722                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9723                    documentation: None,
 9724                },
 9725                lsp::ParameterInformation {
 9726                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9727                    documentation: None,
 9728                },
 9729            ]),
 9730            active_parameter: None,
 9731        }],
 9732        active_signature: Some(0),
 9733        active_parameter: Some(1),
 9734    };
 9735    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9736    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9737        .await;
 9738
 9739    // When selecting a range, the popover is gone.
 9740    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9741    cx.update_editor(|editor, window, cx| {
 9742        editor.change_selections(None, window, cx, |s| {
 9743            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9744        })
 9745    });
 9746    cx.assert_editor_state(indoc! {"
 9747        fn main() {
 9748            sample(param1, «ˇparam2»);
 9749        }
 9750
 9751        fn sample(param1: u8, param2: u8) {}
 9752    "});
 9753    cx.editor(|editor, _, _| {
 9754        assert!(!editor.signature_help_state.is_shown());
 9755    });
 9756
 9757    // When unselecting again, the popover is back if within the brackets.
 9758    cx.update_editor(|editor, window, cx| {
 9759        editor.change_selections(None, window, cx, |s| {
 9760            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9761        })
 9762    });
 9763    cx.assert_editor_state(indoc! {"
 9764        fn main() {
 9765            sample(param1, ˇparam2);
 9766        }
 9767
 9768        fn sample(param1: u8, param2: u8) {}
 9769    "});
 9770    handle_signature_help_request(&mut cx, mocked_response).await;
 9771    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9772        .await;
 9773    cx.editor(|editor, _, _| {
 9774        assert!(editor.signature_help_state.is_shown());
 9775    });
 9776
 9777    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9778    cx.update_editor(|editor, window, cx| {
 9779        editor.change_selections(None, window, cx, |s| {
 9780            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9781            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9782        })
 9783    });
 9784    cx.assert_editor_state(indoc! {"
 9785        fn main() {
 9786            sample(param1, ˇparam2);
 9787        }
 9788
 9789        fn sample(param1: u8, param2: u8) {}
 9790    "});
 9791
 9792    let mocked_response = lsp::SignatureHelp {
 9793        signatures: vec![lsp::SignatureInformation {
 9794            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9795            documentation: None,
 9796            parameters: Some(vec![
 9797                lsp::ParameterInformation {
 9798                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9799                    documentation: None,
 9800                },
 9801                lsp::ParameterInformation {
 9802                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9803                    documentation: None,
 9804                },
 9805            ]),
 9806            active_parameter: None,
 9807        }],
 9808        active_signature: Some(0),
 9809        active_parameter: Some(1),
 9810    };
 9811    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9812    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9813        .await;
 9814    cx.update_editor(|editor, _, cx| {
 9815        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9816    });
 9817    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9818        .await;
 9819    cx.update_editor(|editor, window, cx| {
 9820        editor.change_selections(None, window, cx, |s| {
 9821            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9822        })
 9823    });
 9824    cx.assert_editor_state(indoc! {"
 9825        fn main() {
 9826            sample(param1, «ˇparam2»);
 9827        }
 9828
 9829        fn sample(param1: u8, param2: u8) {}
 9830    "});
 9831    cx.update_editor(|editor, window, cx| {
 9832        editor.change_selections(None, window, cx, |s| {
 9833            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9834        })
 9835    });
 9836    cx.assert_editor_state(indoc! {"
 9837        fn main() {
 9838            sample(param1, ˇparam2);
 9839        }
 9840
 9841        fn sample(param1: u8, param2: u8) {}
 9842    "});
 9843    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9844        .await;
 9845}
 9846
 9847#[gpui::test]
 9848async fn test_completion_mode(cx: &mut TestAppContext) {
 9849    init_test(cx, |_| {});
 9850    let mut cx = EditorLspTestContext::new_rust(
 9851        lsp::ServerCapabilities {
 9852            completion_provider: Some(lsp::CompletionOptions {
 9853                resolve_provider: Some(true),
 9854                ..Default::default()
 9855            }),
 9856            ..Default::default()
 9857        },
 9858        cx,
 9859    )
 9860    .await;
 9861
 9862    struct Run {
 9863        run_description: &'static str,
 9864        initial_state: String,
 9865        buffer_marked_text: String,
 9866        completion_text: &'static str,
 9867        expected_with_insert_mode: String,
 9868        expected_with_replace_mode: String,
 9869        expected_with_replace_subsequence_mode: String,
 9870        expected_with_replace_suffix_mode: String,
 9871    }
 9872
 9873    let runs = [
 9874        Run {
 9875            run_description: "Start of word matches completion text",
 9876            initial_state: "before ediˇ after".into(),
 9877            buffer_marked_text: "before <edi|> after".into(),
 9878            completion_text: "editor",
 9879            expected_with_insert_mode: "before editorˇ after".into(),
 9880            expected_with_replace_mode: "before editorˇ after".into(),
 9881            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9882            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9883        },
 9884        Run {
 9885            run_description: "Accept same text at the middle of the word",
 9886            initial_state: "before ediˇtor after".into(),
 9887            buffer_marked_text: "before <edi|tor> after".into(),
 9888            completion_text: "editor",
 9889            expected_with_insert_mode: "before editorˇtor after".into(),
 9890            expected_with_replace_mode: "before editorˇ after".into(),
 9891            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9892            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9893        },
 9894        Run {
 9895            run_description: "End of word matches completion text -- cursor at end",
 9896            initial_state: "before torˇ after".into(),
 9897            buffer_marked_text: "before <tor|> after".into(),
 9898            completion_text: "editor",
 9899            expected_with_insert_mode: "before editorˇ after".into(),
 9900            expected_with_replace_mode: "before editorˇ after".into(),
 9901            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9902            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9903        },
 9904        Run {
 9905            run_description: "End of word matches completion text -- cursor at start",
 9906            initial_state: "before ˇtor after".into(),
 9907            buffer_marked_text: "before <|tor> after".into(),
 9908            completion_text: "editor",
 9909            expected_with_insert_mode: "before editorˇtor after".into(),
 9910            expected_with_replace_mode: "before editorˇ after".into(),
 9911            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9912            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9913        },
 9914        Run {
 9915            run_description: "Prepend text containing whitespace",
 9916            initial_state: "pˇfield: bool".into(),
 9917            buffer_marked_text: "<p|field>: bool".into(),
 9918            completion_text: "pub ",
 9919            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9920            expected_with_replace_mode: "pub ˇ: bool".into(),
 9921            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9922            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9923        },
 9924        Run {
 9925            run_description: "Add element to start of list",
 9926            initial_state: "[element_ˇelement_2]".into(),
 9927            buffer_marked_text: "[<element_|element_2>]".into(),
 9928            completion_text: "element_1",
 9929            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9930            expected_with_replace_mode: "[element_1ˇ]".into(),
 9931            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9932            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9933        },
 9934        Run {
 9935            run_description: "Add element to start of list -- first and second elements are equal",
 9936            initial_state: "[elˇelement]".into(),
 9937            buffer_marked_text: "[<el|element>]".into(),
 9938            completion_text: "element",
 9939            expected_with_insert_mode: "[elementˇelement]".into(),
 9940            expected_with_replace_mode: "[elementˇ]".into(),
 9941            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9942            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9943        },
 9944        Run {
 9945            run_description: "Ends with matching suffix",
 9946            initial_state: "SubˇError".into(),
 9947            buffer_marked_text: "<Sub|Error>".into(),
 9948            completion_text: "SubscriptionError",
 9949            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9950            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9951            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9952            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9953        },
 9954        Run {
 9955            run_description: "Suffix is a subsequence -- contiguous",
 9956            initial_state: "SubˇErr".into(),
 9957            buffer_marked_text: "<Sub|Err>".into(),
 9958            completion_text: "SubscriptionError",
 9959            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9960            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9961            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9962            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9963        },
 9964        Run {
 9965            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9966            initial_state: "Suˇscrirr".into(),
 9967            buffer_marked_text: "<Su|scrirr>".into(),
 9968            completion_text: "SubscriptionError",
 9969            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9970            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9971            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9972            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9973        },
 9974        Run {
 9975            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9976            initial_state: "foo(indˇix)".into(),
 9977            buffer_marked_text: "foo(<ind|ix>)".into(),
 9978            completion_text: "node_index",
 9979            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9980            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9981            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9982            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9983        },
 9984    ];
 9985
 9986    for run in runs {
 9987        let run_variations = [
 9988            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9989            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9990            (
 9991                LspInsertMode::ReplaceSubsequence,
 9992                run.expected_with_replace_subsequence_mode,
 9993            ),
 9994            (
 9995                LspInsertMode::ReplaceSuffix,
 9996                run.expected_with_replace_suffix_mode,
 9997            ),
 9998        ];
 9999
10000        for (lsp_insert_mode, expected_text) in run_variations {
10001            eprintln!(
10002                "run = {:?}, mode = {lsp_insert_mode:.?}",
10003                run.run_description,
10004            );
10005
10006            update_test_language_settings(&mut cx, |settings| {
10007                settings.defaults.completions = Some(CompletionSettings {
10008                    lsp_insert_mode,
10009                    words: WordsCompletionMode::Disabled,
10010                    lsp: true,
10011                    lsp_fetch_timeout_ms: 0,
10012                });
10013            });
10014
10015            cx.set_state(&run.initial_state);
10016            cx.update_editor(|editor, window, cx| {
10017                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10018            });
10019
10020            let counter = Arc::new(AtomicUsize::new(0));
10021            handle_completion_request_with_insert_and_replace(
10022                &mut cx,
10023                &run.buffer_marked_text,
10024                vec![run.completion_text],
10025                counter.clone(),
10026            )
10027            .await;
10028            cx.condition(|editor, _| editor.context_menu_visible())
10029                .await;
10030            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10031
10032            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10033                editor
10034                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10035                    .unwrap()
10036            });
10037            cx.assert_editor_state(&expected_text);
10038            handle_resolve_completion_request(&mut cx, None).await;
10039            apply_additional_edits.await.unwrap();
10040        }
10041    }
10042}
10043
10044#[gpui::test]
10045async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10046    init_test(cx, |_| {});
10047    let mut cx = EditorLspTestContext::new_rust(
10048        lsp::ServerCapabilities {
10049            completion_provider: Some(lsp::CompletionOptions {
10050                resolve_provider: Some(true),
10051                ..Default::default()
10052            }),
10053            ..Default::default()
10054        },
10055        cx,
10056    )
10057    .await;
10058
10059    let initial_state = "SubˇError";
10060    let buffer_marked_text = "<Sub|Error>";
10061    let completion_text = "SubscriptionError";
10062    let expected_with_insert_mode = "SubscriptionErrorˇError";
10063    let expected_with_replace_mode = "SubscriptionErrorˇ";
10064
10065    update_test_language_settings(&mut cx, |settings| {
10066        settings.defaults.completions = Some(CompletionSettings {
10067            words: WordsCompletionMode::Disabled,
10068            // set the opposite here to ensure that the action is overriding the default behavior
10069            lsp_insert_mode: LspInsertMode::Insert,
10070            lsp: true,
10071            lsp_fetch_timeout_ms: 0,
10072        });
10073    });
10074
10075    cx.set_state(initial_state);
10076    cx.update_editor(|editor, window, cx| {
10077        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10078    });
10079
10080    let counter = Arc::new(AtomicUsize::new(0));
10081    handle_completion_request_with_insert_and_replace(
10082        &mut cx,
10083        &buffer_marked_text,
10084        vec![completion_text],
10085        counter.clone(),
10086    )
10087    .await;
10088    cx.condition(|editor, _| editor.context_menu_visible())
10089        .await;
10090    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10091
10092    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10093        editor
10094            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10095            .unwrap()
10096    });
10097    cx.assert_editor_state(&expected_with_replace_mode);
10098    handle_resolve_completion_request(&mut cx, None).await;
10099    apply_additional_edits.await.unwrap();
10100
10101    update_test_language_settings(&mut cx, |settings| {
10102        settings.defaults.completions = Some(CompletionSettings {
10103            words: WordsCompletionMode::Disabled,
10104            // set the opposite here to ensure that the action is overriding the default behavior
10105            lsp_insert_mode: LspInsertMode::Replace,
10106            lsp: true,
10107            lsp_fetch_timeout_ms: 0,
10108        });
10109    });
10110
10111    cx.set_state(initial_state);
10112    cx.update_editor(|editor, window, cx| {
10113        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10114    });
10115    handle_completion_request_with_insert_and_replace(
10116        &mut cx,
10117        &buffer_marked_text,
10118        vec![completion_text],
10119        counter.clone(),
10120    )
10121    .await;
10122    cx.condition(|editor, _| editor.context_menu_visible())
10123        .await;
10124    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10125
10126    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10127        editor
10128            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10129            .unwrap()
10130    });
10131    cx.assert_editor_state(&expected_with_insert_mode);
10132    handle_resolve_completion_request(&mut cx, None).await;
10133    apply_additional_edits.await.unwrap();
10134}
10135
10136#[gpui::test]
10137async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10138    init_test(cx, |_| {});
10139    let mut cx = EditorLspTestContext::new_rust(
10140        lsp::ServerCapabilities {
10141            completion_provider: Some(lsp::CompletionOptions {
10142                resolve_provider: Some(true),
10143                ..Default::default()
10144            }),
10145            ..Default::default()
10146        },
10147        cx,
10148    )
10149    .await;
10150
10151    // scenario: surrounding text matches completion text
10152    let completion_text = "to_offset";
10153    let initial_state = indoc! {"
10154        1. buf.to_offˇsuffix
10155        2. buf.to_offˇsuf
10156        3. buf.to_offˇfix
10157        4. buf.to_offˇ
10158        5. into_offˇensive
10159        6. ˇsuffix
10160        7. let ˇ //
10161        8. aaˇzz
10162        9. buf.to_off«zzzzzˇ»suffix
10163        10. buf.«ˇzzzzz»suffix
10164        11. to_off«ˇzzzzz»
10165
10166        buf.to_offˇsuffix  // newest cursor
10167    "};
10168    let completion_marked_buffer = indoc! {"
10169        1. buf.to_offsuffix
10170        2. buf.to_offsuf
10171        3. buf.to_offfix
10172        4. buf.to_off
10173        5. into_offensive
10174        6. suffix
10175        7. let  //
10176        8. aazz
10177        9. buf.to_offzzzzzsuffix
10178        10. buf.zzzzzsuffix
10179        11. to_offzzzzz
10180
10181        buf.<to_off|suffix>  // newest cursor
10182    "};
10183    let expected = indoc! {"
10184        1. buf.to_offsetˇ
10185        2. buf.to_offsetˇsuf
10186        3. buf.to_offsetˇfix
10187        4. buf.to_offsetˇ
10188        5. into_offsetˇensive
10189        6. to_offsetˇsuffix
10190        7. let to_offsetˇ //
10191        8. aato_offsetˇzz
10192        9. buf.to_offsetˇ
10193        10. buf.to_offsetˇsuffix
10194        11. to_offsetˇ
10195
10196        buf.to_offsetˇ  // newest cursor
10197    "};
10198    cx.set_state(initial_state);
10199    cx.update_editor(|editor, window, cx| {
10200        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10201    });
10202    handle_completion_request_with_insert_and_replace(
10203        &mut cx,
10204        completion_marked_buffer,
10205        vec![completion_text],
10206        Arc::new(AtomicUsize::new(0)),
10207    )
10208    .await;
10209    cx.condition(|editor, _| editor.context_menu_visible())
10210        .await;
10211    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10212        editor
10213            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10214            .unwrap()
10215    });
10216    cx.assert_editor_state(expected);
10217    handle_resolve_completion_request(&mut cx, None).await;
10218    apply_additional_edits.await.unwrap();
10219
10220    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10221    let completion_text = "foo_and_bar";
10222    let initial_state = indoc! {"
10223        1. ooanbˇ
10224        2. zooanbˇ
10225        3. ooanbˇz
10226        4. zooanbˇz
10227        5. ooanˇ
10228        6. oanbˇ
10229
10230        ooanbˇ
10231    "};
10232    let completion_marked_buffer = indoc! {"
10233        1. ooanb
10234        2. zooanb
10235        3. ooanbz
10236        4. zooanbz
10237        5. ooan
10238        6. oanb
10239
10240        <ooanb|>
10241    "};
10242    let expected = indoc! {"
10243        1. foo_and_barˇ
10244        2. zfoo_and_barˇ
10245        3. foo_and_barˇz
10246        4. zfoo_and_barˇz
10247        5. ooanfoo_and_barˇ
10248        6. oanbfoo_and_barˇ
10249
10250        foo_and_barˇ
10251    "};
10252    cx.set_state(initial_state);
10253    cx.update_editor(|editor, window, cx| {
10254        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10255    });
10256    handle_completion_request_with_insert_and_replace(
10257        &mut cx,
10258        completion_marked_buffer,
10259        vec![completion_text],
10260        Arc::new(AtomicUsize::new(0)),
10261    )
10262    .await;
10263    cx.condition(|editor, _| editor.context_menu_visible())
10264        .await;
10265    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10266        editor
10267            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10268            .unwrap()
10269    });
10270    cx.assert_editor_state(expected);
10271    handle_resolve_completion_request(&mut cx, None).await;
10272    apply_additional_edits.await.unwrap();
10273
10274    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10275    // (expects the same as if it was inserted at the end)
10276    let completion_text = "foo_and_bar";
10277    let initial_state = indoc! {"
10278        1. ooˇanb
10279        2. zooˇanb
10280        3. ooˇanbz
10281        4. zooˇanbz
10282
10283        ooˇanb
10284    "};
10285    let completion_marked_buffer = indoc! {"
10286        1. ooanb
10287        2. zooanb
10288        3. ooanbz
10289        4. zooanbz
10290
10291        <oo|anb>
10292    "};
10293    let expected = indoc! {"
10294        1. foo_and_barˇ
10295        2. zfoo_and_barˇ
10296        3. foo_and_barˇz
10297        4. zfoo_and_barˇz
10298
10299        foo_and_barˇ
10300    "};
10301    cx.set_state(initial_state);
10302    cx.update_editor(|editor, window, cx| {
10303        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10304    });
10305    handle_completion_request_with_insert_and_replace(
10306        &mut cx,
10307        completion_marked_buffer,
10308        vec![completion_text],
10309        Arc::new(AtomicUsize::new(0)),
10310    )
10311    .await;
10312    cx.condition(|editor, _| editor.context_menu_visible())
10313        .await;
10314    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10315        editor
10316            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10317            .unwrap()
10318    });
10319    cx.assert_editor_state(expected);
10320    handle_resolve_completion_request(&mut cx, None).await;
10321    apply_additional_edits.await.unwrap();
10322}
10323
10324// This used to crash
10325#[gpui::test]
10326async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10327    init_test(cx, |_| {});
10328
10329    let buffer_text = indoc! {"
10330        fn main() {
10331            10.satu;
10332
10333            //
10334            // separate cursors so they open in different excerpts (manually reproducible)
10335            //
10336
10337            10.satu20;
10338        }
10339    "};
10340    let multibuffer_text_with_selections = indoc! {"
10341        fn main() {
10342            10.satuˇ;
10343
10344            //
10345
10346            //
10347
10348            10.satuˇ20;
10349        }
10350    "};
10351    let expected_multibuffer = indoc! {"
10352        fn main() {
10353            10.saturating_sub()ˇ;
10354
10355            //
10356
10357            //
10358
10359            10.saturating_sub()ˇ;
10360        }
10361    "};
10362
10363    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10364    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10365
10366    let fs = FakeFs::new(cx.executor());
10367    fs.insert_tree(
10368        path!("/a"),
10369        json!({
10370            "main.rs": buffer_text,
10371        }),
10372    )
10373    .await;
10374
10375    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10376    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10377    language_registry.add(rust_lang());
10378    let mut fake_servers = language_registry.register_fake_lsp(
10379        "Rust",
10380        FakeLspAdapter {
10381            capabilities: lsp::ServerCapabilities {
10382                completion_provider: Some(lsp::CompletionOptions {
10383                    resolve_provider: None,
10384                    ..lsp::CompletionOptions::default()
10385                }),
10386                ..lsp::ServerCapabilities::default()
10387            },
10388            ..FakeLspAdapter::default()
10389        },
10390    );
10391    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10392    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10393    let buffer = project
10394        .update(cx, |project, cx| {
10395            project.open_local_buffer(path!("/a/main.rs"), cx)
10396        })
10397        .await
10398        .unwrap();
10399
10400    let multi_buffer = cx.new(|cx| {
10401        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10402        multi_buffer.push_excerpts(
10403            buffer.clone(),
10404            [ExcerptRange::new(0..first_excerpt_end)],
10405            cx,
10406        );
10407        multi_buffer.push_excerpts(
10408            buffer.clone(),
10409            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10410            cx,
10411        );
10412        multi_buffer
10413    });
10414
10415    let editor = workspace
10416        .update(cx, |_, window, cx| {
10417            cx.new(|cx| {
10418                Editor::new(
10419                    EditorMode::Full {
10420                        scale_ui_elements_with_buffer_font_size: false,
10421                        show_active_line_background: false,
10422                    },
10423                    multi_buffer.clone(),
10424                    Some(project.clone()),
10425                    window,
10426                    cx,
10427                )
10428            })
10429        })
10430        .unwrap();
10431
10432    let pane = workspace
10433        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10434        .unwrap();
10435    pane.update_in(cx, |pane, window, cx| {
10436        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10437    });
10438
10439    let fake_server = fake_servers.next().await.unwrap();
10440
10441    editor.update_in(cx, |editor, window, cx| {
10442        editor.change_selections(None, window, cx, |s| {
10443            s.select_ranges([
10444                Point::new(1, 11)..Point::new(1, 11),
10445                Point::new(7, 11)..Point::new(7, 11),
10446            ])
10447        });
10448
10449        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10450    });
10451
10452    editor.update_in(cx, |editor, window, cx| {
10453        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10454    });
10455
10456    fake_server
10457        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10458            let completion_item = lsp::CompletionItem {
10459                label: "saturating_sub()".into(),
10460                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10461                    lsp::InsertReplaceEdit {
10462                        new_text: "saturating_sub()".to_owned(),
10463                        insert: lsp::Range::new(
10464                            lsp::Position::new(7, 7),
10465                            lsp::Position::new(7, 11),
10466                        ),
10467                        replace: lsp::Range::new(
10468                            lsp::Position::new(7, 7),
10469                            lsp::Position::new(7, 13),
10470                        ),
10471                    },
10472                )),
10473                ..lsp::CompletionItem::default()
10474            };
10475
10476            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10477        })
10478        .next()
10479        .await
10480        .unwrap();
10481
10482    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10483        .await;
10484
10485    editor
10486        .update_in(cx, |editor, window, cx| {
10487            editor
10488                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10489                .unwrap()
10490        })
10491        .await
10492        .unwrap();
10493
10494    editor.update(cx, |editor, cx| {
10495        assert_text_with_selections(editor, expected_multibuffer, cx);
10496    })
10497}
10498
10499#[gpui::test]
10500async fn test_completion(cx: &mut TestAppContext) {
10501    init_test(cx, |_| {});
10502
10503    let mut cx = EditorLspTestContext::new_rust(
10504        lsp::ServerCapabilities {
10505            completion_provider: Some(lsp::CompletionOptions {
10506                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10507                resolve_provider: Some(true),
10508                ..Default::default()
10509            }),
10510            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10511            ..Default::default()
10512        },
10513        cx,
10514    )
10515    .await;
10516    let counter = Arc::new(AtomicUsize::new(0));
10517
10518    cx.set_state(indoc! {"
10519        oneˇ
10520        two
10521        three
10522    "});
10523    cx.simulate_keystroke(".");
10524    handle_completion_request(
10525        &mut cx,
10526        indoc! {"
10527            one.|<>
10528            two
10529            three
10530        "},
10531        vec!["first_completion", "second_completion"],
10532        counter.clone(),
10533    )
10534    .await;
10535    cx.condition(|editor, _| editor.context_menu_visible())
10536        .await;
10537    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10538
10539    let _handler = handle_signature_help_request(
10540        &mut cx,
10541        lsp::SignatureHelp {
10542            signatures: vec![lsp::SignatureInformation {
10543                label: "test signature".to_string(),
10544                documentation: None,
10545                parameters: Some(vec![lsp::ParameterInformation {
10546                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10547                    documentation: None,
10548                }]),
10549                active_parameter: None,
10550            }],
10551            active_signature: None,
10552            active_parameter: None,
10553        },
10554    );
10555    cx.update_editor(|editor, window, cx| {
10556        assert!(
10557            !editor.signature_help_state.is_shown(),
10558            "No signature help was called for"
10559        );
10560        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10561    });
10562    cx.run_until_parked();
10563    cx.update_editor(|editor, _, _| {
10564        assert!(
10565            !editor.signature_help_state.is_shown(),
10566            "No signature help should be shown when completions menu is open"
10567        );
10568    });
10569
10570    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10571        editor.context_menu_next(&Default::default(), window, cx);
10572        editor
10573            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10574            .unwrap()
10575    });
10576    cx.assert_editor_state(indoc! {"
10577        one.second_completionˇ
10578        two
10579        three
10580    "});
10581
10582    handle_resolve_completion_request(
10583        &mut cx,
10584        Some(vec![
10585            (
10586                //This overlaps with the primary completion edit which is
10587                //misbehavior from the LSP spec, test that we filter it out
10588                indoc! {"
10589                    one.second_ˇcompletion
10590                    two
10591                    threeˇ
10592                "},
10593                "overlapping additional edit",
10594            ),
10595            (
10596                indoc! {"
10597                    one.second_completion
10598                    two
10599                    threeˇ
10600                "},
10601                "\nadditional edit",
10602            ),
10603        ]),
10604    )
10605    .await;
10606    apply_additional_edits.await.unwrap();
10607    cx.assert_editor_state(indoc! {"
10608        one.second_completionˇ
10609        two
10610        three
10611        additional edit
10612    "});
10613
10614    cx.set_state(indoc! {"
10615        one.second_completion
10616        twoˇ
10617        threeˇ
10618        additional edit
10619    "});
10620    cx.simulate_keystroke(" ");
10621    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10622    cx.simulate_keystroke("s");
10623    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10624
10625    cx.assert_editor_state(indoc! {"
10626        one.second_completion
10627        two sˇ
10628        three sˇ
10629        additional edit
10630    "});
10631    handle_completion_request(
10632        &mut cx,
10633        indoc! {"
10634            one.second_completion
10635            two s
10636            three <s|>
10637            additional edit
10638        "},
10639        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10640        counter.clone(),
10641    )
10642    .await;
10643    cx.condition(|editor, _| editor.context_menu_visible())
10644        .await;
10645    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10646
10647    cx.simulate_keystroke("i");
10648
10649    handle_completion_request(
10650        &mut cx,
10651        indoc! {"
10652            one.second_completion
10653            two si
10654            three <si|>
10655            additional edit
10656        "},
10657        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10658        counter.clone(),
10659    )
10660    .await;
10661    cx.condition(|editor, _| editor.context_menu_visible())
10662        .await;
10663    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10664
10665    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10666        editor
10667            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10668            .unwrap()
10669    });
10670    cx.assert_editor_state(indoc! {"
10671        one.second_completion
10672        two sixth_completionˇ
10673        three sixth_completionˇ
10674        additional edit
10675    "});
10676
10677    apply_additional_edits.await.unwrap();
10678
10679    update_test_language_settings(&mut cx, |settings| {
10680        settings.defaults.show_completions_on_input = Some(false);
10681    });
10682    cx.set_state("editorˇ");
10683    cx.simulate_keystroke(".");
10684    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10685    cx.simulate_keystrokes("c l o");
10686    cx.assert_editor_state("editor.cloˇ");
10687    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10688    cx.update_editor(|editor, window, cx| {
10689        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10690    });
10691    handle_completion_request(
10692        &mut cx,
10693        "editor.<clo|>",
10694        vec!["close", "clobber"],
10695        counter.clone(),
10696    )
10697    .await;
10698    cx.condition(|editor, _| editor.context_menu_visible())
10699        .await;
10700    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10701
10702    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10703        editor
10704            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10705            .unwrap()
10706    });
10707    cx.assert_editor_state("editor.closeˇ");
10708    handle_resolve_completion_request(&mut cx, None).await;
10709    apply_additional_edits.await.unwrap();
10710}
10711
10712#[gpui::test]
10713async fn test_word_completion(cx: &mut TestAppContext) {
10714    let lsp_fetch_timeout_ms = 10;
10715    init_test(cx, |language_settings| {
10716        language_settings.defaults.completions = Some(CompletionSettings {
10717            words: WordsCompletionMode::Fallback,
10718            lsp: true,
10719            lsp_fetch_timeout_ms: 10,
10720            lsp_insert_mode: LspInsertMode::Insert,
10721        });
10722    });
10723
10724    let mut cx = EditorLspTestContext::new_rust(
10725        lsp::ServerCapabilities {
10726            completion_provider: Some(lsp::CompletionOptions {
10727                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10728                ..lsp::CompletionOptions::default()
10729            }),
10730            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10731            ..lsp::ServerCapabilities::default()
10732        },
10733        cx,
10734    )
10735    .await;
10736
10737    let throttle_completions = Arc::new(AtomicBool::new(false));
10738
10739    let lsp_throttle_completions = throttle_completions.clone();
10740    let _completion_requests_handler =
10741        cx.lsp
10742            .server
10743            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10744                let lsp_throttle_completions = lsp_throttle_completions.clone();
10745                let cx = cx.clone();
10746                async move {
10747                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10748                        cx.background_executor()
10749                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10750                            .await;
10751                    }
10752                    Ok(Some(lsp::CompletionResponse::Array(vec![
10753                        lsp::CompletionItem {
10754                            label: "first".into(),
10755                            ..lsp::CompletionItem::default()
10756                        },
10757                        lsp::CompletionItem {
10758                            label: "last".into(),
10759                            ..lsp::CompletionItem::default()
10760                        },
10761                    ])))
10762                }
10763            });
10764
10765    cx.set_state(indoc! {"
10766        oneˇ
10767        two
10768        three
10769    "});
10770    cx.simulate_keystroke(".");
10771    cx.executor().run_until_parked();
10772    cx.condition(|editor, _| editor.context_menu_visible())
10773        .await;
10774    cx.update_editor(|editor, window, cx| {
10775        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10776        {
10777            assert_eq!(
10778                completion_menu_entries(&menu),
10779                &["first", "last"],
10780                "When LSP server is fast to reply, no fallback word completions are used"
10781            );
10782        } else {
10783            panic!("expected completion menu to be open");
10784        }
10785        editor.cancel(&Cancel, window, cx);
10786    });
10787    cx.executor().run_until_parked();
10788    cx.condition(|editor, _| !editor.context_menu_visible())
10789        .await;
10790
10791    throttle_completions.store(true, atomic::Ordering::Release);
10792    cx.simulate_keystroke(".");
10793    cx.executor()
10794        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10795    cx.executor().run_until_parked();
10796    cx.condition(|editor, _| editor.context_menu_visible())
10797        .await;
10798    cx.update_editor(|editor, _, _| {
10799        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10800        {
10801            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10802                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10803        } else {
10804            panic!("expected completion menu to be open");
10805        }
10806    });
10807}
10808
10809#[gpui::test]
10810async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10811    init_test(cx, |language_settings| {
10812        language_settings.defaults.completions = Some(CompletionSettings {
10813            words: WordsCompletionMode::Enabled,
10814            lsp: true,
10815            lsp_fetch_timeout_ms: 0,
10816            lsp_insert_mode: LspInsertMode::Insert,
10817        });
10818    });
10819
10820    let mut cx = EditorLspTestContext::new_rust(
10821        lsp::ServerCapabilities {
10822            completion_provider: Some(lsp::CompletionOptions {
10823                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10824                ..lsp::CompletionOptions::default()
10825            }),
10826            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10827            ..lsp::ServerCapabilities::default()
10828        },
10829        cx,
10830    )
10831    .await;
10832
10833    let _completion_requests_handler =
10834        cx.lsp
10835            .server
10836            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10837                Ok(Some(lsp::CompletionResponse::Array(vec![
10838                    lsp::CompletionItem {
10839                        label: "first".into(),
10840                        ..lsp::CompletionItem::default()
10841                    },
10842                    lsp::CompletionItem {
10843                        label: "last".into(),
10844                        ..lsp::CompletionItem::default()
10845                    },
10846                ])))
10847            });
10848
10849    cx.set_state(indoc! {"ˇ
10850        first
10851        last
10852        second
10853    "});
10854    cx.simulate_keystroke(".");
10855    cx.executor().run_until_parked();
10856    cx.condition(|editor, _| editor.context_menu_visible())
10857        .await;
10858    cx.update_editor(|editor, _, _| {
10859        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10860        {
10861            assert_eq!(
10862                completion_menu_entries(&menu),
10863                &["first", "last", "second"],
10864                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10865            );
10866        } else {
10867            panic!("expected completion menu to be open");
10868        }
10869    });
10870}
10871
10872#[gpui::test]
10873async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10874    init_test(cx, |language_settings| {
10875        language_settings.defaults.completions = Some(CompletionSettings {
10876            words: WordsCompletionMode::Disabled,
10877            lsp: true,
10878            lsp_fetch_timeout_ms: 0,
10879            lsp_insert_mode: LspInsertMode::Insert,
10880        });
10881    });
10882
10883    let mut cx = EditorLspTestContext::new_rust(
10884        lsp::ServerCapabilities {
10885            completion_provider: Some(lsp::CompletionOptions {
10886                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10887                ..lsp::CompletionOptions::default()
10888            }),
10889            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10890            ..lsp::ServerCapabilities::default()
10891        },
10892        cx,
10893    )
10894    .await;
10895
10896    let _completion_requests_handler =
10897        cx.lsp
10898            .server
10899            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10900                panic!("LSP completions should not be queried when dealing with word completions")
10901            });
10902
10903    cx.set_state(indoc! {"ˇ
10904        first
10905        last
10906        second
10907    "});
10908    cx.update_editor(|editor, window, cx| {
10909        editor.show_word_completions(&ShowWordCompletions, window, cx);
10910    });
10911    cx.executor().run_until_parked();
10912    cx.condition(|editor, _| editor.context_menu_visible())
10913        .await;
10914    cx.update_editor(|editor, _, _| {
10915        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10916        {
10917            assert_eq!(
10918                completion_menu_entries(&menu),
10919                &["first", "last", "second"],
10920                "`ShowWordCompletions` action should show word completions"
10921            );
10922        } else {
10923            panic!("expected completion menu to be open");
10924        }
10925    });
10926
10927    cx.simulate_keystroke("l");
10928    cx.executor().run_until_parked();
10929    cx.condition(|editor, _| editor.context_menu_visible())
10930        .await;
10931    cx.update_editor(|editor, _, _| {
10932        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10933        {
10934            assert_eq!(
10935                completion_menu_entries(&menu),
10936                &["last"],
10937                "After showing word completions, further editing should filter them and not query the LSP"
10938            );
10939        } else {
10940            panic!("expected completion menu to be open");
10941        }
10942    });
10943}
10944
10945#[gpui::test]
10946async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10947    init_test(cx, |language_settings| {
10948        language_settings.defaults.completions = Some(CompletionSettings {
10949            words: WordsCompletionMode::Fallback,
10950            lsp: false,
10951            lsp_fetch_timeout_ms: 0,
10952            lsp_insert_mode: LspInsertMode::Insert,
10953        });
10954    });
10955
10956    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10957
10958    cx.set_state(indoc! {"ˇ
10959        0_usize
10960        let
10961        33
10962        4.5f32
10963    "});
10964    cx.update_editor(|editor, window, cx| {
10965        editor.show_completions(&ShowCompletions::default(), window, cx);
10966    });
10967    cx.executor().run_until_parked();
10968    cx.condition(|editor, _| editor.context_menu_visible())
10969        .await;
10970    cx.update_editor(|editor, window, cx| {
10971        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10972        {
10973            assert_eq!(
10974                completion_menu_entries(&menu),
10975                &["let"],
10976                "With no digits in the completion query, no digits should be in the word completions"
10977            );
10978        } else {
10979            panic!("expected completion menu to be open");
10980        }
10981        editor.cancel(&Cancel, window, cx);
10982    });
10983
10984    cx.set_state(indoc! {"10985        0_usize
10986        let
10987        3
10988        33.35f32
10989    "});
10990    cx.update_editor(|editor, window, cx| {
10991        editor.show_completions(&ShowCompletions::default(), window, cx);
10992    });
10993    cx.executor().run_until_parked();
10994    cx.condition(|editor, _| editor.context_menu_visible())
10995        .await;
10996    cx.update_editor(|editor, _, _| {
10997        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10998        {
10999            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11000                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11001        } else {
11002            panic!("expected completion menu to be open");
11003        }
11004    });
11005}
11006
11007fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11008    let position = || lsp::Position {
11009        line: params.text_document_position.position.line,
11010        character: params.text_document_position.position.character,
11011    };
11012    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11013        range: lsp::Range {
11014            start: position(),
11015            end: position(),
11016        },
11017        new_text: text.to_string(),
11018    }))
11019}
11020
11021#[gpui::test]
11022async fn test_multiline_completion(cx: &mut TestAppContext) {
11023    init_test(cx, |_| {});
11024
11025    let fs = FakeFs::new(cx.executor());
11026    fs.insert_tree(
11027        path!("/a"),
11028        json!({
11029            "main.ts": "a",
11030        }),
11031    )
11032    .await;
11033
11034    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11035    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11036    let typescript_language = Arc::new(Language::new(
11037        LanguageConfig {
11038            name: "TypeScript".into(),
11039            matcher: LanguageMatcher {
11040                path_suffixes: vec!["ts".to_string()],
11041                ..LanguageMatcher::default()
11042            },
11043            line_comments: vec!["// ".into()],
11044            ..LanguageConfig::default()
11045        },
11046        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11047    ));
11048    language_registry.add(typescript_language.clone());
11049    let mut fake_servers = language_registry.register_fake_lsp(
11050        "TypeScript",
11051        FakeLspAdapter {
11052            capabilities: lsp::ServerCapabilities {
11053                completion_provider: Some(lsp::CompletionOptions {
11054                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11055                    ..lsp::CompletionOptions::default()
11056                }),
11057                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11058                ..lsp::ServerCapabilities::default()
11059            },
11060            // Emulate vtsls label generation
11061            label_for_completion: Some(Box::new(|item, _| {
11062                let text = if let Some(description) = item
11063                    .label_details
11064                    .as_ref()
11065                    .and_then(|label_details| label_details.description.as_ref())
11066                {
11067                    format!("{} {}", item.label, description)
11068                } else if let Some(detail) = &item.detail {
11069                    format!("{} {}", item.label, detail)
11070                } else {
11071                    item.label.clone()
11072                };
11073                let len = text.len();
11074                Some(language::CodeLabel {
11075                    text,
11076                    runs: Vec::new(),
11077                    filter_range: 0..len,
11078                })
11079            })),
11080            ..FakeLspAdapter::default()
11081        },
11082    );
11083    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11084    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11085    let worktree_id = workspace
11086        .update(cx, |workspace, _window, cx| {
11087            workspace.project().update(cx, |project, cx| {
11088                project.worktrees(cx).next().unwrap().read(cx).id()
11089            })
11090        })
11091        .unwrap();
11092    let _buffer = project
11093        .update(cx, |project, cx| {
11094            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11095        })
11096        .await
11097        .unwrap();
11098    let editor = workspace
11099        .update(cx, |workspace, window, cx| {
11100            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11101        })
11102        .unwrap()
11103        .await
11104        .unwrap()
11105        .downcast::<Editor>()
11106        .unwrap();
11107    let fake_server = fake_servers.next().await.unwrap();
11108
11109    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11110    let multiline_label_2 = "a\nb\nc\n";
11111    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11112    let multiline_description = "d\ne\nf\n";
11113    let multiline_detail_2 = "g\nh\ni\n";
11114
11115    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11116        move |params, _| async move {
11117            Ok(Some(lsp::CompletionResponse::Array(vec![
11118                lsp::CompletionItem {
11119                    label: multiline_label.to_string(),
11120                    text_edit: gen_text_edit(&params, "new_text_1"),
11121                    ..lsp::CompletionItem::default()
11122                },
11123                lsp::CompletionItem {
11124                    label: "single line label 1".to_string(),
11125                    detail: Some(multiline_detail.to_string()),
11126                    text_edit: gen_text_edit(&params, "new_text_2"),
11127                    ..lsp::CompletionItem::default()
11128                },
11129                lsp::CompletionItem {
11130                    label: "single line label 2".to_string(),
11131                    label_details: Some(lsp::CompletionItemLabelDetails {
11132                        description: Some(multiline_description.to_string()),
11133                        detail: None,
11134                    }),
11135                    text_edit: gen_text_edit(&params, "new_text_2"),
11136                    ..lsp::CompletionItem::default()
11137                },
11138                lsp::CompletionItem {
11139                    label: multiline_label_2.to_string(),
11140                    detail: Some(multiline_detail_2.to_string()),
11141                    text_edit: gen_text_edit(&params, "new_text_3"),
11142                    ..lsp::CompletionItem::default()
11143                },
11144                lsp::CompletionItem {
11145                    label: "Label with many     spaces and \t but without newlines".to_string(),
11146                    detail: Some(
11147                        "Details with many     spaces and \t but without newlines".to_string(),
11148                    ),
11149                    text_edit: gen_text_edit(&params, "new_text_4"),
11150                    ..lsp::CompletionItem::default()
11151                },
11152            ])))
11153        },
11154    );
11155
11156    editor.update_in(cx, |editor, window, cx| {
11157        cx.focus_self(window);
11158        editor.move_to_end(&MoveToEnd, window, cx);
11159        editor.handle_input(".", window, cx);
11160    });
11161    cx.run_until_parked();
11162    completion_handle.next().await.unwrap();
11163
11164    editor.update(cx, |editor, _| {
11165        assert!(editor.context_menu_visible());
11166        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11167        {
11168            let completion_labels = menu
11169                .completions
11170                .borrow()
11171                .iter()
11172                .map(|c| c.label.text.clone())
11173                .collect::<Vec<_>>();
11174            assert_eq!(
11175                completion_labels,
11176                &[
11177                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11178                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11179                    "single line label 2 d e f ",
11180                    "a b c g h i ",
11181                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11182                ],
11183                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11184            );
11185
11186            for completion in menu
11187                .completions
11188                .borrow()
11189                .iter() {
11190                    assert_eq!(
11191                        completion.label.filter_range,
11192                        0..completion.label.text.len(),
11193                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11194                    );
11195                }
11196        } else {
11197            panic!("expected completion menu to be open");
11198        }
11199    });
11200}
11201
11202#[gpui::test]
11203async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11204    init_test(cx, |_| {});
11205    let mut cx = EditorLspTestContext::new_rust(
11206        lsp::ServerCapabilities {
11207            completion_provider: Some(lsp::CompletionOptions {
11208                trigger_characters: Some(vec![".".to_string()]),
11209                ..Default::default()
11210            }),
11211            ..Default::default()
11212        },
11213        cx,
11214    )
11215    .await;
11216    cx.lsp
11217        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11218            Ok(Some(lsp::CompletionResponse::Array(vec![
11219                lsp::CompletionItem {
11220                    label: "first".into(),
11221                    ..Default::default()
11222                },
11223                lsp::CompletionItem {
11224                    label: "last".into(),
11225                    ..Default::default()
11226                },
11227            ])))
11228        });
11229    cx.set_state("variableˇ");
11230    cx.simulate_keystroke(".");
11231    cx.executor().run_until_parked();
11232
11233    cx.update_editor(|editor, _, _| {
11234        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11235        {
11236            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11237        } else {
11238            panic!("expected completion menu to be open");
11239        }
11240    });
11241
11242    cx.update_editor(|editor, window, cx| {
11243        editor.move_page_down(&MovePageDown::default(), window, cx);
11244        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11245        {
11246            assert!(
11247                menu.selected_item == 1,
11248                "expected PageDown to select the last item from the context menu"
11249            );
11250        } else {
11251            panic!("expected completion menu to stay open after PageDown");
11252        }
11253    });
11254
11255    cx.update_editor(|editor, window, cx| {
11256        editor.move_page_up(&MovePageUp::default(), window, cx);
11257        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11258        {
11259            assert!(
11260                menu.selected_item == 0,
11261                "expected PageUp to select the first item from the context menu"
11262            );
11263        } else {
11264            panic!("expected completion menu to stay open after PageUp");
11265        }
11266    });
11267}
11268
11269#[gpui::test]
11270async fn test_completion_sort(cx: &mut TestAppContext) {
11271    init_test(cx, |_| {});
11272    let mut cx = EditorLspTestContext::new_rust(
11273        lsp::ServerCapabilities {
11274            completion_provider: Some(lsp::CompletionOptions {
11275                trigger_characters: Some(vec![".".to_string()]),
11276                ..Default::default()
11277            }),
11278            ..Default::default()
11279        },
11280        cx,
11281    )
11282    .await;
11283    cx.lsp
11284        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11285            Ok(Some(lsp::CompletionResponse::Array(vec![
11286                lsp::CompletionItem {
11287                    label: "Range".into(),
11288                    sort_text: Some("a".into()),
11289                    ..Default::default()
11290                },
11291                lsp::CompletionItem {
11292                    label: "r".into(),
11293                    sort_text: Some("b".into()),
11294                    ..Default::default()
11295                },
11296                lsp::CompletionItem {
11297                    label: "ret".into(),
11298                    sort_text: Some("c".into()),
11299                    ..Default::default()
11300                },
11301                lsp::CompletionItem {
11302                    label: "return".into(),
11303                    sort_text: Some("d".into()),
11304                    ..Default::default()
11305                },
11306                lsp::CompletionItem {
11307                    label: "slice".into(),
11308                    sort_text: Some("d".into()),
11309                    ..Default::default()
11310                },
11311            ])))
11312        });
11313    cx.set_state("");
11314    cx.executor().run_until_parked();
11315    cx.update_editor(|editor, window, cx| {
11316        editor.show_completions(
11317            &ShowCompletions {
11318                trigger: Some("r".into()),
11319            },
11320            window,
11321            cx,
11322        );
11323    });
11324    cx.executor().run_until_parked();
11325
11326    cx.update_editor(|editor, _, _| {
11327        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11328        {
11329            assert_eq!(
11330                completion_menu_entries(&menu),
11331                &["r", "ret", "Range", "return"]
11332            );
11333        } else {
11334            panic!("expected completion menu to be open");
11335        }
11336    });
11337}
11338
11339#[gpui::test]
11340async fn test_as_is_completions(cx: &mut TestAppContext) {
11341    init_test(cx, |_| {});
11342    let mut cx = EditorLspTestContext::new_rust(
11343        lsp::ServerCapabilities {
11344            completion_provider: Some(lsp::CompletionOptions {
11345                ..Default::default()
11346            }),
11347            ..Default::default()
11348        },
11349        cx,
11350    )
11351    .await;
11352    cx.lsp
11353        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11354            Ok(Some(lsp::CompletionResponse::Array(vec![
11355                lsp::CompletionItem {
11356                    label: "unsafe".into(),
11357                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11358                        range: lsp::Range {
11359                            start: lsp::Position {
11360                                line: 1,
11361                                character: 2,
11362                            },
11363                            end: lsp::Position {
11364                                line: 1,
11365                                character: 3,
11366                            },
11367                        },
11368                        new_text: "unsafe".to_string(),
11369                    })),
11370                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11371                    ..Default::default()
11372                },
11373            ])))
11374        });
11375    cx.set_state("fn a() {}\n");
11376    cx.executor().run_until_parked();
11377    cx.update_editor(|editor, window, cx| {
11378        editor.show_completions(
11379            &ShowCompletions {
11380                trigger: Some("\n".into()),
11381            },
11382            window,
11383            cx,
11384        );
11385    });
11386    cx.executor().run_until_parked();
11387
11388    cx.update_editor(|editor, window, cx| {
11389        editor.confirm_completion(&Default::default(), window, cx)
11390    });
11391    cx.executor().run_until_parked();
11392    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11393}
11394
11395#[gpui::test]
11396async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11397    init_test(cx, |_| {});
11398
11399    let mut cx = EditorLspTestContext::new_rust(
11400        lsp::ServerCapabilities {
11401            completion_provider: Some(lsp::CompletionOptions {
11402                trigger_characters: Some(vec![".".to_string()]),
11403                resolve_provider: Some(true),
11404                ..Default::default()
11405            }),
11406            ..Default::default()
11407        },
11408        cx,
11409    )
11410    .await;
11411
11412    cx.set_state("fn main() { let a = 2ˇ; }");
11413    cx.simulate_keystroke(".");
11414    let completion_item = lsp::CompletionItem {
11415        label: "Some".into(),
11416        kind: Some(lsp::CompletionItemKind::SNIPPET),
11417        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11418        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11419            kind: lsp::MarkupKind::Markdown,
11420            value: "```rust\nSome(2)\n```".to_string(),
11421        })),
11422        deprecated: Some(false),
11423        sort_text: Some("Some".to_string()),
11424        filter_text: Some("Some".to_string()),
11425        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11426        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11427            range: lsp::Range {
11428                start: lsp::Position {
11429                    line: 0,
11430                    character: 22,
11431                },
11432                end: lsp::Position {
11433                    line: 0,
11434                    character: 22,
11435                },
11436            },
11437            new_text: "Some(2)".to_string(),
11438        })),
11439        additional_text_edits: Some(vec![lsp::TextEdit {
11440            range: lsp::Range {
11441                start: lsp::Position {
11442                    line: 0,
11443                    character: 20,
11444                },
11445                end: lsp::Position {
11446                    line: 0,
11447                    character: 22,
11448                },
11449            },
11450            new_text: "".to_string(),
11451        }]),
11452        ..Default::default()
11453    };
11454
11455    let closure_completion_item = completion_item.clone();
11456    let counter = Arc::new(AtomicUsize::new(0));
11457    let counter_clone = counter.clone();
11458    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11459        let task_completion_item = closure_completion_item.clone();
11460        counter_clone.fetch_add(1, atomic::Ordering::Release);
11461        async move {
11462            Ok(Some(lsp::CompletionResponse::Array(vec![
11463                task_completion_item,
11464            ])))
11465        }
11466    });
11467
11468    cx.condition(|editor, _| editor.context_menu_visible())
11469        .await;
11470    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11471    assert!(request.next().await.is_some());
11472    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11473
11474    cx.simulate_keystrokes("S o m");
11475    cx.condition(|editor, _| editor.context_menu_visible())
11476        .await;
11477    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11478    assert!(request.next().await.is_some());
11479    assert!(request.next().await.is_some());
11480    assert!(request.next().await.is_some());
11481    request.close();
11482    assert!(request.next().await.is_none());
11483    assert_eq!(
11484        counter.load(atomic::Ordering::Acquire),
11485        4,
11486        "With the completions menu open, only one LSP request should happen per input"
11487    );
11488}
11489
11490#[gpui::test]
11491async fn test_toggle_comment(cx: &mut TestAppContext) {
11492    init_test(cx, |_| {});
11493    let mut cx = EditorTestContext::new(cx).await;
11494    let language = Arc::new(Language::new(
11495        LanguageConfig {
11496            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11497            ..Default::default()
11498        },
11499        Some(tree_sitter_rust::LANGUAGE.into()),
11500    ));
11501    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11502
11503    // If multiple selections intersect a line, the line is only toggled once.
11504    cx.set_state(indoc! {"
11505        fn a() {
11506            «//b();
11507            ˇ»// «c();
11508            //ˇ»  d();
11509        }
11510    "});
11511
11512    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11513
11514    cx.assert_editor_state(indoc! {"
11515        fn a() {
11516            «b();
11517            c();
11518            ˇ» d();
11519        }
11520    "});
11521
11522    // The comment prefix is inserted at the same column for every line in a
11523    // selection.
11524    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11525
11526    cx.assert_editor_state(indoc! {"
11527        fn a() {
11528            // «b();
11529            // c();
11530            ˇ»//  d();
11531        }
11532    "});
11533
11534    // If a selection ends at the beginning of a line, that line is not toggled.
11535    cx.set_selections_state(indoc! {"
11536        fn a() {
11537            // b();
11538            «// c();
11539        ˇ»    //  d();
11540        }
11541    "});
11542
11543    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11544
11545    cx.assert_editor_state(indoc! {"
11546        fn a() {
11547            // b();
11548            «c();
11549        ˇ»    //  d();
11550        }
11551    "});
11552
11553    // If a selection span a single line and is empty, the line is toggled.
11554    cx.set_state(indoc! {"
11555        fn a() {
11556            a();
11557            b();
11558        ˇ
11559        }
11560    "});
11561
11562    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11563
11564    cx.assert_editor_state(indoc! {"
11565        fn a() {
11566            a();
11567            b();
11568        //•ˇ
11569        }
11570    "});
11571
11572    // If a selection span multiple lines, empty lines are not toggled.
11573    cx.set_state(indoc! {"
11574        fn a() {
11575            «a();
11576
11577            c();ˇ»
11578        }
11579    "});
11580
11581    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11582
11583    cx.assert_editor_state(indoc! {"
11584        fn a() {
11585            // «a();
11586
11587            // c();ˇ»
11588        }
11589    "});
11590
11591    // If a selection includes multiple comment prefixes, all lines are uncommented.
11592    cx.set_state(indoc! {"
11593        fn a() {
11594            «// a();
11595            /// b();
11596            //! c();ˇ»
11597        }
11598    "});
11599
11600    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11601
11602    cx.assert_editor_state(indoc! {"
11603        fn a() {
11604            «a();
11605            b();
11606            c();ˇ»
11607        }
11608    "});
11609}
11610
11611#[gpui::test]
11612async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11613    init_test(cx, |_| {});
11614    let mut cx = EditorTestContext::new(cx).await;
11615    let language = Arc::new(Language::new(
11616        LanguageConfig {
11617            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11618            ..Default::default()
11619        },
11620        Some(tree_sitter_rust::LANGUAGE.into()),
11621    ));
11622    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11623
11624    let toggle_comments = &ToggleComments {
11625        advance_downwards: false,
11626        ignore_indent: true,
11627    };
11628
11629    // If multiple selections intersect a line, the line is only toggled once.
11630    cx.set_state(indoc! {"
11631        fn a() {
11632        //    «b();
11633        //    c();
11634        //    ˇ» d();
11635        }
11636    "});
11637
11638    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11639
11640    cx.assert_editor_state(indoc! {"
11641        fn a() {
11642            «b();
11643            c();
11644            ˇ» d();
11645        }
11646    "});
11647
11648    // The comment prefix is inserted at the beginning of each line
11649    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11650
11651    cx.assert_editor_state(indoc! {"
11652        fn a() {
11653        //    «b();
11654        //    c();
11655        //    ˇ» d();
11656        }
11657    "});
11658
11659    // If a selection ends at the beginning of a line, that line is not toggled.
11660    cx.set_selections_state(indoc! {"
11661        fn a() {
11662        //    b();
11663        //    «c();
11664        ˇ»//     d();
11665        }
11666    "});
11667
11668    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11669
11670    cx.assert_editor_state(indoc! {"
11671        fn a() {
11672        //    b();
11673            «c();
11674        ˇ»//     d();
11675        }
11676    "});
11677
11678    // If a selection span a single line and is empty, the line is toggled.
11679    cx.set_state(indoc! {"
11680        fn a() {
11681            a();
11682            b();
11683        ˇ
11684        }
11685    "});
11686
11687    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11688
11689    cx.assert_editor_state(indoc! {"
11690        fn a() {
11691            a();
11692            b();
11693        //ˇ
11694        }
11695    "});
11696
11697    // If a selection span multiple lines, empty lines are not toggled.
11698    cx.set_state(indoc! {"
11699        fn a() {
11700            «a();
11701
11702            c();ˇ»
11703        }
11704    "});
11705
11706    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11707
11708    cx.assert_editor_state(indoc! {"
11709        fn a() {
11710        //    «a();
11711
11712        //    c();ˇ»
11713        }
11714    "});
11715
11716    // If a selection includes multiple comment prefixes, all lines are uncommented.
11717    cx.set_state(indoc! {"
11718        fn a() {
11719        //    «a();
11720        ///    b();
11721        //!    c();ˇ»
11722        }
11723    "});
11724
11725    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11726
11727    cx.assert_editor_state(indoc! {"
11728        fn a() {
11729            «a();
11730            b();
11731            c();ˇ»
11732        }
11733    "});
11734}
11735
11736#[gpui::test]
11737async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11738    init_test(cx, |_| {});
11739
11740    let language = Arc::new(Language::new(
11741        LanguageConfig {
11742            line_comments: vec!["// ".into()],
11743            ..Default::default()
11744        },
11745        Some(tree_sitter_rust::LANGUAGE.into()),
11746    ));
11747
11748    let mut cx = EditorTestContext::new(cx).await;
11749
11750    cx.language_registry().add(language.clone());
11751    cx.update_buffer(|buffer, cx| {
11752        buffer.set_language(Some(language), cx);
11753    });
11754
11755    let toggle_comments = &ToggleComments {
11756        advance_downwards: true,
11757        ignore_indent: false,
11758    };
11759
11760    // Single cursor on one line -> advance
11761    // Cursor moves horizontally 3 characters as well on non-blank line
11762    cx.set_state(indoc!(
11763        "fn a() {
11764             ˇdog();
11765             cat();
11766        }"
11767    ));
11768    cx.update_editor(|editor, window, cx| {
11769        editor.toggle_comments(toggle_comments, window, cx);
11770    });
11771    cx.assert_editor_state(indoc!(
11772        "fn a() {
11773             // dog();
11774             catˇ();
11775        }"
11776    ));
11777
11778    // Single selection on one line -> don't advance
11779    cx.set_state(indoc!(
11780        "fn a() {
11781             «dog()ˇ»;
11782             cat();
11783        }"
11784    ));
11785    cx.update_editor(|editor, window, cx| {
11786        editor.toggle_comments(toggle_comments, window, cx);
11787    });
11788    cx.assert_editor_state(indoc!(
11789        "fn a() {
11790             // «dog()ˇ»;
11791             cat();
11792        }"
11793    ));
11794
11795    // Multiple cursors on one line -> advance
11796    cx.set_state(indoc!(
11797        "fn a() {
11798             ˇdˇog();
11799             cat();
11800        }"
11801    ));
11802    cx.update_editor(|editor, window, cx| {
11803        editor.toggle_comments(toggle_comments, window, cx);
11804    });
11805    cx.assert_editor_state(indoc!(
11806        "fn a() {
11807             // dog();
11808             catˇ(ˇ);
11809        }"
11810    ));
11811
11812    // Multiple cursors on one line, with selection -> don't advance
11813    cx.set_state(indoc!(
11814        "fn a() {
11815             ˇdˇog«()ˇ»;
11816             cat();
11817        }"
11818    ));
11819    cx.update_editor(|editor, window, cx| {
11820        editor.toggle_comments(toggle_comments, window, cx);
11821    });
11822    cx.assert_editor_state(indoc!(
11823        "fn a() {
11824             // ˇdˇog«()ˇ»;
11825             cat();
11826        }"
11827    ));
11828
11829    // Single cursor on one line -> advance
11830    // Cursor moves to column 0 on blank line
11831    cx.set_state(indoc!(
11832        "fn a() {
11833             ˇdog();
11834
11835             cat();
11836        }"
11837    ));
11838    cx.update_editor(|editor, window, cx| {
11839        editor.toggle_comments(toggle_comments, window, cx);
11840    });
11841    cx.assert_editor_state(indoc!(
11842        "fn a() {
11843             // dog();
11844        ˇ
11845             cat();
11846        }"
11847    ));
11848
11849    // Single cursor on one line -> advance
11850    // Cursor starts and ends at column 0
11851    cx.set_state(indoc!(
11852        "fn a() {
11853         ˇ    dog();
11854             cat();
11855        }"
11856    ));
11857    cx.update_editor(|editor, window, cx| {
11858        editor.toggle_comments(toggle_comments, window, cx);
11859    });
11860    cx.assert_editor_state(indoc!(
11861        "fn a() {
11862             // dog();
11863         ˇ    cat();
11864        }"
11865    ));
11866}
11867
11868#[gpui::test]
11869async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11870    init_test(cx, |_| {});
11871
11872    let mut cx = EditorTestContext::new(cx).await;
11873
11874    let html_language = Arc::new(
11875        Language::new(
11876            LanguageConfig {
11877                name: "HTML".into(),
11878                block_comment: Some(("<!-- ".into(), " -->".into())),
11879                ..Default::default()
11880            },
11881            Some(tree_sitter_html::LANGUAGE.into()),
11882        )
11883        .with_injection_query(
11884            r#"
11885            (script_element
11886                (raw_text) @injection.content
11887                (#set! injection.language "javascript"))
11888            "#,
11889        )
11890        .unwrap(),
11891    );
11892
11893    let javascript_language = Arc::new(Language::new(
11894        LanguageConfig {
11895            name: "JavaScript".into(),
11896            line_comments: vec!["// ".into()],
11897            ..Default::default()
11898        },
11899        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11900    ));
11901
11902    cx.language_registry().add(html_language.clone());
11903    cx.language_registry().add(javascript_language.clone());
11904    cx.update_buffer(|buffer, cx| {
11905        buffer.set_language(Some(html_language), cx);
11906    });
11907
11908    // Toggle comments for empty selections
11909    cx.set_state(
11910        &r#"
11911            <p>A</p>ˇ
11912            <p>B</p>ˇ
11913            <p>C</p>ˇ
11914        "#
11915        .unindent(),
11916    );
11917    cx.update_editor(|editor, window, cx| {
11918        editor.toggle_comments(&ToggleComments::default(), window, cx)
11919    });
11920    cx.assert_editor_state(
11921        &r#"
11922            <!-- <p>A</p>ˇ -->
11923            <!-- <p>B</p>ˇ -->
11924            <!-- <p>C</p>ˇ -->
11925        "#
11926        .unindent(),
11927    );
11928    cx.update_editor(|editor, window, cx| {
11929        editor.toggle_comments(&ToggleComments::default(), window, cx)
11930    });
11931    cx.assert_editor_state(
11932        &r#"
11933            <p>A</p>ˇ
11934            <p>B</p>ˇ
11935            <p>C</p>ˇ
11936        "#
11937        .unindent(),
11938    );
11939
11940    // Toggle comments for mixture of empty and non-empty selections, where
11941    // multiple selections occupy a given line.
11942    cx.set_state(
11943        &r#"
11944            <p>A«</p>
11945            <p>ˇ»B</p>ˇ
11946            <p>C«</p>
11947            <p>ˇ»D</p>ˇ
11948        "#
11949        .unindent(),
11950    );
11951
11952    cx.update_editor(|editor, window, cx| {
11953        editor.toggle_comments(&ToggleComments::default(), window, cx)
11954    });
11955    cx.assert_editor_state(
11956        &r#"
11957            <!-- <p>A«</p>
11958            <p>ˇ»B</p>ˇ -->
11959            <!-- <p>C«</p>
11960            <p>ˇ»D</p>ˇ -->
11961        "#
11962        .unindent(),
11963    );
11964    cx.update_editor(|editor, window, cx| {
11965        editor.toggle_comments(&ToggleComments::default(), window, cx)
11966    });
11967    cx.assert_editor_state(
11968        &r#"
11969            <p>A«</p>
11970            <p>ˇ»B</p>ˇ
11971            <p>C«</p>
11972            <p>ˇ»D</p>ˇ
11973        "#
11974        .unindent(),
11975    );
11976
11977    // Toggle comments when different languages are active for different
11978    // selections.
11979    cx.set_state(
11980        &r#"
11981            ˇ<script>
11982                ˇvar x = new Y();
11983            ˇ</script>
11984        "#
11985        .unindent(),
11986    );
11987    cx.executor().run_until_parked();
11988    cx.update_editor(|editor, window, cx| {
11989        editor.toggle_comments(&ToggleComments::default(), window, cx)
11990    });
11991    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11992    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11993    cx.assert_editor_state(
11994        &r#"
11995            <!-- ˇ<script> -->
11996                // ˇvar x = new Y();
11997            <!-- ˇ</script> -->
11998        "#
11999        .unindent(),
12000    );
12001}
12002
12003#[gpui::test]
12004fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
12005    init_test(cx, |_| {});
12006
12007    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12008    let multibuffer = cx.new(|cx| {
12009        let mut multibuffer = MultiBuffer::new(ReadWrite);
12010        multibuffer.push_excerpts(
12011            buffer.clone(),
12012            [
12013                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12014                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12015            ],
12016            cx,
12017        );
12018        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12019        multibuffer
12020    });
12021
12022    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12023    editor.update_in(cx, |editor, window, cx| {
12024        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12025        editor.change_selections(None, window, cx, |s| {
12026            s.select_ranges([
12027                Point::new(0, 0)..Point::new(0, 0),
12028                Point::new(1, 0)..Point::new(1, 0),
12029            ])
12030        });
12031
12032        editor.handle_input("X", window, cx);
12033        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12034        assert_eq!(
12035            editor.selections.ranges(cx),
12036            [
12037                Point::new(0, 1)..Point::new(0, 1),
12038                Point::new(1, 1)..Point::new(1, 1),
12039            ]
12040        );
12041
12042        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12043        editor.change_selections(None, window, cx, |s| {
12044            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12045        });
12046        editor.backspace(&Default::default(), window, cx);
12047        assert_eq!(editor.text(cx), "Xa\nbbb");
12048        assert_eq!(
12049            editor.selections.ranges(cx),
12050            [Point::new(1, 0)..Point::new(1, 0)]
12051        );
12052
12053        editor.change_selections(None, window, cx, |s| {
12054            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12055        });
12056        editor.backspace(&Default::default(), window, cx);
12057        assert_eq!(editor.text(cx), "X\nbb");
12058        assert_eq!(
12059            editor.selections.ranges(cx),
12060            [Point::new(0, 1)..Point::new(0, 1)]
12061        );
12062    });
12063}
12064
12065#[gpui::test]
12066fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12067    init_test(cx, |_| {});
12068
12069    let markers = vec![('[', ']').into(), ('(', ')').into()];
12070    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12071        indoc! {"
12072            [aaaa
12073            (bbbb]
12074            cccc)",
12075        },
12076        markers.clone(),
12077    );
12078    let excerpt_ranges = markers.into_iter().map(|marker| {
12079        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12080        ExcerptRange::new(context.clone())
12081    });
12082    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12083    let multibuffer = cx.new(|cx| {
12084        let mut multibuffer = MultiBuffer::new(ReadWrite);
12085        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12086        multibuffer
12087    });
12088
12089    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12090    editor.update_in(cx, |editor, window, cx| {
12091        let (expected_text, selection_ranges) = marked_text_ranges(
12092            indoc! {"
12093                aaaa
12094                bˇbbb
12095                bˇbbˇb
12096                cccc"
12097            },
12098            true,
12099        );
12100        assert_eq!(editor.text(cx), expected_text);
12101        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12102
12103        editor.handle_input("X", window, cx);
12104
12105        let (expected_text, expected_selections) = marked_text_ranges(
12106            indoc! {"
12107                aaaa
12108                bXˇbbXb
12109                bXˇbbXˇb
12110                cccc"
12111            },
12112            false,
12113        );
12114        assert_eq!(editor.text(cx), expected_text);
12115        assert_eq!(editor.selections.ranges(cx), expected_selections);
12116
12117        editor.newline(&Newline, window, cx);
12118        let (expected_text, expected_selections) = marked_text_ranges(
12119            indoc! {"
12120                aaaa
12121                bX
12122                ˇbbX
12123                b
12124                bX
12125                ˇbbX
12126                ˇb
12127                cccc"
12128            },
12129            false,
12130        );
12131        assert_eq!(editor.text(cx), expected_text);
12132        assert_eq!(editor.selections.ranges(cx), expected_selections);
12133    });
12134}
12135
12136#[gpui::test]
12137fn test_refresh_selections(cx: &mut TestAppContext) {
12138    init_test(cx, |_| {});
12139
12140    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12141    let mut excerpt1_id = None;
12142    let multibuffer = cx.new(|cx| {
12143        let mut multibuffer = MultiBuffer::new(ReadWrite);
12144        excerpt1_id = multibuffer
12145            .push_excerpts(
12146                buffer.clone(),
12147                [
12148                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12149                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12150                ],
12151                cx,
12152            )
12153            .into_iter()
12154            .next();
12155        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12156        multibuffer
12157    });
12158
12159    let editor = cx.add_window(|window, cx| {
12160        let mut editor = build_editor(multibuffer.clone(), window, cx);
12161        let snapshot = editor.snapshot(window, cx);
12162        editor.change_selections(None, window, cx, |s| {
12163            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12164        });
12165        editor.begin_selection(
12166            Point::new(2, 1).to_display_point(&snapshot),
12167            true,
12168            1,
12169            window,
12170            cx,
12171        );
12172        assert_eq!(
12173            editor.selections.ranges(cx),
12174            [
12175                Point::new(1, 3)..Point::new(1, 3),
12176                Point::new(2, 1)..Point::new(2, 1),
12177            ]
12178        );
12179        editor
12180    });
12181
12182    // Refreshing selections is a no-op when excerpts haven't changed.
12183    _ = editor.update(cx, |editor, window, cx| {
12184        editor.change_selections(None, window, cx, |s| s.refresh());
12185        assert_eq!(
12186            editor.selections.ranges(cx),
12187            [
12188                Point::new(1, 3)..Point::new(1, 3),
12189                Point::new(2, 1)..Point::new(2, 1),
12190            ]
12191        );
12192    });
12193
12194    multibuffer.update(cx, |multibuffer, cx| {
12195        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12196    });
12197    _ = editor.update(cx, |editor, window, cx| {
12198        // Removing an excerpt causes the first selection to become degenerate.
12199        assert_eq!(
12200            editor.selections.ranges(cx),
12201            [
12202                Point::new(0, 0)..Point::new(0, 0),
12203                Point::new(0, 1)..Point::new(0, 1)
12204            ]
12205        );
12206
12207        // Refreshing selections will relocate the first selection to the original buffer
12208        // location.
12209        editor.change_selections(None, window, cx, |s| s.refresh());
12210        assert_eq!(
12211            editor.selections.ranges(cx),
12212            [
12213                Point::new(0, 1)..Point::new(0, 1),
12214                Point::new(0, 3)..Point::new(0, 3)
12215            ]
12216        );
12217        assert!(editor.selections.pending_anchor().is_some());
12218    });
12219}
12220
12221#[gpui::test]
12222fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12223    init_test(cx, |_| {});
12224
12225    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12226    let mut excerpt1_id = None;
12227    let multibuffer = cx.new(|cx| {
12228        let mut multibuffer = MultiBuffer::new(ReadWrite);
12229        excerpt1_id = multibuffer
12230            .push_excerpts(
12231                buffer.clone(),
12232                [
12233                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12234                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12235                ],
12236                cx,
12237            )
12238            .into_iter()
12239            .next();
12240        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12241        multibuffer
12242    });
12243
12244    let editor = cx.add_window(|window, cx| {
12245        let mut editor = build_editor(multibuffer.clone(), window, cx);
12246        let snapshot = editor.snapshot(window, cx);
12247        editor.begin_selection(
12248            Point::new(1, 3).to_display_point(&snapshot),
12249            false,
12250            1,
12251            window,
12252            cx,
12253        );
12254        assert_eq!(
12255            editor.selections.ranges(cx),
12256            [Point::new(1, 3)..Point::new(1, 3)]
12257        );
12258        editor
12259    });
12260
12261    multibuffer.update(cx, |multibuffer, cx| {
12262        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12263    });
12264    _ = editor.update(cx, |editor, window, cx| {
12265        assert_eq!(
12266            editor.selections.ranges(cx),
12267            [Point::new(0, 0)..Point::new(0, 0)]
12268        );
12269
12270        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12271        editor.change_selections(None, window, cx, |s| s.refresh());
12272        assert_eq!(
12273            editor.selections.ranges(cx),
12274            [Point::new(0, 3)..Point::new(0, 3)]
12275        );
12276        assert!(editor.selections.pending_anchor().is_some());
12277    });
12278}
12279
12280#[gpui::test]
12281async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12282    init_test(cx, |_| {});
12283
12284    let language = Arc::new(
12285        Language::new(
12286            LanguageConfig {
12287                brackets: BracketPairConfig {
12288                    pairs: vec![
12289                        BracketPair {
12290                            start: "{".to_string(),
12291                            end: "}".to_string(),
12292                            close: true,
12293                            surround: true,
12294                            newline: true,
12295                        },
12296                        BracketPair {
12297                            start: "/* ".to_string(),
12298                            end: " */".to_string(),
12299                            close: true,
12300                            surround: true,
12301                            newline: true,
12302                        },
12303                    ],
12304                    ..Default::default()
12305                },
12306                ..Default::default()
12307            },
12308            Some(tree_sitter_rust::LANGUAGE.into()),
12309        )
12310        .with_indents_query("")
12311        .unwrap(),
12312    );
12313
12314    let text = concat!(
12315        "{   }\n",     //
12316        "  x\n",       //
12317        "  /*   */\n", //
12318        "x\n",         //
12319        "{{} }\n",     //
12320    );
12321
12322    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12323    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12324    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12325    editor
12326        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12327        .await;
12328
12329    editor.update_in(cx, |editor, window, cx| {
12330        editor.change_selections(None, window, cx, |s| {
12331            s.select_display_ranges([
12332                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12333                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12334                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12335            ])
12336        });
12337        editor.newline(&Newline, window, cx);
12338
12339        assert_eq!(
12340            editor.buffer().read(cx).read(cx).text(),
12341            concat!(
12342                "{ \n",    // Suppress rustfmt
12343                "\n",      //
12344                "}\n",     //
12345                "  x\n",   //
12346                "  /* \n", //
12347                "  \n",    //
12348                "  */\n",  //
12349                "x\n",     //
12350                "{{} \n",  //
12351                "}\n",     //
12352            )
12353        );
12354    });
12355}
12356
12357#[gpui::test]
12358fn test_highlighted_ranges(cx: &mut TestAppContext) {
12359    init_test(cx, |_| {});
12360
12361    let editor = cx.add_window(|window, cx| {
12362        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12363        build_editor(buffer.clone(), window, cx)
12364    });
12365
12366    _ = editor.update(cx, |editor, window, cx| {
12367        struct Type1;
12368        struct Type2;
12369
12370        let buffer = editor.buffer.read(cx).snapshot(cx);
12371
12372        let anchor_range =
12373            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12374
12375        editor.highlight_background::<Type1>(
12376            &[
12377                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12378                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12379                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12380                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12381            ],
12382            |_| Hsla::red(),
12383            cx,
12384        );
12385        editor.highlight_background::<Type2>(
12386            &[
12387                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12388                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12389                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12390                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12391            ],
12392            |_| Hsla::green(),
12393            cx,
12394        );
12395
12396        let snapshot = editor.snapshot(window, cx);
12397        let mut highlighted_ranges = editor.background_highlights_in_range(
12398            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12399            &snapshot,
12400            cx.theme().colors(),
12401        );
12402        // Enforce a consistent ordering based on color without relying on the ordering of the
12403        // highlight's `TypeId` which is non-executor.
12404        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12405        assert_eq!(
12406            highlighted_ranges,
12407            &[
12408                (
12409                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12410                    Hsla::red(),
12411                ),
12412                (
12413                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12414                    Hsla::red(),
12415                ),
12416                (
12417                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12418                    Hsla::green(),
12419                ),
12420                (
12421                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12422                    Hsla::green(),
12423                ),
12424            ]
12425        );
12426        assert_eq!(
12427            editor.background_highlights_in_range(
12428                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12429                &snapshot,
12430                cx.theme().colors(),
12431            ),
12432            &[(
12433                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12434                Hsla::red(),
12435            )]
12436        );
12437    });
12438}
12439
12440#[gpui::test]
12441async fn test_following(cx: &mut TestAppContext) {
12442    init_test(cx, |_| {});
12443
12444    let fs = FakeFs::new(cx.executor());
12445    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12446
12447    let buffer = project.update(cx, |project, cx| {
12448        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12449        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12450    });
12451    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12452    let follower = cx.update(|cx| {
12453        cx.open_window(
12454            WindowOptions {
12455                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12456                    gpui::Point::new(px(0.), px(0.)),
12457                    gpui::Point::new(px(10.), px(80.)),
12458                ))),
12459                ..Default::default()
12460            },
12461            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12462        )
12463        .unwrap()
12464    });
12465
12466    let is_still_following = Rc::new(RefCell::new(true));
12467    let follower_edit_event_count = Rc::new(RefCell::new(0));
12468    let pending_update = Rc::new(RefCell::new(None));
12469    let leader_entity = leader.root(cx).unwrap();
12470    let follower_entity = follower.root(cx).unwrap();
12471    _ = follower.update(cx, {
12472        let update = pending_update.clone();
12473        let is_still_following = is_still_following.clone();
12474        let follower_edit_event_count = follower_edit_event_count.clone();
12475        |_, window, cx| {
12476            cx.subscribe_in(
12477                &leader_entity,
12478                window,
12479                move |_, leader, event, window, cx| {
12480                    leader.read(cx).add_event_to_update_proto(
12481                        event,
12482                        &mut update.borrow_mut(),
12483                        window,
12484                        cx,
12485                    );
12486                },
12487            )
12488            .detach();
12489
12490            cx.subscribe_in(
12491                &follower_entity,
12492                window,
12493                move |_, _, event: &EditorEvent, _window, _cx| {
12494                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12495                        *is_still_following.borrow_mut() = false;
12496                    }
12497
12498                    if let EditorEvent::BufferEdited = event {
12499                        *follower_edit_event_count.borrow_mut() += 1;
12500                    }
12501                },
12502            )
12503            .detach();
12504        }
12505    });
12506
12507    // Update the selections only
12508    _ = leader.update(cx, |leader, window, cx| {
12509        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12510    });
12511    follower
12512        .update(cx, |follower, window, cx| {
12513            follower.apply_update_proto(
12514                &project,
12515                pending_update.borrow_mut().take().unwrap(),
12516                window,
12517                cx,
12518            )
12519        })
12520        .unwrap()
12521        .await
12522        .unwrap();
12523    _ = follower.update(cx, |follower, _, cx| {
12524        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12525    });
12526    assert!(*is_still_following.borrow());
12527    assert_eq!(*follower_edit_event_count.borrow(), 0);
12528
12529    // Update the scroll position only
12530    _ = leader.update(cx, |leader, window, cx| {
12531        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12532    });
12533    follower
12534        .update(cx, |follower, window, cx| {
12535            follower.apply_update_proto(
12536                &project,
12537                pending_update.borrow_mut().take().unwrap(),
12538                window,
12539                cx,
12540            )
12541        })
12542        .unwrap()
12543        .await
12544        .unwrap();
12545    assert_eq!(
12546        follower
12547            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12548            .unwrap(),
12549        gpui::Point::new(1.5, 3.5)
12550    );
12551    assert!(*is_still_following.borrow());
12552    assert_eq!(*follower_edit_event_count.borrow(), 0);
12553
12554    // Update the selections and scroll position. The follower's scroll position is updated
12555    // via autoscroll, not via the leader's exact scroll position.
12556    _ = leader.update(cx, |leader, window, cx| {
12557        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12558        leader.request_autoscroll(Autoscroll::newest(), cx);
12559        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12560    });
12561    follower
12562        .update(cx, |follower, window, cx| {
12563            follower.apply_update_proto(
12564                &project,
12565                pending_update.borrow_mut().take().unwrap(),
12566                window,
12567                cx,
12568            )
12569        })
12570        .unwrap()
12571        .await
12572        .unwrap();
12573    _ = follower.update(cx, |follower, _, cx| {
12574        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12575        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12576    });
12577    assert!(*is_still_following.borrow());
12578
12579    // Creating a pending selection that precedes another selection
12580    _ = leader.update(cx, |leader, window, cx| {
12581        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12582        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12583    });
12584    follower
12585        .update(cx, |follower, window, cx| {
12586            follower.apply_update_proto(
12587                &project,
12588                pending_update.borrow_mut().take().unwrap(),
12589                window,
12590                cx,
12591            )
12592        })
12593        .unwrap()
12594        .await
12595        .unwrap();
12596    _ = follower.update(cx, |follower, _, cx| {
12597        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12598    });
12599    assert!(*is_still_following.borrow());
12600
12601    // Extend the pending selection so that it surrounds another selection
12602    _ = leader.update(cx, |leader, window, cx| {
12603        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12604    });
12605    follower
12606        .update(cx, |follower, window, cx| {
12607            follower.apply_update_proto(
12608                &project,
12609                pending_update.borrow_mut().take().unwrap(),
12610                window,
12611                cx,
12612            )
12613        })
12614        .unwrap()
12615        .await
12616        .unwrap();
12617    _ = follower.update(cx, |follower, _, cx| {
12618        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12619    });
12620
12621    // Scrolling locally breaks the follow
12622    _ = follower.update(cx, |follower, window, cx| {
12623        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12624        follower.set_scroll_anchor(
12625            ScrollAnchor {
12626                anchor: top_anchor,
12627                offset: gpui::Point::new(0.0, 0.5),
12628            },
12629            window,
12630            cx,
12631        );
12632    });
12633    assert!(!(*is_still_following.borrow()));
12634}
12635
12636#[gpui::test]
12637async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12638    init_test(cx, |_| {});
12639
12640    let fs = FakeFs::new(cx.executor());
12641    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12642    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12643    let pane = workspace
12644        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12645        .unwrap();
12646
12647    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12648
12649    let leader = pane.update_in(cx, |_, window, cx| {
12650        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12651        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12652    });
12653
12654    // Start following the editor when it has no excerpts.
12655    let mut state_message =
12656        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12657    let workspace_entity = workspace.root(cx).unwrap();
12658    let follower_1 = cx
12659        .update_window(*workspace.deref(), |_, window, cx| {
12660            Editor::from_state_proto(
12661                workspace_entity,
12662                ViewId {
12663                    creator: Default::default(),
12664                    id: 0,
12665                },
12666                &mut state_message,
12667                window,
12668                cx,
12669            )
12670        })
12671        .unwrap()
12672        .unwrap()
12673        .await
12674        .unwrap();
12675
12676    let update_message = Rc::new(RefCell::new(None));
12677    follower_1.update_in(cx, {
12678        let update = update_message.clone();
12679        |_, window, cx| {
12680            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12681                leader.read(cx).add_event_to_update_proto(
12682                    event,
12683                    &mut update.borrow_mut(),
12684                    window,
12685                    cx,
12686                );
12687            })
12688            .detach();
12689        }
12690    });
12691
12692    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12693        (
12694            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12695            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12696        )
12697    });
12698
12699    // Insert some excerpts.
12700    leader.update(cx, |leader, cx| {
12701        leader.buffer.update(cx, |multibuffer, cx| {
12702            let excerpt_ids = multibuffer.push_excerpts(
12703                buffer_1.clone(),
12704                [
12705                    ExcerptRange::new(1..6),
12706                    ExcerptRange::new(12..15),
12707                    ExcerptRange::new(0..3),
12708                ],
12709                cx,
12710            );
12711            multibuffer.insert_excerpts_after(
12712                excerpt_ids[0],
12713                buffer_2.clone(),
12714                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
12715                cx,
12716            );
12717        });
12718    });
12719
12720    // Apply the update of adding the excerpts.
12721    follower_1
12722        .update_in(cx, |follower, window, cx| {
12723            follower.apply_update_proto(
12724                &project,
12725                update_message.borrow().clone().unwrap(),
12726                window,
12727                cx,
12728            )
12729        })
12730        .await
12731        .unwrap();
12732    assert_eq!(
12733        follower_1.update(cx, |editor, cx| editor.text(cx)),
12734        leader.update(cx, |editor, cx| editor.text(cx))
12735    );
12736    update_message.borrow_mut().take();
12737
12738    // Start following separately after it already has excerpts.
12739    let mut state_message =
12740        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12741    let workspace_entity = workspace.root(cx).unwrap();
12742    let follower_2 = cx
12743        .update_window(*workspace.deref(), |_, window, cx| {
12744            Editor::from_state_proto(
12745                workspace_entity,
12746                ViewId {
12747                    creator: Default::default(),
12748                    id: 0,
12749                },
12750                &mut state_message,
12751                window,
12752                cx,
12753            )
12754        })
12755        .unwrap()
12756        .unwrap()
12757        .await
12758        .unwrap();
12759    assert_eq!(
12760        follower_2.update(cx, |editor, cx| editor.text(cx)),
12761        leader.update(cx, |editor, cx| editor.text(cx))
12762    );
12763
12764    // Remove some excerpts.
12765    leader.update(cx, |leader, cx| {
12766        leader.buffer.update(cx, |multibuffer, cx| {
12767            let excerpt_ids = multibuffer.excerpt_ids();
12768            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12769            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12770        });
12771    });
12772
12773    // Apply the update of removing the excerpts.
12774    follower_1
12775        .update_in(cx, |follower, window, cx| {
12776            follower.apply_update_proto(
12777                &project,
12778                update_message.borrow().clone().unwrap(),
12779                window,
12780                cx,
12781            )
12782        })
12783        .await
12784        .unwrap();
12785    follower_2
12786        .update_in(cx, |follower, window, cx| {
12787            follower.apply_update_proto(
12788                &project,
12789                update_message.borrow().clone().unwrap(),
12790                window,
12791                cx,
12792            )
12793        })
12794        .await
12795        .unwrap();
12796    update_message.borrow_mut().take();
12797    assert_eq!(
12798        follower_1.update(cx, |editor, cx| editor.text(cx)),
12799        leader.update(cx, |editor, cx| editor.text(cx))
12800    );
12801}
12802
12803#[gpui::test]
12804async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12805    init_test(cx, |_| {});
12806
12807    let mut cx = EditorTestContext::new(cx).await;
12808    let lsp_store =
12809        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12810
12811    cx.set_state(indoc! {"
12812        ˇfn func(abc def: i32) -> u32 {
12813        }
12814    "});
12815
12816    cx.update(|_, cx| {
12817        lsp_store.update(cx, |lsp_store, cx| {
12818            lsp_store
12819                .update_diagnostics(
12820                    LanguageServerId(0),
12821                    lsp::PublishDiagnosticsParams {
12822                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12823                        version: None,
12824                        diagnostics: vec![
12825                            lsp::Diagnostic {
12826                                range: lsp::Range::new(
12827                                    lsp::Position::new(0, 11),
12828                                    lsp::Position::new(0, 12),
12829                                ),
12830                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12831                                ..Default::default()
12832                            },
12833                            lsp::Diagnostic {
12834                                range: lsp::Range::new(
12835                                    lsp::Position::new(0, 12),
12836                                    lsp::Position::new(0, 15),
12837                                ),
12838                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12839                                ..Default::default()
12840                            },
12841                            lsp::Diagnostic {
12842                                range: lsp::Range::new(
12843                                    lsp::Position::new(0, 25),
12844                                    lsp::Position::new(0, 28),
12845                                ),
12846                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12847                                ..Default::default()
12848                            },
12849                        ],
12850                    },
12851                    &[],
12852                    cx,
12853                )
12854                .unwrap()
12855        });
12856    });
12857
12858    executor.run_until_parked();
12859
12860    cx.update_editor(|editor, window, cx| {
12861        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12862    });
12863
12864    cx.assert_editor_state(indoc! {"
12865        fn func(abc def: i32) -> ˇu32 {
12866        }
12867    "});
12868
12869    cx.update_editor(|editor, window, cx| {
12870        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12871    });
12872
12873    cx.assert_editor_state(indoc! {"
12874        fn func(abc ˇdef: i32) -> u32 {
12875        }
12876    "});
12877
12878    cx.update_editor(|editor, window, cx| {
12879        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12880    });
12881
12882    cx.assert_editor_state(indoc! {"
12883        fn func(abcˇ def: i32) -> u32 {
12884        }
12885    "});
12886
12887    cx.update_editor(|editor, window, cx| {
12888        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12889    });
12890
12891    cx.assert_editor_state(indoc! {"
12892        fn func(abc def: i32) -> ˇu32 {
12893        }
12894    "});
12895}
12896
12897#[gpui::test]
12898async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12899    init_test(cx, |_| {});
12900
12901    let mut cx = EditorTestContext::new(cx).await;
12902
12903    cx.set_state(indoc! {"
12904        fn func(abˇc def: i32) -> u32 {
12905        }
12906    "});
12907    let lsp_store =
12908        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12909
12910    cx.update(|_, cx| {
12911        lsp_store.update(cx, |lsp_store, cx| {
12912            lsp_store.update_diagnostics(
12913                LanguageServerId(0),
12914                lsp::PublishDiagnosticsParams {
12915                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12916                    version: None,
12917                    diagnostics: vec![lsp::Diagnostic {
12918                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12919                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12920                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12921                        ..Default::default()
12922                    }],
12923                },
12924                &[],
12925                cx,
12926            )
12927        })
12928    }).unwrap();
12929    cx.run_until_parked();
12930    cx.update_editor(|editor, window, cx| {
12931        hover_popover::hover(editor, &Default::default(), window, cx)
12932    });
12933    cx.run_until_parked();
12934    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12935}
12936
12937#[gpui::test]
12938async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12939    init_test(cx, |_| {});
12940
12941    let mut cx = EditorTestContext::new(cx).await;
12942
12943    let diff_base = r#"
12944        use some::mod;
12945
12946        const A: u32 = 42;
12947
12948        fn main() {
12949            println!("hello");
12950
12951            println!("world");
12952        }
12953        "#
12954    .unindent();
12955
12956    // Edits are modified, removed, modified, added
12957    cx.set_state(
12958        &r#"
12959        use some::modified;
12960
12961        ˇ
12962        fn main() {
12963            println!("hello there");
12964
12965            println!("around the");
12966            println!("world");
12967        }
12968        "#
12969        .unindent(),
12970    );
12971
12972    cx.set_head_text(&diff_base);
12973    executor.run_until_parked();
12974
12975    cx.update_editor(|editor, window, cx| {
12976        //Wrap around the bottom of the buffer
12977        for _ in 0..3 {
12978            editor.go_to_next_hunk(&GoToHunk, window, cx);
12979        }
12980    });
12981
12982    cx.assert_editor_state(
12983        &r#"
12984        ˇuse some::modified;
12985
12986
12987        fn main() {
12988            println!("hello there");
12989
12990            println!("around the");
12991            println!("world");
12992        }
12993        "#
12994        .unindent(),
12995    );
12996
12997    cx.update_editor(|editor, window, cx| {
12998        //Wrap around the top of the buffer
12999        for _ in 0..2 {
13000            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13001        }
13002    });
13003
13004    cx.assert_editor_state(
13005        &r#"
13006        use some::modified;
13007
13008
13009        fn main() {
13010        ˇ    println!("hello there");
13011
13012            println!("around the");
13013            println!("world");
13014        }
13015        "#
13016        .unindent(),
13017    );
13018
13019    cx.update_editor(|editor, window, cx| {
13020        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13021    });
13022
13023    cx.assert_editor_state(
13024        &r#"
13025        use some::modified;
13026
13027        ˇ
13028        fn main() {
13029            println!("hello there");
13030
13031            println!("around the");
13032            println!("world");
13033        }
13034        "#
13035        .unindent(),
13036    );
13037
13038    cx.update_editor(|editor, window, cx| {
13039        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13040    });
13041
13042    cx.assert_editor_state(
13043        &r#"
13044        ˇuse some::modified;
13045
13046
13047        fn main() {
13048            println!("hello there");
13049
13050            println!("around the");
13051            println!("world");
13052        }
13053        "#
13054        .unindent(),
13055    );
13056
13057    cx.update_editor(|editor, window, cx| {
13058        for _ in 0..2 {
13059            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13060        }
13061    });
13062
13063    cx.assert_editor_state(
13064        &r#"
13065        use some::modified;
13066
13067
13068        fn main() {
13069        ˇ    println!("hello there");
13070
13071            println!("around the");
13072            println!("world");
13073        }
13074        "#
13075        .unindent(),
13076    );
13077
13078    cx.update_editor(|editor, window, cx| {
13079        editor.fold(&Fold, window, cx);
13080    });
13081
13082    cx.update_editor(|editor, window, cx| {
13083        editor.go_to_next_hunk(&GoToHunk, window, cx);
13084    });
13085
13086    cx.assert_editor_state(
13087        &r#"
13088        ˇuse some::modified;
13089
13090
13091        fn main() {
13092            println!("hello there");
13093
13094            println!("around the");
13095            println!("world");
13096        }
13097        "#
13098        .unindent(),
13099    );
13100}
13101
13102#[test]
13103fn test_split_words() {
13104    fn split(text: &str) -> Vec<&str> {
13105        split_words(text).collect()
13106    }
13107
13108    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13109    assert_eq!(split("hello_world"), &["hello_", "world"]);
13110    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13111    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13112    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13113    assert_eq!(split("helloworld"), &["helloworld"]);
13114
13115    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13116}
13117
13118#[gpui::test]
13119async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13120    init_test(cx, |_| {});
13121
13122    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13123    let mut assert = |before, after| {
13124        let _state_context = cx.set_state(before);
13125        cx.run_until_parked();
13126        cx.update_editor(|editor, window, cx| {
13127            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13128        });
13129        cx.run_until_parked();
13130        cx.assert_editor_state(after);
13131    };
13132
13133    // Outside bracket jumps to outside of matching bracket
13134    assert("console.logˇ(var);", "console.log(var)ˇ;");
13135    assert("console.log(var)ˇ;", "console.logˇ(var);");
13136
13137    // Inside bracket jumps to inside of matching bracket
13138    assert("console.log(ˇvar);", "console.log(varˇ);");
13139    assert("console.log(varˇ);", "console.log(ˇvar);");
13140
13141    // When outside a bracket and inside, favor jumping to the inside bracket
13142    assert(
13143        "console.log('foo', [1, 2, 3]ˇ);",
13144        "console.log(ˇ'foo', [1, 2, 3]);",
13145    );
13146    assert(
13147        "console.log(ˇ'foo', [1, 2, 3]);",
13148        "console.log('foo', [1, 2, 3]ˇ);",
13149    );
13150
13151    // Bias forward if two options are equally likely
13152    assert(
13153        "let result = curried_fun()ˇ();",
13154        "let result = curried_fun()()ˇ;",
13155    );
13156
13157    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13158    assert(
13159        indoc! {"
13160            function test() {
13161                console.log('test')ˇ
13162            }"},
13163        indoc! {"
13164            function test() {
13165                console.logˇ('test')
13166            }"},
13167    );
13168}
13169
13170#[gpui::test]
13171async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13172    init_test(cx, |_| {});
13173
13174    let fs = FakeFs::new(cx.executor());
13175    fs.insert_tree(
13176        path!("/a"),
13177        json!({
13178            "main.rs": "fn main() { let a = 5; }",
13179            "other.rs": "// Test file",
13180        }),
13181    )
13182    .await;
13183    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13184
13185    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13186    language_registry.add(Arc::new(Language::new(
13187        LanguageConfig {
13188            name: "Rust".into(),
13189            matcher: LanguageMatcher {
13190                path_suffixes: vec!["rs".to_string()],
13191                ..Default::default()
13192            },
13193            brackets: BracketPairConfig {
13194                pairs: vec![BracketPair {
13195                    start: "{".to_string(),
13196                    end: "}".to_string(),
13197                    close: true,
13198                    surround: true,
13199                    newline: true,
13200                }],
13201                disabled_scopes_by_bracket_ix: Vec::new(),
13202            },
13203            ..Default::default()
13204        },
13205        Some(tree_sitter_rust::LANGUAGE.into()),
13206    )));
13207    let mut fake_servers = language_registry.register_fake_lsp(
13208        "Rust",
13209        FakeLspAdapter {
13210            capabilities: lsp::ServerCapabilities {
13211                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13212                    first_trigger_character: "{".to_string(),
13213                    more_trigger_character: None,
13214                }),
13215                ..Default::default()
13216            },
13217            ..Default::default()
13218        },
13219    );
13220
13221    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13222
13223    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13224
13225    let worktree_id = workspace
13226        .update(cx, |workspace, _, cx| {
13227            workspace.project().update(cx, |project, cx| {
13228                project.worktrees(cx).next().unwrap().read(cx).id()
13229            })
13230        })
13231        .unwrap();
13232
13233    let buffer = project
13234        .update(cx, |project, cx| {
13235            project.open_local_buffer(path!("/a/main.rs"), cx)
13236        })
13237        .await
13238        .unwrap();
13239    let editor_handle = workspace
13240        .update(cx, |workspace, window, cx| {
13241            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13242        })
13243        .unwrap()
13244        .await
13245        .unwrap()
13246        .downcast::<Editor>()
13247        .unwrap();
13248
13249    cx.executor().start_waiting();
13250    let fake_server = fake_servers.next().await.unwrap();
13251
13252    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13253        |params, _| async move {
13254            assert_eq!(
13255                params.text_document_position.text_document.uri,
13256                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13257            );
13258            assert_eq!(
13259                params.text_document_position.position,
13260                lsp::Position::new(0, 21),
13261            );
13262
13263            Ok(Some(vec![lsp::TextEdit {
13264                new_text: "]".to_string(),
13265                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13266            }]))
13267        },
13268    );
13269
13270    editor_handle.update_in(cx, |editor, window, cx| {
13271        window.focus(&editor.focus_handle(cx));
13272        editor.change_selections(None, window, cx, |s| {
13273            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13274        });
13275        editor.handle_input("{", window, cx);
13276    });
13277
13278    cx.executor().run_until_parked();
13279
13280    buffer.update(cx, |buffer, _| {
13281        assert_eq!(
13282            buffer.text(),
13283            "fn main() { let a = {5}; }",
13284            "No extra braces from on type formatting should appear in the buffer"
13285        )
13286    });
13287}
13288
13289#[gpui::test]
13290async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13291    init_test(cx, |_| {});
13292
13293    let fs = FakeFs::new(cx.executor());
13294    fs.insert_tree(
13295        path!("/a"),
13296        json!({
13297            "main.rs": "fn main() { let a = 5; }",
13298            "other.rs": "// Test file",
13299        }),
13300    )
13301    .await;
13302
13303    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13304
13305    let server_restarts = Arc::new(AtomicUsize::new(0));
13306    let closure_restarts = Arc::clone(&server_restarts);
13307    let language_server_name = "test language server";
13308    let language_name: LanguageName = "Rust".into();
13309
13310    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13311    language_registry.add(Arc::new(Language::new(
13312        LanguageConfig {
13313            name: language_name.clone(),
13314            matcher: LanguageMatcher {
13315                path_suffixes: vec!["rs".to_string()],
13316                ..Default::default()
13317            },
13318            ..Default::default()
13319        },
13320        Some(tree_sitter_rust::LANGUAGE.into()),
13321    )));
13322    let mut fake_servers = language_registry.register_fake_lsp(
13323        "Rust",
13324        FakeLspAdapter {
13325            name: language_server_name,
13326            initialization_options: Some(json!({
13327                "testOptionValue": true
13328            })),
13329            initializer: Some(Box::new(move |fake_server| {
13330                let task_restarts = Arc::clone(&closure_restarts);
13331                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13332                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13333                    futures::future::ready(Ok(()))
13334                });
13335            })),
13336            ..Default::default()
13337        },
13338    );
13339
13340    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13341    let _buffer = project
13342        .update(cx, |project, cx| {
13343            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13344        })
13345        .await
13346        .unwrap();
13347    let _fake_server = fake_servers.next().await.unwrap();
13348    update_test_language_settings(cx, |language_settings| {
13349        language_settings.languages.insert(
13350            language_name.clone(),
13351            LanguageSettingsContent {
13352                tab_size: NonZeroU32::new(8),
13353                ..Default::default()
13354            },
13355        );
13356    });
13357    cx.executor().run_until_parked();
13358    assert_eq!(
13359        server_restarts.load(atomic::Ordering::Acquire),
13360        0,
13361        "Should not restart LSP server on an unrelated change"
13362    );
13363
13364    update_test_project_settings(cx, |project_settings| {
13365        project_settings.lsp.insert(
13366            "Some other server name".into(),
13367            LspSettings {
13368                binary: None,
13369                settings: None,
13370                initialization_options: Some(json!({
13371                    "some other init value": false
13372                })),
13373                enable_lsp_tasks: false,
13374            },
13375        );
13376    });
13377    cx.executor().run_until_parked();
13378    assert_eq!(
13379        server_restarts.load(atomic::Ordering::Acquire),
13380        0,
13381        "Should not restart LSP server on an unrelated LSP settings change"
13382    );
13383
13384    update_test_project_settings(cx, |project_settings| {
13385        project_settings.lsp.insert(
13386            language_server_name.into(),
13387            LspSettings {
13388                binary: None,
13389                settings: None,
13390                initialization_options: Some(json!({
13391                    "anotherInitValue": false
13392                })),
13393                enable_lsp_tasks: false,
13394            },
13395        );
13396    });
13397    cx.executor().run_until_parked();
13398    assert_eq!(
13399        server_restarts.load(atomic::Ordering::Acquire),
13400        1,
13401        "Should restart LSP server on a related LSP settings change"
13402    );
13403
13404    update_test_project_settings(cx, |project_settings| {
13405        project_settings.lsp.insert(
13406            language_server_name.into(),
13407            LspSettings {
13408                binary: None,
13409                settings: None,
13410                initialization_options: Some(json!({
13411                    "anotherInitValue": false
13412                })),
13413                enable_lsp_tasks: false,
13414            },
13415        );
13416    });
13417    cx.executor().run_until_parked();
13418    assert_eq!(
13419        server_restarts.load(atomic::Ordering::Acquire),
13420        1,
13421        "Should not restart LSP server on a related LSP settings change that is the same"
13422    );
13423
13424    update_test_project_settings(cx, |project_settings| {
13425        project_settings.lsp.insert(
13426            language_server_name.into(),
13427            LspSettings {
13428                binary: None,
13429                settings: None,
13430                initialization_options: None,
13431                enable_lsp_tasks: false,
13432            },
13433        );
13434    });
13435    cx.executor().run_until_parked();
13436    assert_eq!(
13437        server_restarts.load(atomic::Ordering::Acquire),
13438        2,
13439        "Should restart LSP server on another related LSP settings change"
13440    );
13441}
13442
13443#[gpui::test]
13444async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13445    init_test(cx, |_| {});
13446
13447    let mut cx = EditorLspTestContext::new_rust(
13448        lsp::ServerCapabilities {
13449            completion_provider: Some(lsp::CompletionOptions {
13450                trigger_characters: Some(vec![".".to_string()]),
13451                resolve_provider: Some(true),
13452                ..Default::default()
13453            }),
13454            ..Default::default()
13455        },
13456        cx,
13457    )
13458    .await;
13459
13460    cx.set_state("fn main() { let a = 2ˇ; }");
13461    cx.simulate_keystroke(".");
13462    let completion_item = lsp::CompletionItem {
13463        label: "some".into(),
13464        kind: Some(lsp::CompletionItemKind::SNIPPET),
13465        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13466        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13467            kind: lsp::MarkupKind::Markdown,
13468            value: "```rust\nSome(2)\n```".to_string(),
13469        })),
13470        deprecated: Some(false),
13471        sort_text: Some("fffffff2".to_string()),
13472        filter_text: Some("some".to_string()),
13473        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13474        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13475            range: lsp::Range {
13476                start: lsp::Position {
13477                    line: 0,
13478                    character: 22,
13479                },
13480                end: lsp::Position {
13481                    line: 0,
13482                    character: 22,
13483                },
13484            },
13485            new_text: "Some(2)".to_string(),
13486        })),
13487        additional_text_edits: Some(vec![lsp::TextEdit {
13488            range: lsp::Range {
13489                start: lsp::Position {
13490                    line: 0,
13491                    character: 20,
13492                },
13493                end: lsp::Position {
13494                    line: 0,
13495                    character: 22,
13496                },
13497            },
13498            new_text: "".to_string(),
13499        }]),
13500        ..Default::default()
13501    };
13502
13503    let closure_completion_item = completion_item.clone();
13504    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13505        let task_completion_item = closure_completion_item.clone();
13506        async move {
13507            Ok(Some(lsp::CompletionResponse::Array(vec![
13508                task_completion_item,
13509            ])))
13510        }
13511    });
13512
13513    request.next().await;
13514
13515    cx.condition(|editor, _| editor.context_menu_visible())
13516        .await;
13517    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13518        editor
13519            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13520            .unwrap()
13521    });
13522    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13523
13524    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13525        let task_completion_item = completion_item.clone();
13526        async move { Ok(task_completion_item) }
13527    })
13528    .next()
13529    .await
13530    .unwrap();
13531    apply_additional_edits.await.unwrap();
13532    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13533}
13534
13535#[gpui::test]
13536async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13537    init_test(cx, |_| {});
13538
13539    let mut cx = EditorLspTestContext::new_rust(
13540        lsp::ServerCapabilities {
13541            completion_provider: Some(lsp::CompletionOptions {
13542                trigger_characters: Some(vec![".".to_string()]),
13543                resolve_provider: Some(true),
13544                ..Default::default()
13545            }),
13546            ..Default::default()
13547        },
13548        cx,
13549    )
13550    .await;
13551
13552    cx.set_state("fn main() { let a = 2ˇ; }");
13553    cx.simulate_keystroke(".");
13554
13555    let item1 = lsp::CompletionItem {
13556        label: "method id()".to_string(),
13557        filter_text: Some("id".to_string()),
13558        detail: None,
13559        documentation: None,
13560        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13561            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13562            new_text: ".id".to_string(),
13563        })),
13564        ..lsp::CompletionItem::default()
13565    };
13566
13567    let item2 = lsp::CompletionItem {
13568        label: "other".to_string(),
13569        filter_text: Some("other".to_string()),
13570        detail: None,
13571        documentation: None,
13572        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13573            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13574            new_text: ".other".to_string(),
13575        })),
13576        ..lsp::CompletionItem::default()
13577    };
13578
13579    let item1 = item1.clone();
13580    cx.set_request_handler::<lsp::request::Completion, _, _>({
13581        let item1 = item1.clone();
13582        move |_, _, _| {
13583            let item1 = item1.clone();
13584            let item2 = item2.clone();
13585            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13586        }
13587    })
13588    .next()
13589    .await;
13590
13591    cx.condition(|editor, _| editor.context_menu_visible())
13592        .await;
13593    cx.update_editor(|editor, _, _| {
13594        let context_menu = editor.context_menu.borrow_mut();
13595        let context_menu = context_menu
13596            .as_ref()
13597            .expect("Should have the context menu deployed");
13598        match context_menu {
13599            CodeContextMenu::Completions(completions_menu) => {
13600                let completions = completions_menu.completions.borrow_mut();
13601                assert_eq!(
13602                    completions
13603                        .iter()
13604                        .map(|completion| &completion.label.text)
13605                        .collect::<Vec<_>>(),
13606                    vec!["method id()", "other"]
13607                )
13608            }
13609            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13610        }
13611    });
13612
13613    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13614        let item1 = item1.clone();
13615        move |_, item_to_resolve, _| {
13616            let item1 = item1.clone();
13617            async move {
13618                if item1 == item_to_resolve {
13619                    Ok(lsp::CompletionItem {
13620                        label: "method id()".to_string(),
13621                        filter_text: Some("id".to_string()),
13622                        detail: Some("Now resolved!".to_string()),
13623                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13624                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13625                            range: lsp::Range::new(
13626                                lsp::Position::new(0, 22),
13627                                lsp::Position::new(0, 22),
13628                            ),
13629                            new_text: ".id".to_string(),
13630                        })),
13631                        ..lsp::CompletionItem::default()
13632                    })
13633                } else {
13634                    Ok(item_to_resolve)
13635                }
13636            }
13637        }
13638    })
13639    .next()
13640    .await
13641    .unwrap();
13642    cx.run_until_parked();
13643
13644    cx.update_editor(|editor, window, cx| {
13645        editor.context_menu_next(&Default::default(), window, cx);
13646    });
13647
13648    cx.update_editor(|editor, _, _| {
13649        let context_menu = editor.context_menu.borrow_mut();
13650        let context_menu = context_menu
13651            .as_ref()
13652            .expect("Should have the context menu deployed");
13653        match context_menu {
13654            CodeContextMenu::Completions(completions_menu) => {
13655                let completions = completions_menu.completions.borrow_mut();
13656                assert_eq!(
13657                    completions
13658                        .iter()
13659                        .map(|completion| &completion.label.text)
13660                        .collect::<Vec<_>>(),
13661                    vec!["method id() Now resolved!", "other"],
13662                    "Should update first completion label, but not second as the filter text did not match."
13663                );
13664            }
13665            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13666        }
13667    });
13668}
13669
13670#[gpui::test]
13671async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13672    init_test(cx, |_| {});
13673
13674    let mut cx = EditorLspTestContext::new_rust(
13675        lsp::ServerCapabilities {
13676            completion_provider: Some(lsp::CompletionOptions {
13677                trigger_characters: Some(vec![".".to_string()]),
13678                resolve_provider: Some(true),
13679                ..Default::default()
13680            }),
13681            ..Default::default()
13682        },
13683        cx,
13684    )
13685    .await;
13686
13687    cx.set_state("fn main() { let a = 2ˇ; }");
13688    cx.simulate_keystroke(".");
13689
13690    let unresolved_item_1 = lsp::CompletionItem {
13691        label: "id".to_string(),
13692        filter_text: Some("id".to_string()),
13693        detail: None,
13694        documentation: None,
13695        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13696            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13697            new_text: ".id".to_string(),
13698        })),
13699        ..lsp::CompletionItem::default()
13700    };
13701    let resolved_item_1 = lsp::CompletionItem {
13702        additional_text_edits: Some(vec![lsp::TextEdit {
13703            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13704            new_text: "!!".to_string(),
13705        }]),
13706        ..unresolved_item_1.clone()
13707    };
13708    let unresolved_item_2 = lsp::CompletionItem {
13709        label: "other".to_string(),
13710        filter_text: Some("other".to_string()),
13711        detail: None,
13712        documentation: None,
13713        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13714            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13715            new_text: ".other".to_string(),
13716        })),
13717        ..lsp::CompletionItem::default()
13718    };
13719    let resolved_item_2 = lsp::CompletionItem {
13720        additional_text_edits: Some(vec![lsp::TextEdit {
13721            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13722            new_text: "??".to_string(),
13723        }]),
13724        ..unresolved_item_2.clone()
13725    };
13726
13727    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13728    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13729    cx.lsp
13730        .server
13731        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13732            let unresolved_item_1 = unresolved_item_1.clone();
13733            let resolved_item_1 = resolved_item_1.clone();
13734            let unresolved_item_2 = unresolved_item_2.clone();
13735            let resolved_item_2 = resolved_item_2.clone();
13736            let resolve_requests_1 = resolve_requests_1.clone();
13737            let resolve_requests_2 = resolve_requests_2.clone();
13738            move |unresolved_request, _| {
13739                let unresolved_item_1 = unresolved_item_1.clone();
13740                let resolved_item_1 = resolved_item_1.clone();
13741                let unresolved_item_2 = unresolved_item_2.clone();
13742                let resolved_item_2 = resolved_item_2.clone();
13743                let resolve_requests_1 = resolve_requests_1.clone();
13744                let resolve_requests_2 = resolve_requests_2.clone();
13745                async move {
13746                    if unresolved_request == unresolved_item_1 {
13747                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13748                        Ok(resolved_item_1.clone())
13749                    } else if unresolved_request == unresolved_item_2 {
13750                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13751                        Ok(resolved_item_2.clone())
13752                    } else {
13753                        panic!("Unexpected completion item {unresolved_request:?}")
13754                    }
13755                }
13756            }
13757        })
13758        .detach();
13759
13760    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13761        let unresolved_item_1 = unresolved_item_1.clone();
13762        let unresolved_item_2 = unresolved_item_2.clone();
13763        async move {
13764            Ok(Some(lsp::CompletionResponse::Array(vec![
13765                unresolved_item_1,
13766                unresolved_item_2,
13767            ])))
13768        }
13769    })
13770    .next()
13771    .await;
13772
13773    cx.condition(|editor, _| editor.context_menu_visible())
13774        .await;
13775    cx.update_editor(|editor, _, _| {
13776        let context_menu = editor.context_menu.borrow_mut();
13777        let context_menu = context_menu
13778            .as_ref()
13779            .expect("Should have the context menu deployed");
13780        match context_menu {
13781            CodeContextMenu::Completions(completions_menu) => {
13782                let completions = completions_menu.completions.borrow_mut();
13783                assert_eq!(
13784                    completions
13785                        .iter()
13786                        .map(|completion| &completion.label.text)
13787                        .collect::<Vec<_>>(),
13788                    vec!["id", "other"]
13789                )
13790            }
13791            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13792        }
13793    });
13794    cx.run_until_parked();
13795
13796    cx.update_editor(|editor, window, cx| {
13797        editor.context_menu_next(&ContextMenuNext, window, cx);
13798    });
13799    cx.run_until_parked();
13800    cx.update_editor(|editor, window, cx| {
13801        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13802    });
13803    cx.run_until_parked();
13804    cx.update_editor(|editor, window, cx| {
13805        editor.context_menu_next(&ContextMenuNext, window, cx);
13806    });
13807    cx.run_until_parked();
13808    cx.update_editor(|editor, window, cx| {
13809        editor
13810            .compose_completion(&ComposeCompletion::default(), window, cx)
13811            .expect("No task returned")
13812    })
13813    .await
13814    .expect("Completion failed");
13815    cx.run_until_parked();
13816
13817    cx.update_editor(|editor, _, cx| {
13818        assert_eq!(
13819            resolve_requests_1.load(atomic::Ordering::Acquire),
13820            1,
13821            "Should always resolve once despite multiple selections"
13822        );
13823        assert_eq!(
13824            resolve_requests_2.load(atomic::Ordering::Acquire),
13825            1,
13826            "Should always resolve once after multiple selections and applying the completion"
13827        );
13828        assert_eq!(
13829            editor.text(cx),
13830            "fn main() { let a = ??.other; }",
13831            "Should use resolved data when applying the completion"
13832        );
13833    });
13834}
13835
13836#[gpui::test]
13837async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13838    init_test(cx, |_| {});
13839
13840    let item_0 = lsp::CompletionItem {
13841        label: "abs".into(),
13842        insert_text: Some("abs".into()),
13843        data: Some(json!({ "very": "special"})),
13844        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13845        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13846            lsp::InsertReplaceEdit {
13847                new_text: "abs".to_string(),
13848                insert: lsp::Range::default(),
13849                replace: lsp::Range::default(),
13850            },
13851        )),
13852        ..lsp::CompletionItem::default()
13853    };
13854    let items = iter::once(item_0.clone())
13855        .chain((11..51).map(|i| lsp::CompletionItem {
13856            label: format!("item_{}", i),
13857            insert_text: Some(format!("item_{}", i)),
13858            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13859            ..lsp::CompletionItem::default()
13860        }))
13861        .collect::<Vec<_>>();
13862
13863    let default_commit_characters = vec!["?".to_string()];
13864    let default_data = json!({ "default": "data"});
13865    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13866    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13867    let default_edit_range = lsp::Range {
13868        start: lsp::Position {
13869            line: 0,
13870            character: 5,
13871        },
13872        end: lsp::Position {
13873            line: 0,
13874            character: 5,
13875        },
13876    };
13877
13878    let mut cx = EditorLspTestContext::new_rust(
13879        lsp::ServerCapabilities {
13880            completion_provider: Some(lsp::CompletionOptions {
13881                trigger_characters: Some(vec![".".to_string()]),
13882                resolve_provider: Some(true),
13883                ..Default::default()
13884            }),
13885            ..Default::default()
13886        },
13887        cx,
13888    )
13889    .await;
13890
13891    cx.set_state("fn main() { let a = 2ˇ; }");
13892    cx.simulate_keystroke(".");
13893
13894    let completion_data = default_data.clone();
13895    let completion_characters = default_commit_characters.clone();
13896    let completion_items = items.clone();
13897    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13898        let default_data = completion_data.clone();
13899        let default_commit_characters = completion_characters.clone();
13900        let items = completion_items.clone();
13901        async move {
13902            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13903                items,
13904                item_defaults: Some(lsp::CompletionListItemDefaults {
13905                    data: Some(default_data.clone()),
13906                    commit_characters: Some(default_commit_characters.clone()),
13907                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13908                        default_edit_range,
13909                    )),
13910                    insert_text_format: Some(default_insert_text_format),
13911                    insert_text_mode: Some(default_insert_text_mode),
13912                }),
13913                ..lsp::CompletionList::default()
13914            })))
13915        }
13916    })
13917    .next()
13918    .await;
13919
13920    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13921    cx.lsp
13922        .server
13923        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13924            let closure_resolved_items = resolved_items.clone();
13925            move |item_to_resolve, _| {
13926                let closure_resolved_items = closure_resolved_items.clone();
13927                async move {
13928                    closure_resolved_items.lock().push(item_to_resolve.clone());
13929                    Ok(item_to_resolve)
13930                }
13931            }
13932        })
13933        .detach();
13934
13935    cx.condition(|editor, _| editor.context_menu_visible())
13936        .await;
13937    cx.run_until_parked();
13938    cx.update_editor(|editor, _, _| {
13939        let menu = editor.context_menu.borrow_mut();
13940        match menu.as_ref().expect("should have the completions menu") {
13941            CodeContextMenu::Completions(completions_menu) => {
13942                assert_eq!(
13943                    completions_menu
13944                        .entries
13945                        .borrow()
13946                        .iter()
13947                        .map(|mat| mat.string.clone())
13948                        .collect::<Vec<String>>(),
13949                    items
13950                        .iter()
13951                        .map(|completion| completion.label.clone())
13952                        .collect::<Vec<String>>()
13953                );
13954            }
13955            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13956        }
13957    });
13958    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13959    // with 4 from the end.
13960    assert_eq!(
13961        *resolved_items.lock(),
13962        [&items[0..16], &items[items.len() - 4..items.len()]]
13963            .concat()
13964            .iter()
13965            .cloned()
13966            .map(|mut item| {
13967                if item.data.is_none() {
13968                    item.data = Some(default_data.clone());
13969                }
13970                item
13971            })
13972            .collect::<Vec<lsp::CompletionItem>>(),
13973        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13974    );
13975    resolved_items.lock().clear();
13976
13977    cx.update_editor(|editor, window, cx| {
13978        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13979    });
13980    cx.run_until_parked();
13981    // Completions that have already been resolved are skipped.
13982    assert_eq!(
13983        *resolved_items.lock(),
13984        items[items.len() - 16..items.len() - 4]
13985            .iter()
13986            .cloned()
13987            .map(|mut item| {
13988                if item.data.is_none() {
13989                    item.data = Some(default_data.clone());
13990                }
13991                item
13992            })
13993            .collect::<Vec<lsp::CompletionItem>>()
13994    );
13995    resolved_items.lock().clear();
13996}
13997
13998#[gpui::test]
13999async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
14000    init_test(cx, |_| {});
14001
14002    let mut cx = EditorLspTestContext::new(
14003        Language::new(
14004            LanguageConfig {
14005                matcher: LanguageMatcher {
14006                    path_suffixes: vec!["jsx".into()],
14007                    ..Default::default()
14008                },
14009                overrides: [(
14010                    "element".into(),
14011                    LanguageConfigOverride {
14012                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
14013                        ..Default::default()
14014                    },
14015                )]
14016                .into_iter()
14017                .collect(),
14018                ..Default::default()
14019            },
14020            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
14021        )
14022        .with_override_query("(jsx_self_closing_element) @element")
14023        .unwrap(),
14024        lsp::ServerCapabilities {
14025            completion_provider: Some(lsp::CompletionOptions {
14026                trigger_characters: Some(vec![":".to_string()]),
14027                ..Default::default()
14028            }),
14029            ..Default::default()
14030        },
14031        cx,
14032    )
14033    .await;
14034
14035    cx.lsp
14036        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
14037            Ok(Some(lsp::CompletionResponse::Array(vec![
14038                lsp::CompletionItem {
14039                    label: "bg-blue".into(),
14040                    ..Default::default()
14041                },
14042                lsp::CompletionItem {
14043                    label: "bg-red".into(),
14044                    ..Default::default()
14045                },
14046                lsp::CompletionItem {
14047                    label: "bg-yellow".into(),
14048                    ..Default::default()
14049                },
14050            ])))
14051        });
14052
14053    cx.set_state(r#"<p class="bgˇ" />"#);
14054
14055    // Trigger completion when typing a dash, because the dash is an extra
14056    // word character in the 'element' scope, which contains the cursor.
14057    cx.simulate_keystroke("-");
14058    cx.executor().run_until_parked();
14059    cx.update_editor(|editor, _, _| {
14060        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14061        {
14062            assert_eq!(
14063                completion_menu_entries(&menu),
14064                &["bg-red", "bg-blue", "bg-yellow"]
14065            );
14066        } else {
14067            panic!("expected completion menu to be open");
14068        }
14069    });
14070
14071    cx.simulate_keystroke("l");
14072    cx.executor().run_until_parked();
14073    cx.update_editor(|editor, _, _| {
14074        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14075        {
14076            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14077        } else {
14078            panic!("expected completion menu to be open");
14079        }
14080    });
14081
14082    // When filtering completions, consider the character after the '-' to
14083    // be the start of a subword.
14084    cx.set_state(r#"<p class="yelˇ" />"#);
14085    cx.simulate_keystroke("l");
14086    cx.executor().run_until_parked();
14087    cx.update_editor(|editor, _, _| {
14088        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14089        {
14090            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14091        } else {
14092            panic!("expected completion menu to be open");
14093        }
14094    });
14095}
14096
14097fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14098    let entries = menu.entries.borrow();
14099    entries.iter().map(|mat| mat.string.clone()).collect()
14100}
14101
14102#[gpui::test]
14103async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14104    init_test(cx, |settings| {
14105        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14106            FormatterList(vec![Formatter::Prettier].into()),
14107        ))
14108    });
14109
14110    let fs = FakeFs::new(cx.executor());
14111    fs.insert_file(path!("/file.ts"), Default::default()).await;
14112
14113    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14114    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14115
14116    language_registry.add(Arc::new(Language::new(
14117        LanguageConfig {
14118            name: "TypeScript".into(),
14119            matcher: LanguageMatcher {
14120                path_suffixes: vec!["ts".to_string()],
14121                ..Default::default()
14122            },
14123            ..Default::default()
14124        },
14125        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14126    )));
14127    update_test_language_settings(cx, |settings| {
14128        settings.defaults.prettier = Some(PrettierSettings {
14129            allowed: true,
14130            ..PrettierSettings::default()
14131        });
14132    });
14133
14134    let test_plugin = "test_plugin";
14135    let _ = language_registry.register_fake_lsp(
14136        "TypeScript",
14137        FakeLspAdapter {
14138            prettier_plugins: vec![test_plugin],
14139            ..Default::default()
14140        },
14141    );
14142
14143    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14144    let buffer = project
14145        .update(cx, |project, cx| {
14146            project.open_local_buffer(path!("/file.ts"), cx)
14147        })
14148        .await
14149        .unwrap();
14150
14151    let buffer_text = "one\ntwo\nthree\n";
14152    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14153    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14154    editor.update_in(cx, |editor, window, cx| {
14155        editor.set_text(buffer_text, window, cx)
14156    });
14157
14158    editor
14159        .update_in(cx, |editor, window, cx| {
14160            editor.perform_format(
14161                project.clone(),
14162                FormatTrigger::Manual,
14163                FormatTarget::Buffers,
14164                window,
14165                cx,
14166            )
14167        })
14168        .unwrap()
14169        .await;
14170    assert_eq!(
14171        editor.update(cx, |editor, cx| editor.text(cx)),
14172        buffer_text.to_string() + prettier_format_suffix,
14173        "Test prettier formatting was not applied to the original buffer text",
14174    );
14175
14176    update_test_language_settings(cx, |settings| {
14177        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14178    });
14179    let format = editor.update_in(cx, |editor, window, cx| {
14180        editor.perform_format(
14181            project.clone(),
14182            FormatTrigger::Manual,
14183            FormatTarget::Buffers,
14184            window,
14185            cx,
14186        )
14187    });
14188    format.await.unwrap();
14189    assert_eq!(
14190        editor.update(cx, |editor, cx| editor.text(cx)),
14191        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14192        "Autoformatting (via test prettier) was not applied to the original buffer text",
14193    );
14194}
14195
14196#[gpui::test]
14197async fn test_addition_reverts(cx: &mut TestAppContext) {
14198    init_test(cx, |_| {});
14199    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14200    let base_text = indoc! {r#"
14201        struct Row;
14202        struct Row1;
14203        struct Row2;
14204
14205        struct Row4;
14206        struct Row5;
14207        struct Row6;
14208
14209        struct Row8;
14210        struct Row9;
14211        struct Row10;"#};
14212
14213    // When addition hunks are not adjacent to carets, no hunk revert is performed
14214    assert_hunk_revert(
14215        indoc! {r#"struct Row;
14216                   struct Row1;
14217                   struct Row1.1;
14218                   struct Row1.2;
14219                   struct Row2;ˇ
14220
14221                   struct Row4;
14222                   struct Row5;
14223                   struct Row6;
14224
14225                   struct Row8;
14226                   ˇstruct Row9;
14227                   struct Row9.1;
14228                   struct Row9.2;
14229                   struct Row9.3;
14230                   struct Row10;"#},
14231        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14232        indoc! {r#"struct Row;
14233                   struct Row1;
14234                   struct Row1.1;
14235                   struct Row1.2;
14236                   struct Row2;ˇ
14237
14238                   struct Row4;
14239                   struct Row5;
14240                   struct Row6;
14241
14242                   struct Row8;
14243                   ˇstruct Row9;
14244                   struct Row9.1;
14245                   struct Row9.2;
14246                   struct Row9.3;
14247                   struct Row10;"#},
14248        base_text,
14249        &mut cx,
14250    );
14251    // Same for selections
14252    assert_hunk_revert(
14253        indoc! {r#"struct Row;
14254                   struct Row1;
14255                   struct Row2;
14256                   struct Row2.1;
14257                   struct Row2.2;
14258                   «ˇ
14259                   struct Row4;
14260                   struct» Row5;
14261                   «struct Row6;
14262                   ˇ»
14263                   struct Row9.1;
14264                   struct Row9.2;
14265                   struct Row9.3;
14266                   struct Row8;
14267                   struct Row9;
14268                   struct Row10;"#},
14269        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14270        indoc! {r#"struct Row;
14271                   struct Row1;
14272                   struct Row2;
14273                   struct Row2.1;
14274                   struct Row2.2;
14275                   «ˇ
14276                   struct Row4;
14277                   struct» Row5;
14278                   «struct Row6;
14279                   ˇ»
14280                   struct Row9.1;
14281                   struct Row9.2;
14282                   struct Row9.3;
14283                   struct Row8;
14284                   struct Row9;
14285                   struct Row10;"#},
14286        base_text,
14287        &mut cx,
14288    );
14289
14290    // When carets and selections intersect the addition hunks, those are reverted.
14291    // Adjacent carets got merged.
14292    assert_hunk_revert(
14293        indoc! {r#"struct Row;
14294                   ˇ// something on the top
14295                   struct Row1;
14296                   struct Row2;
14297                   struct Roˇw3.1;
14298                   struct Row2.2;
14299                   struct Row2.3;ˇ
14300
14301                   struct Row4;
14302                   struct ˇRow5.1;
14303                   struct Row5.2;
14304                   struct «Rowˇ»5.3;
14305                   struct Row5;
14306                   struct Row6;
14307                   ˇ
14308                   struct Row9.1;
14309                   struct «Rowˇ»9.2;
14310                   struct «ˇRow»9.3;
14311                   struct Row8;
14312                   struct Row9;
14313                   «ˇ// something on bottom»
14314                   struct Row10;"#},
14315        vec![
14316            DiffHunkStatusKind::Added,
14317            DiffHunkStatusKind::Added,
14318            DiffHunkStatusKind::Added,
14319            DiffHunkStatusKind::Added,
14320            DiffHunkStatusKind::Added,
14321        ],
14322        indoc! {r#"struct Row;
14323                   ˇstruct Row1;
14324                   struct Row2;
14325                   ˇ
14326                   struct Row4;
14327                   ˇstruct Row5;
14328                   struct Row6;
14329                   ˇ
14330                   ˇstruct Row8;
14331                   struct Row9;
14332                   ˇstruct Row10;"#},
14333        base_text,
14334        &mut cx,
14335    );
14336}
14337
14338#[gpui::test]
14339async fn test_modification_reverts(cx: &mut TestAppContext) {
14340    init_test(cx, |_| {});
14341    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14342    let base_text = indoc! {r#"
14343        struct Row;
14344        struct Row1;
14345        struct Row2;
14346
14347        struct Row4;
14348        struct Row5;
14349        struct Row6;
14350
14351        struct Row8;
14352        struct Row9;
14353        struct Row10;"#};
14354
14355    // Modification hunks behave the same as the addition ones.
14356    assert_hunk_revert(
14357        indoc! {r#"struct Row;
14358                   struct Row1;
14359                   struct Row33;
14360                   ˇ
14361                   struct Row4;
14362                   struct Row5;
14363                   struct Row6;
14364                   ˇ
14365                   struct Row99;
14366                   struct Row9;
14367                   struct Row10;"#},
14368        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14369        indoc! {r#"struct Row;
14370                   struct Row1;
14371                   struct Row33;
14372                   ˇ
14373                   struct Row4;
14374                   struct Row5;
14375                   struct Row6;
14376                   ˇ
14377                   struct Row99;
14378                   struct Row9;
14379                   struct Row10;"#},
14380        base_text,
14381        &mut cx,
14382    );
14383    assert_hunk_revert(
14384        indoc! {r#"struct Row;
14385                   struct Row1;
14386                   struct Row33;
14387                   «ˇ
14388                   struct Row4;
14389                   struct» Row5;
14390                   «struct Row6;
14391                   ˇ»
14392                   struct Row99;
14393                   struct Row9;
14394                   struct Row10;"#},
14395        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14396        indoc! {r#"struct Row;
14397                   struct Row1;
14398                   struct Row33;
14399                   «ˇ
14400                   struct Row4;
14401                   struct» Row5;
14402                   «struct Row6;
14403                   ˇ»
14404                   struct Row99;
14405                   struct Row9;
14406                   struct Row10;"#},
14407        base_text,
14408        &mut cx,
14409    );
14410
14411    assert_hunk_revert(
14412        indoc! {r#"ˇstruct Row1.1;
14413                   struct Row1;
14414                   «ˇstr»uct Row22;
14415
14416                   struct ˇRow44;
14417                   struct Row5;
14418                   struct «Rˇ»ow66;ˇ
14419
14420                   «struˇ»ct Row88;
14421                   struct Row9;
14422                   struct Row1011;ˇ"#},
14423        vec![
14424            DiffHunkStatusKind::Modified,
14425            DiffHunkStatusKind::Modified,
14426            DiffHunkStatusKind::Modified,
14427            DiffHunkStatusKind::Modified,
14428            DiffHunkStatusKind::Modified,
14429            DiffHunkStatusKind::Modified,
14430        ],
14431        indoc! {r#"struct Row;
14432                   ˇstruct Row1;
14433                   struct Row2;
14434                   ˇ
14435                   struct Row4;
14436                   ˇstruct Row5;
14437                   struct Row6;
14438                   ˇ
14439                   struct Row8;
14440                   ˇstruct Row9;
14441                   struct Row10;ˇ"#},
14442        base_text,
14443        &mut cx,
14444    );
14445}
14446
14447#[gpui::test]
14448async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14449    init_test(cx, |_| {});
14450    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14451    let base_text = indoc! {r#"
14452        one
14453
14454        two
14455        three
14456        "#};
14457
14458    cx.set_head_text(base_text);
14459    cx.set_state("\nˇ\n");
14460    cx.executor().run_until_parked();
14461    cx.update_editor(|editor, _window, cx| {
14462        editor.expand_selected_diff_hunks(cx);
14463    });
14464    cx.executor().run_until_parked();
14465    cx.update_editor(|editor, window, cx| {
14466        editor.backspace(&Default::default(), window, cx);
14467    });
14468    cx.run_until_parked();
14469    cx.assert_state_with_diff(
14470        indoc! {r#"
14471
14472        - two
14473        - threeˇ
14474        +
14475        "#}
14476        .to_string(),
14477    );
14478}
14479
14480#[gpui::test]
14481async fn test_deletion_reverts(cx: &mut TestAppContext) {
14482    init_test(cx, |_| {});
14483    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14484    let base_text = indoc! {r#"struct Row;
14485struct Row1;
14486struct Row2;
14487
14488struct Row4;
14489struct Row5;
14490struct Row6;
14491
14492struct Row8;
14493struct Row9;
14494struct Row10;"#};
14495
14496    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14497    assert_hunk_revert(
14498        indoc! {r#"struct Row;
14499                   struct Row2;
14500
14501                   ˇstruct Row4;
14502                   struct Row5;
14503                   struct Row6;
14504                   ˇ
14505                   struct Row8;
14506                   struct Row10;"#},
14507        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14508        indoc! {r#"struct Row;
14509                   struct Row2;
14510
14511                   ˇstruct Row4;
14512                   struct Row5;
14513                   struct Row6;
14514                   ˇ
14515                   struct Row8;
14516                   struct Row10;"#},
14517        base_text,
14518        &mut cx,
14519    );
14520    assert_hunk_revert(
14521        indoc! {r#"struct Row;
14522                   struct Row2;
14523
14524                   «ˇstruct Row4;
14525                   struct» Row5;
14526                   «struct Row6;
14527                   ˇ»
14528                   struct Row8;
14529                   struct Row10;"#},
14530        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14531        indoc! {r#"struct Row;
14532                   struct Row2;
14533
14534                   «ˇstruct Row4;
14535                   struct» Row5;
14536                   «struct Row6;
14537                   ˇ»
14538                   struct Row8;
14539                   struct Row10;"#},
14540        base_text,
14541        &mut cx,
14542    );
14543
14544    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14545    assert_hunk_revert(
14546        indoc! {r#"struct Row;
14547                   ˇstruct Row2;
14548
14549                   struct Row4;
14550                   struct Row5;
14551                   struct Row6;
14552
14553                   struct Row8;ˇ
14554                   struct Row10;"#},
14555        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14556        indoc! {r#"struct Row;
14557                   struct Row1;
14558                   ˇstruct Row2;
14559
14560                   struct Row4;
14561                   struct Row5;
14562                   struct Row6;
14563
14564                   struct Row8;ˇ
14565                   struct Row9;
14566                   struct Row10;"#},
14567        base_text,
14568        &mut cx,
14569    );
14570    assert_hunk_revert(
14571        indoc! {r#"struct Row;
14572                   struct Row2«ˇ;
14573                   struct Row4;
14574                   struct» Row5;
14575                   «struct Row6;
14576
14577                   struct Row8;ˇ»
14578                   struct Row10;"#},
14579        vec![
14580            DiffHunkStatusKind::Deleted,
14581            DiffHunkStatusKind::Deleted,
14582            DiffHunkStatusKind::Deleted,
14583        ],
14584        indoc! {r#"struct Row;
14585                   struct Row1;
14586                   struct Row2«ˇ;
14587
14588                   struct Row4;
14589                   struct» Row5;
14590                   «struct Row6;
14591
14592                   struct Row8;ˇ»
14593                   struct Row9;
14594                   struct Row10;"#},
14595        base_text,
14596        &mut cx,
14597    );
14598}
14599
14600#[gpui::test]
14601async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14602    init_test(cx, |_| {});
14603
14604    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14605    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14606    let base_text_3 =
14607        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14608
14609    let text_1 = edit_first_char_of_every_line(base_text_1);
14610    let text_2 = edit_first_char_of_every_line(base_text_2);
14611    let text_3 = edit_first_char_of_every_line(base_text_3);
14612
14613    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14614    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14615    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14616
14617    let multibuffer = cx.new(|cx| {
14618        let mut multibuffer = MultiBuffer::new(ReadWrite);
14619        multibuffer.push_excerpts(
14620            buffer_1.clone(),
14621            [
14622                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14623                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14624                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14625            ],
14626            cx,
14627        );
14628        multibuffer.push_excerpts(
14629            buffer_2.clone(),
14630            [
14631                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14632                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14633                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14634            ],
14635            cx,
14636        );
14637        multibuffer.push_excerpts(
14638            buffer_3.clone(),
14639            [
14640                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14641                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14642                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14643            ],
14644            cx,
14645        );
14646        multibuffer
14647    });
14648
14649    let fs = FakeFs::new(cx.executor());
14650    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14651    let (editor, cx) = cx
14652        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14653    editor.update_in(cx, |editor, _window, cx| {
14654        for (buffer, diff_base) in [
14655            (buffer_1.clone(), base_text_1),
14656            (buffer_2.clone(), base_text_2),
14657            (buffer_3.clone(), base_text_3),
14658        ] {
14659            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14660            editor
14661                .buffer
14662                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14663        }
14664    });
14665    cx.executor().run_until_parked();
14666
14667    editor.update_in(cx, |editor, window, cx| {
14668        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}");
14669        editor.select_all(&SelectAll, window, cx);
14670        editor.git_restore(&Default::default(), window, cx);
14671    });
14672    cx.executor().run_until_parked();
14673
14674    // When all ranges are selected, all buffer hunks are reverted.
14675    editor.update(cx, |editor, cx| {
14676        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");
14677    });
14678    buffer_1.update(cx, |buffer, _| {
14679        assert_eq!(buffer.text(), base_text_1);
14680    });
14681    buffer_2.update(cx, |buffer, _| {
14682        assert_eq!(buffer.text(), base_text_2);
14683    });
14684    buffer_3.update(cx, |buffer, _| {
14685        assert_eq!(buffer.text(), base_text_3);
14686    });
14687
14688    editor.update_in(cx, |editor, window, cx| {
14689        editor.undo(&Default::default(), window, cx);
14690    });
14691
14692    editor.update_in(cx, |editor, window, cx| {
14693        editor.change_selections(None, window, cx, |s| {
14694            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14695        });
14696        editor.git_restore(&Default::default(), window, cx);
14697    });
14698
14699    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14700    // but not affect buffer_2 and its related excerpts.
14701    editor.update(cx, |editor, cx| {
14702        assert_eq!(
14703            editor.text(cx),
14704            "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}"
14705        );
14706    });
14707    buffer_1.update(cx, |buffer, _| {
14708        assert_eq!(buffer.text(), base_text_1);
14709    });
14710    buffer_2.update(cx, |buffer, _| {
14711        assert_eq!(
14712            buffer.text(),
14713            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14714        );
14715    });
14716    buffer_3.update(cx, |buffer, _| {
14717        assert_eq!(
14718            buffer.text(),
14719            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14720        );
14721    });
14722
14723    fn edit_first_char_of_every_line(text: &str) -> String {
14724        text.split('\n')
14725            .map(|line| format!("X{}", &line[1..]))
14726            .collect::<Vec<_>>()
14727            .join("\n")
14728    }
14729}
14730
14731#[gpui::test]
14732async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14733    init_test(cx, |_| {});
14734
14735    let cols = 4;
14736    let rows = 10;
14737    let sample_text_1 = sample_text(rows, cols, 'a');
14738    assert_eq!(
14739        sample_text_1,
14740        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14741    );
14742    let sample_text_2 = sample_text(rows, cols, 'l');
14743    assert_eq!(
14744        sample_text_2,
14745        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14746    );
14747    let sample_text_3 = sample_text(rows, cols, 'v');
14748    assert_eq!(
14749        sample_text_3,
14750        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14751    );
14752
14753    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14754    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14755    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14756
14757    let multi_buffer = cx.new(|cx| {
14758        let mut multibuffer = MultiBuffer::new(ReadWrite);
14759        multibuffer.push_excerpts(
14760            buffer_1.clone(),
14761            [
14762                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14763                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14764                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14765            ],
14766            cx,
14767        );
14768        multibuffer.push_excerpts(
14769            buffer_2.clone(),
14770            [
14771                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14772                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14773                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14774            ],
14775            cx,
14776        );
14777        multibuffer.push_excerpts(
14778            buffer_3.clone(),
14779            [
14780                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14781                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14782                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14783            ],
14784            cx,
14785        );
14786        multibuffer
14787    });
14788
14789    let fs = FakeFs::new(cx.executor());
14790    fs.insert_tree(
14791        "/a",
14792        json!({
14793            "main.rs": sample_text_1,
14794            "other.rs": sample_text_2,
14795            "lib.rs": sample_text_3,
14796        }),
14797    )
14798    .await;
14799    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14800    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14801    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14802    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14803        Editor::new(
14804            EditorMode::full(),
14805            multi_buffer,
14806            Some(project.clone()),
14807            window,
14808            cx,
14809        )
14810    });
14811    let multibuffer_item_id = workspace
14812        .update(cx, |workspace, window, cx| {
14813            assert!(
14814                workspace.active_item(cx).is_none(),
14815                "active item should be None before the first item is added"
14816            );
14817            workspace.add_item_to_active_pane(
14818                Box::new(multi_buffer_editor.clone()),
14819                None,
14820                true,
14821                window,
14822                cx,
14823            );
14824            let active_item = workspace
14825                .active_item(cx)
14826                .expect("should have an active item after adding the multi buffer");
14827            assert!(
14828                !active_item.is_singleton(cx),
14829                "A multi buffer was expected to active after adding"
14830            );
14831            active_item.item_id()
14832        })
14833        .unwrap();
14834    cx.executor().run_until_parked();
14835
14836    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14837        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14838            s.select_ranges(Some(1..2))
14839        });
14840        editor.open_excerpts(&OpenExcerpts, window, cx);
14841    });
14842    cx.executor().run_until_parked();
14843    let first_item_id = workspace
14844        .update(cx, |workspace, window, cx| {
14845            let active_item = workspace
14846                .active_item(cx)
14847                .expect("should have an active item after navigating into the 1st buffer");
14848            let first_item_id = active_item.item_id();
14849            assert_ne!(
14850                first_item_id, multibuffer_item_id,
14851                "Should navigate into the 1st buffer and activate it"
14852            );
14853            assert!(
14854                active_item.is_singleton(cx),
14855                "New active item should be a singleton buffer"
14856            );
14857            assert_eq!(
14858                active_item
14859                    .act_as::<Editor>(cx)
14860                    .expect("should have navigated into an editor for the 1st buffer")
14861                    .read(cx)
14862                    .text(cx),
14863                sample_text_1
14864            );
14865
14866            workspace
14867                .go_back(workspace.active_pane().downgrade(), window, cx)
14868                .detach_and_log_err(cx);
14869
14870            first_item_id
14871        })
14872        .unwrap();
14873    cx.executor().run_until_parked();
14874    workspace
14875        .update(cx, |workspace, _, cx| {
14876            let active_item = workspace
14877                .active_item(cx)
14878                .expect("should have an active item after navigating back");
14879            assert_eq!(
14880                active_item.item_id(),
14881                multibuffer_item_id,
14882                "Should navigate back to the multi buffer"
14883            );
14884            assert!(!active_item.is_singleton(cx));
14885        })
14886        .unwrap();
14887
14888    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14889        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14890            s.select_ranges(Some(39..40))
14891        });
14892        editor.open_excerpts(&OpenExcerpts, window, cx);
14893    });
14894    cx.executor().run_until_parked();
14895    let second_item_id = workspace
14896        .update(cx, |workspace, window, cx| {
14897            let active_item = workspace
14898                .active_item(cx)
14899                .expect("should have an active item after navigating into the 2nd buffer");
14900            let second_item_id = active_item.item_id();
14901            assert_ne!(
14902                second_item_id, multibuffer_item_id,
14903                "Should navigate away from the multibuffer"
14904            );
14905            assert_ne!(
14906                second_item_id, first_item_id,
14907                "Should navigate into the 2nd buffer and activate it"
14908            );
14909            assert!(
14910                active_item.is_singleton(cx),
14911                "New active item should be a singleton buffer"
14912            );
14913            assert_eq!(
14914                active_item
14915                    .act_as::<Editor>(cx)
14916                    .expect("should have navigated into an editor")
14917                    .read(cx)
14918                    .text(cx),
14919                sample_text_2
14920            );
14921
14922            workspace
14923                .go_back(workspace.active_pane().downgrade(), window, cx)
14924                .detach_and_log_err(cx);
14925
14926            second_item_id
14927        })
14928        .unwrap();
14929    cx.executor().run_until_parked();
14930    workspace
14931        .update(cx, |workspace, _, cx| {
14932            let active_item = workspace
14933                .active_item(cx)
14934                .expect("should have an active item after navigating back from the 2nd buffer");
14935            assert_eq!(
14936                active_item.item_id(),
14937                multibuffer_item_id,
14938                "Should navigate back from the 2nd buffer to the multi buffer"
14939            );
14940            assert!(!active_item.is_singleton(cx));
14941        })
14942        .unwrap();
14943
14944    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14945        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14946            s.select_ranges(Some(70..70))
14947        });
14948        editor.open_excerpts(&OpenExcerpts, window, cx);
14949    });
14950    cx.executor().run_until_parked();
14951    workspace
14952        .update(cx, |workspace, window, cx| {
14953            let active_item = workspace
14954                .active_item(cx)
14955                .expect("should have an active item after navigating into the 3rd buffer");
14956            let third_item_id = active_item.item_id();
14957            assert_ne!(
14958                third_item_id, multibuffer_item_id,
14959                "Should navigate into the 3rd buffer and activate it"
14960            );
14961            assert_ne!(third_item_id, first_item_id);
14962            assert_ne!(third_item_id, second_item_id);
14963            assert!(
14964                active_item.is_singleton(cx),
14965                "New active item should be a singleton buffer"
14966            );
14967            assert_eq!(
14968                active_item
14969                    .act_as::<Editor>(cx)
14970                    .expect("should have navigated into an editor")
14971                    .read(cx)
14972                    .text(cx),
14973                sample_text_3
14974            );
14975
14976            workspace
14977                .go_back(workspace.active_pane().downgrade(), window, cx)
14978                .detach_and_log_err(cx);
14979        })
14980        .unwrap();
14981    cx.executor().run_until_parked();
14982    workspace
14983        .update(cx, |workspace, _, cx| {
14984            let active_item = workspace
14985                .active_item(cx)
14986                .expect("should have an active item after navigating back from the 3rd buffer");
14987            assert_eq!(
14988                active_item.item_id(),
14989                multibuffer_item_id,
14990                "Should navigate back from the 3rd buffer to the multi buffer"
14991            );
14992            assert!(!active_item.is_singleton(cx));
14993        })
14994        .unwrap();
14995}
14996
14997#[gpui::test]
14998async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14999    init_test(cx, |_| {});
15000
15001    let mut cx = EditorTestContext::new(cx).await;
15002
15003    let diff_base = r#"
15004        use some::mod;
15005
15006        const A: u32 = 42;
15007
15008        fn main() {
15009            println!("hello");
15010
15011            println!("world");
15012        }
15013        "#
15014    .unindent();
15015
15016    cx.set_state(
15017        &r#"
15018        use some::modified;
15019
15020        ˇ
15021        fn main() {
15022            println!("hello there");
15023
15024            println!("around the");
15025            println!("world");
15026        }
15027        "#
15028        .unindent(),
15029    );
15030
15031    cx.set_head_text(&diff_base);
15032    executor.run_until_parked();
15033
15034    cx.update_editor(|editor, window, cx| {
15035        editor.go_to_next_hunk(&GoToHunk, window, cx);
15036        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15037    });
15038    executor.run_until_parked();
15039    cx.assert_state_with_diff(
15040        r#"
15041          use some::modified;
15042
15043
15044          fn main() {
15045        -     println!("hello");
15046        + ˇ    println!("hello there");
15047
15048              println!("around the");
15049              println!("world");
15050          }
15051        "#
15052        .unindent(),
15053    );
15054
15055    cx.update_editor(|editor, window, cx| {
15056        for _ in 0..2 {
15057            editor.go_to_next_hunk(&GoToHunk, window, cx);
15058            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15059        }
15060    });
15061    executor.run_until_parked();
15062    cx.assert_state_with_diff(
15063        r#"
15064        - use some::mod;
15065        + ˇuse some::modified;
15066
15067
15068          fn main() {
15069        -     println!("hello");
15070        +     println!("hello there");
15071
15072        +     println!("around the");
15073              println!("world");
15074          }
15075        "#
15076        .unindent(),
15077    );
15078
15079    cx.update_editor(|editor, window, cx| {
15080        editor.go_to_next_hunk(&GoToHunk, window, cx);
15081        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15082    });
15083    executor.run_until_parked();
15084    cx.assert_state_with_diff(
15085        r#"
15086        - use some::mod;
15087        + use some::modified;
15088
15089        - const A: u32 = 42;
15090          ˇ
15091          fn main() {
15092        -     println!("hello");
15093        +     println!("hello there");
15094
15095        +     println!("around the");
15096              println!("world");
15097          }
15098        "#
15099        .unindent(),
15100    );
15101
15102    cx.update_editor(|editor, window, cx| {
15103        editor.cancel(&Cancel, window, cx);
15104    });
15105
15106    cx.assert_state_with_diff(
15107        r#"
15108          use some::modified;
15109
15110          ˇ
15111          fn main() {
15112              println!("hello there");
15113
15114              println!("around the");
15115              println!("world");
15116          }
15117        "#
15118        .unindent(),
15119    );
15120}
15121
15122#[gpui::test]
15123async fn test_diff_base_change_with_expanded_diff_hunks(
15124    executor: BackgroundExecutor,
15125    cx: &mut TestAppContext,
15126) {
15127    init_test(cx, |_| {});
15128
15129    let mut cx = EditorTestContext::new(cx).await;
15130
15131    let diff_base = r#"
15132        use some::mod1;
15133        use some::mod2;
15134
15135        const A: u32 = 42;
15136        const B: u32 = 42;
15137        const C: u32 = 42;
15138
15139        fn main() {
15140            println!("hello");
15141
15142            println!("world");
15143        }
15144        "#
15145    .unindent();
15146
15147    cx.set_state(
15148        &r#"
15149        use some::mod2;
15150
15151        const A: u32 = 42;
15152        const C: u32 = 42;
15153
15154        fn main(ˇ) {
15155            //println!("hello");
15156
15157            println!("world");
15158            //
15159            //
15160        }
15161        "#
15162        .unindent(),
15163    );
15164
15165    cx.set_head_text(&diff_base);
15166    executor.run_until_parked();
15167
15168    cx.update_editor(|editor, window, cx| {
15169        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15170    });
15171    executor.run_until_parked();
15172    cx.assert_state_with_diff(
15173        r#"
15174        - use some::mod1;
15175          use some::mod2;
15176
15177          const A: u32 = 42;
15178        - const B: u32 = 42;
15179          const C: u32 = 42;
15180
15181          fn main(ˇ) {
15182        -     println!("hello");
15183        +     //println!("hello");
15184
15185              println!("world");
15186        +     //
15187        +     //
15188          }
15189        "#
15190        .unindent(),
15191    );
15192
15193    cx.set_head_text("new diff base!");
15194    executor.run_until_parked();
15195    cx.assert_state_with_diff(
15196        r#"
15197        - new diff base!
15198        + use some::mod2;
15199        +
15200        + const A: u32 = 42;
15201        + const C: u32 = 42;
15202        +
15203        + fn main(ˇ) {
15204        +     //println!("hello");
15205        +
15206        +     println!("world");
15207        +     //
15208        +     //
15209        + }
15210        "#
15211        .unindent(),
15212    );
15213}
15214
15215#[gpui::test]
15216async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15217    init_test(cx, |_| {});
15218
15219    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15220    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15221    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15222    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15223    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15224    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15225
15226    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15227    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15228    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15229
15230    let multi_buffer = cx.new(|cx| {
15231        let mut multibuffer = MultiBuffer::new(ReadWrite);
15232        multibuffer.push_excerpts(
15233            buffer_1.clone(),
15234            [
15235                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15236                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15237                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15238            ],
15239            cx,
15240        );
15241        multibuffer.push_excerpts(
15242            buffer_2.clone(),
15243            [
15244                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15245                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15246                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15247            ],
15248            cx,
15249        );
15250        multibuffer.push_excerpts(
15251            buffer_3.clone(),
15252            [
15253                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15254                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15255                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15256            ],
15257            cx,
15258        );
15259        multibuffer
15260    });
15261
15262    let editor =
15263        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15264    editor
15265        .update(cx, |editor, _window, cx| {
15266            for (buffer, diff_base) in [
15267                (buffer_1.clone(), file_1_old),
15268                (buffer_2.clone(), file_2_old),
15269                (buffer_3.clone(), file_3_old),
15270            ] {
15271                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15272                editor
15273                    .buffer
15274                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15275            }
15276        })
15277        .unwrap();
15278
15279    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15280    cx.run_until_parked();
15281
15282    cx.assert_editor_state(
15283        &"
15284            ˇaaa
15285            ccc
15286            ddd
15287
15288            ggg
15289            hhh
15290
15291
15292            lll
15293            mmm
15294            NNN
15295
15296            qqq
15297            rrr
15298
15299            uuu
15300            111
15301            222
15302            333
15303
15304            666
15305            777
15306
15307            000
15308            !!!"
15309        .unindent(),
15310    );
15311
15312    cx.update_editor(|editor, window, cx| {
15313        editor.select_all(&SelectAll, window, cx);
15314        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15315    });
15316    cx.executor().run_until_parked();
15317
15318    cx.assert_state_with_diff(
15319        "
15320            «aaa
15321          - bbb
15322            ccc
15323            ddd
15324
15325            ggg
15326            hhh
15327
15328
15329            lll
15330            mmm
15331          - nnn
15332          + NNN
15333
15334            qqq
15335            rrr
15336
15337            uuu
15338            111
15339            222
15340            333
15341
15342          + 666
15343            777
15344
15345            000
15346            !!!ˇ»"
15347            .unindent(),
15348    );
15349}
15350
15351#[gpui::test]
15352async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15353    init_test(cx, |_| {});
15354
15355    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15356    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15357
15358    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15359    let multi_buffer = cx.new(|cx| {
15360        let mut multibuffer = MultiBuffer::new(ReadWrite);
15361        multibuffer.push_excerpts(
15362            buffer.clone(),
15363            [
15364                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15365                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15366                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15367            ],
15368            cx,
15369        );
15370        multibuffer
15371    });
15372
15373    let editor =
15374        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15375    editor
15376        .update(cx, |editor, _window, cx| {
15377            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15378            editor
15379                .buffer
15380                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15381        })
15382        .unwrap();
15383
15384    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15385    cx.run_until_parked();
15386
15387    cx.update_editor(|editor, window, cx| {
15388        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15389    });
15390    cx.executor().run_until_parked();
15391
15392    // When the start of a hunk coincides with the start of its excerpt,
15393    // the hunk is expanded. When the start of a a hunk is earlier than
15394    // the start of its excerpt, the hunk is not expanded.
15395    cx.assert_state_with_diff(
15396        "
15397            ˇaaa
15398          - bbb
15399          + BBB
15400
15401          - ddd
15402          - eee
15403          + DDD
15404          + EEE
15405            fff
15406
15407            iii
15408        "
15409        .unindent(),
15410    );
15411}
15412
15413#[gpui::test]
15414async fn test_edits_around_expanded_insertion_hunks(
15415    executor: BackgroundExecutor,
15416    cx: &mut TestAppContext,
15417) {
15418    init_test(cx, |_| {});
15419
15420    let mut cx = EditorTestContext::new(cx).await;
15421
15422    let diff_base = r#"
15423        use some::mod1;
15424        use some::mod2;
15425
15426        const A: u32 = 42;
15427
15428        fn main() {
15429            println!("hello");
15430
15431            println!("world");
15432        }
15433        "#
15434    .unindent();
15435    executor.run_until_parked();
15436    cx.set_state(
15437        &r#"
15438        use some::mod1;
15439        use some::mod2;
15440
15441        const A: u32 = 42;
15442        const B: u32 = 42;
15443        const C: u32 = 42;
15444        ˇ
15445
15446        fn main() {
15447            println!("hello");
15448
15449            println!("world");
15450        }
15451        "#
15452        .unindent(),
15453    );
15454
15455    cx.set_head_text(&diff_base);
15456    executor.run_until_parked();
15457
15458    cx.update_editor(|editor, window, cx| {
15459        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15460    });
15461    executor.run_until_parked();
15462
15463    cx.assert_state_with_diff(
15464        r#"
15465        use some::mod1;
15466        use some::mod2;
15467
15468        const A: u32 = 42;
15469      + const B: u32 = 42;
15470      + const C: u32 = 42;
15471      + ˇ
15472
15473        fn main() {
15474            println!("hello");
15475
15476            println!("world");
15477        }
15478      "#
15479        .unindent(),
15480    );
15481
15482    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15483    executor.run_until_parked();
15484
15485    cx.assert_state_with_diff(
15486        r#"
15487        use some::mod1;
15488        use some::mod2;
15489
15490        const A: u32 = 42;
15491      + const B: u32 = 42;
15492      + const C: u32 = 42;
15493      + const D: u32 = 42;
15494      + ˇ
15495
15496        fn main() {
15497            println!("hello");
15498
15499            println!("world");
15500        }
15501      "#
15502        .unindent(),
15503    );
15504
15505    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15506    executor.run_until_parked();
15507
15508    cx.assert_state_with_diff(
15509        r#"
15510        use some::mod1;
15511        use some::mod2;
15512
15513        const A: u32 = 42;
15514      + const B: u32 = 42;
15515      + const C: u32 = 42;
15516      + const D: u32 = 42;
15517      + const E: u32 = 42;
15518      + ˇ
15519
15520        fn main() {
15521            println!("hello");
15522
15523            println!("world");
15524        }
15525      "#
15526        .unindent(),
15527    );
15528
15529    cx.update_editor(|editor, window, cx| {
15530        editor.delete_line(&DeleteLine, window, cx);
15531    });
15532    executor.run_until_parked();
15533
15534    cx.assert_state_with_diff(
15535        r#"
15536        use some::mod1;
15537        use some::mod2;
15538
15539        const A: u32 = 42;
15540      + const B: u32 = 42;
15541      + const C: u32 = 42;
15542      + const D: u32 = 42;
15543      + const E: u32 = 42;
15544        ˇ
15545        fn main() {
15546            println!("hello");
15547
15548            println!("world");
15549        }
15550      "#
15551        .unindent(),
15552    );
15553
15554    cx.update_editor(|editor, window, cx| {
15555        editor.move_up(&MoveUp, window, cx);
15556        editor.delete_line(&DeleteLine, window, cx);
15557        editor.move_up(&MoveUp, window, cx);
15558        editor.delete_line(&DeleteLine, window, cx);
15559        editor.move_up(&MoveUp, window, cx);
15560        editor.delete_line(&DeleteLine, window, cx);
15561    });
15562    executor.run_until_parked();
15563    cx.assert_state_with_diff(
15564        r#"
15565        use some::mod1;
15566        use some::mod2;
15567
15568        const A: u32 = 42;
15569      + const B: u32 = 42;
15570        ˇ
15571        fn main() {
15572            println!("hello");
15573
15574            println!("world");
15575        }
15576      "#
15577        .unindent(),
15578    );
15579
15580    cx.update_editor(|editor, window, cx| {
15581        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15582        editor.delete_line(&DeleteLine, window, cx);
15583    });
15584    executor.run_until_parked();
15585    cx.assert_state_with_diff(
15586        r#"
15587        ˇ
15588        fn main() {
15589            println!("hello");
15590
15591            println!("world");
15592        }
15593      "#
15594        .unindent(),
15595    );
15596}
15597
15598#[gpui::test]
15599async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15600    init_test(cx, |_| {});
15601
15602    let mut cx = EditorTestContext::new(cx).await;
15603    cx.set_head_text(indoc! { "
15604        one
15605        two
15606        three
15607        four
15608        five
15609        "
15610    });
15611    cx.set_state(indoc! { "
15612        one
15613        ˇthree
15614        five
15615    "});
15616    cx.run_until_parked();
15617    cx.update_editor(|editor, window, cx| {
15618        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15619    });
15620    cx.assert_state_with_diff(
15621        indoc! { "
15622        one
15623      - two
15624        ˇthree
15625      - four
15626        five
15627    "}
15628        .to_string(),
15629    );
15630    cx.update_editor(|editor, window, cx| {
15631        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15632    });
15633
15634    cx.assert_state_with_diff(
15635        indoc! { "
15636        one
15637        ˇthree
15638        five
15639    "}
15640        .to_string(),
15641    );
15642
15643    cx.set_state(indoc! { "
15644        one
15645        ˇTWO
15646        three
15647        four
15648        five
15649    "});
15650    cx.run_until_parked();
15651    cx.update_editor(|editor, window, cx| {
15652        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15653    });
15654
15655    cx.assert_state_with_diff(
15656        indoc! { "
15657            one
15658          - two
15659          + ˇTWO
15660            three
15661            four
15662            five
15663        "}
15664        .to_string(),
15665    );
15666    cx.update_editor(|editor, window, cx| {
15667        editor.move_up(&Default::default(), window, cx);
15668        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15669    });
15670    cx.assert_state_with_diff(
15671        indoc! { "
15672            one
15673            ˇTWO
15674            three
15675            four
15676            five
15677        "}
15678        .to_string(),
15679    );
15680}
15681
15682#[gpui::test]
15683async fn test_edits_around_expanded_deletion_hunks(
15684    executor: BackgroundExecutor,
15685    cx: &mut TestAppContext,
15686) {
15687    init_test(cx, |_| {});
15688
15689    let mut cx = EditorTestContext::new(cx).await;
15690
15691    let diff_base = r#"
15692        use some::mod1;
15693        use some::mod2;
15694
15695        const A: u32 = 42;
15696        const B: u32 = 42;
15697        const C: u32 = 42;
15698
15699
15700        fn main() {
15701            println!("hello");
15702
15703            println!("world");
15704        }
15705    "#
15706    .unindent();
15707    executor.run_until_parked();
15708    cx.set_state(
15709        &r#"
15710        use some::mod1;
15711        use some::mod2;
15712
15713        ˇconst B: u32 = 42;
15714        const C: u32 = 42;
15715
15716
15717        fn main() {
15718            println!("hello");
15719
15720            println!("world");
15721        }
15722        "#
15723        .unindent(),
15724    );
15725
15726    cx.set_head_text(&diff_base);
15727    executor.run_until_parked();
15728
15729    cx.update_editor(|editor, window, cx| {
15730        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15731    });
15732    executor.run_until_parked();
15733
15734    cx.assert_state_with_diff(
15735        r#"
15736        use some::mod1;
15737        use some::mod2;
15738
15739      - const A: u32 = 42;
15740        ˇconst B: u32 = 42;
15741        const C: u32 = 42;
15742
15743
15744        fn main() {
15745            println!("hello");
15746
15747            println!("world");
15748        }
15749      "#
15750        .unindent(),
15751    );
15752
15753    cx.update_editor(|editor, window, cx| {
15754        editor.delete_line(&DeleteLine, window, cx);
15755    });
15756    executor.run_until_parked();
15757    cx.assert_state_with_diff(
15758        r#"
15759        use some::mod1;
15760        use some::mod2;
15761
15762      - const A: u32 = 42;
15763      - const B: u32 = 42;
15764        ˇconst C: u32 = 42;
15765
15766
15767        fn main() {
15768            println!("hello");
15769
15770            println!("world");
15771        }
15772      "#
15773        .unindent(),
15774    );
15775
15776    cx.update_editor(|editor, window, cx| {
15777        editor.delete_line(&DeleteLine, window, cx);
15778    });
15779    executor.run_until_parked();
15780    cx.assert_state_with_diff(
15781        r#"
15782        use some::mod1;
15783        use some::mod2;
15784
15785      - const A: u32 = 42;
15786      - const B: u32 = 42;
15787      - const C: u32 = 42;
15788        ˇ
15789
15790        fn main() {
15791            println!("hello");
15792
15793            println!("world");
15794        }
15795      "#
15796        .unindent(),
15797    );
15798
15799    cx.update_editor(|editor, window, cx| {
15800        editor.handle_input("replacement", window, cx);
15801    });
15802    executor.run_until_parked();
15803    cx.assert_state_with_diff(
15804        r#"
15805        use some::mod1;
15806        use some::mod2;
15807
15808      - const A: u32 = 42;
15809      - const B: u32 = 42;
15810      - const C: u32 = 42;
15811      -
15812      + replacementˇ
15813
15814        fn main() {
15815            println!("hello");
15816
15817            println!("world");
15818        }
15819      "#
15820        .unindent(),
15821    );
15822}
15823
15824#[gpui::test]
15825async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15826    init_test(cx, |_| {});
15827
15828    let mut cx = EditorTestContext::new(cx).await;
15829
15830    let base_text = r#"
15831        one
15832        two
15833        three
15834        four
15835        five
15836    "#
15837    .unindent();
15838    executor.run_until_parked();
15839    cx.set_state(
15840        &r#"
15841        one
15842        two
15843        fˇour
15844        five
15845        "#
15846        .unindent(),
15847    );
15848
15849    cx.set_head_text(&base_text);
15850    executor.run_until_parked();
15851
15852    cx.update_editor(|editor, window, cx| {
15853        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15854    });
15855    executor.run_until_parked();
15856
15857    cx.assert_state_with_diff(
15858        r#"
15859          one
15860          two
15861        - three
15862          fˇour
15863          five
15864        "#
15865        .unindent(),
15866    );
15867
15868    cx.update_editor(|editor, window, cx| {
15869        editor.backspace(&Backspace, window, cx);
15870        editor.backspace(&Backspace, window, cx);
15871    });
15872    executor.run_until_parked();
15873    cx.assert_state_with_diff(
15874        r#"
15875          one
15876          two
15877        - threeˇ
15878        - four
15879        + our
15880          five
15881        "#
15882        .unindent(),
15883    );
15884}
15885
15886#[gpui::test]
15887async fn test_edit_after_expanded_modification_hunk(
15888    executor: BackgroundExecutor,
15889    cx: &mut TestAppContext,
15890) {
15891    init_test(cx, |_| {});
15892
15893    let mut cx = EditorTestContext::new(cx).await;
15894
15895    let diff_base = r#"
15896        use some::mod1;
15897        use some::mod2;
15898
15899        const A: u32 = 42;
15900        const B: u32 = 42;
15901        const C: u32 = 42;
15902        const D: u32 = 42;
15903
15904
15905        fn main() {
15906            println!("hello");
15907
15908            println!("world");
15909        }"#
15910    .unindent();
15911
15912    cx.set_state(
15913        &r#"
15914        use some::mod1;
15915        use some::mod2;
15916
15917        const A: u32 = 42;
15918        const B: u32 = 42;
15919        const C: u32 = 43ˇ
15920        const D: u32 = 42;
15921
15922
15923        fn main() {
15924            println!("hello");
15925
15926            println!("world");
15927        }"#
15928        .unindent(),
15929    );
15930
15931    cx.set_head_text(&diff_base);
15932    executor.run_until_parked();
15933    cx.update_editor(|editor, window, cx| {
15934        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15935    });
15936    executor.run_until_parked();
15937
15938    cx.assert_state_with_diff(
15939        r#"
15940        use some::mod1;
15941        use some::mod2;
15942
15943        const A: u32 = 42;
15944        const B: u32 = 42;
15945      - const C: u32 = 42;
15946      + const C: u32 = 43ˇ
15947        const D: u32 = 42;
15948
15949
15950        fn main() {
15951            println!("hello");
15952
15953            println!("world");
15954        }"#
15955        .unindent(),
15956    );
15957
15958    cx.update_editor(|editor, window, cx| {
15959        editor.handle_input("\nnew_line\n", window, cx);
15960    });
15961    executor.run_until_parked();
15962
15963    cx.assert_state_with_diff(
15964        r#"
15965        use some::mod1;
15966        use some::mod2;
15967
15968        const A: u32 = 42;
15969        const B: u32 = 42;
15970      - const C: u32 = 42;
15971      + const C: u32 = 43
15972      + new_line
15973      + ˇ
15974        const D: u32 = 42;
15975
15976
15977        fn main() {
15978            println!("hello");
15979
15980            println!("world");
15981        }"#
15982        .unindent(),
15983    );
15984}
15985
15986#[gpui::test]
15987async fn test_stage_and_unstage_added_file_hunk(
15988    executor: BackgroundExecutor,
15989    cx: &mut TestAppContext,
15990) {
15991    init_test(cx, |_| {});
15992
15993    let mut cx = EditorTestContext::new(cx).await;
15994    cx.update_editor(|editor, _, cx| {
15995        editor.set_expand_all_diff_hunks(cx);
15996    });
15997
15998    let working_copy = r#"
15999            ˇfn main() {
16000                println!("hello, world!");
16001            }
16002        "#
16003    .unindent();
16004
16005    cx.set_state(&working_copy);
16006    executor.run_until_parked();
16007
16008    cx.assert_state_with_diff(
16009        r#"
16010            + ˇfn main() {
16011            +     println!("hello, world!");
16012            + }
16013        "#
16014        .unindent(),
16015    );
16016    cx.assert_index_text(None);
16017
16018    cx.update_editor(|editor, window, cx| {
16019        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16020    });
16021    executor.run_until_parked();
16022    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
16023    cx.assert_state_with_diff(
16024        r#"
16025            + ˇfn main() {
16026            +     println!("hello, world!");
16027            + }
16028        "#
16029        .unindent(),
16030    );
16031
16032    cx.update_editor(|editor, window, cx| {
16033        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16034    });
16035    executor.run_until_parked();
16036    cx.assert_index_text(None);
16037}
16038
16039async fn setup_indent_guides_editor(
16040    text: &str,
16041    cx: &mut TestAppContext,
16042) -> (BufferId, EditorTestContext) {
16043    init_test(cx, |_| {});
16044
16045    let mut cx = EditorTestContext::new(cx).await;
16046
16047    let buffer_id = cx.update_editor(|editor, window, cx| {
16048        editor.set_text(text, window, cx);
16049        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16050
16051        buffer_ids[0]
16052    });
16053
16054    (buffer_id, cx)
16055}
16056
16057fn assert_indent_guides(
16058    range: Range<u32>,
16059    expected: Vec<IndentGuide>,
16060    active_indices: Option<Vec<usize>>,
16061    cx: &mut EditorTestContext,
16062) {
16063    let indent_guides = cx.update_editor(|editor, window, cx| {
16064        let snapshot = editor.snapshot(window, cx).display_snapshot;
16065        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16066            editor,
16067            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16068            true,
16069            &snapshot,
16070            cx,
16071        );
16072
16073        indent_guides.sort_by(|a, b| {
16074            a.depth.cmp(&b.depth).then(
16075                a.start_row
16076                    .cmp(&b.start_row)
16077                    .then(a.end_row.cmp(&b.end_row)),
16078            )
16079        });
16080        indent_guides
16081    });
16082
16083    if let Some(expected) = active_indices {
16084        let active_indices = cx.update_editor(|editor, window, cx| {
16085            let snapshot = editor.snapshot(window, cx).display_snapshot;
16086            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16087        });
16088
16089        assert_eq!(
16090            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16091            expected,
16092            "Active indent guide indices do not match"
16093        );
16094    }
16095
16096    assert_eq!(indent_guides, expected, "Indent guides do not match");
16097}
16098
16099fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16100    IndentGuide {
16101        buffer_id,
16102        start_row: MultiBufferRow(start_row),
16103        end_row: MultiBufferRow(end_row),
16104        depth,
16105        tab_size: 4,
16106        settings: IndentGuideSettings {
16107            enabled: true,
16108            line_width: 1,
16109            active_line_width: 1,
16110            ..Default::default()
16111        },
16112    }
16113}
16114
16115#[gpui::test]
16116async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16117    let (buffer_id, mut cx) = setup_indent_guides_editor(
16118        &"
16119    fn main() {
16120        let a = 1;
16121    }"
16122        .unindent(),
16123        cx,
16124    )
16125    .await;
16126
16127    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16128}
16129
16130#[gpui::test]
16131async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16132    let (buffer_id, mut cx) = setup_indent_guides_editor(
16133        &"
16134    fn main() {
16135        let a = 1;
16136        let b = 2;
16137    }"
16138        .unindent(),
16139        cx,
16140    )
16141    .await;
16142
16143    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16144}
16145
16146#[gpui::test]
16147async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16148    let (buffer_id, mut cx) = setup_indent_guides_editor(
16149        &"
16150    fn main() {
16151        let a = 1;
16152        if a == 3 {
16153            let b = 2;
16154        } else {
16155            let c = 3;
16156        }
16157    }"
16158        .unindent(),
16159        cx,
16160    )
16161    .await;
16162
16163    assert_indent_guides(
16164        0..8,
16165        vec![
16166            indent_guide(buffer_id, 1, 6, 0),
16167            indent_guide(buffer_id, 3, 3, 1),
16168            indent_guide(buffer_id, 5, 5, 1),
16169        ],
16170        None,
16171        &mut cx,
16172    );
16173}
16174
16175#[gpui::test]
16176async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16177    let (buffer_id, mut cx) = setup_indent_guides_editor(
16178        &"
16179    fn main() {
16180        let a = 1;
16181            let b = 2;
16182        let c = 3;
16183    }"
16184        .unindent(),
16185        cx,
16186    )
16187    .await;
16188
16189    assert_indent_guides(
16190        0..5,
16191        vec![
16192            indent_guide(buffer_id, 1, 3, 0),
16193            indent_guide(buffer_id, 2, 2, 1),
16194        ],
16195        None,
16196        &mut cx,
16197    );
16198}
16199
16200#[gpui::test]
16201async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16202    let (buffer_id, mut cx) = setup_indent_guides_editor(
16203        &"
16204        fn main() {
16205            let a = 1;
16206
16207            let c = 3;
16208        }"
16209        .unindent(),
16210        cx,
16211    )
16212    .await;
16213
16214    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16215}
16216
16217#[gpui::test]
16218async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16219    let (buffer_id, mut cx) = setup_indent_guides_editor(
16220        &"
16221        fn main() {
16222            let a = 1;
16223
16224            let c = 3;
16225
16226            if a == 3 {
16227                let b = 2;
16228            } else {
16229                let c = 3;
16230            }
16231        }"
16232        .unindent(),
16233        cx,
16234    )
16235    .await;
16236
16237    assert_indent_guides(
16238        0..11,
16239        vec![
16240            indent_guide(buffer_id, 1, 9, 0),
16241            indent_guide(buffer_id, 6, 6, 1),
16242            indent_guide(buffer_id, 8, 8, 1),
16243        ],
16244        None,
16245        &mut cx,
16246    );
16247}
16248
16249#[gpui::test]
16250async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16251    let (buffer_id, mut cx) = setup_indent_guides_editor(
16252        &"
16253        fn main() {
16254            let a = 1;
16255
16256            let c = 3;
16257
16258            if a == 3 {
16259                let b = 2;
16260            } else {
16261                let c = 3;
16262            }
16263        }"
16264        .unindent(),
16265        cx,
16266    )
16267    .await;
16268
16269    assert_indent_guides(
16270        1..11,
16271        vec![
16272            indent_guide(buffer_id, 1, 9, 0),
16273            indent_guide(buffer_id, 6, 6, 1),
16274            indent_guide(buffer_id, 8, 8, 1),
16275        ],
16276        None,
16277        &mut cx,
16278    );
16279}
16280
16281#[gpui::test]
16282async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16283    let (buffer_id, mut cx) = setup_indent_guides_editor(
16284        &"
16285        fn main() {
16286            let a = 1;
16287
16288            let c = 3;
16289
16290            if a == 3 {
16291                let b = 2;
16292            } else {
16293                let c = 3;
16294            }
16295        }"
16296        .unindent(),
16297        cx,
16298    )
16299    .await;
16300
16301    assert_indent_guides(
16302        1..10,
16303        vec![
16304            indent_guide(buffer_id, 1, 9, 0),
16305            indent_guide(buffer_id, 6, 6, 1),
16306            indent_guide(buffer_id, 8, 8, 1),
16307        ],
16308        None,
16309        &mut cx,
16310    );
16311}
16312
16313#[gpui::test]
16314async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16315    let (buffer_id, mut cx) = setup_indent_guides_editor(
16316        &"
16317        block1
16318            block2
16319                block3
16320                    block4
16321            block2
16322        block1
16323        block1"
16324            .unindent(),
16325        cx,
16326    )
16327    .await;
16328
16329    assert_indent_guides(
16330        1..10,
16331        vec![
16332            indent_guide(buffer_id, 1, 4, 0),
16333            indent_guide(buffer_id, 2, 3, 1),
16334            indent_guide(buffer_id, 3, 3, 2),
16335        ],
16336        None,
16337        &mut cx,
16338    );
16339}
16340
16341#[gpui::test]
16342async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16343    let (buffer_id, mut cx) = setup_indent_guides_editor(
16344        &"
16345        block1
16346            block2
16347                block3
16348
16349        block1
16350        block1"
16351            .unindent(),
16352        cx,
16353    )
16354    .await;
16355
16356    assert_indent_guides(
16357        0..6,
16358        vec![
16359            indent_guide(buffer_id, 1, 2, 0),
16360            indent_guide(buffer_id, 2, 2, 1),
16361        ],
16362        None,
16363        &mut cx,
16364    );
16365}
16366
16367#[gpui::test]
16368async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16369    let (buffer_id, mut cx) = setup_indent_guides_editor(
16370        &"
16371        block1
16372
16373
16374
16375            block2
16376        "
16377        .unindent(),
16378        cx,
16379    )
16380    .await;
16381
16382    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16383}
16384
16385#[gpui::test]
16386async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16387    let (buffer_id, mut cx) = setup_indent_guides_editor(
16388        &"
16389        def a:
16390        \tb = 3
16391        \tif True:
16392        \t\tc = 4
16393        \t\td = 5
16394        \tprint(b)
16395        "
16396        .unindent(),
16397        cx,
16398    )
16399    .await;
16400
16401    assert_indent_guides(
16402        0..6,
16403        vec![
16404            indent_guide(buffer_id, 1, 6, 0),
16405            indent_guide(buffer_id, 3, 4, 1),
16406        ],
16407        None,
16408        &mut cx,
16409    );
16410}
16411
16412#[gpui::test]
16413async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16414    let (buffer_id, mut cx) = setup_indent_guides_editor(
16415        &"
16416    fn main() {
16417        let a = 1;
16418    }"
16419        .unindent(),
16420        cx,
16421    )
16422    .await;
16423
16424    cx.update_editor(|editor, window, cx| {
16425        editor.change_selections(None, window, cx, |s| {
16426            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16427        });
16428    });
16429
16430    assert_indent_guides(
16431        0..3,
16432        vec![indent_guide(buffer_id, 1, 1, 0)],
16433        Some(vec![0]),
16434        &mut cx,
16435    );
16436}
16437
16438#[gpui::test]
16439async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16440    let (buffer_id, mut cx) = setup_indent_guides_editor(
16441        &"
16442    fn main() {
16443        if 1 == 2 {
16444            let a = 1;
16445        }
16446    }"
16447        .unindent(),
16448        cx,
16449    )
16450    .await;
16451
16452    cx.update_editor(|editor, window, cx| {
16453        editor.change_selections(None, window, cx, |s| {
16454            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16455        });
16456    });
16457
16458    assert_indent_guides(
16459        0..4,
16460        vec![
16461            indent_guide(buffer_id, 1, 3, 0),
16462            indent_guide(buffer_id, 2, 2, 1),
16463        ],
16464        Some(vec![1]),
16465        &mut cx,
16466    );
16467
16468    cx.update_editor(|editor, window, cx| {
16469        editor.change_selections(None, window, cx, |s| {
16470            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16471        });
16472    });
16473
16474    assert_indent_guides(
16475        0..4,
16476        vec![
16477            indent_guide(buffer_id, 1, 3, 0),
16478            indent_guide(buffer_id, 2, 2, 1),
16479        ],
16480        Some(vec![1]),
16481        &mut cx,
16482    );
16483
16484    cx.update_editor(|editor, window, cx| {
16485        editor.change_selections(None, window, cx, |s| {
16486            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16487        });
16488    });
16489
16490    assert_indent_guides(
16491        0..4,
16492        vec![
16493            indent_guide(buffer_id, 1, 3, 0),
16494            indent_guide(buffer_id, 2, 2, 1),
16495        ],
16496        Some(vec![0]),
16497        &mut cx,
16498    );
16499}
16500
16501#[gpui::test]
16502async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16503    let (buffer_id, mut cx) = setup_indent_guides_editor(
16504        &"
16505    fn main() {
16506        let a = 1;
16507
16508        let b = 2;
16509    }"
16510        .unindent(),
16511        cx,
16512    )
16513    .await;
16514
16515    cx.update_editor(|editor, window, cx| {
16516        editor.change_selections(None, window, cx, |s| {
16517            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16518        });
16519    });
16520
16521    assert_indent_guides(
16522        0..5,
16523        vec![indent_guide(buffer_id, 1, 3, 0)],
16524        Some(vec![0]),
16525        &mut cx,
16526    );
16527}
16528
16529#[gpui::test]
16530async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16531    let (buffer_id, mut cx) = setup_indent_guides_editor(
16532        &"
16533    def m:
16534        a = 1
16535        pass"
16536            .unindent(),
16537        cx,
16538    )
16539    .await;
16540
16541    cx.update_editor(|editor, window, cx| {
16542        editor.change_selections(None, window, cx, |s| {
16543            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16544        });
16545    });
16546
16547    assert_indent_guides(
16548        0..3,
16549        vec![indent_guide(buffer_id, 1, 2, 0)],
16550        Some(vec![0]),
16551        &mut cx,
16552    );
16553}
16554
16555#[gpui::test]
16556async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16557    init_test(cx, |_| {});
16558    let mut cx = EditorTestContext::new(cx).await;
16559    let text = indoc! {
16560        "
16561        impl A {
16562            fn b() {
16563                0;
16564                3;
16565                5;
16566                6;
16567                7;
16568            }
16569        }
16570        "
16571    };
16572    let base_text = indoc! {
16573        "
16574        impl A {
16575            fn b() {
16576                0;
16577                1;
16578                2;
16579                3;
16580                4;
16581            }
16582            fn c() {
16583                5;
16584                6;
16585                7;
16586            }
16587        }
16588        "
16589    };
16590
16591    cx.update_editor(|editor, window, cx| {
16592        editor.set_text(text, window, cx);
16593
16594        editor.buffer().update(cx, |multibuffer, cx| {
16595            let buffer = multibuffer.as_singleton().unwrap();
16596            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16597
16598            multibuffer.set_all_diff_hunks_expanded(cx);
16599            multibuffer.add_diff(diff, cx);
16600
16601            buffer.read(cx).remote_id()
16602        })
16603    });
16604    cx.run_until_parked();
16605
16606    cx.assert_state_with_diff(
16607        indoc! { "
16608          impl A {
16609              fn b() {
16610                  0;
16611        -         1;
16612        -         2;
16613                  3;
16614        -         4;
16615        -     }
16616        -     fn c() {
16617                  5;
16618                  6;
16619                  7;
16620              }
16621          }
16622          ˇ"
16623        }
16624        .to_string(),
16625    );
16626
16627    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16628        editor
16629            .snapshot(window, cx)
16630            .buffer_snapshot
16631            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16632            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16633            .collect::<Vec<_>>()
16634    });
16635    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16636    assert_eq!(
16637        actual_guides,
16638        vec![
16639            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16640            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16641            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16642        ]
16643    );
16644}
16645
16646#[gpui::test]
16647async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16648    init_test(cx, |_| {});
16649    let mut cx = EditorTestContext::new(cx).await;
16650
16651    let diff_base = r#"
16652        a
16653        b
16654        c
16655        "#
16656    .unindent();
16657
16658    cx.set_state(
16659        &r#"
16660        ˇA
16661        b
16662        C
16663        "#
16664        .unindent(),
16665    );
16666    cx.set_head_text(&diff_base);
16667    cx.update_editor(|editor, window, cx| {
16668        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16669    });
16670    executor.run_until_parked();
16671
16672    let both_hunks_expanded = r#"
16673        - a
16674        + ˇA
16675          b
16676        - c
16677        + C
16678        "#
16679    .unindent();
16680
16681    cx.assert_state_with_diff(both_hunks_expanded.clone());
16682
16683    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16684        let snapshot = editor.snapshot(window, cx);
16685        let hunks = editor
16686            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16687            .collect::<Vec<_>>();
16688        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16689        let buffer_id = hunks[0].buffer_id;
16690        hunks
16691            .into_iter()
16692            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16693            .collect::<Vec<_>>()
16694    });
16695    assert_eq!(hunk_ranges.len(), 2);
16696
16697    cx.update_editor(|editor, _, cx| {
16698        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16699    });
16700    executor.run_until_parked();
16701
16702    let second_hunk_expanded = r#"
16703          ˇA
16704          b
16705        - c
16706        + C
16707        "#
16708    .unindent();
16709
16710    cx.assert_state_with_diff(second_hunk_expanded);
16711
16712    cx.update_editor(|editor, _, cx| {
16713        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16714    });
16715    executor.run_until_parked();
16716
16717    cx.assert_state_with_diff(both_hunks_expanded.clone());
16718
16719    cx.update_editor(|editor, _, cx| {
16720        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16721    });
16722    executor.run_until_parked();
16723
16724    let first_hunk_expanded = r#"
16725        - a
16726        + ˇA
16727          b
16728          C
16729        "#
16730    .unindent();
16731
16732    cx.assert_state_with_diff(first_hunk_expanded);
16733
16734    cx.update_editor(|editor, _, cx| {
16735        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16736    });
16737    executor.run_until_parked();
16738
16739    cx.assert_state_with_diff(both_hunks_expanded);
16740
16741    cx.set_state(
16742        &r#"
16743        ˇA
16744        b
16745        "#
16746        .unindent(),
16747    );
16748    cx.run_until_parked();
16749
16750    // TODO this cursor position seems bad
16751    cx.assert_state_with_diff(
16752        r#"
16753        - ˇa
16754        + A
16755          b
16756        "#
16757        .unindent(),
16758    );
16759
16760    cx.update_editor(|editor, window, cx| {
16761        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16762    });
16763
16764    cx.assert_state_with_diff(
16765        r#"
16766            - ˇa
16767            + A
16768              b
16769            - c
16770            "#
16771        .unindent(),
16772    );
16773
16774    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16775        let snapshot = editor.snapshot(window, cx);
16776        let hunks = editor
16777            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16778            .collect::<Vec<_>>();
16779        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16780        let buffer_id = hunks[0].buffer_id;
16781        hunks
16782            .into_iter()
16783            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16784            .collect::<Vec<_>>()
16785    });
16786    assert_eq!(hunk_ranges.len(), 2);
16787
16788    cx.update_editor(|editor, _, cx| {
16789        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16790    });
16791    executor.run_until_parked();
16792
16793    cx.assert_state_with_diff(
16794        r#"
16795        - ˇa
16796        + A
16797          b
16798        "#
16799        .unindent(),
16800    );
16801}
16802
16803#[gpui::test]
16804async fn test_toggle_deletion_hunk_at_start_of_file(
16805    executor: BackgroundExecutor,
16806    cx: &mut TestAppContext,
16807) {
16808    init_test(cx, |_| {});
16809    let mut cx = EditorTestContext::new(cx).await;
16810
16811    let diff_base = r#"
16812        a
16813        b
16814        c
16815        "#
16816    .unindent();
16817
16818    cx.set_state(
16819        &r#"
16820        ˇb
16821        c
16822        "#
16823        .unindent(),
16824    );
16825    cx.set_head_text(&diff_base);
16826    cx.update_editor(|editor, window, cx| {
16827        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16828    });
16829    executor.run_until_parked();
16830
16831    let hunk_expanded = r#"
16832        - a
16833          ˇb
16834          c
16835        "#
16836    .unindent();
16837
16838    cx.assert_state_with_diff(hunk_expanded.clone());
16839
16840    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16841        let snapshot = editor.snapshot(window, cx);
16842        let hunks = editor
16843            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16844            .collect::<Vec<_>>();
16845        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16846        let buffer_id = hunks[0].buffer_id;
16847        hunks
16848            .into_iter()
16849            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16850            .collect::<Vec<_>>()
16851    });
16852    assert_eq!(hunk_ranges.len(), 1);
16853
16854    cx.update_editor(|editor, _, cx| {
16855        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16856    });
16857    executor.run_until_parked();
16858
16859    let hunk_collapsed = r#"
16860          ˇb
16861          c
16862        "#
16863    .unindent();
16864
16865    cx.assert_state_with_diff(hunk_collapsed);
16866
16867    cx.update_editor(|editor, _, cx| {
16868        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16869    });
16870    executor.run_until_parked();
16871
16872    cx.assert_state_with_diff(hunk_expanded.clone());
16873}
16874
16875#[gpui::test]
16876async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16877    init_test(cx, |_| {});
16878
16879    let fs = FakeFs::new(cx.executor());
16880    fs.insert_tree(
16881        path!("/test"),
16882        json!({
16883            ".git": {},
16884            "file-1": "ONE\n",
16885            "file-2": "TWO\n",
16886            "file-3": "THREE\n",
16887        }),
16888    )
16889    .await;
16890
16891    fs.set_head_for_repo(
16892        path!("/test/.git").as_ref(),
16893        &[
16894            ("file-1".into(), "one\n".into()),
16895            ("file-2".into(), "two\n".into()),
16896            ("file-3".into(), "three\n".into()),
16897        ],
16898    );
16899
16900    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16901    let mut buffers = vec![];
16902    for i in 1..=3 {
16903        let buffer = project
16904            .update(cx, |project, cx| {
16905                let path = format!(path!("/test/file-{}"), i);
16906                project.open_local_buffer(path, cx)
16907            })
16908            .await
16909            .unwrap();
16910        buffers.push(buffer);
16911    }
16912
16913    let multibuffer = cx.new(|cx| {
16914        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16915        multibuffer.set_all_diff_hunks_expanded(cx);
16916        for buffer in &buffers {
16917            let snapshot = buffer.read(cx).snapshot();
16918            multibuffer.set_excerpts_for_path(
16919                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16920                buffer.clone(),
16921                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16922                DEFAULT_MULTIBUFFER_CONTEXT,
16923                cx,
16924            );
16925        }
16926        multibuffer
16927    });
16928
16929    let editor = cx.add_window(|window, cx| {
16930        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16931    });
16932    cx.run_until_parked();
16933
16934    let snapshot = editor
16935        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16936        .unwrap();
16937    let hunks = snapshot
16938        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16939        .map(|hunk| match hunk {
16940            DisplayDiffHunk::Unfolded {
16941                display_row_range, ..
16942            } => display_row_range,
16943            DisplayDiffHunk::Folded { .. } => unreachable!(),
16944        })
16945        .collect::<Vec<_>>();
16946    assert_eq!(
16947        hunks,
16948        [
16949            DisplayRow(2)..DisplayRow(4),
16950            DisplayRow(7)..DisplayRow(9),
16951            DisplayRow(12)..DisplayRow(14),
16952        ]
16953    );
16954}
16955
16956#[gpui::test]
16957async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16958    init_test(cx, |_| {});
16959
16960    let mut cx = EditorTestContext::new(cx).await;
16961    cx.set_head_text(indoc! { "
16962        one
16963        two
16964        three
16965        four
16966        five
16967        "
16968    });
16969    cx.set_index_text(indoc! { "
16970        one
16971        two
16972        three
16973        four
16974        five
16975        "
16976    });
16977    cx.set_state(indoc! {"
16978        one
16979        TWO
16980        ˇTHREE
16981        FOUR
16982        five
16983    "});
16984    cx.run_until_parked();
16985    cx.update_editor(|editor, window, cx| {
16986        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16987    });
16988    cx.run_until_parked();
16989    cx.assert_index_text(Some(indoc! {"
16990        one
16991        TWO
16992        THREE
16993        FOUR
16994        five
16995    "}));
16996    cx.set_state(indoc! { "
16997        one
16998        TWO
16999        ˇTHREE-HUNDRED
17000        FOUR
17001        five
17002    "});
17003    cx.run_until_parked();
17004    cx.update_editor(|editor, window, cx| {
17005        let snapshot = editor.snapshot(window, cx);
17006        let hunks = editor
17007            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
17008            .collect::<Vec<_>>();
17009        assert_eq!(hunks.len(), 1);
17010        assert_eq!(
17011            hunks[0].status(),
17012            DiffHunkStatus {
17013                kind: DiffHunkStatusKind::Modified,
17014                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
17015            }
17016        );
17017
17018        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
17019    });
17020    cx.run_until_parked();
17021    cx.assert_index_text(Some(indoc! {"
17022        one
17023        TWO
17024        THREE-HUNDRED
17025        FOUR
17026        five
17027    "}));
17028}
17029
17030#[gpui::test]
17031fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
17032    init_test(cx, |_| {});
17033
17034    let editor = cx.add_window(|window, cx| {
17035        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
17036        build_editor(buffer, window, cx)
17037    });
17038
17039    let render_args = Arc::new(Mutex::new(None));
17040    let snapshot = editor
17041        .update(cx, |editor, window, cx| {
17042            let snapshot = editor.buffer().read(cx).snapshot(cx);
17043            let range =
17044                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
17045
17046            struct RenderArgs {
17047                row: MultiBufferRow,
17048                folded: bool,
17049                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17050            }
17051
17052            let crease = Crease::inline(
17053                range,
17054                FoldPlaceholder::test(),
17055                {
17056                    let toggle_callback = render_args.clone();
17057                    move |row, folded, callback, _window, _cx| {
17058                        *toggle_callback.lock() = Some(RenderArgs {
17059                            row,
17060                            folded,
17061                            callback,
17062                        });
17063                        div()
17064                    }
17065                },
17066                |_row, _folded, _window, _cx| div(),
17067            );
17068
17069            editor.insert_creases(Some(crease), cx);
17070            let snapshot = editor.snapshot(window, cx);
17071            let _div = snapshot.render_crease_toggle(
17072                MultiBufferRow(1),
17073                false,
17074                cx.entity().clone(),
17075                window,
17076                cx,
17077            );
17078            snapshot
17079        })
17080        .unwrap();
17081
17082    let render_args = render_args.lock().take().unwrap();
17083    assert_eq!(render_args.row, MultiBufferRow(1));
17084    assert!(!render_args.folded);
17085    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17086
17087    cx.update_window(*editor, |_, window, cx| {
17088        (render_args.callback)(true, window, cx)
17089    })
17090    .unwrap();
17091    let snapshot = editor
17092        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17093        .unwrap();
17094    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17095
17096    cx.update_window(*editor, |_, window, cx| {
17097        (render_args.callback)(false, window, cx)
17098    })
17099    .unwrap();
17100    let snapshot = editor
17101        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17102        .unwrap();
17103    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17104}
17105
17106#[gpui::test]
17107async fn test_input_text(cx: &mut TestAppContext) {
17108    init_test(cx, |_| {});
17109    let mut cx = EditorTestContext::new(cx).await;
17110
17111    cx.set_state(
17112        &r#"ˇone
17113        two
17114
17115        three
17116        fourˇ
17117        five
17118
17119        siˇx"#
17120            .unindent(),
17121    );
17122
17123    cx.dispatch_action(HandleInput(String::new()));
17124    cx.assert_editor_state(
17125        &r#"ˇone
17126        two
17127
17128        three
17129        fourˇ
17130        five
17131
17132        siˇx"#
17133            .unindent(),
17134    );
17135
17136    cx.dispatch_action(HandleInput("AAAA".to_string()));
17137    cx.assert_editor_state(
17138        &r#"AAAAˇone
17139        two
17140
17141        three
17142        fourAAAAˇ
17143        five
17144
17145        siAAAAˇx"#
17146            .unindent(),
17147    );
17148}
17149
17150#[gpui::test]
17151async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17152    init_test(cx, |_| {});
17153
17154    let mut cx = EditorTestContext::new(cx).await;
17155    cx.set_state(
17156        r#"let foo = 1;
17157let foo = 2;
17158let foo = 3;
17159let fooˇ = 4;
17160let foo = 5;
17161let foo = 6;
17162let foo = 7;
17163let foo = 8;
17164let foo = 9;
17165let foo = 10;
17166let foo = 11;
17167let foo = 12;
17168let foo = 13;
17169let foo = 14;
17170let foo = 15;"#,
17171    );
17172
17173    cx.update_editor(|e, window, cx| {
17174        assert_eq!(
17175            e.next_scroll_position,
17176            NextScrollCursorCenterTopBottom::Center,
17177            "Default next scroll direction is center",
17178        );
17179
17180        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17181        assert_eq!(
17182            e.next_scroll_position,
17183            NextScrollCursorCenterTopBottom::Top,
17184            "After center, next scroll direction should be top",
17185        );
17186
17187        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17188        assert_eq!(
17189            e.next_scroll_position,
17190            NextScrollCursorCenterTopBottom::Bottom,
17191            "After top, next scroll direction should be bottom",
17192        );
17193
17194        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17195        assert_eq!(
17196            e.next_scroll_position,
17197            NextScrollCursorCenterTopBottom::Center,
17198            "After bottom, scrolling should start over",
17199        );
17200
17201        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17202        assert_eq!(
17203            e.next_scroll_position,
17204            NextScrollCursorCenterTopBottom::Top,
17205            "Scrolling continues if retriggered fast enough"
17206        );
17207    });
17208
17209    cx.executor()
17210        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17211    cx.executor().run_until_parked();
17212    cx.update_editor(|e, _, _| {
17213        assert_eq!(
17214            e.next_scroll_position,
17215            NextScrollCursorCenterTopBottom::Center,
17216            "If scrolling is not triggered fast enough, it should reset"
17217        );
17218    });
17219}
17220
17221#[gpui::test]
17222async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17223    init_test(cx, |_| {});
17224    let mut cx = EditorLspTestContext::new_rust(
17225        lsp::ServerCapabilities {
17226            definition_provider: Some(lsp::OneOf::Left(true)),
17227            references_provider: Some(lsp::OneOf::Left(true)),
17228            ..lsp::ServerCapabilities::default()
17229        },
17230        cx,
17231    )
17232    .await;
17233
17234    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17235        let go_to_definition = cx
17236            .lsp
17237            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17238                move |params, _| async move {
17239                    if empty_go_to_definition {
17240                        Ok(None)
17241                    } else {
17242                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17243                            uri: params.text_document_position_params.text_document.uri,
17244                            range: lsp::Range::new(
17245                                lsp::Position::new(4, 3),
17246                                lsp::Position::new(4, 6),
17247                            ),
17248                        })))
17249                    }
17250                },
17251            );
17252        let references = cx
17253            .lsp
17254            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17255                Ok(Some(vec![lsp::Location {
17256                    uri: params.text_document_position.text_document.uri,
17257                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17258                }]))
17259            });
17260        (go_to_definition, references)
17261    };
17262
17263    cx.set_state(
17264        &r#"fn one() {
17265            let mut a = ˇtwo();
17266        }
17267
17268        fn two() {}"#
17269            .unindent(),
17270    );
17271    set_up_lsp_handlers(false, &mut cx);
17272    let navigated = cx
17273        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17274        .await
17275        .expect("Failed to navigate to definition");
17276    assert_eq!(
17277        navigated,
17278        Navigated::Yes,
17279        "Should have navigated to definition from the GetDefinition response"
17280    );
17281    cx.assert_editor_state(
17282        &r#"fn one() {
17283            let mut a = two();
17284        }
17285
17286        fn «twoˇ»() {}"#
17287            .unindent(),
17288    );
17289
17290    let editors = cx.update_workspace(|workspace, _, cx| {
17291        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17292    });
17293    cx.update_editor(|_, _, test_editor_cx| {
17294        assert_eq!(
17295            editors.len(),
17296            1,
17297            "Initially, only one, test, editor should be open in the workspace"
17298        );
17299        assert_eq!(
17300            test_editor_cx.entity(),
17301            editors.last().expect("Asserted len is 1").clone()
17302        );
17303    });
17304
17305    set_up_lsp_handlers(true, &mut cx);
17306    let navigated = cx
17307        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17308        .await
17309        .expect("Failed to navigate to lookup references");
17310    assert_eq!(
17311        navigated,
17312        Navigated::Yes,
17313        "Should have navigated to references as a fallback after empty GoToDefinition response"
17314    );
17315    // We should not change the selections in the existing file,
17316    // if opening another milti buffer with the references
17317    cx.assert_editor_state(
17318        &r#"fn one() {
17319            let mut a = two();
17320        }
17321
17322        fn «twoˇ»() {}"#
17323            .unindent(),
17324    );
17325    let editors = cx.update_workspace(|workspace, _, cx| {
17326        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17327    });
17328    cx.update_editor(|_, _, test_editor_cx| {
17329        assert_eq!(
17330            editors.len(),
17331            2,
17332            "After falling back to references search, we open a new editor with the results"
17333        );
17334        let references_fallback_text = editors
17335            .into_iter()
17336            .find(|new_editor| *new_editor != test_editor_cx.entity())
17337            .expect("Should have one non-test editor now")
17338            .read(test_editor_cx)
17339            .text(test_editor_cx);
17340        assert_eq!(
17341            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17342            "Should use the range from the references response and not the GoToDefinition one"
17343        );
17344    });
17345}
17346
17347#[gpui::test]
17348async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17349    init_test(cx, |_| {});
17350    cx.update(|cx| {
17351        let mut editor_settings = EditorSettings::get_global(cx).clone();
17352        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17353        EditorSettings::override_global(editor_settings, cx);
17354    });
17355    let mut cx = EditorLspTestContext::new_rust(
17356        lsp::ServerCapabilities {
17357            definition_provider: Some(lsp::OneOf::Left(true)),
17358            references_provider: Some(lsp::OneOf::Left(true)),
17359            ..lsp::ServerCapabilities::default()
17360        },
17361        cx,
17362    )
17363    .await;
17364    let original_state = r#"fn one() {
17365        let mut a = ˇtwo();
17366    }
17367
17368    fn two() {}"#
17369        .unindent();
17370    cx.set_state(&original_state);
17371
17372    let mut go_to_definition = cx
17373        .lsp
17374        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17375            move |_, _| async move { Ok(None) },
17376        );
17377    let _references = cx
17378        .lsp
17379        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17380            panic!("Should not call for references with no go to definition fallback")
17381        });
17382
17383    let navigated = cx
17384        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17385        .await
17386        .expect("Failed to navigate to lookup references");
17387    go_to_definition
17388        .next()
17389        .await
17390        .expect("Should have called the go_to_definition handler");
17391
17392    assert_eq!(
17393        navigated,
17394        Navigated::No,
17395        "Should have navigated to references as a fallback after empty GoToDefinition response"
17396    );
17397    cx.assert_editor_state(&original_state);
17398    let editors = cx.update_workspace(|workspace, _, cx| {
17399        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17400    });
17401    cx.update_editor(|_, _, _| {
17402        assert_eq!(
17403            editors.len(),
17404            1,
17405            "After unsuccessful fallback, no other editor should have been opened"
17406        );
17407    });
17408}
17409
17410#[gpui::test]
17411async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17412    init_test(cx, |_| {});
17413
17414    let language = Arc::new(Language::new(
17415        LanguageConfig::default(),
17416        Some(tree_sitter_rust::LANGUAGE.into()),
17417    ));
17418
17419    let text = r#"
17420        #[cfg(test)]
17421        mod tests() {
17422            #[test]
17423            fn runnable_1() {
17424                let a = 1;
17425            }
17426
17427            #[test]
17428            fn runnable_2() {
17429                let a = 1;
17430                let b = 2;
17431            }
17432        }
17433    "#
17434    .unindent();
17435
17436    let fs = FakeFs::new(cx.executor());
17437    fs.insert_file("/file.rs", Default::default()).await;
17438
17439    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17440    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17441    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17442    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17443    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17444
17445    let editor = cx.new_window_entity(|window, cx| {
17446        Editor::new(
17447            EditorMode::full(),
17448            multi_buffer,
17449            Some(project.clone()),
17450            window,
17451            cx,
17452        )
17453    });
17454
17455    editor.update_in(cx, |editor, window, cx| {
17456        let snapshot = editor.buffer().read(cx).snapshot(cx);
17457        editor.tasks.insert(
17458            (buffer.read(cx).remote_id(), 3),
17459            RunnableTasks {
17460                templates: vec![],
17461                offset: snapshot.anchor_before(43),
17462                column: 0,
17463                extra_variables: HashMap::default(),
17464                context_range: BufferOffset(43)..BufferOffset(85),
17465            },
17466        );
17467        editor.tasks.insert(
17468            (buffer.read(cx).remote_id(), 8),
17469            RunnableTasks {
17470                templates: vec![],
17471                offset: snapshot.anchor_before(86),
17472                column: 0,
17473                extra_variables: HashMap::default(),
17474                context_range: BufferOffset(86)..BufferOffset(191),
17475            },
17476        );
17477
17478        // Test finding task when cursor is inside function body
17479        editor.change_selections(None, window, cx, |s| {
17480            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17481        });
17482        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17483        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17484
17485        // Test finding task when cursor is on function name
17486        editor.change_selections(None, window, cx, |s| {
17487            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17488        });
17489        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17490        assert_eq!(row, 8, "Should find task when cursor is on function name");
17491    });
17492}
17493
17494#[gpui::test]
17495async fn test_folding_buffers(cx: &mut TestAppContext) {
17496    init_test(cx, |_| {});
17497
17498    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17499    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17500    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17501
17502    let fs = FakeFs::new(cx.executor());
17503    fs.insert_tree(
17504        path!("/a"),
17505        json!({
17506            "first.rs": sample_text_1,
17507            "second.rs": sample_text_2,
17508            "third.rs": sample_text_3,
17509        }),
17510    )
17511    .await;
17512    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17513    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17514    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17515    let worktree = project.update(cx, |project, cx| {
17516        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17517        assert_eq!(worktrees.len(), 1);
17518        worktrees.pop().unwrap()
17519    });
17520    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17521
17522    let buffer_1 = project
17523        .update(cx, |project, cx| {
17524            project.open_buffer((worktree_id, "first.rs"), cx)
17525        })
17526        .await
17527        .unwrap();
17528    let buffer_2 = project
17529        .update(cx, |project, cx| {
17530            project.open_buffer((worktree_id, "second.rs"), cx)
17531        })
17532        .await
17533        .unwrap();
17534    let buffer_3 = project
17535        .update(cx, |project, cx| {
17536            project.open_buffer((worktree_id, "third.rs"), cx)
17537        })
17538        .await
17539        .unwrap();
17540
17541    let multi_buffer = cx.new(|cx| {
17542        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17543        multi_buffer.push_excerpts(
17544            buffer_1.clone(),
17545            [
17546                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17547                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17548                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17549            ],
17550            cx,
17551        );
17552        multi_buffer.push_excerpts(
17553            buffer_2.clone(),
17554            [
17555                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17556                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17557                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17558            ],
17559            cx,
17560        );
17561        multi_buffer.push_excerpts(
17562            buffer_3.clone(),
17563            [
17564                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17565                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17566                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17567            ],
17568            cx,
17569        );
17570        multi_buffer
17571    });
17572    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17573        Editor::new(
17574            EditorMode::full(),
17575            multi_buffer.clone(),
17576            Some(project.clone()),
17577            window,
17578            cx,
17579        )
17580    });
17581
17582    assert_eq!(
17583        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17584        "\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",
17585    );
17586
17587    multi_buffer_editor.update(cx, |editor, cx| {
17588        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17589    });
17590    assert_eq!(
17591        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17592        "\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",
17593        "After folding the first buffer, its text should not be displayed"
17594    );
17595
17596    multi_buffer_editor.update(cx, |editor, cx| {
17597        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17598    });
17599    assert_eq!(
17600        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17601        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17602        "After folding the second buffer, its text should not be displayed"
17603    );
17604
17605    multi_buffer_editor.update(cx, |editor, cx| {
17606        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17607    });
17608    assert_eq!(
17609        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17610        "\n\n\n\n\n",
17611        "After folding the third buffer, its text should not be displayed"
17612    );
17613
17614    // Emulate selection inside the fold logic, that should work
17615    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17616        editor
17617            .snapshot(window, cx)
17618            .next_line_boundary(Point::new(0, 4));
17619    });
17620
17621    multi_buffer_editor.update(cx, |editor, cx| {
17622        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17623    });
17624    assert_eq!(
17625        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17626        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17627        "After unfolding the second buffer, its text should be displayed"
17628    );
17629
17630    // Typing inside of buffer 1 causes that buffer to be unfolded.
17631    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17632        assert_eq!(
17633            multi_buffer
17634                .read(cx)
17635                .snapshot(cx)
17636                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17637                .collect::<String>(),
17638            "bbbb"
17639        );
17640        editor.change_selections(None, window, cx, |selections| {
17641            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17642        });
17643        editor.handle_input("B", window, cx);
17644    });
17645
17646    assert_eq!(
17647        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17648        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17649        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17650    );
17651
17652    multi_buffer_editor.update(cx, |editor, cx| {
17653        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17654    });
17655    assert_eq!(
17656        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17657        "\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",
17658        "After unfolding the all buffers, all original text should be displayed"
17659    );
17660}
17661
17662#[gpui::test]
17663async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17664    init_test(cx, |_| {});
17665
17666    let sample_text_1 = "1111\n2222\n3333".to_string();
17667    let sample_text_2 = "4444\n5555\n6666".to_string();
17668    let sample_text_3 = "7777\n8888\n9999".to_string();
17669
17670    let fs = FakeFs::new(cx.executor());
17671    fs.insert_tree(
17672        path!("/a"),
17673        json!({
17674            "first.rs": sample_text_1,
17675            "second.rs": sample_text_2,
17676            "third.rs": sample_text_3,
17677        }),
17678    )
17679    .await;
17680    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17681    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17682    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17683    let worktree = project.update(cx, |project, cx| {
17684        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17685        assert_eq!(worktrees.len(), 1);
17686        worktrees.pop().unwrap()
17687    });
17688    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17689
17690    let buffer_1 = project
17691        .update(cx, |project, cx| {
17692            project.open_buffer((worktree_id, "first.rs"), cx)
17693        })
17694        .await
17695        .unwrap();
17696    let buffer_2 = project
17697        .update(cx, |project, cx| {
17698            project.open_buffer((worktree_id, "second.rs"), cx)
17699        })
17700        .await
17701        .unwrap();
17702    let buffer_3 = project
17703        .update(cx, |project, cx| {
17704            project.open_buffer((worktree_id, "third.rs"), cx)
17705        })
17706        .await
17707        .unwrap();
17708
17709    let multi_buffer = cx.new(|cx| {
17710        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17711        multi_buffer.push_excerpts(
17712            buffer_1.clone(),
17713            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17714            cx,
17715        );
17716        multi_buffer.push_excerpts(
17717            buffer_2.clone(),
17718            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17719            cx,
17720        );
17721        multi_buffer.push_excerpts(
17722            buffer_3.clone(),
17723            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17724            cx,
17725        );
17726        multi_buffer
17727    });
17728
17729    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17730        Editor::new(
17731            EditorMode::full(),
17732            multi_buffer,
17733            Some(project.clone()),
17734            window,
17735            cx,
17736        )
17737    });
17738
17739    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17740    assert_eq!(
17741        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17742        full_text,
17743    );
17744
17745    multi_buffer_editor.update(cx, |editor, cx| {
17746        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17747    });
17748    assert_eq!(
17749        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17750        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17751        "After folding the first buffer, its text should not be displayed"
17752    );
17753
17754    multi_buffer_editor.update(cx, |editor, cx| {
17755        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17756    });
17757
17758    assert_eq!(
17759        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17760        "\n\n\n\n\n\n7777\n8888\n9999",
17761        "After folding the second buffer, its text should not be displayed"
17762    );
17763
17764    multi_buffer_editor.update(cx, |editor, cx| {
17765        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17766    });
17767    assert_eq!(
17768        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17769        "\n\n\n\n\n",
17770        "After folding the third buffer, its text should not be displayed"
17771    );
17772
17773    multi_buffer_editor.update(cx, |editor, cx| {
17774        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17775    });
17776    assert_eq!(
17777        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17778        "\n\n\n\n4444\n5555\n6666\n\n",
17779        "After unfolding the second buffer, its text should be displayed"
17780    );
17781
17782    multi_buffer_editor.update(cx, |editor, cx| {
17783        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17784    });
17785    assert_eq!(
17786        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17787        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17788        "After unfolding the first buffer, its text should be displayed"
17789    );
17790
17791    multi_buffer_editor.update(cx, |editor, cx| {
17792        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17793    });
17794    assert_eq!(
17795        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17796        full_text,
17797        "After unfolding all buffers, all original text should be displayed"
17798    );
17799}
17800
17801#[gpui::test]
17802async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17803    init_test(cx, |_| {});
17804
17805    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17806
17807    let fs = FakeFs::new(cx.executor());
17808    fs.insert_tree(
17809        path!("/a"),
17810        json!({
17811            "main.rs": sample_text,
17812        }),
17813    )
17814    .await;
17815    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17816    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17817    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17818    let worktree = project.update(cx, |project, cx| {
17819        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17820        assert_eq!(worktrees.len(), 1);
17821        worktrees.pop().unwrap()
17822    });
17823    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17824
17825    let buffer_1 = project
17826        .update(cx, |project, cx| {
17827            project.open_buffer((worktree_id, "main.rs"), cx)
17828        })
17829        .await
17830        .unwrap();
17831
17832    let multi_buffer = cx.new(|cx| {
17833        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17834        multi_buffer.push_excerpts(
17835            buffer_1.clone(),
17836            [ExcerptRange::new(
17837                Point::new(0, 0)
17838                    ..Point::new(
17839                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17840                        0,
17841                    ),
17842            )],
17843            cx,
17844        );
17845        multi_buffer
17846    });
17847    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17848        Editor::new(
17849            EditorMode::full(),
17850            multi_buffer,
17851            Some(project.clone()),
17852            window,
17853            cx,
17854        )
17855    });
17856
17857    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17858    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17859        enum TestHighlight {}
17860        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17861        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17862        editor.highlight_text::<TestHighlight>(
17863            vec![highlight_range.clone()],
17864            HighlightStyle::color(Hsla::green()),
17865            cx,
17866        );
17867        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17868    });
17869
17870    let full_text = format!("\n\n{sample_text}");
17871    assert_eq!(
17872        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17873        full_text,
17874    );
17875}
17876
17877#[gpui::test]
17878async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17879    init_test(cx, |_| {});
17880    cx.update(|cx| {
17881        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17882            "keymaps/default-linux.json",
17883            cx,
17884        )
17885        .unwrap();
17886        cx.bind_keys(default_key_bindings);
17887    });
17888
17889    let (editor, cx) = cx.add_window_view(|window, cx| {
17890        let multi_buffer = MultiBuffer::build_multi(
17891            [
17892                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17893                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17894                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17895                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17896            ],
17897            cx,
17898        );
17899        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17900
17901        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17902        // fold all but the second buffer, so that we test navigating between two
17903        // adjacent folded buffers, as well as folded buffers at the start and
17904        // end the multibuffer
17905        editor.fold_buffer(buffer_ids[0], cx);
17906        editor.fold_buffer(buffer_ids[2], cx);
17907        editor.fold_buffer(buffer_ids[3], cx);
17908
17909        editor
17910    });
17911    cx.simulate_resize(size(px(1000.), px(1000.)));
17912
17913    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17914    cx.assert_excerpts_with_selections(indoc! {"
17915        [EXCERPT]
17916        ˇ[FOLDED]
17917        [EXCERPT]
17918        a1
17919        b1
17920        [EXCERPT]
17921        [FOLDED]
17922        [EXCERPT]
17923        [FOLDED]
17924        "
17925    });
17926    cx.simulate_keystroke("down");
17927    cx.assert_excerpts_with_selections(indoc! {"
17928        [EXCERPT]
17929        [FOLDED]
17930        [EXCERPT]
17931        ˇa1
17932        b1
17933        [EXCERPT]
17934        [FOLDED]
17935        [EXCERPT]
17936        [FOLDED]
17937        "
17938    });
17939    cx.simulate_keystroke("down");
17940    cx.assert_excerpts_with_selections(indoc! {"
17941        [EXCERPT]
17942        [FOLDED]
17943        [EXCERPT]
17944        a1
17945        ˇb1
17946        [EXCERPT]
17947        [FOLDED]
17948        [EXCERPT]
17949        [FOLDED]
17950        "
17951    });
17952    cx.simulate_keystroke("down");
17953    cx.assert_excerpts_with_selections(indoc! {"
17954        [EXCERPT]
17955        [FOLDED]
17956        [EXCERPT]
17957        a1
17958        b1
17959        ˇ[EXCERPT]
17960        [FOLDED]
17961        [EXCERPT]
17962        [FOLDED]
17963        "
17964    });
17965    cx.simulate_keystroke("down");
17966    cx.assert_excerpts_with_selections(indoc! {"
17967        [EXCERPT]
17968        [FOLDED]
17969        [EXCERPT]
17970        a1
17971        b1
17972        [EXCERPT]
17973        ˇ[FOLDED]
17974        [EXCERPT]
17975        [FOLDED]
17976        "
17977    });
17978    for _ in 0..5 {
17979        cx.simulate_keystroke("down");
17980        cx.assert_excerpts_with_selections(indoc! {"
17981            [EXCERPT]
17982            [FOLDED]
17983            [EXCERPT]
17984            a1
17985            b1
17986            [EXCERPT]
17987            [FOLDED]
17988            [EXCERPT]
17989            ˇ[FOLDED]
17990            "
17991        });
17992    }
17993
17994    cx.simulate_keystroke("up");
17995    cx.assert_excerpts_with_selections(indoc! {"
17996        [EXCERPT]
17997        [FOLDED]
17998        [EXCERPT]
17999        a1
18000        b1
18001        [EXCERPT]
18002        ˇ[FOLDED]
18003        [EXCERPT]
18004        [FOLDED]
18005        "
18006    });
18007    cx.simulate_keystroke("up");
18008    cx.assert_excerpts_with_selections(indoc! {"
18009        [EXCERPT]
18010        [FOLDED]
18011        [EXCERPT]
18012        a1
18013        b1
18014        ˇ[EXCERPT]
18015        [FOLDED]
18016        [EXCERPT]
18017        [FOLDED]
18018        "
18019    });
18020    cx.simulate_keystroke("up");
18021    cx.assert_excerpts_with_selections(indoc! {"
18022        [EXCERPT]
18023        [FOLDED]
18024        [EXCERPT]
18025        a1
18026        ˇb1
18027        [EXCERPT]
18028        [FOLDED]
18029        [EXCERPT]
18030        [FOLDED]
18031        "
18032    });
18033    cx.simulate_keystroke("up");
18034    cx.assert_excerpts_with_selections(indoc! {"
18035        [EXCERPT]
18036        [FOLDED]
18037        [EXCERPT]
18038        ˇa1
18039        b1
18040        [EXCERPT]
18041        [FOLDED]
18042        [EXCERPT]
18043        [FOLDED]
18044        "
18045    });
18046    for _ in 0..5 {
18047        cx.simulate_keystroke("up");
18048        cx.assert_excerpts_with_selections(indoc! {"
18049            [EXCERPT]
18050            ˇ[FOLDED]
18051            [EXCERPT]
18052            a1
18053            b1
18054            [EXCERPT]
18055            [FOLDED]
18056            [EXCERPT]
18057            [FOLDED]
18058            "
18059        });
18060    }
18061}
18062
18063#[gpui::test]
18064async fn test_inline_completion_text(cx: &mut TestAppContext) {
18065    init_test(cx, |_| {});
18066
18067    // Simple insertion
18068    assert_highlighted_edits(
18069        "Hello, world!",
18070        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18071        true,
18072        cx,
18073        |highlighted_edits, cx| {
18074            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18075            assert_eq!(highlighted_edits.highlights.len(), 1);
18076            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18077            assert_eq!(
18078                highlighted_edits.highlights[0].1.background_color,
18079                Some(cx.theme().status().created_background)
18080            );
18081        },
18082    )
18083    .await;
18084
18085    // Replacement
18086    assert_highlighted_edits(
18087        "This is a test.",
18088        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18089        false,
18090        cx,
18091        |highlighted_edits, cx| {
18092            assert_eq!(highlighted_edits.text, "That is a test.");
18093            assert_eq!(highlighted_edits.highlights.len(), 1);
18094            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18095            assert_eq!(
18096                highlighted_edits.highlights[0].1.background_color,
18097                Some(cx.theme().status().created_background)
18098            );
18099        },
18100    )
18101    .await;
18102
18103    // Multiple edits
18104    assert_highlighted_edits(
18105        "Hello, world!",
18106        vec![
18107            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18108            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18109        ],
18110        false,
18111        cx,
18112        |highlighted_edits, cx| {
18113            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18114            assert_eq!(highlighted_edits.highlights.len(), 2);
18115            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18116            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18117            assert_eq!(
18118                highlighted_edits.highlights[0].1.background_color,
18119                Some(cx.theme().status().created_background)
18120            );
18121            assert_eq!(
18122                highlighted_edits.highlights[1].1.background_color,
18123                Some(cx.theme().status().created_background)
18124            );
18125        },
18126    )
18127    .await;
18128
18129    // Multiple lines with edits
18130    assert_highlighted_edits(
18131        "First line\nSecond line\nThird line\nFourth line",
18132        vec![
18133            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18134            (
18135                Point::new(2, 0)..Point::new(2, 10),
18136                "New third line".to_string(),
18137            ),
18138            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18139        ],
18140        false,
18141        cx,
18142        |highlighted_edits, cx| {
18143            assert_eq!(
18144                highlighted_edits.text,
18145                "Second modified\nNew third line\nFourth updated line"
18146            );
18147            assert_eq!(highlighted_edits.highlights.len(), 3);
18148            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18149            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18150            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18151            for highlight in &highlighted_edits.highlights {
18152                assert_eq!(
18153                    highlight.1.background_color,
18154                    Some(cx.theme().status().created_background)
18155                );
18156            }
18157        },
18158    )
18159    .await;
18160}
18161
18162#[gpui::test]
18163async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18164    init_test(cx, |_| {});
18165
18166    // Deletion
18167    assert_highlighted_edits(
18168        "Hello, world!",
18169        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18170        true,
18171        cx,
18172        |highlighted_edits, cx| {
18173            assert_eq!(highlighted_edits.text, "Hello, world!");
18174            assert_eq!(highlighted_edits.highlights.len(), 1);
18175            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18176            assert_eq!(
18177                highlighted_edits.highlights[0].1.background_color,
18178                Some(cx.theme().status().deleted_background)
18179            );
18180        },
18181    )
18182    .await;
18183
18184    // Insertion
18185    assert_highlighted_edits(
18186        "Hello, world!",
18187        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18188        true,
18189        cx,
18190        |highlighted_edits, cx| {
18191            assert_eq!(highlighted_edits.highlights.len(), 1);
18192            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18193            assert_eq!(
18194                highlighted_edits.highlights[0].1.background_color,
18195                Some(cx.theme().status().created_background)
18196            );
18197        },
18198    )
18199    .await;
18200}
18201
18202async fn assert_highlighted_edits(
18203    text: &str,
18204    edits: Vec<(Range<Point>, String)>,
18205    include_deletions: bool,
18206    cx: &mut TestAppContext,
18207    assertion_fn: impl Fn(HighlightedText, &App),
18208) {
18209    let window = cx.add_window(|window, cx| {
18210        let buffer = MultiBuffer::build_simple(text, cx);
18211        Editor::new(EditorMode::full(), buffer, None, window, cx)
18212    });
18213    let cx = &mut VisualTestContext::from_window(*window, cx);
18214
18215    let (buffer, snapshot) = window
18216        .update(cx, |editor, _window, cx| {
18217            (
18218                editor.buffer().clone(),
18219                editor.buffer().read(cx).snapshot(cx),
18220            )
18221        })
18222        .unwrap();
18223
18224    let edits = edits
18225        .into_iter()
18226        .map(|(range, edit)| {
18227            (
18228                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18229                edit,
18230            )
18231        })
18232        .collect::<Vec<_>>();
18233
18234    let text_anchor_edits = edits
18235        .clone()
18236        .into_iter()
18237        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18238        .collect::<Vec<_>>();
18239
18240    let edit_preview = window
18241        .update(cx, |_, _window, cx| {
18242            buffer
18243                .read(cx)
18244                .as_singleton()
18245                .unwrap()
18246                .read(cx)
18247                .preview_edits(text_anchor_edits.into(), cx)
18248        })
18249        .unwrap()
18250        .await;
18251
18252    cx.update(|_window, cx| {
18253        let highlighted_edits = inline_completion_edit_text(
18254            &snapshot.as_singleton().unwrap().2,
18255            &edits,
18256            &edit_preview,
18257            include_deletions,
18258            cx,
18259        );
18260        assertion_fn(highlighted_edits, cx)
18261    });
18262}
18263
18264#[track_caller]
18265fn assert_breakpoint(
18266    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18267    path: &Arc<Path>,
18268    expected: Vec<(u32, Breakpoint)>,
18269) {
18270    if expected.len() == 0usize {
18271        assert!(!breakpoints.contains_key(path), "{}", path.display());
18272    } else {
18273        let mut breakpoint = breakpoints
18274            .get(path)
18275            .unwrap()
18276            .into_iter()
18277            .map(|breakpoint| {
18278                (
18279                    breakpoint.row,
18280                    Breakpoint {
18281                        message: breakpoint.message.clone(),
18282                        state: breakpoint.state,
18283                        condition: breakpoint.condition.clone(),
18284                        hit_condition: breakpoint.hit_condition.clone(),
18285                    },
18286                )
18287            })
18288            .collect::<Vec<_>>();
18289
18290        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18291
18292        assert_eq!(expected, breakpoint);
18293    }
18294}
18295
18296fn add_log_breakpoint_at_cursor(
18297    editor: &mut Editor,
18298    log_message: &str,
18299    window: &mut Window,
18300    cx: &mut Context<Editor>,
18301) {
18302    let (anchor, bp) = editor
18303        .breakpoints_at_cursors(window, cx)
18304        .first()
18305        .and_then(|(anchor, bp)| {
18306            if let Some(bp) = bp {
18307                Some((*anchor, bp.clone()))
18308            } else {
18309                None
18310            }
18311        })
18312        .unwrap_or_else(|| {
18313            let cursor_position: Point = editor.selections.newest(cx).head();
18314
18315            let breakpoint_position = editor
18316                .snapshot(window, cx)
18317                .display_snapshot
18318                .buffer_snapshot
18319                .anchor_before(Point::new(cursor_position.row, 0));
18320
18321            (breakpoint_position, Breakpoint::new_log(&log_message))
18322        });
18323
18324    editor.edit_breakpoint_at_anchor(
18325        anchor,
18326        bp,
18327        BreakpointEditAction::EditLogMessage(log_message.into()),
18328        cx,
18329    );
18330}
18331
18332#[gpui::test]
18333async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18334    init_test(cx, |_| {});
18335
18336    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18337    let fs = FakeFs::new(cx.executor());
18338    fs.insert_tree(
18339        path!("/a"),
18340        json!({
18341            "main.rs": sample_text,
18342        }),
18343    )
18344    .await;
18345    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18346    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18347    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18348
18349    let fs = FakeFs::new(cx.executor());
18350    fs.insert_tree(
18351        path!("/a"),
18352        json!({
18353            "main.rs": sample_text,
18354        }),
18355    )
18356    .await;
18357    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18358    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18359    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18360    let worktree_id = workspace
18361        .update(cx, |workspace, _window, cx| {
18362            workspace.project().update(cx, |project, cx| {
18363                project.worktrees(cx).next().unwrap().read(cx).id()
18364            })
18365        })
18366        .unwrap();
18367
18368    let buffer = project
18369        .update(cx, |project, cx| {
18370            project.open_buffer((worktree_id, "main.rs"), cx)
18371        })
18372        .await
18373        .unwrap();
18374
18375    let (editor, cx) = cx.add_window_view(|window, cx| {
18376        Editor::new(
18377            EditorMode::full(),
18378            MultiBuffer::build_from_buffer(buffer, cx),
18379            Some(project.clone()),
18380            window,
18381            cx,
18382        )
18383    });
18384
18385    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18386    let abs_path = project.read_with(cx, |project, cx| {
18387        project
18388            .absolute_path(&project_path, cx)
18389            .map(|path_buf| Arc::from(path_buf.to_owned()))
18390            .unwrap()
18391    });
18392
18393    // assert we can add breakpoint on the first line
18394    editor.update_in(cx, |editor, window, cx| {
18395        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18396        editor.move_to_end(&MoveToEnd, window, cx);
18397        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18398    });
18399
18400    let breakpoints = editor.update(cx, |editor, cx| {
18401        editor
18402            .breakpoint_store()
18403            .as_ref()
18404            .unwrap()
18405            .read(cx)
18406            .all_breakpoints(cx)
18407            .clone()
18408    });
18409
18410    assert_eq!(1, breakpoints.len());
18411    assert_breakpoint(
18412        &breakpoints,
18413        &abs_path,
18414        vec![
18415            (0, Breakpoint::new_standard()),
18416            (3, Breakpoint::new_standard()),
18417        ],
18418    );
18419
18420    editor.update_in(cx, |editor, window, cx| {
18421        editor.move_to_beginning(&MoveToBeginning, window, cx);
18422        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18423    });
18424
18425    let breakpoints = editor.update(cx, |editor, cx| {
18426        editor
18427            .breakpoint_store()
18428            .as_ref()
18429            .unwrap()
18430            .read(cx)
18431            .all_breakpoints(cx)
18432            .clone()
18433    });
18434
18435    assert_eq!(1, breakpoints.len());
18436    assert_breakpoint(
18437        &breakpoints,
18438        &abs_path,
18439        vec![(3, Breakpoint::new_standard())],
18440    );
18441
18442    editor.update_in(cx, |editor, window, cx| {
18443        editor.move_to_end(&MoveToEnd, window, cx);
18444        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18445    });
18446
18447    let breakpoints = editor.update(cx, |editor, cx| {
18448        editor
18449            .breakpoint_store()
18450            .as_ref()
18451            .unwrap()
18452            .read(cx)
18453            .all_breakpoints(cx)
18454            .clone()
18455    });
18456
18457    assert_eq!(0, breakpoints.len());
18458    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18459}
18460
18461#[gpui::test]
18462async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18463    init_test(cx, |_| {});
18464
18465    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18466
18467    let fs = FakeFs::new(cx.executor());
18468    fs.insert_tree(
18469        path!("/a"),
18470        json!({
18471            "main.rs": sample_text,
18472        }),
18473    )
18474    .await;
18475    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18476    let (workspace, cx) =
18477        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18478
18479    let worktree_id = workspace.update(cx, |workspace, cx| {
18480        workspace.project().update(cx, |project, cx| {
18481            project.worktrees(cx).next().unwrap().read(cx).id()
18482        })
18483    });
18484
18485    let buffer = project
18486        .update(cx, |project, cx| {
18487            project.open_buffer((worktree_id, "main.rs"), cx)
18488        })
18489        .await
18490        .unwrap();
18491
18492    let (editor, cx) = cx.add_window_view(|window, cx| {
18493        Editor::new(
18494            EditorMode::full(),
18495            MultiBuffer::build_from_buffer(buffer, cx),
18496            Some(project.clone()),
18497            window,
18498            cx,
18499        )
18500    });
18501
18502    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18503    let abs_path = project.read_with(cx, |project, cx| {
18504        project
18505            .absolute_path(&project_path, cx)
18506            .map(|path_buf| Arc::from(path_buf.to_owned()))
18507            .unwrap()
18508    });
18509
18510    editor.update_in(cx, |editor, window, cx| {
18511        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18512    });
18513
18514    let breakpoints = editor.update(cx, |editor, cx| {
18515        editor
18516            .breakpoint_store()
18517            .as_ref()
18518            .unwrap()
18519            .read(cx)
18520            .all_breakpoints(cx)
18521            .clone()
18522    });
18523
18524    assert_breakpoint(
18525        &breakpoints,
18526        &abs_path,
18527        vec![(0, Breakpoint::new_log("hello world"))],
18528    );
18529
18530    // Removing a log message from a log breakpoint should remove it
18531    editor.update_in(cx, |editor, window, cx| {
18532        add_log_breakpoint_at_cursor(editor, "", window, cx);
18533    });
18534
18535    let breakpoints = editor.update(cx, |editor, cx| {
18536        editor
18537            .breakpoint_store()
18538            .as_ref()
18539            .unwrap()
18540            .read(cx)
18541            .all_breakpoints(cx)
18542            .clone()
18543    });
18544
18545    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18546
18547    editor.update_in(cx, |editor, window, cx| {
18548        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18549        editor.move_to_end(&MoveToEnd, window, cx);
18550        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18551        // Not adding a log message to a standard breakpoint shouldn't remove it
18552        add_log_breakpoint_at_cursor(editor, "", window, cx);
18553    });
18554
18555    let breakpoints = editor.update(cx, |editor, cx| {
18556        editor
18557            .breakpoint_store()
18558            .as_ref()
18559            .unwrap()
18560            .read(cx)
18561            .all_breakpoints(cx)
18562            .clone()
18563    });
18564
18565    assert_breakpoint(
18566        &breakpoints,
18567        &abs_path,
18568        vec![
18569            (0, Breakpoint::new_standard()),
18570            (3, Breakpoint::new_standard()),
18571        ],
18572    );
18573
18574    editor.update_in(cx, |editor, window, cx| {
18575        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18576    });
18577
18578    let breakpoints = editor.update(cx, |editor, cx| {
18579        editor
18580            .breakpoint_store()
18581            .as_ref()
18582            .unwrap()
18583            .read(cx)
18584            .all_breakpoints(cx)
18585            .clone()
18586    });
18587
18588    assert_breakpoint(
18589        &breakpoints,
18590        &abs_path,
18591        vec![
18592            (0, Breakpoint::new_standard()),
18593            (3, Breakpoint::new_log("hello world")),
18594        ],
18595    );
18596
18597    editor.update_in(cx, |editor, window, cx| {
18598        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18599    });
18600
18601    let breakpoints = editor.update(cx, |editor, cx| {
18602        editor
18603            .breakpoint_store()
18604            .as_ref()
18605            .unwrap()
18606            .read(cx)
18607            .all_breakpoints(cx)
18608            .clone()
18609    });
18610
18611    assert_breakpoint(
18612        &breakpoints,
18613        &abs_path,
18614        vec![
18615            (0, Breakpoint::new_standard()),
18616            (3, Breakpoint::new_log("hello Earth!!")),
18617        ],
18618    );
18619}
18620
18621/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18622/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18623/// or when breakpoints were placed out of order. This tests for a regression too
18624#[gpui::test]
18625async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18626    init_test(cx, |_| {});
18627
18628    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18629    let fs = FakeFs::new(cx.executor());
18630    fs.insert_tree(
18631        path!("/a"),
18632        json!({
18633            "main.rs": sample_text,
18634        }),
18635    )
18636    .await;
18637    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18638    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18639    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18640
18641    let fs = FakeFs::new(cx.executor());
18642    fs.insert_tree(
18643        path!("/a"),
18644        json!({
18645            "main.rs": sample_text,
18646        }),
18647    )
18648    .await;
18649    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18650    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18651    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18652    let worktree_id = workspace
18653        .update(cx, |workspace, _window, cx| {
18654            workspace.project().update(cx, |project, cx| {
18655                project.worktrees(cx).next().unwrap().read(cx).id()
18656            })
18657        })
18658        .unwrap();
18659
18660    let buffer = project
18661        .update(cx, |project, cx| {
18662            project.open_buffer((worktree_id, "main.rs"), cx)
18663        })
18664        .await
18665        .unwrap();
18666
18667    let (editor, cx) = cx.add_window_view(|window, cx| {
18668        Editor::new(
18669            EditorMode::full(),
18670            MultiBuffer::build_from_buffer(buffer, cx),
18671            Some(project.clone()),
18672            window,
18673            cx,
18674        )
18675    });
18676
18677    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18678    let abs_path = project.read_with(cx, |project, cx| {
18679        project
18680            .absolute_path(&project_path, cx)
18681            .map(|path_buf| Arc::from(path_buf.to_owned()))
18682            .unwrap()
18683    });
18684
18685    // assert we can add breakpoint on the first line
18686    editor.update_in(cx, |editor, window, cx| {
18687        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18688        editor.move_to_end(&MoveToEnd, window, cx);
18689        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18690        editor.move_up(&MoveUp, window, cx);
18691        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18692    });
18693
18694    let breakpoints = editor.update(cx, |editor, cx| {
18695        editor
18696            .breakpoint_store()
18697            .as_ref()
18698            .unwrap()
18699            .read(cx)
18700            .all_breakpoints(cx)
18701            .clone()
18702    });
18703
18704    assert_eq!(1, breakpoints.len());
18705    assert_breakpoint(
18706        &breakpoints,
18707        &abs_path,
18708        vec![
18709            (0, Breakpoint::new_standard()),
18710            (2, Breakpoint::new_standard()),
18711            (3, Breakpoint::new_standard()),
18712        ],
18713    );
18714
18715    editor.update_in(cx, |editor, window, cx| {
18716        editor.move_to_beginning(&MoveToBeginning, window, cx);
18717        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18718        editor.move_to_end(&MoveToEnd, window, cx);
18719        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18720        // Disabling a breakpoint that doesn't exist should do nothing
18721        editor.move_up(&MoveUp, window, cx);
18722        editor.move_up(&MoveUp, window, cx);
18723        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18724    });
18725
18726    let breakpoints = editor.update(cx, |editor, cx| {
18727        editor
18728            .breakpoint_store()
18729            .as_ref()
18730            .unwrap()
18731            .read(cx)
18732            .all_breakpoints(cx)
18733            .clone()
18734    });
18735
18736    let disable_breakpoint = {
18737        let mut bp = Breakpoint::new_standard();
18738        bp.state = BreakpointState::Disabled;
18739        bp
18740    };
18741
18742    assert_eq!(1, breakpoints.len());
18743    assert_breakpoint(
18744        &breakpoints,
18745        &abs_path,
18746        vec![
18747            (0, disable_breakpoint.clone()),
18748            (2, Breakpoint::new_standard()),
18749            (3, disable_breakpoint.clone()),
18750        ],
18751    );
18752
18753    editor.update_in(cx, |editor, window, cx| {
18754        editor.move_to_beginning(&MoveToBeginning, window, cx);
18755        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18756        editor.move_to_end(&MoveToEnd, window, cx);
18757        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18758        editor.move_up(&MoveUp, window, cx);
18759        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18760    });
18761
18762    let breakpoints = editor.update(cx, |editor, cx| {
18763        editor
18764            .breakpoint_store()
18765            .as_ref()
18766            .unwrap()
18767            .read(cx)
18768            .all_breakpoints(cx)
18769            .clone()
18770    });
18771
18772    assert_eq!(1, breakpoints.len());
18773    assert_breakpoint(
18774        &breakpoints,
18775        &abs_path,
18776        vec![
18777            (0, Breakpoint::new_standard()),
18778            (2, disable_breakpoint),
18779            (3, Breakpoint::new_standard()),
18780        ],
18781    );
18782}
18783
18784#[gpui::test]
18785async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18786    init_test(cx, |_| {});
18787    let capabilities = lsp::ServerCapabilities {
18788        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18789            prepare_provider: Some(true),
18790            work_done_progress_options: Default::default(),
18791        })),
18792        ..Default::default()
18793    };
18794    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18795
18796    cx.set_state(indoc! {"
18797        struct Fˇoo {}
18798    "});
18799
18800    cx.update_editor(|editor, _, cx| {
18801        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18802        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18803        editor.highlight_background::<DocumentHighlightRead>(
18804            &[highlight_range],
18805            |c| c.editor_document_highlight_read_background,
18806            cx,
18807        );
18808    });
18809
18810    let mut prepare_rename_handler = cx
18811        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18812            move |_, _, _| async move {
18813                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18814                    start: lsp::Position {
18815                        line: 0,
18816                        character: 7,
18817                    },
18818                    end: lsp::Position {
18819                        line: 0,
18820                        character: 10,
18821                    },
18822                })))
18823            },
18824        );
18825    let prepare_rename_task = cx
18826        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18827        .expect("Prepare rename was not started");
18828    prepare_rename_handler.next().await.unwrap();
18829    prepare_rename_task.await.expect("Prepare rename failed");
18830
18831    let mut rename_handler =
18832        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18833            let edit = lsp::TextEdit {
18834                range: lsp::Range {
18835                    start: lsp::Position {
18836                        line: 0,
18837                        character: 7,
18838                    },
18839                    end: lsp::Position {
18840                        line: 0,
18841                        character: 10,
18842                    },
18843                },
18844                new_text: "FooRenamed".to_string(),
18845            };
18846            Ok(Some(lsp::WorkspaceEdit::new(
18847                // Specify the same edit twice
18848                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18849            )))
18850        });
18851    let rename_task = cx
18852        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18853        .expect("Confirm rename was not started");
18854    rename_handler.next().await.unwrap();
18855    rename_task.await.expect("Confirm rename failed");
18856    cx.run_until_parked();
18857
18858    // Despite two edits, only one is actually applied as those are identical
18859    cx.assert_editor_state(indoc! {"
18860        struct FooRenamedˇ {}
18861    "});
18862}
18863
18864#[gpui::test]
18865async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18866    init_test(cx, |_| {});
18867    // These capabilities indicate that the server does not support prepare rename.
18868    let capabilities = lsp::ServerCapabilities {
18869        rename_provider: Some(lsp::OneOf::Left(true)),
18870        ..Default::default()
18871    };
18872    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18873
18874    cx.set_state(indoc! {"
18875        struct Fˇoo {}
18876    "});
18877
18878    cx.update_editor(|editor, _window, cx| {
18879        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18880        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18881        editor.highlight_background::<DocumentHighlightRead>(
18882            &[highlight_range],
18883            |c| c.editor_document_highlight_read_background,
18884            cx,
18885        );
18886    });
18887
18888    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18889        .expect("Prepare rename was not started")
18890        .await
18891        .expect("Prepare rename failed");
18892
18893    let mut rename_handler =
18894        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18895            let edit = lsp::TextEdit {
18896                range: lsp::Range {
18897                    start: lsp::Position {
18898                        line: 0,
18899                        character: 7,
18900                    },
18901                    end: lsp::Position {
18902                        line: 0,
18903                        character: 10,
18904                    },
18905                },
18906                new_text: "FooRenamed".to_string(),
18907            };
18908            Ok(Some(lsp::WorkspaceEdit::new(
18909                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18910            )))
18911        });
18912    let rename_task = cx
18913        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18914        .expect("Confirm rename was not started");
18915    rename_handler.next().await.unwrap();
18916    rename_task.await.expect("Confirm rename failed");
18917    cx.run_until_parked();
18918
18919    // Correct range is renamed, as `surrounding_word` is used to find it.
18920    cx.assert_editor_state(indoc! {"
18921        struct FooRenamedˇ {}
18922    "});
18923}
18924
18925#[gpui::test]
18926async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18927    init_test(cx, |_| {});
18928    let mut cx = EditorTestContext::new(cx).await;
18929
18930    let language = Arc::new(
18931        Language::new(
18932            LanguageConfig::default(),
18933            Some(tree_sitter_html::LANGUAGE.into()),
18934        )
18935        .with_brackets_query(
18936            r#"
18937            ("<" @open "/>" @close)
18938            ("</" @open ">" @close)
18939            ("<" @open ">" @close)
18940            ("\"" @open "\"" @close)
18941            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18942        "#,
18943        )
18944        .unwrap(),
18945    );
18946    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18947
18948    cx.set_state(indoc! {"
18949        <span>ˇ</span>
18950    "});
18951    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18952    cx.assert_editor_state(indoc! {"
18953        <span>
18954        ˇ
18955        </span>
18956    "});
18957
18958    cx.set_state(indoc! {"
18959        <span><span></span>ˇ</span>
18960    "});
18961    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18962    cx.assert_editor_state(indoc! {"
18963        <span><span></span>
18964        ˇ</span>
18965    "});
18966
18967    cx.set_state(indoc! {"
18968        <span>ˇ
18969        </span>
18970    "});
18971    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18972    cx.assert_editor_state(indoc! {"
18973        <span>
18974        ˇ
18975        </span>
18976    "});
18977}
18978
18979#[gpui::test(iterations = 10)]
18980async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18981    init_test(cx, |_| {});
18982
18983    let fs = FakeFs::new(cx.executor());
18984    fs.insert_tree(
18985        path!("/dir"),
18986        json!({
18987            "a.ts": "a",
18988        }),
18989    )
18990    .await;
18991
18992    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18993    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18994    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18995
18996    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18997    language_registry.add(Arc::new(Language::new(
18998        LanguageConfig {
18999            name: "TypeScript".into(),
19000            matcher: LanguageMatcher {
19001                path_suffixes: vec!["ts".to_string()],
19002                ..Default::default()
19003            },
19004            ..Default::default()
19005        },
19006        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
19007    )));
19008    let mut fake_language_servers = language_registry.register_fake_lsp(
19009        "TypeScript",
19010        FakeLspAdapter {
19011            capabilities: lsp::ServerCapabilities {
19012                code_lens_provider: Some(lsp::CodeLensOptions {
19013                    resolve_provider: Some(true),
19014                }),
19015                execute_command_provider: Some(lsp::ExecuteCommandOptions {
19016                    commands: vec!["_the/command".to_string()],
19017                    ..lsp::ExecuteCommandOptions::default()
19018                }),
19019                ..lsp::ServerCapabilities::default()
19020            },
19021            ..FakeLspAdapter::default()
19022        },
19023    );
19024
19025    let (buffer, _handle) = project
19026        .update(cx, |p, cx| {
19027            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
19028        })
19029        .await
19030        .unwrap();
19031    cx.executor().run_until_parked();
19032
19033    let fake_server = fake_language_servers.next().await.unwrap();
19034
19035    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
19036    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
19037    drop(buffer_snapshot);
19038    let actions = cx
19039        .update_window(*workspace, |_, window, cx| {
19040            project.code_actions(&buffer, anchor..anchor, window, cx)
19041        })
19042        .unwrap();
19043
19044    fake_server
19045        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
19046            Ok(Some(vec![
19047                lsp::CodeLens {
19048                    range: lsp::Range::default(),
19049                    command: Some(lsp::Command {
19050                        title: "Code lens command".to_owned(),
19051                        command: "_the/command".to_owned(),
19052                        arguments: None,
19053                    }),
19054                    data: None,
19055                },
19056                lsp::CodeLens {
19057                    range: lsp::Range::default(),
19058                    command: Some(lsp::Command {
19059                        title: "Command not in capabilities".to_owned(),
19060                        command: "not in capabilities".to_owned(),
19061                        arguments: None,
19062                    }),
19063                    data: None,
19064                },
19065                lsp::CodeLens {
19066                    range: lsp::Range {
19067                        start: lsp::Position {
19068                            line: 1,
19069                            character: 1,
19070                        },
19071                        end: lsp::Position {
19072                            line: 1,
19073                            character: 1,
19074                        },
19075                    },
19076                    command: Some(lsp::Command {
19077                        title: "Command not in range".to_owned(),
19078                        command: "_the/command".to_owned(),
19079                        arguments: None,
19080                    }),
19081                    data: None,
19082                },
19083            ]))
19084        })
19085        .next()
19086        .await;
19087
19088    let actions = actions.await.unwrap();
19089    assert_eq!(
19090        actions.len(),
19091        1,
19092        "Should have only one valid action for the 0..0 range"
19093    );
19094    let action = actions[0].clone();
19095    let apply = project.update(cx, |project, cx| {
19096        project.apply_code_action(buffer.clone(), action, true, cx)
19097    });
19098
19099    // Resolving the code action does not populate its edits. In absence of
19100    // edits, we must execute the given command.
19101    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19102        |mut lens, _| async move {
19103            let lens_command = lens.command.as_mut().expect("should have a command");
19104            assert_eq!(lens_command.title, "Code lens command");
19105            lens_command.arguments = Some(vec![json!("the-argument")]);
19106            Ok(lens)
19107        },
19108    );
19109
19110    // While executing the command, the language server sends the editor
19111    // a `workspaceEdit` request.
19112    fake_server
19113        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19114            let fake = fake_server.clone();
19115            move |params, _| {
19116                assert_eq!(params.command, "_the/command");
19117                let fake = fake.clone();
19118                async move {
19119                    fake.server
19120                        .request::<lsp::request::ApplyWorkspaceEdit>(
19121                            lsp::ApplyWorkspaceEditParams {
19122                                label: None,
19123                                edit: lsp::WorkspaceEdit {
19124                                    changes: Some(
19125                                        [(
19126                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19127                                            vec![lsp::TextEdit {
19128                                                range: lsp::Range::new(
19129                                                    lsp::Position::new(0, 0),
19130                                                    lsp::Position::new(0, 0),
19131                                                ),
19132                                                new_text: "X".into(),
19133                                            }],
19134                                        )]
19135                                        .into_iter()
19136                                        .collect(),
19137                                    ),
19138                                    ..Default::default()
19139                                },
19140                            },
19141                        )
19142                        .await
19143                        .unwrap();
19144                    Ok(Some(json!(null)))
19145                }
19146            }
19147        })
19148        .next()
19149        .await;
19150
19151    // Applying the code lens command returns a project transaction containing the edits
19152    // sent by the language server in its `workspaceEdit` request.
19153    let transaction = apply.await.unwrap();
19154    assert!(transaction.0.contains_key(&buffer));
19155    buffer.update(cx, |buffer, cx| {
19156        assert_eq!(buffer.text(), "Xa");
19157        buffer.undo(cx);
19158        assert_eq!(buffer.text(), "a");
19159    });
19160}
19161
19162#[gpui::test]
19163async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19164    init_test(cx, |_| {});
19165
19166    let fs = FakeFs::new(cx.executor());
19167    let main_text = r#"fn main() {
19168println!("1");
19169println!("2");
19170println!("3");
19171println!("4");
19172println!("5");
19173}"#;
19174    let lib_text = "mod foo {}";
19175    fs.insert_tree(
19176        path!("/a"),
19177        json!({
19178            "lib.rs": lib_text,
19179            "main.rs": main_text,
19180        }),
19181    )
19182    .await;
19183
19184    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19185    let (workspace, cx) =
19186        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19187    let worktree_id = workspace.update(cx, |workspace, cx| {
19188        workspace.project().update(cx, |project, cx| {
19189            project.worktrees(cx).next().unwrap().read(cx).id()
19190        })
19191    });
19192
19193    let expected_ranges = vec![
19194        Point::new(0, 0)..Point::new(0, 0),
19195        Point::new(1, 0)..Point::new(1, 1),
19196        Point::new(2, 0)..Point::new(2, 2),
19197        Point::new(3, 0)..Point::new(3, 3),
19198    ];
19199
19200    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19201    let editor_1 = workspace
19202        .update_in(cx, |workspace, window, cx| {
19203            workspace.open_path(
19204                (worktree_id, "main.rs"),
19205                Some(pane_1.downgrade()),
19206                true,
19207                window,
19208                cx,
19209            )
19210        })
19211        .unwrap()
19212        .await
19213        .downcast::<Editor>()
19214        .unwrap();
19215    pane_1.update(cx, |pane, cx| {
19216        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19217        open_editor.update(cx, |editor, cx| {
19218            assert_eq!(
19219                editor.display_text(cx),
19220                main_text,
19221                "Original main.rs text on initial open",
19222            );
19223            assert_eq!(
19224                editor
19225                    .selections
19226                    .all::<Point>(cx)
19227                    .into_iter()
19228                    .map(|s| s.range())
19229                    .collect::<Vec<_>>(),
19230                vec![Point::zero()..Point::zero()],
19231                "Default selections on initial open",
19232            );
19233        })
19234    });
19235    editor_1.update_in(cx, |editor, window, cx| {
19236        editor.change_selections(None, window, cx, |s| {
19237            s.select_ranges(expected_ranges.clone());
19238        });
19239    });
19240
19241    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19242        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19243    });
19244    let editor_2 = workspace
19245        .update_in(cx, |workspace, window, cx| {
19246            workspace.open_path(
19247                (worktree_id, "main.rs"),
19248                Some(pane_2.downgrade()),
19249                true,
19250                window,
19251                cx,
19252            )
19253        })
19254        .unwrap()
19255        .await
19256        .downcast::<Editor>()
19257        .unwrap();
19258    pane_2.update(cx, |pane, cx| {
19259        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19260        open_editor.update(cx, |editor, cx| {
19261            assert_eq!(
19262                editor.display_text(cx),
19263                main_text,
19264                "Original main.rs text on initial open in another panel",
19265            );
19266            assert_eq!(
19267                editor
19268                    .selections
19269                    .all::<Point>(cx)
19270                    .into_iter()
19271                    .map(|s| s.range())
19272                    .collect::<Vec<_>>(),
19273                vec![Point::zero()..Point::zero()],
19274                "Default selections on initial open in another panel",
19275            );
19276        })
19277    });
19278
19279    editor_2.update_in(cx, |editor, window, cx| {
19280        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19281    });
19282
19283    let _other_editor_1 = workspace
19284        .update_in(cx, |workspace, window, cx| {
19285            workspace.open_path(
19286                (worktree_id, "lib.rs"),
19287                Some(pane_1.downgrade()),
19288                true,
19289                window,
19290                cx,
19291            )
19292        })
19293        .unwrap()
19294        .await
19295        .downcast::<Editor>()
19296        .unwrap();
19297    pane_1
19298        .update_in(cx, |pane, window, cx| {
19299            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19300                .unwrap()
19301        })
19302        .await
19303        .unwrap();
19304    drop(editor_1);
19305    pane_1.update(cx, |pane, cx| {
19306        pane.active_item()
19307            .unwrap()
19308            .downcast::<Editor>()
19309            .unwrap()
19310            .update(cx, |editor, cx| {
19311                assert_eq!(
19312                    editor.display_text(cx),
19313                    lib_text,
19314                    "Other file should be open and active",
19315                );
19316            });
19317        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19318    });
19319
19320    let _other_editor_2 = workspace
19321        .update_in(cx, |workspace, window, cx| {
19322            workspace.open_path(
19323                (worktree_id, "lib.rs"),
19324                Some(pane_2.downgrade()),
19325                true,
19326                window,
19327                cx,
19328            )
19329        })
19330        .unwrap()
19331        .await
19332        .downcast::<Editor>()
19333        .unwrap();
19334    pane_2
19335        .update_in(cx, |pane, window, cx| {
19336            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19337                .unwrap()
19338        })
19339        .await
19340        .unwrap();
19341    drop(editor_2);
19342    pane_2.update(cx, |pane, cx| {
19343        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19344        open_editor.update(cx, |editor, cx| {
19345            assert_eq!(
19346                editor.display_text(cx),
19347                lib_text,
19348                "Other file should be open and active in another panel too",
19349            );
19350        });
19351        assert_eq!(
19352            pane.items().count(),
19353            1,
19354            "No other editors should be open in another pane",
19355        );
19356    });
19357
19358    let _editor_1_reopened = workspace
19359        .update_in(cx, |workspace, window, cx| {
19360            workspace.open_path(
19361                (worktree_id, "main.rs"),
19362                Some(pane_1.downgrade()),
19363                true,
19364                window,
19365                cx,
19366            )
19367        })
19368        .unwrap()
19369        .await
19370        .downcast::<Editor>()
19371        .unwrap();
19372    let _editor_2_reopened = workspace
19373        .update_in(cx, |workspace, window, cx| {
19374            workspace.open_path(
19375                (worktree_id, "main.rs"),
19376                Some(pane_2.downgrade()),
19377                true,
19378                window,
19379                cx,
19380            )
19381        })
19382        .unwrap()
19383        .await
19384        .downcast::<Editor>()
19385        .unwrap();
19386    pane_1.update(cx, |pane, cx| {
19387        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19388        open_editor.update(cx, |editor, cx| {
19389            assert_eq!(
19390                editor.display_text(cx),
19391                main_text,
19392                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19393            );
19394            assert_eq!(
19395                editor
19396                    .selections
19397                    .all::<Point>(cx)
19398                    .into_iter()
19399                    .map(|s| s.range())
19400                    .collect::<Vec<_>>(),
19401                expected_ranges,
19402                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19403            );
19404        })
19405    });
19406    pane_2.update(cx, |pane, cx| {
19407        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19408        open_editor.update(cx, |editor, cx| {
19409            assert_eq!(
19410                editor.display_text(cx),
19411                r#"fn main() {
19412⋯rintln!("1");
19413⋯intln!("2");
19414⋯ntln!("3");
19415println!("4");
19416println!("5");
19417}"#,
19418                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19419            );
19420            assert_eq!(
19421                editor
19422                    .selections
19423                    .all::<Point>(cx)
19424                    .into_iter()
19425                    .map(|s| s.range())
19426                    .collect::<Vec<_>>(),
19427                vec![Point::zero()..Point::zero()],
19428                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19429            );
19430        })
19431    });
19432}
19433
19434#[gpui::test]
19435async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19436    init_test(cx, |_| {});
19437
19438    let fs = FakeFs::new(cx.executor());
19439    let main_text = r#"fn main() {
19440println!("1");
19441println!("2");
19442println!("3");
19443println!("4");
19444println!("5");
19445}"#;
19446    let lib_text = "mod foo {}";
19447    fs.insert_tree(
19448        path!("/a"),
19449        json!({
19450            "lib.rs": lib_text,
19451            "main.rs": main_text,
19452        }),
19453    )
19454    .await;
19455
19456    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19457    let (workspace, cx) =
19458        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19459    let worktree_id = workspace.update(cx, |workspace, cx| {
19460        workspace.project().update(cx, |project, cx| {
19461            project.worktrees(cx).next().unwrap().read(cx).id()
19462        })
19463    });
19464
19465    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19466    let editor = workspace
19467        .update_in(cx, |workspace, window, cx| {
19468            workspace.open_path(
19469                (worktree_id, "main.rs"),
19470                Some(pane.downgrade()),
19471                true,
19472                window,
19473                cx,
19474            )
19475        })
19476        .unwrap()
19477        .await
19478        .downcast::<Editor>()
19479        .unwrap();
19480    pane.update(cx, |pane, cx| {
19481        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19482        open_editor.update(cx, |editor, cx| {
19483            assert_eq!(
19484                editor.display_text(cx),
19485                main_text,
19486                "Original main.rs text on initial open",
19487            );
19488        })
19489    });
19490    editor.update_in(cx, |editor, window, cx| {
19491        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19492    });
19493
19494    cx.update_global(|store: &mut SettingsStore, cx| {
19495        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19496            s.restore_on_file_reopen = Some(false);
19497        });
19498    });
19499    editor.update_in(cx, |editor, window, cx| {
19500        editor.fold_ranges(
19501            vec![
19502                Point::new(1, 0)..Point::new(1, 1),
19503                Point::new(2, 0)..Point::new(2, 2),
19504                Point::new(3, 0)..Point::new(3, 3),
19505            ],
19506            false,
19507            window,
19508            cx,
19509        );
19510    });
19511    pane.update_in(cx, |pane, window, cx| {
19512        pane.close_all_items(&CloseAllItems::default(), window, cx)
19513            .unwrap()
19514    })
19515    .await
19516    .unwrap();
19517    pane.update(cx, |pane, _| {
19518        assert!(pane.active_item().is_none());
19519    });
19520    cx.update_global(|store: &mut SettingsStore, cx| {
19521        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19522            s.restore_on_file_reopen = Some(true);
19523        });
19524    });
19525
19526    let _editor_reopened = workspace
19527        .update_in(cx, |workspace, window, cx| {
19528            workspace.open_path(
19529                (worktree_id, "main.rs"),
19530                Some(pane.downgrade()),
19531                true,
19532                window,
19533                cx,
19534            )
19535        })
19536        .unwrap()
19537        .await
19538        .downcast::<Editor>()
19539        .unwrap();
19540    pane.update(cx, |pane, cx| {
19541        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19542        open_editor.update(cx, |editor, cx| {
19543            assert_eq!(
19544                editor.display_text(cx),
19545                main_text,
19546                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19547            );
19548        })
19549    });
19550}
19551
19552#[gpui::test]
19553async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19554    struct EmptyModalView {
19555        focus_handle: gpui::FocusHandle,
19556    }
19557    impl EventEmitter<DismissEvent> for EmptyModalView {}
19558    impl Render for EmptyModalView {
19559        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19560            div()
19561        }
19562    }
19563    impl Focusable for EmptyModalView {
19564        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19565            self.focus_handle.clone()
19566        }
19567    }
19568    impl workspace::ModalView for EmptyModalView {}
19569    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19570        EmptyModalView {
19571            focus_handle: cx.focus_handle(),
19572        }
19573    }
19574
19575    init_test(cx, |_| {});
19576
19577    let fs = FakeFs::new(cx.executor());
19578    let project = Project::test(fs, [], cx).await;
19579    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19580    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19581    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19582    let editor = cx.new_window_entity(|window, cx| {
19583        Editor::new(
19584            EditorMode::full(),
19585            buffer,
19586            Some(project.clone()),
19587            window,
19588            cx,
19589        )
19590    });
19591    workspace
19592        .update(cx, |workspace, window, cx| {
19593            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19594        })
19595        .unwrap();
19596    editor.update_in(cx, |editor, window, cx| {
19597        editor.open_context_menu(&OpenContextMenu, window, cx);
19598        assert!(editor.mouse_context_menu.is_some());
19599    });
19600    workspace
19601        .update(cx, |workspace, window, cx| {
19602            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19603        })
19604        .unwrap();
19605    cx.read(|cx| {
19606        assert!(editor.read(cx).mouse_context_menu.is_none());
19607    });
19608}
19609
19610fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19611    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19612    point..point
19613}
19614
19615fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19616    let (text, ranges) = marked_text_ranges(marked_text, true);
19617    assert_eq!(editor.text(cx), text);
19618    assert_eq!(
19619        editor.selections.ranges(cx),
19620        ranges,
19621        "Assert selections are {}",
19622        marked_text
19623    );
19624}
19625
19626pub fn handle_signature_help_request(
19627    cx: &mut EditorLspTestContext,
19628    mocked_response: lsp::SignatureHelp,
19629) -> impl Future<Output = ()> + use<> {
19630    let mut request =
19631        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19632            let mocked_response = mocked_response.clone();
19633            async move { Ok(Some(mocked_response)) }
19634        });
19635
19636    async move {
19637        request.next().await;
19638    }
19639}
19640
19641/// Handle completion request passing a marked string specifying where the completion
19642/// should be triggered from using '|' character, what range should be replaced, and what completions
19643/// should be returned using '<' and '>' to delimit the range.
19644///
19645/// Also see `handle_completion_request_with_insert_and_replace`.
19646#[track_caller]
19647pub fn handle_completion_request(
19648    cx: &mut EditorLspTestContext,
19649    marked_string: &str,
19650    completions: Vec<&'static str>,
19651    counter: Arc<AtomicUsize>,
19652) -> impl Future<Output = ()> {
19653    let complete_from_marker: TextRangeMarker = '|'.into();
19654    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19655    let (_, mut marked_ranges) = marked_text_ranges_by(
19656        marked_string,
19657        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19658    );
19659
19660    let complete_from_position =
19661        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19662    let replace_range =
19663        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19664
19665    let mut request =
19666        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19667            let completions = completions.clone();
19668            counter.fetch_add(1, atomic::Ordering::Release);
19669            async move {
19670                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19671                assert_eq!(
19672                    params.text_document_position.position,
19673                    complete_from_position
19674                );
19675                Ok(Some(lsp::CompletionResponse::Array(
19676                    completions
19677                        .iter()
19678                        .map(|completion_text| lsp::CompletionItem {
19679                            label: completion_text.to_string(),
19680                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19681                                range: replace_range,
19682                                new_text: completion_text.to_string(),
19683                            })),
19684                            ..Default::default()
19685                        })
19686                        .collect(),
19687                )))
19688            }
19689        });
19690
19691    async move {
19692        request.next().await;
19693    }
19694}
19695
19696/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19697/// given instead, which also contains an `insert` range.
19698///
19699/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19700/// that is, `replace_range.start..cursor_pos`.
19701pub fn handle_completion_request_with_insert_and_replace(
19702    cx: &mut EditorLspTestContext,
19703    marked_string: &str,
19704    completions: Vec<&'static str>,
19705    counter: Arc<AtomicUsize>,
19706) -> impl Future<Output = ()> {
19707    let complete_from_marker: TextRangeMarker = '|'.into();
19708    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19709    let (_, mut marked_ranges) = marked_text_ranges_by(
19710        marked_string,
19711        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19712    );
19713
19714    let complete_from_position =
19715        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19716    let replace_range =
19717        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19718
19719    let mut request =
19720        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19721            let completions = completions.clone();
19722            counter.fetch_add(1, atomic::Ordering::Release);
19723            async move {
19724                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19725                assert_eq!(
19726                    params.text_document_position.position, complete_from_position,
19727                    "marker `|` position doesn't match",
19728                );
19729                Ok(Some(lsp::CompletionResponse::Array(
19730                    completions
19731                        .iter()
19732                        .map(|completion_text| lsp::CompletionItem {
19733                            label: completion_text.to_string(),
19734                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19735                                lsp::InsertReplaceEdit {
19736                                    insert: lsp::Range {
19737                                        start: replace_range.start,
19738                                        end: complete_from_position,
19739                                    },
19740                                    replace: replace_range,
19741                                    new_text: completion_text.to_string(),
19742                                },
19743                            )),
19744                            ..Default::default()
19745                        })
19746                        .collect(),
19747                )))
19748            }
19749        });
19750
19751    async move {
19752        request.next().await;
19753    }
19754}
19755
19756fn handle_resolve_completion_request(
19757    cx: &mut EditorLspTestContext,
19758    edits: Option<Vec<(&'static str, &'static str)>>,
19759) -> impl Future<Output = ()> {
19760    let edits = edits.map(|edits| {
19761        edits
19762            .iter()
19763            .map(|(marked_string, new_text)| {
19764                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19765                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19766                lsp::TextEdit::new(replace_range, new_text.to_string())
19767            })
19768            .collect::<Vec<_>>()
19769    });
19770
19771    let mut request =
19772        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19773            let edits = edits.clone();
19774            async move {
19775                Ok(lsp::CompletionItem {
19776                    additional_text_edits: edits,
19777                    ..Default::default()
19778                })
19779            }
19780        });
19781
19782    async move {
19783        request.next().await;
19784    }
19785}
19786
19787pub(crate) fn update_test_language_settings(
19788    cx: &mut TestAppContext,
19789    f: impl Fn(&mut AllLanguageSettingsContent),
19790) {
19791    cx.update(|cx| {
19792        SettingsStore::update_global(cx, |store, cx| {
19793            store.update_user_settings::<AllLanguageSettings>(cx, f);
19794        });
19795    });
19796}
19797
19798pub(crate) fn update_test_project_settings(
19799    cx: &mut TestAppContext,
19800    f: impl Fn(&mut ProjectSettings),
19801) {
19802    cx.update(|cx| {
19803        SettingsStore::update_global(cx, |store, cx| {
19804            store.update_user_settings::<ProjectSettings>(cx, f);
19805        });
19806    });
19807}
19808
19809pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19810    cx.update(|cx| {
19811        assets::Assets.load_test_fonts(cx);
19812        let store = SettingsStore::test(cx);
19813        cx.set_global(store);
19814        theme::init(theme::LoadThemes::JustBase, cx);
19815        release_channel::init(SemanticVersion::default(), cx);
19816        client::init_settings(cx);
19817        language::init(cx);
19818        Project::init_settings(cx);
19819        workspace::init_settings(cx);
19820        crate::init(cx);
19821    });
19822
19823    update_test_language_settings(cx, f);
19824}
19825
19826#[track_caller]
19827fn assert_hunk_revert(
19828    not_reverted_text_with_selections: &str,
19829    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19830    expected_reverted_text_with_selections: &str,
19831    base_text: &str,
19832    cx: &mut EditorLspTestContext,
19833) {
19834    cx.set_state(not_reverted_text_with_selections);
19835    cx.set_head_text(base_text);
19836    cx.executor().run_until_parked();
19837
19838    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19839        let snapshot = editor.snapshot(window, cx);
19840        let reverted_hunk_statuses = snapshot
19841            .buffer_snapshot
19842            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19843            .map(|hunk| hunk.status().kind)
19844            .collect::<Vec<_>>();
19845
19846        editor.git_restore(&Default::default(), window, cx);
19847        reverted_hunk_statuses
19848    });
19849    cx.executor().run_until_parked();
19850    cx.assert_editor_state(expected_reverted_text_with_selections);
19851    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19852}