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, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    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// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 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\n");
 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\nthree");
 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\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 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(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\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(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.defaults.tab_size = NonZeroU32::new(3)
 2924    });
 2925
 2926    let mut cx = EditorTestContext::new(cx).await;
 2927    cx.set_state(indoc! {"
 2928         ˇ
 2929        \t ˇ
 2930        \t  ˇ
 2931        \t   ˇ
 2932         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2933    "});
 2934
 2935    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2936    cx.assert_editor_state(indoc! {"
 2937           ˇ
 2938        \t   ˇ
 2939        \t   ˇ
 2940        \t      ˇ
 2941         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2942    "});
 2943}
 2944
 2945#[gpui::test]
 2946async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2947    init_test(cx, |settings| {
 2948        settings.defaults.tab_size = NonZeroU32::new(4)
 2949    });
 2950
 2951    let language = Arc::new(
 2952        Language::new(
 2953            LanguageConfig::default(),
 2954            Some(tree_sitter_rust::LANGUAGE.into()),
 2955        )
 2956        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2957        .unwrap(),
 2958    );
 2959
 2960    let mut cx = EditorTestContext::new(cx).await;
 2961    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2962    cx.set_state(indoc! {"
 2963        fn a() {
 2964            if b {
 2965        \t ˇc
 2966            }
 2967        }
 2968    "});
 2969
 2970    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2971    cx.assert_editor_state(indoc! {"
 2972        fn a() {
 2973            if b {
 2974                ˇc
 2975            }
 2976        }
 2977    "});
 2978}
 2979
 2980#[gpui::test]
 2981async fn test_indent_outdent(cx: &mut TestAppContext) {
 2982    init_test(cx, |settings| {
 2983        settings.defaults.tab_size = NonZeroU32::new(4);
 2984    });
 2985
 2986    let mut cx = EditorTestContext::new(cx).await;
 2987
 2988    cx.set_state(indoc! {"
 2989          «oneˇ» «twoˇ»
 2990        three
 2991         four
 2992    "});
 2993    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2994    cx.assert_editor_state(indoc! {"
 2995            «oneˇ» «twoˇ»
 2996        three
 2997         four
 2998    "});
 2999
 3000    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3001    cx.assert_editor_state(indoc! {"
 3002        «oneˇ» «twoˇ»
 3003        three
 3004         four
 3005    "});
 3006
 3007    // select across line ending
 3008    cx.set_state(indoc! {"
 3009        one two
 3010        t«hree
 3011        ˇ» four
 3012    "});
 3013    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3014    cx.assert_editor_state(indoc! {"
 3015        one two
 3016            t«hree
 3017        ˇ» four
 3018    "});
 3019
 3020    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3021    cx.assert_editor_state(indoc! {"
 3022        one two
 3023        t«hree
 3024        ˇ» four
 3025    "});
 3026
 3027    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3028    cx.set_state(indoc! {"
 3029        one two
 3030        ˇthree
 3031            four
 3032    "});
 3033    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3034    cx.assert_editor_state(indoc! {"
 3035        one two
 3036            ˇthree
 3037            four
 3038    "});
 3039
 3040    cx.set_state(indoc! {"
 3041        one two
 3042        ˇ    three
 3043            four
 3044    "});
 3045    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3046    cx.assert_editor_state(indoc! {"
 3047        one two
 3048        ˇthree
 3049            four
 3050    "});
 3051}
 3052
 3053#[gpui::test]
 3054async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3055    init_test(cx, |settings| {
 3056        settings.defaults.hard_tabs = Some(true);
 3057    });
 3058
 3059    let mut cx = EditorTestContext::new(cx).await;
 3060
 3061    // select two ranges on one line
 3062    cx.set_state(indoc! {"
 3063        «oneˇ» «twoˇ»
 3064        three
 3065        four
 3066    "});
 3067    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3068    cx.assert_editor_state(indoc! {"
 3069        \t«oneˇ» «twoˇ»
 3070        three
 3071        four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        \t\t«oneˇ» «twoˇ»
 3076        three
 3077        four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        \t«oneˇ» «twoˇ»
 3082        three
 3083        four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        «oneˇ» «twoˇ»
 3088        three
 3089        four
 3090    "});
 3091
 3092    // select across a line ending
 3093    cx.set_state(indoc! {"
 3094        one two
 3095        t«hree
 3096        ˇ»four
 3097    "});
 3098    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3099    cx.assert_editor_state(indoc! {"
 3100        one two
 3101        \tt«hree
 3102        ˇ»four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        \t\tt«hree
 3108        ˇ»four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        \tt«hree
 3114        ˇ»four
 3115    "});
 3116    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3117    cx.assert_editor_state(indoc! {"
 3118        one two
 3119        t«hree
 3120        ˇ»four
 3121    "});
 3122
 3123    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3124    cx.set_state(indoc! {"
 3125        one two
 3126        ˇthree
 3127        four
 3128    "});
 3129    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3130    cx.assert_editor_state(indoc! {"
 3131        one two
 3132        ˇthree
 3133        four
 3134    "});
 3135    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3136    cx.assert_editor_state(indoc! {"
 3137        one two
 3138        \tˇthree
 3139        four
 3140    "});
 3141    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3142    cx.assert_editor_state(indoc! {"
 3143        one two
 3144        ˇthree
 3145        four
 3146    "});
 3147}
 3148
 3149#[gpui::test]
 3150fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3151    init_test(cx, |settings| {
 3152        settings.languages.extend([
 3153            (
 3154                "TOML".into(),
 3155                LanguageSettingsContent {
 3156                    tab_size: NonZeroU32::new(2),
 3157                    ..Default::default()
 3158                },
 3159            ),
 3160            (
 3161                "Rust".into(),
 3162                LanguageSettingsContent {
 3163                    tab_size: NonZeroU32::new(4),
 3164                    ..Default::default()
 3165                },
 3166            ),
 3167        ]);
 3168    });
 3169
 3170    let toml_language = Arc::new(Language::new(
 3171        LanguageConfig {
 3172            name: "TOML".into(),
 3173            ..Default::default()
 3174        },
 3175        None,
 3176    ));
 3177    let rust_language = Arc::new(Language::new(
 3178        LanguageConfig {
 3179            name: "Rust".into(),
 3180            ..Default::default()
 3181        },
 3182        None,
 3183    ));
 3184
 3185    let toml_buffer =
 3186        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3187    let rust_buffer =
 3188        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3189    let multibuffer = cx.new(|cx| {
 3190        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3191        multibuffer.push_excerpts(
 3192            toml_buffer.clone(),
 3193            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3194            cx,
 3195        );
 3196        multibuffer.push_excerpts(
 3197            rust_buffer.clone(),
 3198            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3199            cx,
 3200        );
 3201        multibuffer
 3202    });
 3203
 3204    cx.add_window(|window, cx| {
 3205        let mut editor = build_editor(multibuffer, window, cx);
 3206
 3207        assert_eq!(
 3208            editor.text(cx),
 3209            indoc! {"
 3210                a = 1
 3211                b = 2
 3212
 3213                const c: usize = 3;
 3214            "}
 3215        );
 3216
 3217        select_ranges(
 3218            &mut editor,
 3219            indoc! {"
 3220                «aˇ» = 1
 3221                b = 2
 3222
 3223                «const c:ˇ» usize = 3;
 3224            "},
 3225            window,
 3226            cx,
 3227        );
 3228
 3229        editor.tab(&Tab, window, cx);
 3230        assert_text_with_selections(
 3231            &mut editor,
 3232            indoc! {"
 3233                  «aˇ» = 1
 3234                b = 2
 3235
 3236                    «const c:ˇ» usize = 3;
 3237            "},
 3238            cx,
 3239        );
 3240        editor.backtab(&Backtab, window, cx);
 3241        assert_text_with_selections(
 3242            &mut editor,
 3243            indoc! {"
 3244                «aˇ» = 1
 3245                b = 2
 3246
 3247                «const c:ˇ» usize = 3;
 3248            "},
 3249            cx,
 3250        );
 3251
 3252        editor
 3253    });
 3254}
 3255
 3256#[gpui::test]
 3257async fn test_backspace(cx: &mut TestAppContext) {
 3258    init_test(cx, |_| {});
 3259
 3260    let mut cx = EditorTestContext::new(cx).await;
 3261
 3262    // Basic backspace
 3263    cx.set_state(indoc! {"
 3264        onˇe two three
 3265        fou«rˇ» five six
 3266        seven «ˇeight nine
 3267        »ten
 3268    "});
 3269    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3270    cx.assert_editor_state(indoc! {"
 3271        oˇe two three
 3272        fouˇ five six
 3273        seven ˇten
 3274    "});
 3275
 3276    // Test backspace inside and around indents
 3277    cx.set_state(indoc! {"
 3278        zero
 3279            ˇone
 3280                ˇtwo
 3281            ˇ ˇ ˇ  three
 3282        ˇ  ˇ  four
 3283    "});
 3284    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3285    cx.assert_editor_state(indoc! {"
 3286        zero
 3287        ˇone
 3288            ˇtwo
 3289        ˇ  threeˇ  four
 3290    "});
 3291}
 3292
 3293#[gpui::test]
 3294async fn test_delete(cx: &mut TestAppContext) {
 3295    init_test(cx, |_| {});
 3296
 3297    let mut cx = EditorTestContext::new(cx).await;
 3298    cx.set_state(indoc! {"
 3299        onˇe two three
 3300        fou«rˇ» five six
 3301        seven «ˇeight nine
 3302        »ten
 3303    "});
 3304    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3305    cx.assert_editor_state(indoc! {"
 3306        onˇ two three
 3307        fouˇ five six
 3308        seven ˇten
 3309    "});
 3310}
 3311
 3312#[gpui::test]
 3313fn test_delete_line(cx: &mut TestAppContext) {
 3314    init_test(cx, |_| {});
 3315
 3316    let editor = cx.add_window(|window, cx| {
 3317        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3318        build_editor(buffer, window, cx)
 3319    });
 3320    _ = editor.update(cx, |editor, window, cx| {
 3321        editor.change_selections(None, window, cx, |s| {
 3322            s.select_display_ranges([
 3323                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3324                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3325                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3326            ])
 3327        });
 3328        editor.delete_line(&DeleteLine, window, cx);
 3329        assert_eq!(editor.display_text(cx), "ghi");
 3330        assert_eq!(
 3331            editor.selections.display_ranges(cx),
 3332            vec![
 3333                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3334                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3335            ]
 3336        );
 3337    });
 3338
 3339    let editor = cx.add_window(|window, cx| {
 3340        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3341        build_editor(buffer, window, cx)
 3342    });
 3343    _ = editor.update(cx, |editor, window, cx| {
 3344        editor.change_selections(None, window, cx, |s| {
 3345            s.select_display_ranges([
 3346                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3347            ])
 3348        });
 3349        editor.delete_line(&DeleteLine, window, cx);
 3350        assert_eq!(editor.display_text(cx), "ghi\n");
 3351        assert_eq!(
 3352            editor.selections.display_ranges(cx),
 3353            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3354        );
 3355    });
 3356}
 3357
 3358#[gpui::test]
 3359fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3360    init_test(cx, |_| {});
 3361
 3362    cx.add_window(|window, cx| {
 3363        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3364        let mut editor = build_editor(buffer.clone(), window, cx);
 3365        let buffer = buffer.read(cx).as_singleton().unwrap();
 3366
 3367        assert_eq!(
 3368            editor.selections.ranges::<Point>(cx),
 3369            &[Point::new(0, 0)..Point::new(0, 0)]
 3370        );
 3371
 3372        // When on single line, replace newline at end by space
 3373        editor.join_lines(&JoinLines, window, cx);
 3374        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3375        assert_eq!(
 3376            editor.selections.ranges::<Point>(cx),
 3377            &[Point::new(0, 3)..Point::new(0, 3)]
 3378        );
 3379
 3380        // When multiple lines are selected, remove newlines that are spanned by the selection
 3381        editor.change_selections(None, window, cx, |s| {
 3382            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3383        });
 3384        editor.join_lines(&JoinLines, window, cx);
 3385        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3386        assert_eq!(
 3387            editor.selections.ranges::<Point>(cx),
 3388            &[Point::new(0, 11)..Point::new(0, 11)]
 3389        );
 3390
 3391        // Undo should be transactional
 3392        editor.undo(&Undo, window, cx);
 3393        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3394        assert_eq!(
 3395            editor.selections.ranges::<Point>(cx),
 3396            &[Point::new(0, 5)..Point::new(2, 2)]
 3397        );
 3398
 3399        // When joining an empty line don't insert a space
 3400        editor.change_selections(None, window, cx, |s| {
 3401            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3402        });
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            [Point::new(2, 3)..Point::new(2, 3)]
 3408        );
 3409
 3410        // We can remove trailing newlines
 3411        editor.join_lines(&JoinLines, window, cx);
 3412        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3413        assert_eq!(
 3414            editor.selections.ranges::<Point>(cx),
 3415            [Point::new(2, 3)..Point::new(2, 3)]
 3416        );
 3417
 3418        // We don't blow up on the last line
 3419        editor.join_lines(&JoinLines, window, cx);
 3420        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3421        assert_eq!(
 3422            editor.selections.ranges::<Point>(cx),
 3423            [Point::new(2, 3)..Point::new(2, 3)]
 3424        );
 3425
 3426        // reset to test indentation
 3427        editor.buffer.update(cx, |buffer, cx| {
 3428            buffer.edit(
 3429                [
 3430                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3431                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3432                ],
 3433                None,
 3434                cx,
 3435            )
 3436        });
 3437
 3438        // We remove any leading spaces
 3439        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3440        editor.change_selections(None, window, cx, |s| {
 3441            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3442        });
 3443        editor.join_lines(&JoinLines, window, cx);
 3444        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3445
 3446        // We don't insert a space for a line containing only spaces
 3447        editor.join_lines(&JoinLines, window, cx);
 3448        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3449
 3450        // We ignore any leading tabs
 3451        editor.join_lines(&JoinLines, window, cx);
 3452        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3453
 3454        editor
 3455    });
 3456}
 3457
 3458#[gpui::test]
 3459fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3460    init_test(cx, |_| {});
 3461
 3462    cx.add_window(|window, cx| {
 3463        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3464        let mut editor = build_editor(buffer.clone(), window, cx);
 3465        let buffer = buffer.read(cx).as_singleton().unwrap();
 3466
 3467        editor.change_selections(None, window, cx, |s| {
 3468            s.select_ranges([
 3469                Point::new(0, 2)..Point::new(1, 1),
 3470                Point::new(1, 2)..Point::new(1, 2),
 3471                Point::new(3, 1)..Point::new(3, 2),
 3472            ])
 3473        });
 3474
 3475        editor.join_lines(&JoinLines, window, cx);
 3476        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3477
 3478        assert_eq!(
 3479            editor.selections.ranges::<Point>(cx),
 3480            [
 3481                Point::new(0, 7)..Point::new(0, 7),
 3482                Point::new(1, 3)..Point::new(1, 3)
 3483            ]
 3484        );
 3485        editor
 3486    });
 3487}
 3488
 3489#[gpui::test]
 3490async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3491    init_test(cx, |_| {});
 3492
 3493    let mut cx = EditorTestContext::new(cx).await;
 3494
 3495    let diff_base = r#"
 3496        Line 0
 3497        Line 1
 3498        Line 2
 3499        Line 3
 3500        "#
 3501    .unindent();
 3502
 3503    cx.set_state(
 3504        &r#"
 3505        ˇLine 0
 3506        Line 1
 3507        Line 2
 3508        Line 3
 3509        "#
 3510        .unindent(),
 3511    );
 3512
 3513    cx.set_head_text(&diff_base);
 3514    executor.run_until_parked();
 3515
 3516    // Join lines
 3517    cx.update_editor(|editor, window, cx| {
 3518        editor.join_lines(&JoinLines, window, cx);
 3519    });
 3520    executor.run_until_parked();
 3521
 3522    cx.assert_editor_state(
 3523        &r#"
 3524        Line 0ˇ Line 1
 3525        Line 2
 3526        Line 3
 3527        "#
 3528        .unindent(),
 3529    );
 3530    // Join again
 3531    cx.update_editor(|editor, window, cx| {
 3532        editor.join_lines(&JoinLines, window, cx);
 3533    });
 3534    executor.run_until_parked();
 3535
 3536    cx.assert_editor_state(
 3537        &r#"
 3538        Line 0 Line 1ˇ Line 2
 3539        Line 3
 3540        "#
 3541        .unindent(),
 3542    );
 3543}
 3544
 3545#[gpui::test]
 3546async fn test_custom_newlines_cause_no_false_positive_diffs(
 3547    executor: BackgroundExecutor,
 3548    cx: &mut TestAppContext,
 3549) {
 3550    init_test(cx, |_| {});
 3551    let mut cx = EditorTestContext::new(cx).await;
 3552    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3553    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3554    executor.run_until_parked();
 3555
 3556    cx.update_editor(|editor, window, cx| {
 3557        let snapshot = editor.snapshot(window, cx);
 3558        assert_eq!(
 3559            snapshot
 3560                .buffer_snapshot
 3561                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3562                .collect::<Vec<_>>(),
 3563            Vec::new(),
 3564            "Should not have any diffs for files with custom newlines"
 3565        );
 3566    });
 3567}
 3568
 3569#[gpui::test]
 3570async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let mut cx = EditorTestContext::new(cx).await;
 3574
 3575    // Test sort_lines_case_insensitive()
 3576    cx.set_state(indoc! {"
 3577        «z
 3578        y
 3579        x
 3580        Z
 3581        Y
 3582        Xˇ»
 3583    "});
 3584    cx.update_editor(|e, window, cx| {
 3585        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3586    });
 3587    cx.assert_editor_state(indoc! {"
 3588        «x
 3589        X
 3590        y
 3591        Y
 3592        z
 3593        Zˇ»
 3594    "});
 3595
 3596    // Test reverse_lines()
 3597    cx.set_state(indoc! {"
 3598        «5
 3599        4
 3600        3
 3601        2
 3602        1ˇ»
 3603    "});
 3604    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3605    cx.assert_editor_state(indoc! {"
 3606        «1
 3607        2
 3608        3
 3609        4
 3610        5ˇ»
 3611    "});
 3612
 3613    // Skip testing shuffle_line()
 3614
 3615    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3616    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3617
 3618    // Don't manipulate when cursor is on single line, but expand the selection
 3619    cx.set_state(indoc! {"
 3620        ddˇdd
 3621        ccc
 3622        bb
 3623        a
 3624    "});
 3625    cx.update_editor(|e, window, cx| {
 3626        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3627    });
 3628    cx.assert_editor_state(indoc! {"
 3629        «ddddˇ»
 3630        ccc
 3631        bb
 3632        a
 3633    "});
 3634
 3635    // Basic manipulate case
 3636    // Start selection moves to column 0
 3637    // End of selection shrinks to fit shorter line
 3638    cx.set_state(indoc! {"
 3639        dd«d
 3640        ccc
 3641        bb
 3642        aaaaaˇ»
 3643    "});
 3644    cx.update_editor(|e, window, cx| {
 3645        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3646    });
 3647    cx.assert_editor_state(indoc! {"
 3648        «aaaaa
 3649        bb
 3650        ccc
 3651        dddˇ»
 3652    "});
 3653
 3654    // Manipulate case with newlines
 3655    cx.set_state(indoc! {"
 3656        dd«d
 3657        ccc
 3658
 3659        bb
 3660        aaaaa
 3661
 3662        ˇ»
 3663    "});
 3664    cx.update_editor(|e, window, cx| {
 3665        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3666    });
 3667    cx.assert_editor_state(indoc! {"
 3668        «
 3669
 3670        aaaaa
 3671        bb
 3672        ccc
 3673        dddˇ»
 3674
 3675    "});
 3676
 3677    // Adding new line
 3678    cx.set_state(indoc! {"
 3679        aa«a
 3680        bbˇ»b
 3681    "});
 3682    cx.update_editor(|e, window, cx| {
 3683        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3684    });
 3685    cx.assert_editor_state(indoc! {"
 3686        «aaa
 3687        bbb
 3688        added_lineˇ»
 3689    "});
 3690
 3691    // Removing line
 3692    cx.set_state(indoc! {"
 3693        aa«a
 3694        bbbˇ»
 3695    "});
 3696    cx.update_editor(|e, window, cx| {
 3697        e.manipulate_lines(window, cx, |lines| {
 3698            lines.pop();
 3699        })
 3700    });
 3701    cx.assert_editor_state(indoc! {"
 3702        «aaaˇ»
 3703    "});
 3704
 3705    // Removing all lines
 3706    cx.set_state(indoc! {"
 3707        aa«a
 3708        bbbˇ»
 3709    "});
 3710    cx.update_editor(|e, window, cx| {
 3711        e.manipulate_lines(window, cx, |lines| {
 3712            lines.drain(..);
 3713        })
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        ˇ
 3717    "});
 3718}
 3719
 3720#[gpui::test]
 3721async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3722    init_test(cx, |_| {});
 3723
 3724    let mut cx = EditorTestContext::new(cx).await;
 3725
 3726    // Consider continuous selection as single selection
 3727    cx.set_state(indoc! {"
 3728        Aaa«aa
 3729        cˇ»c«c
 3730        bb
 3731        aaaˇ»aa
 3732    "});
 3733    cx.update_editor(|e, window, cx| {
 3734        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3735    });
 3736    cx.assert_editor_state(indoc! {"
 3737        «Aaaaa
 3738        ccc
 3739        bb
 3740        aaaaaˇ»
 3741    "});
 3742
 3743    cx.set_state(indoc! {"
 3744        Aaa«aa
 3745        cˇ»c«c
 3746        bb
 3747        aaaˇ»aa
 3748    "});
 3749    cx.update_editor(|e, window, cx| {
 3750        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3751    });
 3752    cx.assert_editor_state(indoc! {"
 3753        «Aaaaa
 3754        ccc
 3755        bbˇ»
 3756    "});
 3757
 3758    // Consider non continuous selection as distinct dedup operations
 3759    cx.set_state(indoc! {"
 3760        «aaaaa
 3761        bb
 3762        aaaaa
 3763        aaaaaˇ»
 3764
 3765        aaa«aaˇ»
 3766    "});
 3767    cx.update_editor(|e, window, cx| {
 3768        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3769    });
 3770    cx.assert_editor_state(indoc! {"
 3771        «aaaaa
 3772        bbˇ»
 3773
 3774        «aaaaaˇ»
 3775    "});
 3776}
 3777
 3778#[gpui::test]
 3779async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3780    init_test(cx, |_| {});
 3781
 3782    let mut cx = EditorTestContext::new(cx).await;
 3783
 3784    cx.set_state(indoc! {"
 3785        «Aaa
 3786        aAa
 3787        Aaaˇ»
 3788    "});
 3789    cx.update_editor(|e, window, cx| {
 3790        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3791    });
 3792    cx.assert_editor_state(indoc! {"
 3793        «Aaa
 3794        aAaˇ»
 3795    "});
 3796
 3797    cx.set_state(indoc! {"
 3798        «Aaa
 3799        aAa
 3800        aaAˇ»
 3801    "});
 3802    cx.update_editor(|e, window, cx| {
 3803        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3804    });
 3805    cx.assert_editor_state(indoc! {"
 3806        «Aaaˇ»
 3807    "});
 3808}
 3809
 3810#[gpui::test]
 3811async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3812    init_test(cx, |_| {});
 3813
 3814    let mut cx = EditorTestContext::new(cx).await;
 3815
 3816    // Manipulate with multiple selections on a single line
 3817    cx.set_state(indoc! {"
 3818        dd«dd
 3819        cˇ»c«c
 3820        bb
 3821        aaaˇ»aa
 3822    "});
 3823    cx.update_editor(|e, window, cx| {
 3824        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3825    });
 3826    cx.assert_editor_state(indoc! {"
 3827        «aaaaa
 3828        bb
 3829        ccc
 3830        ddddˇ»
 3831    "});
 3832
 3833    // Manipulate with multiple disjoin selections
 3834    cx.set_state(indoc! {"
 3835 3836        4
 3837        3
 3838        2
 3839        1ˇ»
 3840
 3841        dd«dd
 3842        ccc
 3843        bb
 3844        aaaˇ»aa
 3845    "});
 3846    cx.update_editor(|e, window, cx| {
 3847        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3848    });
 3849    cx.assert_editor_state(indoc! {"
 3850        «1
 3851        2
 3852        3
 3853        4
 3854        5ˇ»
 3855
 3856        «aaaaa
 3857        bb
 3858        ccc
 3859        ddddˇ»
 3860    "});
 3861
 3862    // Adding lines on each selection
 3863    cx.set_state(indoc! {"
 3864 3865        1ˇ»
 3866
 3867        bb«bb
 3868        aaaˇ»aa
 3869    "});
 3870    cx.update_editor(|e, window, cx| {
 3871        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3872    });
 3873    cx.assert_editor_state(indoc! {"
 3874        «2
 3875        1
 3876        added lineˇ»
 3877
 3878        «bbbb
 3879        aaaaa
 3880        added lineˇ»
 3881    "});
 3882
 3883    // Removing lines on each selection
 3884    cx.set_state(indoc! {"
 3885 3886        1ˇ»
 3887
 3888        bb«bb
 3889        aaaˇ»aa
 3890    "});
 3891    cx.update_editor(|e, window, cx| {
 3892        e.manipulate_lines(window, cx, |lines| {
 3893            lines.pop();
 3894        })
 3895    });
 3896    cx.assert_editor_state(indoc! {"
 3897        «2ˇ»
 3898
 3899        «bbbbˇ»
 3900    "});
 3901}
 3902
 3903#[gpui::test]
 3904async fn test_toggle_case(cx: &mut TestAppContext) {
 3905    init_test(cx, |_| {});
 3906
 3907    let mut cx = EditorTestContext::new(cx).await;
 3908
 3909    // If all lower case -> upper case
 3910    cx.set_state(indoc! {"
 3911        «hello worldˇ»
 3912    "});
 3913    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3914    cx.assert_editor_state(indoc! {"
 3915        «HELLO WORLDˇ»
 3916    "});
 3917
 3918    // If all upper case -> lower case
 3919    cx.set_state(indoc! {"
 3920        «HELLO WORLDˇ»
 3921    "});
 3922    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3923    cx.assert_editor_state(indoc! {"
 3924        «hello worldˇ»
 3925    "});
 3926
 3927    // If any upper case characters are identified -> lower case
 3928    // This matches JetBrains IDEs
 3929    cx.set_state(indoc! {"
 3930        «hEllo worldˇ»
 3931    "});
 3932    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3933    cx.assert_editor_state(indoc! {"
 3934        «hello worldˇ»
 3935    "});
 3936}
 3937
 3938#[gpui::test]
 3939async fn test_manipulate_text(cx: &mut TestAppContext) {
 3940    init_test(cx, |_| {});
 3941
 3942    let mut cx = EditorTestContext::new(cx).await;
 3943
 3944    // Test convert_to_upper_case()
 3945    cx.set_state(indoc! {"
 3946        «hello worldˇ»
 3947    "});
 3948    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3949    cx.assert_editor_state(indoc! {"
 3950        «HELLO WORLDˇ»
 3951    "});
 3952
 3953    // Test convert_to_lower_case()
 3954    cx.set_state(indoc! {"
 3955        «HELLO WORLDˇ»
 3956    "});
 3957    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3958    cx.assert_editor_state(indoc! {"
 3959        «hello worldˇ»
 3960    "});
 3961
 3962    // Test multiple line, single selection case
 3963    cx.set_state(indoc! {"
 3964        «The quick brown
 3965        fox jumps over
 3966        the lazy dogˇ»
 3967    "});
 3968    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3969    cx.assert_editor_state(indoc! {"
 3970        «The Quick Brown
 3971        Fox Jumps Over
 3972        The Lazy Dogˇ»
 3973    "});
 3974
 3975    // Test multiple line, single selection case
 3976    cx.set_state(indoc! {"
 3977        «The quick brown
 3978        fox jumps over
 3979        the lazy dogˇ»
 3980    "});
 3981    cx.update_editor(|e, window, cx| {
 3982        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3983    });
 3984    cx.assert_editor_state(indoc! {"
 3985        «TheQuickBrown
 3986        FoxJumpsOver
 3987        TheLazyDogˇ»
 3988    "});
 3989
 3990    // From here on out, test more complex cases of manipulate_text()
 3991
 3992    // Test no selection case - should affect words cursors are in
 3993    // Cursor at beginning, middle, and end of word
 3994    cx.set_state(indoc! {"
 3995        ˇhello big beauˇtiful worldˇ
 3996    "});
 3997    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3998    cx.assert_editor_state(indoc! {"
 3999        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4000    "});
 4001
 4002    // Test multiple selections on a single line and across multiple lines
 4003    cx.set_state(indoc! {"
 4004        «Theˇ» quick «brown
 4005        foxˇ» jumps «overˇ»
 4006        the «lazyˇ» dog
 4007    "});
 4008    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4009    cx.assert_editor_state(indoc! {"
 4010        «THEˇ» quick «BROWN
 4011        FOXˇ» jumps «OVERˇ»
 4012        the «LAZYˇ» dog
 4013    "});
 4014
 4015    // Test case where text length grows
 4016    cx.set_state(indoc! {"
 4017        «tschüߡ»
 4018    "});
 4019    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4020    cx.assert_editor_state(indoc! {"
 4021        «TSCHÜSSˇ»
 4022    "});
 4023
 4024    // Test to make sure we don't crash when text shrinks
 4025    cx.set_state(indoc! {"
 4026        aaa_bbbˇ
 4027    "});
 4028    cx.update_editor(|e, window, cx| {
 4029        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4030    });
 4031    cx.assert_editor_state(indoc! {"
 4032        «aaaBbbˇ»
 4033    "});
 4034
 4035    // Test to make sure we all aware of the fact that each word can grow and shrink
 4036    // Final selections should be aware of this fact
 4037    cx.set_state(indoc! {"
 4038        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4039    "});
 4040    cx.update_editor(|e, window, cx| {
 4041        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4042    });
 4043    cx.assert_editor_state(indoc! {"
 4044        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4045    "});
 4046
 4047    cx.set_state(indoc! {"
 4048        «hElLo, WoRld!ˇ»
 4049    "});
 4050    cx.update_editor(|e, window, cx| {
 4051        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4052    });
 4053    cx.assert_editor_state(indoc! {"
 4054        «HeLlO, wOrLD!ˇ»
 4055    "});
 4056}
 4057
 4058#[gpui::test]
 4059fn test_duplicate_line(cx: &mut TestAppContext) {
 4060    init_test(cx, |_| {});
 4061
 4062    let editor = cx.add_window(|window, cx| {
 4063        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4064        build_editor(buffer, window, cx)
 4065    });
 4066    _ = editor.update(cx, |editor, window, cx| {
 4067        editor.change_selections(None, window, cx, |s| {
 4068            s.select_display_ranges([
 4069                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4070                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4071                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4072                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4073            ])
 4074        });
 4075        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4076        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4077        assert_eq!(
 4078            editor.selections.display_ranges(cx),
 4079            vec![
 4080                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4081                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4082                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4083                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4084            ]
 4085        );
 4086    });
 4087
 4088    let editor = cx.add_window(|window, cx| {
 4089        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4090        build_editor(buffer, window, cx)
 4091    });
 4092    _ = editor.update(cx, |editor, window, cx| {
 4093        editor.change_selections(None, window, cx, |s| {
 4094            s.select_display_ranges([
 4095                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4096                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4097            ])
 4098        });
 4099        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4100        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4101        assert_eq!(
 4102            editor.selections.display_ranges(cx),
 4103            vec![
 4104                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4105                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4106            ]
 4107        );
 4108    });
 4109
 4110    // With `move_upwards` the selections stay in place, except for
 4111    // the lines inserted above them
 4112    let editor = cx.add_window(|window, cx| {
 4113        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4114        build_editor(buffer, window, cx)
 4115    });
 4116    _ = editor.update(cx, |editor, window, cx| {
 4117        editor.change_selections(None, window, cx, |s| {
 4118            s.select_display_ranges([
 4119                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4120                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4121                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4122                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4123            ])
 4124        });
 4125        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4126        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4127        assert_eq!(
 4128            editor.selections.display_ranges(cx),
 4129            vec![
 4130                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4131                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4132                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4133                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4134            ]
 4135        );
 4136    });
 4137
 4138    let editor = cx.add_window(|window, cx| {
 4139        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4140        build_editor(buffer, window, cx)
 4141    });
 4142    _ = editor.update(cx, |editor, window, cx| {
 4143        editor.change_selections(None, window, cx, |s| {
 4144            s.select_display_ranges([
 4145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4146                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4147            ])
 4148        });
 4149        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4150        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4151        assert_eq!(
 4152            editor.selections.display_ranges(cx),
 4153            vec![
 4154                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4155                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4156            ]
 4157        );
 4158    });
 4159
 4160    let editor = cx.add_window(|window, cx| {
 4161        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4162        build_editor(buffer, window, cx)
 4163    });
 4164    _ = editor.update(cx, |editor, window, cx| {
 4165        editor.change_selections(None, window, cx, |s| {
 4166            s.select_display_ranges([
 4167                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4168                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4169            ])
 4170        });
 4171        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4172        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4173        assert_eq!(
 4174            editor.selections.display_ranges(cx),
 4175            vec![
 4176                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4177                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4178            ]
 4179        );
 4180    });
 4181}
 4182
 4183#[gpui::test]
 4184fn test_move_line_up_down(cx: &mut TestAppContext) {
 4185    init_test(cx, |_| {});
 4186
 4187    let editor = cx.add_window(|window, cx| {
 4188        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4189        build_editor(buffer, window, cx)
 4190    });
 4191    _ = editor.update(cx, |editor, window, cx| {
 4192        editor.fold_creases(
 4193            vec![
 4194                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4195                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4196                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4197            ],
 4198            true,
 4199            window,
 4200            cx,
 4201        );
 4202        editor.change_selections(None, window, cx, |s| {
 4203            s.select_display_ranges([
 4204                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4205                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4206                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4207                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4208            ])
 4209        });
 4210        assert_eq!(
 4211            editor.display_text(cx),
 4212            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4213        );
 4214
 4215        editor.move_line_up(&MoveLineUp, window, cx);
 4216        assert_eq!(
 4217            editor.display_text(cx),
 4218            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4219        );
 4220        assert_eq!(
 4221            editor.selections.display_ranges(cx),
 4222            vec![
 4223                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4224                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4225                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4226                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4227            ]
 4228        );
 4229    });
 4230
 4231    _ = editor.update(cx, |editor, window, cx| {
 4232        editor.move_line_down(&MoveLineDown, window, cx);
 4233        assert_eq!(
 4234            editor.display_text(cx),
 4235            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4236        );
 4237        assert_eq!(
 4238            editor.selections.display_ranges(cx),
 4239            vec![
 4240                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4241                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4242                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4243                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4244            ]
 4245        );
 4246    });
 4247
 4248    _ = editor.update(cx, |editor, window, cx| {
 4249        editor.move_line_down(&MoveLineDown, window, cx);
 4250        assert_eq!(
 4251            editor.display_text(cx),
 4252            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4253        );
 4254        assert_eq!(
 4255            editor.selections.display_ranges(cx),
 4256            vec![
 4257                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4258                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4259                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4260                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4261            ]
 4262        );
 4263    });
 4264
 4265    _ = editor.update(cx, |editor, window, cx| {
 4266        editor.move_line_up(&MoveLineUp, window, cx);
 4267        assert_eq!(
 4268            editor.display_text(cx),
 4269            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4270        );
 4271        assert_eq!(
 4272            editor.selections.display_ranges(cx),
 4273            vec![
 4274                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4275                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4276                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4277                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4278            ]
 4279        );
 4280    });
 4281}
 4282
 4283#[gpui::test]
 4284fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4285    init_test(cx, |_| {});
 4286
 4287    let editor = cx.add_window(|window, cx| {
 4288        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4289        build_editor(buffer, window, cx)
 4290    });
 4291    _ = editor.update(cx, |editor, window, cx| {
 4292        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4293        editor.insert_blocks(
 4294            [BlockProperties {
 4295                style: BlockStyle::Fixed,
 4296                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4297                height: Some(1),
 4298                render: Arc::new(|_| div().into_any()),
 4299                priority: 0,
 4300            }],
 4301            Some(Autoscroll::fit()),
 4302            cx,
 4303        );
 4304        editor.change_selections(None, window, cx, |s| {
 4305            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4306        });
 4307        editor.move_line_down(&MoveLineDown, window, cx);
 4308    });
 4309}
 4310
 4311#[gpui::test]
 4312async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4313    init_test(cx, |_| {});
 4314
 4315    let mut cx = EditorTestContext::new(cx).await;
 4316    cx.set_state(
 4317        &"
 4318            ˇzero
 4319            one
 4320            two
 4321            three
 4322            four
 4323            five
 4324        "
 4325        .unindent(),
 4326    );
 4327
 4328    // Create a four-line block that replaces three lines of text.
 4329    cx.update_editor(|editor, window, cx| {
 4330        let snapshot = editor.snapshot(window, cx);
 4331        let snapshot = &snapshot.buffer_snapshot;
 4332        let placement = BlockPlacement::Replace(
 4333            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4334        );
 4335        editor.insert_blocks(
 4336            [BlockProperties {
 4337                placement,
 4338                height: Some(4),
 4339                style: BlockStyle::Sticky,
 4340                render: Arc::new(|_| gpui::div().into_any_element()),
 4341                priority: 0,
 4342            }],
 4343            None,
 4344            cx,
 4345        );
 4346    });
 4347
 4348    // Move down so that the cursor touches the block.
 4349    cx.update_editor(|editor, window, cx| {
 4350        editor.move_down(&Default::default(), window, cx);
 4351    });
 4352    cx.assert_editor_state(
 4353        &"
 4354            zero
 4355            «one
 4356            two
 4357            threeˇ»
 4358            four
 4359            five
 4360        "
 4361        .unindent(),
 4362    );
 4363
 4364    // Move down past the block.
 4365    cx.update_editor(|editor, window, cx| {
 4366        editor.move_down(&Default::default(), window, cx);
 4367    });
 4368    cx.assert_editor_state(
 4369        &"
 4370            zero
 4371            one
 4372            two
 4373            three
 4374            ˇfour
 4375            five
 4376        "
 4377        .unindent(),
 4378    );
 4379}
 4380
 4381#[gpui::test]
 4382fn test_transpose(cx: &mut TestAppContext) {
 4383    init_test(cx, |_| {});
 4384
 4385    _ = cx.add_window(|window, cx| {
 4386        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4387        editor.set_style(EditorStyle::default(), window, cx);
 4388        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4389        editor.transpose(&Default::default(), window, cx);
 4390        assert_eq!(editor.text(cx), "bac");
 4391        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4392
 4393        editor.transpose(&Default::default(), window, cx);
 4394        assert_eq!(editor.text(cx), "bca");
 4395        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4396
 4397        editor.transpose(&Default::default(), window, cx);
 4398        assert_eq!(editor.text(cx), "bac");
 4399        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4400
 4401        editor
 4402    });
 4403
 4404    _ = cx.add_window(|window, cx| {
 4405        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4406        editor.set_style(EditorStyle::default(), window, cx);
 4407        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4408        editor.transpose(&Default::default(), window, cx);
 4409        assert_eq!(editor.text(cx), "acb\nde");
 4410        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4411
 4412        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4413        editor.transpose(&Default::default(), window, cx);
 4414        assert_eq!(editor.text(cx), "acbd\ne");
 4415        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4416
 4417        editor.transpose(&Default::default(), window, cx);
 4418        assert_eq!(editor.text(cx), "acbde\n");
 4419        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4420
 4421        editor.transpose(&Default::default(), window, cx);
 4422        assert_eq!(editor.text(cx), "acbd\ne");
 4423        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4424
 4425        editor
 4426    });
 4427
 4428    _ = cx.add_window(|window, cx| {
 4429        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4430        editor.set_style(EditorStyle::default(), window, cx);
 4431        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4432        editor.transpose(&Default::default(), window, cx);
 4433        assert_eq!(editor.text(cx), "bacd\ne");
 4434        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4435
 4436        editor.transpose(&Default::default(), window, cx);
 4437        assert_eq!(editor.text(cx), "bcade\n");
 4438        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4439
 4440        editor.transpose(&Default::default(), window, cx);
 4441        assert_eq!(editor.text(cx), "bcda\ne");
 4442        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4443
 4444        editor.transpose(&Default::default(), window, cx);
 4445        assert_eq!(editor.text(cx), "bcade\n");
 4446        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4447
 4448        editor.transpose(&Default::default(), window, cx);
 4449        assert_eq!(editor.text(cx), "bcaed\n");
 4450        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4451
 4452        editor
 4453    });
 4454
 4455    _ = cx.add_window(|window, cx| {
 4456        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4457        editor.set_style(EditorStyle::default(), window, cx);
 4458        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4459        editor.transpose(&Default::default(), window, cx);
 4460        assert_eq!(editor.text(cx), "🏀🍐✋");
 4461        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4462
 4463        editor.transpose(&Default::default(), window, cx);
 4464        assert_eq!(editor.text(cx), "🏀✋🍐");
 4465        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4466
 4467        editor.transpose(&Default::default(), window, cx);
 4468        assert_eq!(editor.text(cx), "🏀🍐✋");
 4469        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4470
 4471        editor
 4472    });
 4473}
 4474
 4475#[gpui::test]
 4476async fn test_rewrap(cx: &mut TestAppContext) {
 4477    init_test(cx, |settings| {
 4478        settings.languages.extend([
 4479            (
 4480                "Markdown".into(),
 4481                LanguageSettingsContent {
 4482                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4483                    ..Default::default()
 4484                },
 4485            ),
 4486            (
 4487                "Plain Text".into(),
 4488                LanguageSettingsContent {
 4489                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4490                    ..Default::default()
 4491                },
 4492            ),
 4493        ])
 4494    });
 4495
 4496    let mut cx = EditorTestContext::new(cx).await;
 4497
 4498    let language_with_c_comments = Arc::new(Language::new(
 4499        LanguageConfig {
 4500            line_comments: vec!["// ".into()],
 4501            ..LanguageConfig::default()
 4502        },
 4503        None,
 4504    ));
 4505    let language_with_pound_comments = Arc::new(Language::new(
 4506        LanguageConfig {
 4507            line_comments: vec!["# ".into()],
 4508            ..LanguageConfig::default()
 4509        },
 4510        None,
 4511    ));
 4512    let markdown_language = Arc::new(Language::new(
 4513        LanguageConfig {
 4514            name: "Markdown".into(),
 4515            ..LanguageConfig::default()
 4516        },
 4517        None,
 4518    ));
 4519    let language_with_doc_comments = Arc::new(Language::new(
 4520        LanguageConfig {
 4521            line_comments: vec!["// ".into(), "/// ".into()],
 4522            ..LanguageConfig::default()
 4523        },
 4524        Some(tree_sitter_rust::LANGUAGE.into()),
 4525    ));
 4526
 4527    let plaintext_language = Arc::new(Language::new(
 4528        LanguageConfig {
 4529            name: "Plain Text".into(),
 4530            ..LanguageConfig::default()
 4531        },
 4532        None,
 4533    ));
 4534
 4535    assert_rewrap(
 4536        indoc! {"
 4537            // ˇ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.
 4538        "},
 4539        indoc! {"
 4540            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4541            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4542            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4543            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4544            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4545            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4546            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4547            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4548            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4549            // porttitor id. Aliquam id accumsan eros.
 4550        "},
 4551        language_with_c_comments.clone(),
 4552        &mut cx,
 4553    );
 4554
 4555    // Test that rewrapping works inside of a selection
 4556    assert_rewrap(
 4557        indoc! {"
 4558            «// 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.ˇ»
 4559        "},
 4560        indoc! {"
 4561            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4562            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4563            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4564            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4565            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4566            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4567            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4568            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4569            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4570            // porttitor id. Aliquam id accumsan eros.ˇ»
 4571        "},
 4572        language_with_c_comments.clone(),
 4573        &mut cx,
 4574    );
 4575
 4576    // Test that cursors that expand to the same region are collapsed.
 4577    assert_rewrap(
 4578        indoc! {"
 4579            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4580            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4581            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4582            // ˇ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.
 4583        "},
 4584        indoc! {"
 4585            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4586            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4587            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4588            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4589            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4590            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4591            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4592            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4593            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4594            // porttitor id. Aliquam id accumsan eros.
 4595        "},
 4596        language_with_c_comments.clone(),
 4597        &mut cx,
 4598    );
 4599
 4600    // Test that non-contiguous selections are treated separately.
 4601    assert_rewrap(
 4602        indoc! {"
 4603            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4604            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4605            //
 4606            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4607            // ˇ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.
 4608        "},
 4609        indoc! {"
 4610            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4611            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4612            // auctor, eu lacinia sapien scelerisque.
 4613            //
 4614            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4615            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4616            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4617            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4618            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4619            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4620            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4621        "},
 4622        language_with_c_comments.clone(),
 4623        &mut cx,
 4624    );
 4625
 4626    // Test that different comment prefixes are supported.
 4627    assert_rewrap(
 4628        indoc! {"
 4629            # ˇ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.
 4630        "},
 4631        indoc! {"
 4632            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4633            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4634            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4635            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4636            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4637            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4638            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4639            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4640            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4641            # accumsan eros.
 4642        "},
 4643        language_with_pound_comments.clone(),
 4644        &mut cx,
 4645    );
 4646
 4647    // Test that rewrapping is ignored outside of comments in most languages.
 4648    assert_rewrap(
 4649        indoc! {"
 4650            /// Adds two numbers.
 4651            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4652            fn add(a: u32, b: u32) -> u32 {
 4653                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ˇ
 4654            }
 4655        "},
 4656        indoc! {"
 4657            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4658            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4659            fn add(a: u32, b: u32) -> u32 {
 4660                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ˇ
 4661            }
 4662        "},
 4663        language_with_doc_comments.clone(),
 4664        &mut cx,
 4665    );
 4666
 4667    // Test that rewrapping works in Markdown and Plain Text languages.
 4668    assert_rewrap(
 4669        indoc! {"
 4670            # Hello
 4671
 4672            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.
 4673        "},
 4674        indoc! {"
 4675            # Hello
 4676
 4677            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4678            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4679            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4680            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4681            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4682            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4683            Integer sit amet scelerisque nisi.
 4684        "},
 4685        markdown_language,
 4686        &mut cx,
 4687    );
 4688
 4689    assert_rewrap(
 4690        indoc! {"
 4691            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.
 4692        "},
 4693        indoc! {"
 4694            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4695            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4696            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4697            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4698            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4699            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4700            Integer sit amet scelerisque nisi.
 4701        "},
 4702        plaintext_language,
 4703        &mut cx,
 4704    );
 4705
 4706    // Test rewrapping unaligned comments in a selection.
 4707    assert_rewrap(
 4708        indoc! {"
 4709            fn foo() {
 4710                if true {
 4711            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4712            // Praesent semper egestas tellus id dignissim.ˇ»
 4713                    do_something();
 4714                } else {
 4715                    //
 4716                }
 4717            }
 4718        "},
 4719        indoc! {"
 4720            fn foo() {
 4721                if true {
 4722            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4723                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4724                    // egestas tellus id dignissim.ˇ»
 4725                    do_something();
 4726                } else {
 4727                    //
 4728                }
 4729            }
 4730        "},
 4731        language_with_doc_comments.clone(),
 4732        &mut cx,
 4733    );
 4734
 4735    assert_rewrap(
 4736        indoc! {"
 4737            fn foo() {
 4738                if true {
 4739            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4740            // Praesent semper egestas tellus id dignissim.»
 4741                    do_something();
 4742                } else {
 4743                    //
 4744                }
 4745
 4746            }
 4747        "},
 4748        indoc! {"
 4749            fn foo() {
 4750                if true {
 4751            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4752                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4753                    // egestas tellus id dignissim.»
 4754                    do_something();
 4755                } else {
 4756                    //
 4757                }
 4758
 4759            }
 4760        "},
 4761        language_with_doc_comments.clone(),
 4762        &mut cx,
 4763    );
 4764
 4765    #[track_caller]
 4766    fn assert_rewrap(
 4767        unwrapped_text: &str,
 4768        wrapped_text: &str,
 4769        language: Arc<Language>,
 4770        cx: &mut EditorTestContext,
 4771    ) {
 4772        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4773        cx.set_state(unwrapped_text);
 4774        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4775        cx.assert_editor_state(wrapped_text);
 4776    }
 4777}
 4778
 4779#[gpui::test]
 4780async fn test_hard_wrap(cx: &mut TestAppContext) {
 4781    init_test(cx, |_| {});
 4782    let mut cx = EditorTestContext::new(cx).await;
 4783
 4784    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4785    cx.update_editor(|editor, _, cx| {
 4786        editor.set_hard_wrap(Some(14), cx);
 4787    });
 4788
 4789    cx.set_state(indoc!(
 4790        "
 4791        one two three ˇ
 4792        "
 4793    ));
 4794    cx.simulate_input("four");
 4795    cx.run_until_parked();
 4796
 4797    cx.assert_editor_state(indoc!(
 4798        "
 4799        one two three
 4800        fourˇ
 4801        "
 4802    ));
 4803
 4804    cx.update_editor(|editor, window, cx| {
 4805        editor.newline(&Default::default(), window, cx);
 4806    });
 4807    cx.run_until_parked();
 4808    cx.assert_editor_state(indoc!(
 4809        "
 4810        one two three
 4811        four
 4812        ˇ
 4813        "
 4814    ));
 4815
 4816    cx.simulate_input("five");
 4817    cx.run_until_parked();
 4818    cx.assert_editor_state(indoc!(
 4819        "
 4820        one two three
 4821        four
 4822        fiveˇ
 4823        "
 4824    ));
 4825
 4826    cx.update_editor(|editor, window, cx| {
 4827        editor.newline(&Default::default(), window, cx);
 4828    });
 4829    cx.run_until_parked();
 4830    cx.simulate_input("# ");
 4831    cx.run_until_parked();
 4832    cx.assert_editor_state(indoc!(
 4833        "
 4834        one two three
 4835        four
 4836        five
 4837        # ˇ
 4838        "
 4839    ));
 4840
 4841    cx.update_editor(|editor, window, cx| {
 4842        editor.newline(&Default::default(), window, cx);
 4843    });
 4844    cx.run_until_parked();
 4845    cx.assert_editor_state(indoc!(
 4846        "
 4847        one two three
 4848        four
 4849        five
 4850        #\x20
 4851 4852        "
 4853    ));
 4854
 4855    cx.simulate_input(" 6");
 4856    cx.run_until_parked();
 4857    cx.assert_editor_state(indoc!(
 4858        "
 4859        one two three
 4860        four
 4861        five
 4862        #
 4863        # 6ˇ
 4864        "
 4865    ));
 4866}
 4867
 4868#[gpui::test]
 4869async fn test_clipboard(cx: &mut TestAppContext) {
 4870    init_test(cx, |_| {});
 4871
 4872    let mut cx = EditorTestContext::new(cx).await;
 4873
 4874    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4875    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4876    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4877
 4878    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4879    cx.set_state("two ˇfour ˇsix ˇ");
 4880    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4881    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4882
 4883    // Paste again but with only two cursors. Since the number of cursors doesn't
 4884    // match the number of slices in the clipboard, the entire clipboard text
 4885    // is pasted at each cursor.
 4886    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4887    cx.update_editor(|e, window, cx| {
 4888        e.handle_input("( ", window, cx);
 4889        e.paste(&Paste, window, cx);
 4890        e.handle_input(") ", window, cx);
 4891    });
 4892    cx.assert_editor_state(
 4893        &([
 4894            "( one✅ ",
 4895            "three ",
 4896            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4897            "three ",
 4898            "five ) ˇ",
 4899        ]
 4900        .join("\n")),
 4901    );
 4902
 4903    // Cut with three selections, one of which is full-line.
 4904    cx.set_state(indoc! {"
 4905        1«2ˇ»3
 4906        4ˇ567
 4907        «8ˇ»9"});
 4908    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4909    cx.assert_editor_state(indoc! {"
 4910        1ˇ3
 4911        ˇ9"});
 4912
 4913    // Paste with three selections, noticing how the copied selection that was full-line
 4914    // gets inserted before the second cursor.
 4915    cx.set_state(indoc! {"
 4916        1ˇ3
 4917 4918        «oˇ»ne"});
 4919    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4920    cx.assert_editor_state(indoc! {"
 4921        12ˇ3
 4922        4567
 4923 4924        8ˇne"});
 4925
 4926    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4927    cx.set_state(indoc! {"
 4928        The quick brown
 4929        fox juˇmps over
 4930        the lazy dog"});
 4931    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4932    assert_eq!(
 4933        cx.read_from_clipboard()
 4934            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4935        Some("fox jumps over\n".to_string())
 4936    );
 4937
 4938    // Paste with three selections, noticing how the copied full-line selection is inserted
 4939    // before the empty selections but replaces the selection that is non-empty.
 4940    cx.set_state(indoc! {"
 4941        Tˇhe quick brown
 4942        «foˇ»x jumps over
 4943        tˇhe lazy dog"});
 4944    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4945    cx.assert_editor_state(indoc! {"
 4946        fox jumps over
 4947        Tˇhe quick brown
 4948        fox jumps over
 4949        ˇx jumps over
 4950        fox jumps over
 4951        tˇhe lazy dog"});
 4952}
 4953
 4954#[gpui::test]
 4955async fn test_copy_trim(cx: &mut TestAppContext) {
 4956    init_test(cx, |_| {});
 4957
 4958    let mut cx = EditorTestContext::new(cx).await;
 4959    cx.set_state(
 4960        r#"            «for selection in selections.iter() {
 4961            let mut start = selection.start;
 4962            let mut end = selection.end;
 4963            let is_entire_line = selection.is_empty();
 4964            if is_entire_line {
 4965                start = Point::new(start.row, 0);ˇ»
 4966                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4967            }
 4968        "#,
 4969    );
 4970    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4971    assert_eq!(
 4972        cx.read_from_clipboard()
 4973            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4974        Some(
 4975            "for selection in selections.iter() {
 4976            let mut start = selection.start;
 4977            let mut end = selection.end;
 4978            let is_entire_line = selection.is_empty();
 4979            if is_entire_line {
 4980                start = Point::new(start.row, 0);"
 4981                .to_string()
 4982        ),
 4983        "Regular copying preserves all indentation selected",
 4984    );
 4985    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4986    assert_eq!(
 4987        cx.read_from_clipboard()
 4988            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4989        Some(
 4990            "for selection in selections.iter() {
 4991let mut start = selection.start;
 4992let mut end = selection.end;
 4993let is_entire_line = selection.is_empty();
 4994if is_entire_line {
 4995    start = Point::new(start.row, 0);"
 4996                .to_string()
 4997        ),
 4998        "Copying with stripping should strip all leading whitespaces"
 4999    );
 5000
 5001    cx.set_state(
 5002        r#"       «     for selection in selections.iter() {
 5003            let mut start = selection.start;
 5004            let mut end = selection.end;
 5005            let is_entire_line = selection.is_empty();
 5006            if is_entire_line {
 5007                start = Point::new(start.row, 0);ˇ»
 5008                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5009            }
 5010        "#,
 5011    );
 5012    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5013    assert_eq!(
 5014        cx.read_from_clipboard()
 5015            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5016        Some(
 5017            "     for selection in selections.iter() {
 5018            let mut start = selection.start;
 5019            let mut end = selection.end;
 5020            let is_entire_line = selection.is_empty();
 5021            if is_entire_line {
 5022                start = Point::new(start.row, 0);"
 5023                .to_string()
 5024        ),
 5025        "Regular copying preserves all indentation selected",
 5026    );
 5027    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5028    assert_eq!(
 5029        cx.read_from_clipboard()
 5030            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5031        Some(
 5032            "for selection in selections.iter() {
 5033let mut start = selection.start;
 5034let mut end = selection.end;
 5035let is_entire_line = selection.is_empty();
 5036if is_entire_line {
 5037    start = Point::new(start.row, 0);"
 5038                .to_string()
 5039        ),
 5040        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5041    );
 5042
 5043    cx.set_state(
 5044        r#"       «ˇ     for selection in selections.iter() {
 5045            let mut start = selection.start;
 5046            let mut end = selection.end;
 5047            let is_entire_line = selection.is_empty();
 5048            if is_entire_line {
 5049                start = Point::new(start.row, 0);»
 5050                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5051            }
 5052        "#,
 5053    );
 5054    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5055    assert_eq!(
 5056        cx.read_from_clipboard()
 5057            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5058        Some(
 5059            "     for selection in selections.iter() {
 5060            let mut start = selection.start;
 5061            let mut end = selection.end;
 5062            let is_entire_line = selection.is_empty();
 5063            if is_entire_line {
 5064                start = Point::new(start.row, 0);"
 5065                .to_string()
 5066        ),
 5067        "Regular copying for reverse selection works the same",
 5068    );
 5069    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5070    assert_eq!(
 5071        cx.read_from_clipboard()
 5072            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5073        Some(
 5074            "for selection in selections.iter() {
 5075let mut start = selection.start;
 5076let mut end = selection.end;
 5077let is_entire_line = selection.is_empty();
 5078if is_entire_line {
 5079    start = Point::new(start.row, 0);"
 5080                .to_string()
 5081        ),
 5082        "Copying with stripping for reverse selection works the same"
 5083    );
 5084
 5085    cx.set_state(
 5086        r#"            for selection «in selections.iter() {
 5087            let mut start = selection.start;
 5088            let mut end = selection.end;
 5089            let is_entire_line = selection.is_empty();
 5090            if is_entire_line {
 5091                start = Point::new(start.row, 0);ˇ»
 5092                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5093            }
 5094        "#,
 5095    );
 5096    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5097    assert_eq!(
 5098        cx.read_from_clipboard()
 5099            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5100        Some(
 5101            "in selections.iter() {
 5102            let mut start = selection.start;
 5103            let mut end = selection.end;
 5104            let is_entire_line = selection.is_empty();
 5105            if is_entire_line {
 5106                start = Point::new(start.row, 0);"
 5107                .to_string()
 5108        ),
 5109        "When selecting past the indent, the copying works as usual",
 5110    );
 5111    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5112    assert_eq!(
 5113        cx.read_from_clipboard()
 5114            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5115        Some(
 5116            "in selections.iter() {
 5117            let mut start = selection.start;
 5118            let mut end = selection.end;
 5119            let is_entire_line = selection.is_empty();
 5120            if is_entire_line {
 5121                start = Point::new(start.row, 0);"
 5122                .to_string()
 5123        ),
 5124        "When selecting past the indent, nothing is trimmed"
 5125    );
 5126}
 5127
 5128#[gpui::test]
 5129async fn test_paste_multiline(cx: &mut TestAppContext) {
 5130    init_test(cx, |_| {});
 5131
 5132    let mut cx = EditorTestContext::new(cx).await;
 5133    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5134
 5135    // Cut an indented block, without the leading whitespace.
 5136    cx.set_state(indoc! {"
 5137        const a: B = (
 5138            c(),
 5139            «d(
 5140                e,
 5141                f
 5142            )ˇ»
 5143        );
 5144    "});
 5145    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5146    cx.assert_editor_state(indoc! {"
 5147        const a: B = (
 5148            c(),
 5149            ˇ
 5150        );
 5151    "});
 5152
 5153    // Paste it at the same position.
 5154    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5155    cx.assert_editor_state(indoc! {"
 5156        const a: B = (
 5157            c(),
 5158            d(
 5159                e,
 5160                f
 5161 5162        );
 5163    "});
 5164
 5165    // Paste it at a line with a lower indent level.
 5166    cx.set_state(indoc! {"
 5167        ˇ
 5168        const a: B = (
 5169            c(),
 5170        );
 5171    "});
 5172    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5173    cx.assert_editor_state(indoc! {"
 5174        d(
 5175            e,
 5176            f
 5177 5178        const a: B = (
 5179            c(),
 5180        );
 5181    "});
 5182
 5183    // Cut an indented block, with the leading whitespace.
 5184    cx.set_state(indoc! {"
 5185        const a: B = (
 5186            c(),
 5187        «    d(
 5188                e,
 5189                f
 5190            )
 5191        ˇ»);
 5192    "});
 5193    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5194    cx.assert_editor_state(indoc! {"
 5195        const a: B = (
 5196            c(),
 5197        ˇ);
 5198    "});
 5199
 5200    // Paste it at the same position.
 5201    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5202    cx.assert_editor_state(indoc! {"
 5203        const a: B = (
 5204            c(),
 5205            d(
 5206                e,
 5207                f
 5208            )
 5209        ˇ);
 5210    "});
 5211
 5212    // Paste it at a line with a higher indent level.
 5213    cx.set_state(indoc! {"
 5214        const a: B = (
 5215            c(),
 5216            d(
 5217                e,
 5218 5219            )
 5220        );
 5221    "});
 5222    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5223    cx.assert_editor_state(indoc! {"
 5224        const a: B = (
 5225            c(),
 5226            d(
 5227                e,
 5228                f    d(
 5229                    e,
 5230                    f
 5231                )
 5232        ˇ
 5233            )
 5234        );
 5235    "});
 5236
 5237    // Copy an indented block, starting mid-line
 5238    cx.set_state(indoc! {"
 5239        const a: B = (
 5240            c(),
 5241            somethin«g(
 5242                e,
 5243                f
 5244            )ˇ»
 5245        );
 5246    "});
 5247    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5248
 5249    // Paste it on a line with a lower indent level
 5250    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5251    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5252    cx.assert_editor_state(indoc! {"
 5253        const a: B = (
 5254            c(),
 5255            something(
 5256                e,
 5257                f
 5258            )
 5259        );
 5260        g(
 5261            e,
 5262            f
 5263"});
 5264}
 5265
 5266#[gpui::test]
 5267async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5268    init_test(cx, |_| {});
 5269
 5270    cx.write_to_clipboard(ClipboardItem::new_string(
 5271        "    d(\n        e\n    );\n".into(),
 5272    ));
 5273
 5274    let mut cx = EditorTestContext::new(cx).await;
 5275    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5276
 5277    cx.set_state(indoc! {"
 5278        fn a() {
 5279            b();
 5280            if c() {
 5281                ˇ
 5282            }
 5283        }
 5284    "});
 5285
 5286    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5287    cx.assert_editor_state(indoc! {"
 5288        fn a() {
 5289            b();
 5290            if c() {
 5291                d(
 5292                    e
 5293                );
 5294        ˇ
 5295            }
 5296        }
 5297    "});
 5298
 5299    cx.set_state(indoc! {"
 5300        fn a() {
 5301            b();
 5302            ˇ
 5303        }
 5304    "});
 5305
 5306    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5307    cx.assert_editor_state(indoc! {"
 5308        fn a() {
 5309            b();
 5310            d(
 5311                e
 5312            );
 5313        ˇ
 5314        }
 5315    "});
 5316}
 5317
 5318#[gpui::test]
 5319fn test_select_all(cx: &mut TestAppContext) {
 5320    init_test(cx, |_| {});
 5321
 5322    let editor = cx.add_window(|window, cx| {
 5323        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5324        build_editor(buffer, window, cx)
 5325    });
 5326    _ = editor.update(cx, |editor, window, cx| {
 5327        editor.select_all(&SelectAll, window, cx);
 5328        assert_eq!(
 5329            editor.selections.display_ranges(cx),
 5330            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5331        );
 5332    });
 5333}
 5334
 5335#[gpui::test]
 5336fn test_select_line(cx: &mut TestAppContext) {
 5337    init_test(cx, |_| {});
 5338
 5339    let editor = cx.add_window(|window, cx| {
 5340        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5341        build_editor(buffer, window, cx)
 5342    });
 5343    _ = editor.update(cx, |editor, window, cx| {
 5344        editor.change_selections(None, window, cx, |s| {
 5345            s.select_display_ranges([
 5346                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5347                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5348                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5349                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5350            ])
 5351        });
 5352        editor.select_line(&SelectLine, window, cx);
 5353        assert_eq!(
 5354            editor.selections.display_ranges(cx),
 5355            vec![
 5356                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5357                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5358            ]
 5359        );
 5360    });
 5361
 5362    _ = editor.update(cx, |editor, window, cx| {
 5363        editor.select_line(&SelectLine, window, cx);
 5364        assert_eq!(
 5365            editor.selections.display_ranges(cx),
 5366            vec![
 5367                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5368                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5369            ]
 5370        );
 5371    });
 5372
 5373    _ = editor.update(cx, |editor, window, cx| {
 5374        editor.select_line(&SelectLine, window, cx);
 5375        assert_eq!(
 5376            editor.selections.display_ranges(cx),
 5377            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5378        );
 5379    });
 5380}
 5381
 5382#[gpui::test]
 5383async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5384    init_test(cx, |_| {});
 5385    let mut cx = EditorTestContext::new(cx).await;
 5386
 5387    #[track_caller]
 5388    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5389        cx.set_state(initial_state);
 5390        cx.update_editor(|e, window, cx| {
 5391            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5392        });
 5393        cx.assert_editor_state(expected_state);
 5394    }
 5395
 5396    // Selection starts and ends at the middle of lines, left-to-right
 5397    test(
 5398        &mut cx,
 5399        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5400        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5401    );
 5402    // Same thing, right-to-left
 5403    test(
 5404        &mut cx,
 5405        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5406        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5407    );
 5408
 5409    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5410    test(
 5411        &mut cx,
 5412        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5413        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5414    );
 5415    // Same thing, right-to-left
 5416    test(
 5417        &mut cx,
 5418        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5419        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5420    );
 5421
 5422    // Whole buffer, left-to-right, last line ends with newline
 5423    test(
 5424        &mut cx,
 5425        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5426        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5427    );
 5428    // Same thing, right-to-left
 5429    test(
 5430        &mut cx,
 5431        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5432        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5433    );
 5434
 5435    // Starts at the end of a line, ends at the start of another
 5436    test(
 5437        &mut cx,
 5438        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5439        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5440    );
 5441}
 5442
 5443#[gpui::test]
 5444async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5445    init_test(cx, |_| {});
 5446
 5447    let editor = cx.add_window(|window, cx| {
 5448        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5449        build_editor(buffer, window, cx)
 5450    });
 5451
 5452    // setup
 5453    _ = editor.update(cx, |editor, window, cx| {
 5454        editor.fold_creases(
 5455            vec![
 5456                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5457                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5458                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5459            ],
 5460            true,
 5461            window,
 5462            cx,
 5463        );
 5464        assert_eq!(
 5465            editor.display_text(cx),
 5466            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5467        );
 5468    });
 5469
 5470    _ = editor.update(cx, |editor, window, cx| {
 5471        editor.change_selections(None, window, cx, |s| {
 5472            s.select_display_ranges([
 5473                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5474                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5475                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5476                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5477            ])
 5478        });
 5479        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5480        assert_eq!(
 5481            editor.display_text(cx),
 5482            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5483        );
 5484    });
 5485    EditorTestContext::for_editor(editor, cx)
 5486        .await
 5487        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5488
 5489    _ = editor.update(cx, |editor, window, cx| {
 5490        editor.change_selections(None, window, cx, |s| {
 5491            s.select_display_ranges([
 5492                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5493            ])
 5494        });
 5495        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5496        assert_eq!(
 5497            editor.display_text(cx),
 5498            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5499        );
 5500        assert_eq!(
 5501            editor.selections.display_ranges(cx),
 5502            [
 5503                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5504                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5505                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5506                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5507                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5508                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5509                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5510            ]
 5511        );
 5512    });
 5513    EditorTestContext::for_editor(editor, cx)
 5514        .await
 5515        .assert_editor_state(
 5516            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5517        );
 5518}
 5519
 5520#[gpui::test]
 5521async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5522    init_test(cx, |_| {});
 5523
 5524    let mut cx = EditorTestContext::new(cx).await;
 5525
 5526    cx.set_state(indoc!(
 5527        r#"abc
 5528           defˇghi
 5529
 5530           jk
 5531           nlmo
 5532           "#
 5533    ));
 5534
 5535    cx.update_editor(|editor, window, cx| {
 5536        editor.add_selection_above(&Default::default(), window, cx);
 5537    });
 5538
 5539    cx.assert_editor_state(indoc!(
 5540        r#"abcˇ
 5541           defˇghi
 5542
 5543           jk
 5544           nlmo
 5545           "#
 5546    ));
 5547
 5548    cx.update_editor(|editor, window, cx| {
 5549        editor.add_selection_above(&Default::default(), window, cx);
 5550    });
 5551
 5552    cx.assert_editor_state(indoc!(
 5553        r#"abcˇ
 5554            defˇghi
 5555
 5556            jk
 5557            nlmo
 5558            "#
 5559    ));
 5560
 5561    cx.update_editor(|editor, window, cx| {
 5562        editor.add_selection_below(&Default::default(), window, cx);
 5563    });
 5564
 5565    cx.assert_editor_state(indoc!(
 5566        r#"abc
 5567           defˇghi
 5568
 5569           jk
 5570           nlmo
 5571           "#
 5572    ));
 5573
 5574    cx.update_editor(|editor, window, cx| {
 5575        editor.undo_selection(&Default::default(), window, cx);
 5576    });
 5577
 5578    cx.assert_editor_state(indoc!(
 5579        r#"abcˇ
 5580           defˇghi
 5581
 5582           jk
 5583           nlmo
 5584           "#
 5585    ));
 5586
 5587    cx.update_editor(|editor, window, cx| {
 5588        editor.redo_selection(&Default::default(), window, cx);
 5589    });
 5590
 5591    cx.assert_editor_state(indoc!(
 5592        r#"abc
 5593           defˇghi
 5594
 5595           jk
 5596           nlmo
 5597           "#
 5598    ));
 5599
 5600    cx.update_editor(|editor, window, cx| {
 5601        editor.add_selection_below(&Default::default(), window, cx);
 5602    });
 5603
 5604    cx.assert_editor_state(indoc!(
 5605        r#"abc
 5606           defˇghi
 5607
 5608           jk
 5609           nlmˇo
 5610           "#
 5611    ));
 5612
 5613    cx.update_editor(|editor, window, cx| {
 5614        editor.add_selection_below(&Default::default(), window, cx);
 5615    });
 5616
 5617    cx.assert_editor_state(indoc!(
 5618        r#"abc
 5619           defˇghi
 5620
 5621           jk
 5622           nlmˇo
 5623           "#
 5624    ));
 5625
 5626    // change selections
 5627    cx.set_state(indoc!(
 5628        r#"abc
 5629           def«ˇg»hi
 5630
 5631           jk
 5632           nlmo
 5633           "#
 5634    ));
 5635
 5636    cx.update_editor(|editor, window, cx| {
 5637        editor.add_selection_below(&Default::default(), window, cx);
 5638    });
 5639
 5640    cx.assert_editor_state(indoc!(
 5641        r#"abc
 5642           def«ˇg»hi
 5643
 5644           jk
 5645           nlm«ˇo»
 5646           "#
 5647    ));
 5648
 5649    cx.update_editor(|editor, window, cx| {
 5650        editor.add_selection_below(&Default::default(), window, cx);
 5651    });
 5652
 5653    cx.assert_editor_state(indoc!(
 5654        r#"abc
 5655           def«ˇg»hi
 5656
 5657           jk
 5658           nlm«ˇo»
 5659           "#
 5660    ));
 5661
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_above(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"abc
 5668           def«ˇg»hi
 5669
 5670           jk
 5671           nlmo
 5672           "#
 5673    ));
 5674
 5675    cx.update_editor(|editor, window, cx| {
 5676        editor.add_selection_above(&Default::default(), window, cx);
 5677    });
 5678
 5679    cx.assert_editor_state(indoc!(
 5680        r#"abc
 5681           def«ˇg»hi
 5682
 5683           jk
 5684           nlmo
 5685           "#
 5686    ));
 5687
 5688    // Change selections again
 5689    cx.set_state(indoc!(
 5690        r#"a«bc
 5691           defgˇ»hi
 5692
 5693           jk
 5694           nlmo
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"a«bcˇ»
 5704           d«efgˇ»hi
 5705
 5706           j«kˇ»
 5707           nlmo
 5708           "#
 5709    ));
 5710
 5711    cx.update_editor(|editor, window, cx| {
 5712        editor.add_selection_below(&Default::default(), window, cx);
 5713    });
 5714    cx.assert_editor_state(indoc!(
 5715        r#"a«bcˇ»
 5716           d«efgˇ»hi
 5717
 5718           j«kˇ»
 5719           n«lmoˇ»
 5720           "#
 5721    ));
 5722    cx.update_editor(|editor, window, cx| {
 5723        editor.add_selection_above(&Default::default(), window, cx);
 5724    });
 5725
 5726    cx.assert_editor_state(indoc!(
 5727        r#"a«bcˇ»
 5728           d«efgˇ»hi
 5729
 5730           j«kˇ»
 5731           nlmo
 5732           "#
 5733    ));
 5734
 5735    // Change selections again
 5736    cx.set_state(indoc!(
 5737        r#"abc
 5738           d«ˇefghi
 5739
 5740           jk
 5741           nlm»o
 5742           "#
 5743    ));
 5744
 5745    cx.update_editor(|editor, window, cx| {
 5746        editor.add_selection_above(&Default::default(), window, cx);
 5747    });
 5748
 5749    cx.assert_editor_state(indoc!(
 5750        r#"a«ˇbc»
 5751           d«ˇef»ghi
 5752
 5753           j«ˇk»
 5754           n«ˇlm»o
 5755           "#
 5756    ));
 5757
 5758    cx.update_editor(|editor, window, cx| {
 5759        editor.add_selection_below(&Default::default(), window, cx);
 5760    });
 5761
 5762    cx.assert_editor_state(indoc!(
 5763        r#"abc
 5764           d«ˇef»ghi
 5765
 5766           j«ˇk»
 5767           n«ˇlm»o
 5768           "#
 5769    ));
 5770}
 5771
 5772#[gpui::test]
 5773async fn test_select_next(cx: &mut TestAppContext) {
 5774    init_test(cx, |_| {});
 5775
 5776    let mut cx = EditorTestContext::new(cx).await;
 5777    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5778
 5779    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5780        .unwrap();
 5781    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5782
 5783    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5784        .unwrap();
 5785    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5786
 5787    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5788    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5789
 5790    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5791    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5792
 5793    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5794        .unwrap();
 5795    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5796
 5797    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5798        .unwrap();
 5799    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5800}
 5801
 5802#[gpui::test]
 5803async fn test_select_all_matches(cx: &mut TestAppContext) {
 5804    init_test(cx, |_| {});
 5805
 5806    let mut cx = EditorTestContext::new(cx).await;
 5807
 5808    // Test caret-only selections
 5809    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5810    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5811        .unwrap();
 5812    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5813
 5814    // Test left-to-right selections
 5815    cx.set_state("abc\n«abcˇ»\nabc");
 5816    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5817        .unwrap();
 5818    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5819
 5820    // Test right-to-left selections
 5821    cx.set_state("abc\n«ˇabc»\nabc");
 5822    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5823        .unwrap();
 5824    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5825
 5826    // Test selecting whitespace with caret selection
 5827    cx.set_state("abc\nˇ   abc\nabc");
 5828    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5829        .unwrap();
 5830    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5831
 5832    // Test selecting whitespace with left-to-right selection
 5833    cx.set_state("abc\n«ˇ  »abc\nabc");
 5834    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5835        .unwrap();
 5836    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5837
 5838    // Test no matches with right-to-left selection
 5839    cx.set_state("abc\n«  ˇ»abc\nabc");
 5840    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5841        .unwrap();
 5842    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5843}
 5844
 5845#[gpui::test]
 5846async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5847    init_test(cx, |_| {});
 5848
 5849    let mut cx = EditorTestContext::new(cx).await;
 5850    cx.set_state(
 5851        r#"let foo = 2;
 5852lˇet foo = 2;
 5853let fooˇ = 2;
 5854let foo = 2;
 5855let foo = ˇ2;"#,
 5856    );
 5857
 5858    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5859        .unwrap();
 5860    cx.assert_editor_state(
 5861        r#"let foo = 2;
 5862«letˇ» foo = 2;
 5863let «fooˇ» = 2;
 5864let foo = 2;
 5865let foo = «2ˇ»;"#,
 5866    );
 5867
 5868    // noop for multiple selections with different contents
 5869    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5870        .unwrap();
 5871    cx.assert_editor_state(
 5872        r#"let foo = 2;
 5873«letˇ» foo = 2;
 5874let «fooˇ» = 2;
 5875let foo = 2;
 5876let foo = «2ˇ»;"#,
 5877    );
 5878}
 5879
 5880#[gpui::test]
 5881async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5882    init_test(cx, |_| {});
 5883
 5884    let mut cx =
 5885        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5886
 5887    cx.assert_editor_state(indoc! {"
 5888        ˇbbb
 5889        ccc
 5890
 5891        bbb
 5892        ccc
 5893        "});
 5894    cx.dispatch_action(SelectPrevious::default());
 5895    cx.assert_editor_state(indoc! {"
 5896                «bbbˇ»
 5897                ccc
 5898
 5899                bbb
 5900                ccc
 5901                "});
 5902    cx.dispatch_action(SelectPrevious::default());
 5903    cx.assert_editor_state(indoc! {"
 5904                «bbbˇ»
 5905                ccc
 5906
 5907                «bbbˇ»
 5908                ccc
 5909                "});
 5910}
 5911
 5912#[gpui::test]
 5913async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5914    init_test(cx, |_| {});
 5915
 5916    let mut cx = EditorTestContext::new(cx).await;
 5917    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5918
 5919    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5920        .unwrap();
 5921    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5922
 5923    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5924        .unwrap();
 5925    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5926
 5927    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5928    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5929
 5930    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5931    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5932
 5933    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5934        .unwrap();
 5935    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5936
 5937    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5938        .unwrap();
 5939    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5940
 5941    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5942        .unwrap();
 5943    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5944}
 5945
 5946#[gpui::test]
 5947async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5948    init_test(cx, |_| {});
 5949
 5950    let mut cx = EditorTestContext::new(cx).await;
 5951    cx.set_state("");
 5952
 5953    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5954        .unwrap();
 5955    cx.assert_editor_state("«aˇ»");
 5956    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5957        .unwrap();
 5958    cx.assert_editor_state("«aˇ»");
 5959}
 5960
 5961#[gpui::test]
 5962async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5963    init_test(cx, |_| {});
 5964
 5965    let mut cx = EditorTestContext::new(cx).await;
 5966    cx.set_state(
 5967        r#"let foo = 2;
 5968lˇet foo = 2;
 5969let fooˇ = 2;
 5970let foo = 2;
 5971let foo = ˇ2;"#,
 5972    );
 5973
 5974    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5975        .unwrap();
 5976    cx.assert_editor_state(
 5977        r#"let foo = 2;
 5978«letˇ» foo = 2;
 5979let «fooˇ» = 2;
 5980let foo = 2;
 5981let foo = «2ˇ»;"#,
 5982    );
 5983
 5984    // noop for multiple selections with different contents
 5985    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5986        .unwrap();
 5987    cx.assert_editor_state(
 5988        r#"let foo = 2;
 5989«letˇ» foo = 2;
 5990let «fooˇ» = 2;
 5991let foo = 2;
 5992let foo = «2ˇ»;"#,
 5993    );
 5994}
 5995
 5996#[gpui::test]
 5997async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5998    init_test(cx, |_| {});
 5999
 6000    let mut cx = EditorTestContext::new(cx).await;
 6001    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6002
 6003    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6004        .unwrap();
 6005    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6006
 6007    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6008        .unwrap();
 6009    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6010
 6011    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6012    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6013
 6014    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6015    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6016
 6017    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6018        .unwrap();
 6019    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 6020
 6021    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6022        .unwrap();
 6023    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6024}
 6025
 6026#[gpui::test]
 6027async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6028    init_test(cx, |_| {});
 6029
 6030    let language = Arc::new(Language::new(
 6031        LanguageConfig::default(),
 6032        Some(tree_sitter_rust::LANGUAGE.into()),
 6033    ));
 6034
 6035    let text = r#"
 6036        use mod1::mod2::{mod3, mod4};
 6037
 6038        fn fn_1(param1: bool, param2: &str) {
 6039            let var1 = "text";
 6040        }
 6041    "#
 6042    .unindent();
 6043
 6044    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6045    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6046    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6047
 6048    editor
 6049        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6050        .await;
 6051
 6052    editor.update_in(cx, |editor, window, cx| {
 6053        editor.change_selections(None, window, cx, |s| {
 6054            s.select_display_ranges([
 6055                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6056                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6057                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6058            ]);
 6059        });
 6060        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6061    });
 6062    editor.update(cx, |editor, cx| {
 6063        assert_text_with_selections(
 6064            editor,
 6065            indoc! {r#"
 6066                use mod1::mod2::{mod3, «mod4ˇ»};
 6067
 6068                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6069                    let var1 = "«ˇtext»";
 6070                }
 6071            "#},
 6072            cx,
 6073        );
 6074    });
 6075
 6076    editor.update_in(cx, |editor, window, cx| {
 6077        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6078    });
 6079    editor.update(cx, |editor, cx| {
 6080        assert_text_with_selections(
 6081            editor,
 6082            indoc! {r#"
 6083                use mod1::mod2::«{mod3, mod4}ˇ»;
 6084
 6085                «ˇfn fn_1(param1: bool, param2: &str) {
 6086                    let var1 = "text";
 6087 6088            "#},
 6089            cx,
 6090        );
 6091    });
 6092
 6093    editor.update_in(cx, |editor, window, cx| {
 6094        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6095    });
 6096    assert_eq!(
 6097        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6098        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6099    );
 6100
 6101    // Trying to expand the selected syntax node one more time has no effect.
 6102    editor.update_in(cx, |editor, window, cx| {
 6103        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6104    });
 6105    assert_eq!(
 6106        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6107        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6108    );
 6109
 6110    editor.update_in(cx, |editor, window, cx| {
 6111        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6112    });
 6113    editor.update(cx, |editor, cx| {
 6114        assert_text_with_selections(
 6115            editor,
 6116            indoc! {r#"
 6117                use mod1::mod2::«{mod3, mod4}ˇ»;
 6118
 6119                «ˇfn fn_1(param1: bool, param2: &str) {
 6120                    let var1 = "text";
 6121 6122            "#},
 6123            cx,
 6124        );
 6125    });
 6126
 6127    editor.update_in(cx, |editor, window, cx| {
 6128        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6129    });
 6130    editor.update(cx, |editor, cx| {
 6131        assert_text_with_selections(
 6132            editor,
 6133            indoc! {r#"
 6134                use mod1::mod2::{mod3, «mod4ˇ»};
 6135
 6136                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6137                    let var1 = "«ˇtext»";
 6138                }
 6139            "#},
 6140            cx,
 6141        );
 6142    });
 6143
 6144    editor.update_in(cx, |editor, window, cx| {
 6145        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6146    });
 6147    editor.update(cx, |editor, cx| {
 6148        assert_text_with_selections(
 6149            editor,
 6150            indoc! {r#"
 6151                use mod1::mod2::{mod3, mo«ˇ»d4};
 6152
 6153                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6154                    let var1 = "te«ˇ»xt";
 6155                }
 6156            "#},
 6157            cx,
 6158        );
 6159    });
 6160
 6161    // Trying to shrink the selected syntax node one more time has no effect.
 6162    editor.update_in(cx, |editor, window, cx| {
 6163        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6164    });
 6165    editor.update_in(cx, |editor, _, cx| {
 6166        assert_text_with_selections(
 6167            editor,
 6168            indoc! {r#"
 6169                use mod1::mod2::{mod3, mo«ˇ»d4};
 6170
 6171                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6172                    let var1 = "te«ˇ»xt";
 6173                }
 6174            "#},
 6175            cx,
 6176        );
 6177    });
 6178
 6179    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6180    // a fold.
 6181    editor.update_in(cx, |editor, window, cx| {
 6182        editor.fold_creases(
 6183            vec![
 6184                Crease::simple(
 6185                    Point::new(0, 21)..Point::new(0, 24),
 6186                    FoldPlaceholder::test(),
 6187                ),
 6188                Crease::simple(
 6189                    Point::new(3, 20)..Point::new(3, 22),
 6190                    FoldPlaceholder::test(),
 6191                ),
 6192            ],
 6193            true,
 6194            window,
 6195            cx,
 6196        );
 6197        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6198    });
 6199    editor.update(cx, |editor, cx| {
 6200        assert_text_with_selections(
 6201            editor,
 6202            indoc! {r#"
 6203                use mod1::mod2::«{mod3, mod4}ˇ»;
 6204
 6205                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6206                    «ˇlet var1 = "text";»
 6207                }
 6208            "#},
 6209            cx,
 6210        );
 6211    });
 6212}
 6213
 6214#[gpui::test]
 6215async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6216    init_test(cx, |_| {});
 6217
 6218    let base_text = r#"
 6219        impl A {
 6220            // this is an uncommitted comment
 6221
 6222            fn b() {
 6223                c();
 6224            }
 6225
 6226            // this is another uncommitted comment
 6227
 6228            fn d() {
 6229                // e
 6230                // f
 6231            }
 6232        }
 6233
 6234        fn g() {
 6235            // h
 6236        }
 6237    "#
 6238    .unindent();
 6239
 6240    let text = r#"
 6241        ˇimpl A {
 6242
 6243            fn b() {
 6244                c();
 6245            }
 6246
 6247            fn d() {
 6248                // e
 6249                // f
 6250            }
 6251        }
 6252
 6253        fn g() {
 6254            // h
 6255        }
 6256    "#
 6257    .unindent();
 6258
 6259    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6260    cx.set_state(&text);
 6261    cx.set_head_text(&base_text);
 6262    cx.update_editor(|editor, window, cx| {
 6263        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6264    });
 6265
 6266    cx.assert_state_with_diff(
 6267        "
 6268        ˇimpl A {
 6269      -     // this is an uncommitted comment
 6270
 6271            fn b() {
 6272                c();
 6273            }
 6274
 6275      -     // this is another uncommitted comment
 6276      -
 6277            fn d() {
 6278                // e
 6279                // f
 6280            }
 6281        }
 6282
 6283        fn g() {
 6284            // h
 6285        }
 6286    "
 6287        .unindent(),
 6288    );
 6289
 6290    let expected_display_text = "
 6291        impl A {
 6292            // this is an uncommitted comment
 6293
 6294            fn b() {
 6295 6296            }
 6297
 6298            // this is another uncommitted comment
 6299
 6300            fn d() {
 6301 6302            }
 6303        }
 6304
 6305        fn g() {
 6306 6307        }
 6308        "
 6309    .unindent();
 6310
 6311    cx.update_editor(|editor, window, cx| {
 6312        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6313        assert_eq!(editor.display_text(cx), expected_display_text);
 6314    });
 6315}
 6316
 6317#[gpui::test]
 6318async fn test_autoindent(cx: &mut TestAppContext) {
 6319    init_test(cx, |_| {});
 6320
 6321    let language = Arc::new(
 6322        Language::new(
 6323            LanguageConfig {
 6324                brackets: BracketPairConfig {
 6325                    pairs: vec![
 6326                        BracketPair {
 6327                            start: "{".to_string(),
 6328                            end: "}".to_string(),
 6329                            close: false,
 6330                            surround: false,
 6331                            newline: true,
 6332                        },
 6333                        BracketPair {
 6334                            start: "(".to_string(),
 6335                            end: ")".to_string(),
 6336                            close: false,
 6337                            surround: false,
 6338                            newline: true,
 6339                        },
 6340                    ],
 6341                    ..Default::default()
 6342                },
 6343                ..Default::default()
 6344            },
 6345            Some(tree_sitter_rust::LANGUAGE.into()),
 6346        )
 6347        .with_indents_query(
 6348            r#"
 6349                (_ "(" ")" @end) @indent
 6350                (_ "{" "}" @end) @indent
 6351            "#,
 6352        )
 6353        .unwrap(),
 6354    );
 6355
 6356    let text = "fn a() {}";
 6357
 6358    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6359    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6360    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6361    editor
 6362        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6363        .await;
 6364
 6365    editor.update_in(cx, |editor, window, cx| {
 6366        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6367        editor.newline(&Newline, window, cx);
 6368        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6369        assert_eq!(
 6370            editor.selections.ranges(cx),
 6371            &[
 6372                Point::new(1, 4)..Point::new(1, 4),
 6373                Point::new(3, 4)..Point::new(3, 4),
 6374                Point::new(5, 0)..Point::new(5, 0)
 6375            ]
 6376        );
 6377    });
 6378}
 6379
 6380#[gpui::test]
 6381async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6382    init_test(cx, |_| {});
 6383
 6384    {
 6385        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6386        cx.set_state(indoc! {"
 6387            impl A {
 6388
 6389                fn b() {}
 6390
 6391            «fn c() {
 6392
 6393            }ˇ»
 6394            }
 6395        "});
 6396
 6397        cx.update_editor(|editor, window, cx| {
 6398            editor.autoindent(&Default::default(), window, cx);
 6399        });
 6400
 6401        cx.assert_editor_state(indoc! {"
 6402            impl A {
 6403
 6404                fn b() {}
 6405
 6406                «fn c() {
 6407
 6408                }ˇ»
 6409            }
 6410        "});
 6411    }
 6412
 6413    {
 6414        let mut cx = EditorTestContext::new_multibuffer(
 6415            cx,
 6416            [indoc! { "
 6417                impl A {
 6418                «
 6419                // a
 6420                fn b(){}
 6421                »
 6422                «
 6423                    }
 6424                    fn c(){}
 6425                »
 6426            "}],
 6427        );
 6428
 6429        let buffer = cx.update_editor(|editor, _, cx| {
 6430            let buffer = editor.buffer().update(cx, |buffer, _| {
 6431                buffer.all_buffers().iter().next().unwrap().clone()
 6432            });
 6433            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6434            buffer
 6435        });
 6436
 6437        cx.run_until_parked();
 6438        cx.update_editor(|editor, window, cx| {
 6439            editor.select_all(&Default::default(), window, cx);
 6440            editor.autoindent(&Default::default(), window, cx)
 6441        });
 6442        cx.run_until_parked();
 6443
 6444        cx.update(|_, cx| {
 6445            assert_eq!(
 6446                buffer.read(cx).text(),
 6447                indoc! { "
 6448                    impl A {
 6449
 6450                        // a
 6451                        fn b(){}
 6452
 6453
 6454                    }
 6455                    fn c(){}
 6456
 6457                " }
 6458            )
 6459        });
 6460    }
 6461}
 6462
 6463#[gpui::test]
 6464async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6465    init_test(cx, |_| {});
 6466
 6467    let mut cx = EditorTestContext::new(cx).await;
 6468
 6469    let language = Arc::new(Language::new(
 6470        LanguageConfig {
 6471            brackets: BracketPairConfig {
 6472                pairs: vec![
 6473                    BracketPair {
 6474                        start: "{".to_string(),
 6475                        end: "}".to_string(),
 6476                        close: true,
 6477                        surround: true,
 6478                        newline: true,
 6479                    },
 6480                    BracketPair {
 6481                        start: "(".to_string(),
 6482                        end: ")".to_string(),
 6483                        close: true,
 6484                        surround: true,
 6485                        newline: true,
 6486                    },
 6487                    BracketPair {
 6488                        start: "/*".to_string(),
 6489                        end: " */".to_string(),
 6490                        close: true,
 6491                        surround: true,
 6492                        newline: true,
 6493                    },
 6494                    BracketPair {
 6495                        start: "[".to_string(),
 6496                        end: "]".to_string(),
 6497                        close: false,
 6498                        surround: false,
 6499                        newline: true,
 6500                    },
 6501                    BracketPair {
 6502                        start: "\"".to_string(),
 6503                        end: "\"".to_string(),
 6504                        close: true,
 6505                        surround: true,
 6506                        newline: false,
 6507                    },
 6508                    BracketPair {
 6509                        start: "<".to_string(),
 6510                        end: ">".to_string(),
 6511                        close: false,
 6512                        surround: true,
 6513                        newline: true,
 6514                    },
 6515                ],
 6516                ..Default::default()
 6517            },
 6518            autoclose_before: "})]".to_string(),
 6519            ..Default::default()
 6520        },
 6521        Some(tree_sitter_rust::LANGUAGE.into()),
 6522    ));
 6523
 6524    cx.language_registry().add(language.clone());
 6525    cx.update_buffer(|buffer, cx| {
 6526        buffer.set_language(Some(language), cx);
 6527    });
 6528
 6529    cx.set_state(
 6530        &r#"
 6531            🏀ˇ
 6532            εˇ
 6533            ❤️ˇ
 6534        "#
 6535        .unindent(),
 6536    );
 6537
 6538    // autoclose multiple nested brackets at multiple cursors
 6539    cx.update_editor(|editor, window, cx| {
 6540        editor.handle_input("{", window, cx);
 6541        editor.handle_input("{", window, cx);
 6542        editor.handle_input("{", window, cx);
 6543    });
 6544    cx.assert_editor_state(
 6545        &"
 6546            🏀{{{ˇ}}}
 6547            ε{{{ˇ}}}
 6548            ❤️{{{ˇ}}}
 6549        "
 6550        .unindent(),
 6551    );
 6552
 6553    // insert a different closing bracket
 6554    cx.update_editor(|editor, window, cx| {
 6555        editor.handle_input(")", window, cx);
 6556    });
 6557    cx.assert_editor_state(
 6558        &"
 6559            🏀{{{)ˇ}}}
 6560            ε{{{)ˇ}}}
 6561            ❤️{{{)ˇ}}}
 6562        "
 6563        .unindent(),
 6564    );
 6565
 6566    // skip over the auto-closed brackets when typing a closing bracket
 6567    cx.update_editor(|editor, window, cx| {
 6568        editor.move_right(&MoveRight, window, cx);
 6569        editor.handle_input("}", window, cx);
 6570        editor.handle_input("}", window, cx);
 6571        editor.handle_input("}", window, cx);
 6572    });
 6573    cx.assert_editor_state(
 6574        &"
 6575            🏀{{{)}}}}ˇ
 6576            ε{{{)}}}}ˇ
 6577            ❤️{{{)}}}}ˇ
 6578        "
 6579        .unindent(),
 6580    );
 6581
 6582    // autoclose multi-character pairs
 6583    cx.set_state(
 6584        &"
 6585            ˇ
 6586            ˇ
 6587        "
 6588        .unindent(),
 6589    );
 6590    cx.update_editor(|editor, window, cx| {
 6591        editor.handle_input("/", window, cx);
 6592        editor.handle_input("*", window, cx);
 6593    });
 6594    cx.assert_editor_state(
 6595        &"
 6596            /*ˇ */
 6597            /*ˇ */
 6598        "
 6599        .unindent(),
 6600    );
 6601
 6602    // one cursor autocloses a multi-character pair, one cursor
 6603    // does not autoclose.
 6604    cx.set_state(
 6605        &"
 6606 6607            ˇ
 6608        "
 6609        .unindent(),
 6610    );
 6611    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6612    cx.assert_editor_state(
 6613        &"
 6614            /*ˇ */
 6615 6616        "
 6617        .unindent(),
 6618    );
 6619
 6620    // Don't autoclose if the next character isn't whitespace and isn't
 6621    // listed in the language's "autoclose_before" section.
 6622    cx.set_state("ˇa b");
 6623    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6624    cx.assert_editor_state("{ˇa b");
 6625
 6626    // Don't autoclose if `close` is false for the bracket pair
 6627    cx.set_state("ˇ");
 6628    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6629    cx.assert_editor_state("");
 6630
 6631    // Surround with brackets if text is selected
 6632    cx.set_state("«aˇ» b");
 6633    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6634    cx.assert_editor_state("{«aˇ»} b");
 6635
 6636    // Autoclose when not immediately after a word character
 6637    cx.set_state("a ˇ");
 6638    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6639    cx.assert_editor_state("a \"ˇ\"");
 6640
 6641    // Autoclose pair where the start and end characters are the same
 6642    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6643    cx.assert_editor_state("a \"\"ˇ");
 6644
 6645    // Don't autoclose when immediately after a word character
 6646    cx.set_state("");
 6647    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6648    cx.assert_editor_state("a\"ˇ");
 6649
 6650    // Do autoclose when after a non-word character
 6651    cx.set_state("");
 6652    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6653    cx.assert_editor_state("{\"ˇ\"");
 6654
 6655    // Non identical pairs autoclose regardless of preceding character
 6656    cx.set_state("");
 6657    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6658    cx.assert_editor_state("a{ˇ}");
 6659
 6660    // Don't autoclose pair if autoclose is disabled
 6661    cx.set_state("ˇ");
 6662    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6663    cx.assert_editor_state("");
 6664
 6665    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6666    cx.set_state("«aˇ» b");
 6667    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6668    cx.assert_editor_state("<«aˇ»> b");
 6669}
 6670
 6671#[gpui::test]
 6672async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6673    init_test(cx, |settings| {
 6674        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6675    });
 6676
 6677    let mut cx = EditorTestContext::new(cx).await;
 6678
 6679    let language = Arc::new(Language::new(
 6680        LanguageConfig {
 6681            brackets: BracketPairConfig {
 6682                pairs: vec![
 6683                    BracketPair {
 6684                        start: "{".to_string(),
 6685                        end: "}".to_string(),
 6686                        close: true,
 6687                        surround: true,
 6688                        newline: true,
 6689                    },
 6690                    BracketPair {
 6691                        start: "(".to_string(),
 6692                        end: ")".to_string(),
 6693                        close: true,
 6694                        surround: true,
 6695                        newline: true,
 6696                    },
 6697                    BracketPair {
 6698                        start: "[".to_string(),
 6699                        end: "]".to_string(),
 6700                        close: false,
 6701                        surround: false,
 6702                        newline: true,
 6703                    },
 6704                ],
 6705                ..Default::default()
 6706            },
 6707            autoclose_before: "})]".to_string(),
 6708            ..Default::default()
 6709        },
 6710        Some(tree_sitter_rust::LANGUAGE.into()),
 6711    ));
 6712
 6713    cx.language_registry().add(language.clone());
 6714    cx.update_buffer(|buffer, cx| {
 6715        buffer.set_language(Some(language), cx);
 6716    });
 6717
 6718    cx.set_state(
 6719        &"
 6720            ˇ
 6721            ˇ
 6722            ˇ
 6723        "
 6724        .unindent(),
 6725    );
 6726
 6727    // ensure only matching closing brackets are skipped over
 6728    cx.update_editor(|editor, window, cx| {
 6729        editor.handle_input("}", window, cx);
 6730        editor.move_left(&MoveLeft, window, cx);
 6731        editor.handle_input(")", window, cx);
 6732        editor.move_left(&MoveLeft, window, cx);
 6733    });
 6734    cx.assert_editor_state(
 6735        &"
 6736            ˇ)}
 6737            ˇ)}
 6738            ˇ)}
 6739        "
 6740        .unindent(),
 6741    );
 6742
 6743    // skip-over closing brackets at multiple cursors
 6744    cx.update_editor(|editor, window, cx| {
 6745        editor.handle_input(")", window, cx);
 6746        editor.handle_input("}", window, cx);
 6747    });
 6748    cx.assert_editor_state(
 6749        &"
 6750            )}ˇ
 6751            )}ˇ
 6752            )}ˇ
 6753        "
 6754        .unindent(),
 6755    );
 6756
 6757    // ignore non-close brackets
 6758    cx.update_editor(|editor, window, cx| {
 6759        editor.handle_input("]", window, cx);
 6760        editor.move_left(&MoveLeft, window, cx);
 6761        editor.handle_input("]", window, cx);
 6762    });
 6763    cx.assert_editor_state(
 6764        &"
 6765            )}]ˇ]
 6766            )}]ˇ]
 6767            )}]ˇ]
 6768        "
 6769        .unindent(),
 6770    );
 6771}
 6772
 6773#[gpui::test]
 6774async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6775    init_test(cx, |_| {});
 6776
 6777    let mut cx = EditorTestContext::new(cx).await;
 6778
 6779    let html_language = Arc::new(
 6780        Language::new(
 6781            LanguageConfig {
 6782                name: "HTML".into(),
 6783                brackets: BracketPairConfig {
 6784                    pairs: vec![
 6785                        BracketPair {
 6786                            start: "<".into(),
 6787                            end: ">".into(),
 6788                            close: true,
 6789                            ..Default::default()
 6790                        },
 6791                        BracketPair {
 6792                            start: "{".into(),
 6793                            end: "}".into(),
 6794                            close: true,
 6795                            ..Default::default()
 6796                        },
 6797                        BracketPair {
 6798                            start: "(".into(),
 6799                            end: ")".into(),
 6800                            close: true,
 6801                            ..Default::default()
 6802                        },
 6803                    ],
 6804                    ..Default::default()
 6805                },
 6806                autoclose_before: "})]>".into(),
 6807                ..Default::default()
 6808            },
 6809            Some(tree_sitter_html::LANGUAGE.into()),
 6810        )
 6811        .with_injection_query(
 6812            r#"
 6813            (script_element
 6814                (raw_text) @injection.content
 6815                (#set! injection.language "javascript"))
 6816            "#,
 6817        )
 6818        .unwrap(),
 6819    );
 6820
 6821    let javascript_language = Arc::new(Language::new(
 6822        LanguageConfig {
 6823            name: "JavaScript".into(),
 6824            brackets: BracketPairConfig {
 6825                pairs: vec![
 6826                    BracketPair {
 6827                        start: "/*".into(),
 6828                        end: " */".into(),
 6829                        close: true,
 6830                        ..Default::default()
 6831                    },
 6832                    BracketPair {
 6833                        start: "{".into(),
 6834                        end: "}".into(),
 6835                        close: true,
 6836                        ..Default::default()
 6837                    },
 6838                    BracketPair {
 6839                        start: "(".into(),
 6840                        end: ")".into(),
 6841                        close: true,
 6842                        ..Default::default()
 6843                    },
 6844                ],
 6845                ..Default::default()
 6846            },
 6847            autoclose_before: "})]>".into(),
 6848            ..Default::default()
 6849        },
 6850        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6851    ));
 6852
 6853    cx.language_registry().add(html_language.clone());
 6854    cx.language_registry().add(javascript_language.clone());
 6855
 6856    cx.update_buffer(|buffer, cx| {
 6857        buffer.set_language(Some(html_language), cx);
 6858    });
 6859
 6860    cx.set_state(
 6861        &r#"
 6862            <body>ˇ
 6863                <script>
 6864                    var x = 1;ˇ
 6865                </script>
 6866            </body>ˇ
 6867        "#
 6868        .unindent(),
 6869    );
 6870
 6871    // Precondition: different languages are active at different locations.
 6872    cx.update_editor(|editor, window, cx| {
 6873        let snapshot = editor.snapshot(window, cx);
 6874        let cursors = editor.selections.ranges::<usize>(cx);
 6875        let languages = cursors
 6876            .iter()
 6877            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6878            .collect::<Vec<_>>();
 6879        assert_eq!(
 6880            languages,
 6881            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6882        );
 6883    });
 6884
 6885    // Angle brackets autoclose in HTML, but not JavaScript.
 6886    cx.update_editor(|editor, window, cx| {
 6887        editor.handle_input("<", window, cx);
 6888        editor.handle_input("a", window, cx);
 6889    });
 6890    cx.assert_editor_state(
 6891        &r#"
 6892            <body><aˇ>
 6893                <script>
 6894                    var x = 1;<aˇ
 6895                </script>
 6896            </body><aˇ>
 6897        "#
 6898        .unindent(),
 6899    );
 6900
 6901    // Curly braces and parens autoclose in both HTML and JavaScript.
 6902    cx.update_editor(|editor, window, cx| {
 6903        editor.handle_input(" b=", window, cx);
 6904        editor.handle_input("{", window, cx);
 6905        editor.handle_input("c", window, cx);
 6906        editor.handle_input("(", window, cx);
 6907    });
 6908    cx.assert_editor_state(
 6909        &r#"
 6910            <body><a b={c(ˇ)}>
 6911                <script>
 6912                    var x = 1;<a b={c(ˇ)}
 6913                </script>
 6914            </body><a b={c(ˇ)}>
 6915        "#
 6916        .unindent(),
 6917    );
 6918
 6919    // Brackets that were already autoclosed are skipped.
 6920    cx.update_editor(|editor, window, cx| {
 6921        editor.handle_input(")", window, cx);
 6922        editor.handle_input("d", window, cx);
 6923        editor.handle_input("}", window, cx);
 6924    });
 6925    cx.assert_editor_state(
 6926        &r#"
 6927            <body><a b={c()d}ˇ>
 6928                <script>
 6929                    var x = 1;<a b={c()d}ˇ
 6930                </script>
 6931            </body><a b={c()d}ˇ>
 6932        "#
 6933        .unindent(),
 6934    );
 6935    cx.update_editor(|editor, window, cx| {
 6936        editor.handle_input(">", window, cx);
 6937    });
 6938    cx.assert_editor_state(
 6939        &r#"
 6940            <body><a b={c()d}>ˇ
 6941                <script>
 6942                    var x = 1;<a b={c()d}>ˇ
 6943                </script>
 6944            </body><a b={c()d}>ˇ
 6945        "#
 6946        .unindent(),
 6947    );
 6948
 6949    // Reset
 6950    cx.set_state(
 6951        &r#"
 6952            <body>ˇ
 6953                <script>
 6954                    var x = 1;ˇ
 6955                </script>
 6956            </body>ˇ
 6957        "#
 6958        .unindent(),
 6959    );
 6960
 6961    cx.update_editor(|editor, window, cx| {
 6962        editor.handle_input("<", window, cx);
 6963    });
 6964    cx.assert_editor_state(
 6965        &r#"
 6966            <body><ˇ>
 6967                <script>
 6968                    var x = 1;<ˇ
 6969                </script>
 6970            </body><ˇ>
 6971        "#
 6972        .unindent(),
 6973    );
 6974
 6975    // When backspacing, the closing angle brackets are removed.
 6976    cx.update_editor(|editor, window, cx| {
 6977        editor.backspace(&Backspace, window, cx);
 6978    });
 6979    cx.assert_editor_state(
 6980        &r#"
 6981            <body>ˇ
 6982                <script>
 6983                    var x = 1;ˇ
 6984                </script>
 6985            </body>ˇ
 6986        "#
 6987        .unindent(),
 6988    );
 6989
 6990    // Block comments autoclose in JavaScript, but not HTML.
 6991    cx.update_editor(|editor, window, cx| {
 6992        editor.handle_input("/", window, cx);
 6993        editor.handle_input("*", window, cx);
 6994    });
 6995    cx.assert_editor_state(
 6996        &r#"
 6997            <body>/*ˇ
 6998                <script>
 6999                    var x = 1;/*ˇ */
 7000                </script>
 7001            </body>/*ˇ
 7002        "#
 7003        .unindent(),
 7004    );
 7005}
 7006
 7007#[gpui::test]
 7008async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7009    init_test(cx, |_| {});
 7010
 7011    let mut cx = EditorTestContext::new(cx).await;
 7012
 7013    let rust_language = Arc::new(
 7014        Language::new(
 7015            LanguageConfig {
 7016                name: "Rust".into(),
 7017                brackets: serde_json::from_value(json!([
 7018                    { "start": "{", "end": "}", "close": true, "newline": true },
 7019                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7020                ]))
 7021                .unwrap(),
 7022                autoclose_before: "})]>".into(),
 7023                ..Default::default()
 7024            },
 7025            Some(tree_sitter_rust::LANGUAGE.into()),
 7026        )
 7027        .with_override_query("(string_literal) @string")
 7028        .unwrap(),
 7029    );
 7030
 7031    cx.language_registry().add(rust_language.clone());
 7032    cx.update_buffer(|buffer, cx| {
 7033        buffer.set_language(Some(rust_language), cx);
 7034    });
 7035
 7036    cx.set_state(
 7037        &r#"
 7038            let x = ˇ
 7039        "#
 7040        .unindent(),
 7041    );
 7042
 7043    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7044    cx.update_editor(|editor, window, cx| {
 7045        editor.handle_input("\"", window, cx);
 7046    });
 7047    cx.assert_editor_state(
 7048        &r#"
 7049            let x = "ˇ"
 7050        "#
 7051        .unindent(),
 7052    );
 7053
 7054    // Inserting another quotation mark. The cursor moves across the existing
 7055    // automatically-inserted quotation mark.
 7056    cx.update_editor(|editor, window, cx| {
 7057        editor.handle_input("\"", window, cx);
 7058    });
 7059    cx.assert_editor_state(
 7060        &r#"
 7061            let x = ""ˇ
 7062        "#
 7063        .unindent(),
 7064    );
 7065
 7066    // Reset
 7067    cx.set_state(
 7068        &r#"
 7069            let x = ˇ
 7070        "#
 7071        .unindent(),
 7072    );
 7073
 7074    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7075    cx.update_editor(|editor, window, cx| {
 7076        editor.handle_input("\"", window, cx);
 7077        editor.handle_input(" ", window, cx);
 7078        editor.move_left(&Default::default(), window, cx);
 7079        editor.handle_input("\\", window, cx);
 7080        editor.handle_input("\"", window, cx);
 7081    });
 7082    cx.assert_editor_state(
 7083        &r#"
 7084            let x = "\"ˇ "
 7085        "#
 7086        .unindent(),
 7087    );
 7088
 7089    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7090    // mark. Nothing is inserted.
 7091    cx.update_editor(|editor, window, cx| {
 7092        editor.move_right(&Default::default(), window, cx);
 7093        editor.handle_input("\"", window, cx);
 7094    });
 7095    cx.assert_editor_state(
 7096        &r#"
 7097            let x = "\" "ˇ
 7098        "#
 7099        .unindent(),
 7100    );
 7101}
 7102
 7103#[gpui::test]
 7104async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7105    init_test(cx, |_| {});
 7106
 7107    let language = Arc::new(Language::new(
 7108        LanguageConfig {
 7109            brackets: BracketPairConfig {
 7110                pairs: vec![
 7111                    BracketPair {
 7112                        start: "{".to_string(),
 7113                        end: "}".to_string(),
 7114                        close: true,
 7115                        surround: true,
 7116                        newline: true,
 7117                    },
 7118                    BracketPair {
 7119                        start: "/* ".to_string(),
 7120                        end: "*/".to_string(),
 7121                        close: true,
 7122                        surround: true,
 7123                        ..Default::default()
 7124                    },
 7125                ],
 7126                ..Default::default()
 7127            },
 7128            ..Default::default()
 7129        },
 7130        Some(tree_sitter_rust::LANGUAGE.into()),
 7131    ));
 7132
 7133    let text = r#"
 7134        a
 7135        b
 7136        c
 7137    "#
 7138    .unindent();
 7139
 7140    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7141    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7142    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7143    editor
 7144        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7145        .await;
 7146
 7147    editor.update_in(cx, |editor, window, cx| {
 7148        editor.change_selections(None, window, cx, |s| {
 7149            s.select_display_ranges([
 7150                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7151                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7152                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7153            ])
 7154        });
 7155
 7156        editor.handle_input("{", window, cx);
 7157        editor.handle_input("{", window, cx);
 7158        editor.handle_input("{", window, cx);
 7159        assert_eq!(
 7160            editor.text(cx),
 7161            "
 7162                {{{a}}}
 7163                {{{b}}}
 7164                {{{c}}}
 7165            "
 7166            .unindent()
 7167        );
 7168        assert_eq!(
 7169            editor.selections.display_ranges(cx),
 7170            [
 7171                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7172                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7173                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7174            ]
 7175        );
 7176
 7177        editor.undo(&Undo, window, cx);
 7178        editor.undo(&Undo, window, cx);
 7179        editor.undo(&Undo, window, cx);
 7180        assert_eq!(
 7181            editor.text(cx),
 7182            "
 7183                a
 7184                b
 7185                c
 7186            "
 7187            .unindent()
 7188        );
 7189        assert_eq!(
 7190            editor.selections.display_ranges(cx),
 7191            [
 7192                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7193                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7194                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7195            ]
 7196        );
 7197
 7198        // Ensure inserting the first character of a multi-byte bracket pair
 7199        // doesn't surround the selections with the bracket.
 7200        editor.handle_input("/", window, cx);
 7201        assert_eq!(
 7202            editor.text(cx),
 7203            "
 7204                /
 7205                /
 7206                /
 7207            "
 7208            .unindent()
 7209        );
 7210        assert_eq!(
 7211            editor.selections.display_ranges(cx),
 7212            [
 7213                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7214                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7215                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7216            ]
 7217        );
 7218
 7219        editor.undo(&Undo, window, cx);
 7220        assert_eq!(
 7221            editor.text(cx),
 7222            "
 7223                a
 7224                b
 7225                c
 7226            "
 7227            .unindent()
 7228        );
 7229        assert_eq!(
 7230            editor.selections.display_ranges(cx),
 7231            [
 7232                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7233                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7234                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7235            ]
 7236        );
 7237
 7238        // Ensure inserting the last character of a multi-byte bracket pair
 7239        // doesn't surround the selections with the bracket.
 7240        editor.handle_input("*", window, cx);
 7241        assert_eq!(
 7242            editor.text(cx),
 7243            "
 7244                *
 7245                *
 7246                *
 7247            "
 7248            .unindent()
 7249        );
 7250        assert_eq!(
 7251            editor.selections.display_ranges(cx),
 7252            [
 7253                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7254                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7255                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7256            ]
 7257        );
 7258    });
 7259}
 7260
 7261#[gpui::test]
 7262async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7263    init_test(cx, |_| {});
 7264
 7265    let language = Arc::new(Language::new(
 7266        LanguageConfig {
 7267            brackets: BracketPairConfig {
 7268                pairs: vec![BracketPair {
 7269                    start: "{".to_string(),
 7270                    end: "}".to_string(),
 7271                    close: true,
 7272                    surround: true,
 7273                    newline: true,
 7274                }],
 7275                ..Default::default()
 7276            },
 7277            autoclose_before: "}".to_string(),
 7278            ..Default::default()
 7279        },
 7280        Some(tree_sitter_rust::LANGUAGE.into()),
 7281    ));
 7282
 7283    let text = r#"
 7284        a
 7285        b
 7286        c
 7287    "#
 7288    .unindent();
 7289
 7290    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7291    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7292    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7293    editor
 7294        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7295        .await;
 7296
 7297    editor.update_in(cx, |editor, window, cx| {
 7298        editor.change_selections(None, window, cx, |s| {
 7299            s.select_ranges([
 7300                Point::new(0, 1)..Point::new(0, 1),
 7301                Point::new(1, 1)..Point::new(1, 1),
 7302                Point::new(2, 1)..Point::new(2, 1),
 7303            ])
 7304        });
 7305
 7306        editor.handle_input("{", window, cx);
 7307        editor.handle_input("{", window, cx);
 7308        editor.handle_input("_", window, cx);
 7309        assert_eq!(
 7310            editor.text(cx),
 7311            "
 7312                a{{_}}
 7313                b{{_}}
 7314                c{{_}}
 7315            "
 7316            .unindent()
 7317        );
 7318        assert_eq!(
 7319            editor.selections.ranges::<Point>(cx),
 7320            [
 7321                Point::new(0, 4)..Point::new(0, 4),
 7322                Point::new(1, 4)..Point::new(1, 4),
 7323                Point::new(2, 4)..Point::new(2, 4)
 7324            ]
 7325        );
 7326
 7327        editor.backspace(&Default::default(), window, cx);
 7328        editor.backspace(&Default::default(), window, cx);
 7329        assert_eq!(
 7330            editor.text(cx),
 7331            "
 7332                a{}
 7333                b{}
 7334                c{}
 7335            "
 7336            .unindent()
 7337        );
 7338        assert_eq!(
 7339            editor.selections.ranges::<Point>(cx),
 7340            [
 7341                Point::new(0, 2)..Point::new(0, 2),
 7342                Point::new(1, 2)..Point::new(1, 2),
 7343                Point::new(2, 2)..Point::new(2, 2)
 7344            ]
 7345        );
 7346
 7347        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7348        assert_eq!(
 7349            editor.text(cx),
 7350            "
 7351                a
 7352                b
 7353                c
 7354            "
 7355            .unindent()
 7356        );
 7357        assert_eq!(
 7358            editor.selections.ranges::<Point>(cx),
 7359            [
 7360                Point::new(0, 1)..Point::new(0, 1),
 7361                Point::new(1, 1)..Point::new(1, 1),
 7362                Point::new(2, 1)..Point::new(2, 1)
 7363            ]
 7364        );
 7365    });
 7366}
 7367
 7368#[gpui::test]
 7369async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7370    init_test(cx, |settings| {
 7371        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7372    });
 7373
 7374    let mut cx = EditorTestContext::new(cx).await;
 7375
 7376    let language = Arc::new(Language::new(
 7377        LanguageConfig {
 7378            brackets: BracketPairConfig {
 7379                pairs: vec![
 7380                    BracketPair {
 7381                        start: "{".to_string(),
 7382                        end: "}".to_string(),
 7383                        close: true,
 7384                        surround: true,
 7385                        newline: true,
 7386                    },
 7387                    BracketPair {
 7388                        start: "(".to_string(),
 7389                        end: ")".to_string(),
 7390                        close: true,
 7391                        surround: true,
 7392                        newline: true,
 7393                    },
 7394                    BracketPair {
 7395                        start: "[".to_string(),
 7396                        end: "]".to_string(),
 7397                        close: false,
 7398                        surround: true,
 7399                        newline: true,
 7400                    },
 7401                ],
 7402                ..Default::default()
 7403            },
 7404            autoclose_before: "})]".to_string(),
 7405            ..Default::default()
 7406        },
 7407        Some(tree_sitter_rust::LANGUAGE.into()),
 7408    ));
 7409
 7410    cx.language_registry().add(language.clone());
 7411    cx.update_buffer(|buffer, cx| {
 7412        buffer.set_language(Some(language), cx);
 7413    });
 7414
 7415    cx.set_state(
 7416        &"
 7417            {(ˇ)}
 7418            [[ˇ]]
 7419            {(ˇ)}
 7420        "
 7421        .unindent(),
 7422    );
 7423
 7424    cx.update_editor(|editor, window, cx| {
 7425        editor.backspace(&Default::default(), window, cx);
 7426        editor.backspace(&Default::default(), window, cx);
 7427    });
 7428
 7429    cx.assert_editor_state(
 7430        &"
 7431            ˇ
 7432            ˇ]]
 7433            ˇ
 7434        "
 7435        .unindent(),
 7436    );
 7437
 7438    cx.update_editor(|editor, window, cx| {
 7439        editor.handle_input("{", window, cx);
 7440        editor.handle_input("{", window, cx);
 7441        editor.move_right(&MoveRight, window, cx);
 7442        editor.move_right(&MoveRight, window, cx);
 7443        editor.move_left(&MoveLeft, window, cx);
 7444        editor.move_left(&MoveLeft, window, cx);
 7445        editor.backspace(&Default::default(), window, cx);
 7446    });
 7447
 7448    cx.assert_editor_state(
 7449        &"
 7450            {ˇ}
 7451            {ˇ}]]
 7452            {ˇ}
 7453        "
 7454        .unindent(),
 7455    );
 7456
 7457    cx.update_editor(|editor, window, cx| {
 7458        editor.backspace(&Default::default(), window, cx);
 7459    });
 7460
 7461    cx.assert_editor_state(
 7462        &"
 7463            ˇ
 7464            ˇ]]
 7465            ˇ
 7466        "
 7467        .unindent(),
 7468    );
 7469}
 7470
 7471#[gpui::test]
 7472async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7473    init_test(cx, |_| {});
 7474
 7475    let language = Arc::new(Language::new(
 7476        LanguageConfig::default(),
 7477        Some(tree_sitter_rust::LANGUAGE.into()),
 7478    ));
 7479
 7480    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7481    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7482    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7483    editor
 7484        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7485        .await;
 7486
 7487    editor.update_in(cx, |editor, window, cx| {
 7488        editor.set_auto_replace_emoji_shortcode(true);
 7489
 7490        editor.handle_input("Hello ", window, cx);
 7491        editor.handle_input(":wave", window, cx);
 7492        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7493
 7494        editor.handle_input(":", window, cx);
 7495        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7496
 7497        editor.handle_input(" :smile", window, cx);
 7498        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7499
 7500        editor.handle_input(":", window, cx);
 7501        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7502
 7503        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7504        editor.handle_input(":wave", window, cx);
 7505        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7506
 7507        editor.handle_input(":", window, cx);
 7508        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7509
 7510        editor.handle_input(":1", window, cx);
 7511        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7512
 7513        editor.handle_input(":", window, cx);
 7514        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7515
 7516        // Ensure shortcode does not get replaced when it is part of a word
 7517        editor.handle_input(" Test:wave", window, cx);
 7518        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7519
 7520        editor.handle_input(":", window, cx);
 7521        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7522
 7523        editor.set_auto_replace_emoji_shortcode(false);
 7524
 7525        // Ensure shortcode does not get replaced when auto replace is off
 7526        editor.handle_input(" :wave", window, cx);
 7527        assert_eq!(
 7528            editor.text(cx),
 7529            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7530        );
 7531
 7532        editor.handle_input(":", window, cx);
 7533        assert_eq!(
 7534            editor.text(cx),
 7535            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7536        );
 7537    });
 7538}
 7539
 7540#[gpui::test]
 7541async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7542    init_test(cx, |_| {});
 7543
 7544    let (text, insertion_ranges) = marked_text_ranges(
 7545        indoc! {"
 7546            ˇ
 7547        "},
 7548        false,
 7549    );
 7550
 7551    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7552    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7553
 7554    _ = editor.update_in(cx, |editor, window, cx| {
 7555        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7556
 7557        editor
 7558            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7559            .unwrap();
 7560
 7561        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7562            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7563            assert_eq!(editor.text(cx), expected_text);
 7564            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7565        }
 7566
 7567        assert(
 7568            editor,
 7569            cx,
 7570            indoc! {"
 7571            type «» =•
 7572            "},
 7573        );
 7574
 7575        assert!(editor.context_menu_visible(), "There should be a matches");
 7576    });
 7577}
 7578
 7579#[gpui::test]
 7580async fn test_snippets(cx: &mut TestAppContext) {
 7581    init_test(cx, |_| {});
 7582
 7583    let (text, insertion_ranges) = marked_text_ranges(
 7584        indoc! {"
 7585            a.ˇ b
 7586            a.ˇ b
 7587            a.ˇ b
 7588        "},
 7589        false,
 7590    );
 7591
 7592    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7593    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7594
 7595    editor.update_in(cx, |editor, window, cx| {
 7596        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7597
 7598        editor
 7599            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7600            .unwrap();
 7601
 7602        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7603            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7604            assert_eq!(editor.text(cx), expected_text);
 7605            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7606        }
 7607
 7608        assert(
 7609            editor,
 7610            cx,
 7611            indoc! {"
 7612                a.f(«one», two, «three») b
 7613                a.f(«one», two, «three») b
 7614                a.f(«one», two, «three») b
 7615            "},
 7616        );
 7617
 7618        // Can't move earlier than the first tab stop
 7619        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7620        assert(
 7621            editor,
 7622            cx,
 7623            indoc! {"
 7624                a.f(«one», two, «three») b
 7625                a.f(«one», two, «three») b
 7626                a.f(«one», two, «three») b
 7627            "},
 7628        );
 7629
 7630        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7631        assert(
 7632            editor,
 7633            cx,
 7634            indoc! {"
 7635                a.f(one, «two», three) b
 7636                a.f(one, «two», three) b
 7637                a.f(one, «two», three) b
 7638            "},
 7639        );
 7640
 7641        editor.move_to_prev_snippet_tabstop(window, cx);
 7642        assert(
 7643            editor,
 7644            cx,
 7645            indoc! {"
 7646                a.f(«one», two, «three») b
 7647                a.f(«one», two, «three») b
 7648                a.f(«one», two, «three») b
 7649            "},
 7650        );
 7651
 7652        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7653        assert(
 7654            editor,
 7655            cx,
 7656            indoc! {"
 7657                a.f(one, «two», three) b
 7658                a.f(one, «two», three) b
 7659                a.f(one, «two», three) b
 7660            "},
 7661        );
 7662        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7663        assert(
 7664            editor,
 7665            cx,
 7666            indoc! {"
 7667                a.f(one, two, three)ˇ b
 7668                a.f(one, two, three)ˇ b
 7669                a.f(one, two, three)ˇ b
 7670            "},
 7671        );
 7672
 7673        // As soon as the last tab stop is reached, snippet state is gone
 7674        editor.move_to_prev_snippet_tabstop(window, cx);
 7675        assert(
 7676            editor,
 7677            cx,
 7678            indoc! {"
 7679                a.f(one, two, three)ˇ b
 7680                a.f(one, two, three)ˇ b
 7681                a.f(one, two, three)ˇ b
 7682            "},
 7683        );
 7684    });
 7685}
 7686
 7687#[gpui::test]
 7688async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7689    init_test(cx, |_| {});
 7690
 7691    let fs = FakeFs::new(cx.executor());
 7692    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7693
 7694    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7695
 7696    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7697    language_registry.add(rust_lang());
 7698    let mut fake_servers = language_registry.register_fake_lsp(
 7699        "Rust",
 7700        FakeLspAdapter {
 7701            capabilities: lsp::ServerCapabilities {
 7702                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7703                ..Default::default()
 7704            },
 7705            ..Default::default()
 7706        },
 7707    );
 7708
 7709    let buffer = project
 7710        .update(cx, |project, cx| {
 7711            project.open_local_buffer(path!("/file.rs"), cx)
 7712        })
 7713        .await
 7714        .unwrap();
 7715
 7716    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7717    let (editor, cx) = cx.add_window_view(|window, cx| {
 7718        build_editor_with_project(project.clone(), buffer, window, cx)
 7719    });
 7720    editor.update_in(cx, |editor, window, cx| {
 7721        editor.set_text("one\ntwo\nthree\n", window, cx)
 7722    });
 7723    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7724
 7725    cx.executor().start_waiting();
 7726    let fake_server = fake_servers.next().await.unwrap();
 7727
 7728    let save = editor
 7729        .update_in(cx, |editor, window, cx| {
 7730            editor.save(true, project.clone(), window, cx)
 7731        })
 7732        .unwrap();
 7733    fake_server
 7734        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7735            assert_eq!(
 7736                params.text_document.uri,
 7737                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7738            );
 7739            assert_eq!(params.options.tab_size, 4);
 7740            Ok(Some(vec![lsp::TextEdit::new(
 7741                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7742                ", ".to_string(),
 7743            )]))
 7744        })
 7745        .next()
 7746        .await;
 7747    cx.executor().start_waiting();
 7748    save.await;
 7749
 7750    assert_eq!(
 7751        editor.update(cx, |editor, cx| editor.text(cx)),
 7752        "one, two\nthree\n"
 7753    );
 7754    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7755
 7756    editor.update_in(cx, |editor, window, cx| {
 7757        editor.set_text("one\ntwo\nthree\n", window, cx)
 7758    });
 7759    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7760
 7761    // Ensure we can still save even if formatting hangs.
 7762    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7763        move |params, _| async move {
 7764            assert_eq!(
 7765                params.text_document.uri,
 7766                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7767            );
 7768            futures::future::pending::<()>().await;
 7769            unreachable!()
 7770        },
 7771    );
 7772    let save = editor
 7773        .update_in(cx, |editor, window, cx| {
 7774            editor.save(true, project.clone(), window, cx)
 7775        })
 7776        .unwrap();
 7777    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7778    cx.executor().start_waiting();
 7779    save.await;
 7780    assert_eq!(
 7781        editor.update(cx, |editor, cx| editor.text(cx)),
 7782        "one\ntwo\nthree\n"
 7783    );
 7784    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7785
 7786    // For non-dirty buffer, no formatting request should be sent
 7787    let save = editor
 7788        .update_in(cx, |editor, window, cx| {
 7789            editor.save(true, project.clone(), window, cx)
 7790        })
 7791        .unwrap();
 7792    let _pending_format_request = fake_server
 7793        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7794            panic!("Should not be invoked on non-dirty buffer");
 7795        })
 7796        .next();
 7797    cx.executor().start_waiting();
 7798    save.await;
 7799
 7800    // Set rust language override and assert overridden tabsize is sent to language server
 7801    update_test_language_settings(cx, |settings| {
 7802        settings.languages.insert(
 7803            "Rust".into(),
 7804            LanguageSettingsContent {
 7805                tab_size: NonZeroU32::new(8),
 7806                ..Default::default()
 7807            },
 7808        );
 7809    });
 7810
 7811    editor.update_in(cx, |editor, window, cx| {
 7812        editor.set_text("somehting_new\n", window, cx)
 7813    });
 7814    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7815    let save = editor
 7816        .update_in(cx, |editor, window, cx| {
 7817            editor.save(true, project.clone(), window, cx)
 7818        })
 7819        .unwrap();
 7820    fake_server
 7821        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7822            assert_eq!(
 7823                params.text_document.uri,
 7824                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7825            );
 7826            assert_eq!(params.options.tab_size, 8);
 7827            Ok(Some(vec![]))
 7828        })
 7829        .next()
 7830        .await;
 7831    cx.executor().start_waiting();
 7832    save.await;
 7833}
 7834
 7835#[gpui::test]
 7836async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7837    init_test(cx, |_| {});
 7838
 7839    let cols = 4;
 7840    let rows = 10;
 7841    let sample_text_1 = sample_text(rows, cols, 'a');
 7842    assert_eq!(
 7843        sample_text_1,
 7844        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7845    );
 7846    let sample_text_2 = sample_text(rows, cols, 'l');
 7847    assert_eq!(
 7848        sample_text_2,
 7849        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7850    );
 7851    let sample_text_3 = sample_text(rows, cols, 'v');
 7852    assert_eq!(
 7853        sample_text_3,
 7854        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7855    );
 7856
 7857    let fs = FakeFs::new(cx.executor());
 7858    fs.insert_tree(
 7859        path!("/a"),
 7860        json!({
 7861            "main.rs": sample_text_1,
 7862            "other.rs": sample_text_2,
 7863            "lib.rs": sample_text_3,
 7864        }),
 7865    )
 7866    .await;
 7867
 7868    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7869    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7870    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7871
 7872    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7873    language_registry.add(rust_lang());
 7874    let mut fake_servers = language_registry.register_fake_lsp(
 7875        "Rust",
 7876        FakeLspAdapter {
 7877            capabilities: lsp::ServerCapabilities {
 7878                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7879                ..Default::default()
 7880            },
 7881            ..Default::default()
 7882        },
 7883    );
 7884
 7885    let worktree = project.update(cx, |project, cx| {
 7886        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7887        assert_eq!(worktrees.len(), 1);
 7888        worktrees.pop().unwrap()
 7889    });
 7890    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7891
 7892    let buffer_1 = project
 7893        .update(cx, |project, cx| {
 7894            project.open_buffer((worktree_id, "main.rs"), cx)
 7895        })
 7896        .await
 7897        .unwrap();
 7898    let buffer_2 = project
 7899        .update(cx, |project, cx| {
 7900            project.open_buffer((worktree_id, "other.rs"), cx)
 7901        })
 7902        .await
 7903        .unwrap();
 7904    let buffer_3 = project
 7905        .update(cx, |project, cx| {
 7906            project.open_buffer((worktree_id, "lib.rs"), cx)
 7907        })
 7908        .await
 7909        .unwrap();
 7910
 7911    let multi_buffer = cx.new(|cx| {
 7912        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7913        multi_buffer.push_excerpts(
 7914            buffer_1.clone(),
 7915            [
 7916                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7917                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7918                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7919            ],
 7920            cx,
 7921        );
 7922        multi_buffer.push_excerpts(
 7923            buffer_2.clone(),
 7924            [
 7925                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7926                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7927                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7928            ],
 7929            cx,
 7930        );
 7931        multi_buffer.push_excerpts(
 7932            buffer_3.clone(),
 7933            [
 7934                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7935                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7936                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7937            ],
 7938            cx,
 7939        );
 7940        multi_buffer
 7941    });
 7942    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7943        Editor::new(
 7944            EditorMode::Full,
 7945            multi_buffer,
 7946            Some(project.clone()),
 7947            window,
 7948            cx,
 7949        )
 7950    });
 7951
 7952    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7953        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7954            s.select_ranges(Some(1..2))
 7955        });
 7956        editor.insert("|one|two|three|", window, cx);
 7957    });
 7958    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7959    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7960        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7961            s.select_ranges(Some(60..70))
 7962        });
 7963        editor.insert("|four|five|six|", window, cx);
 7964    });
 7965    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7966
 7967    // First two buffers should be edited, but not the third one.
 7968    assert_eq!(
 7969        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7970        "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}",
 7971    );
 7972    buffer_1.update(cx, |buffer, _| {
 7973        assert!(buffer.is_dirty());
 7974        assert_eq!(
 7975            buffer.text(),
 7976            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7977        )
 7978    });
 7979    buffer_2.update(cx, |buffer, _| {
 7980        assert!(buffer.is_dirty());
 7981        assert_eq!(
 7982            buffer.text(),
 7983            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7984        )
 7985    });
 7986    buffer_3.update(cx, |buffer, _| {
 7987        assert!(!buffer.is_dirty());
 7988        assert_eq!(buffer.text(), sample_text_3,)
 7989    });
 7990    cx.executor().run_until_parked();
 7991
 7992    cx.executor().start_waiting();
 7993    let save = multi_buffer_editor
 7994        .update_in(cx, |editor, window, cx| {
 7995            editor.save(true, project.clone(), window, cx)
 7996        })
 7997        .unwrap();
 7998
 7999    let fake_server = fake_servers.next().await.unwrap();
 8000    fake_server
 8001        .server
 8002        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8003            Ok(Some(vec![lsp::TextEdit::new(
 8004                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8005                format!("[{} formatted]", params.text_document.uri),
 8006            )]))
 8007        })
 8008        .detach();
 8009    save.await;
 8010
 8011    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8012    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8013    assert_eq!(
 8014        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8015        uri!(
 8016            "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}"
 8017        ),
 8018    );
 8019    buffer_1.update(cx, |buffer, _| {
 8020        assert!(!buffer.is_dirty());
 8021        assert_eq!(
 8022            buffer.text(),
 8023            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8024        )
 8025    });
 8026    buffer_2.update(cx, |buffer, _| {
 8027        assert!(!buffer.is_dirty());
 8028        assert_eq!(
 8029            buffer.text(),
 8030            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8031        )
 8032    });
 8033    buffer_3.update(cx, |buffer, _| {
 8034        assert!(!buffer.is_dirty());
 8035        assert_eq!(buffer.text(), sample_text_3,)
 8036    });
 8037}
 8038
 8039#[gpui::test]
 8040async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8041    init_test(cx, |_| {});
 8042
 8043    let fs = FakeFs::new(cx.executor());
 8044    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8045
 8046    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8047
 8048    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8049    language_registry.add(rust_lang());
 8050    let mut fake_servers = language_registry.register_fake_lsp(
 8051        "Rust",
 8052        FakeLspAdapter {
 8053            capabilities: lsp::ServerCapabilities {
 8054                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8055                ..Default::default()
 8056            },
 8057            ..Default::default()
 8058        },
 8059    );
 8060
 8061    let buffer = project
 8062        .update(cx, |project, cx| {
 8063            project.open_local_buffer(path!("/file.rs"), cx)
 8064        })
 8065        .await
 8066        .unwrap();
 8067
 8068    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8069    let (editor, cx) = cx.add_window_view(|window, cx| {
 8070        build_editor_with_project(project.clone(), buffer, window, cx)
 8071    });
 8072    editor.update_in(cx, |editor, window, cx| {
 8073        editor.set_text("one\ntwo\nthree\n", window, cx)
 8074    });
 8075    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8076
 8077    cx.executor().start_waiting();
 8078    let fake_server = fake_servers.next().await.unwrap();
 8079
 8080    let save = editor
 8081        .update_in(cx, |editor, window, cx| {
 8082            editor.save(true, project.clone(), window, cx)
 8083        })
 8084        .unwrap();
 8085    fake_server
 8086        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8087            assert_eq!(
 8088                params.text_document.uri,
 8089                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8090            );
 8091            assert_eq!(params.options.tab_size, 4);
 8092            Ok(Some(vec![lsp::TextEdit::new(
 8093                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8094                ", ".to_string(),
 8095            )]))
 8096        })
 8097        .next()
 8098        .await;
 8099    cx.executor().start_waiting();
 8100    save.await;
 8101    assert_eq!(
 8102        editor.update(cx, |editor, cx| editor.text(cx)),
 8103        "one, two\nthree\n"
 8104    );
 8105    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8106
 8107    editor.update_in(cx, |editor, window, cx| {
 8108        editor.set_text("one\ntwo\nthree\n", window, cx)
 8109    });
 8110    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8111
 8112    // Ensure we can still save even if formatting hangs.
 8113    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8114        move |params, _| async move {
 8115            assert_eq!(
 8116                params.text_document.uri,
 8117                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8118            );
 8119            futures::future::pending::<()>().await;
 8120            unreachable!()
 8121        },
 8122    );
 8123    let save = editor
 8124        .update_in(cx, |editor, window, cx| {
 8125            editor.save(true, project.clone(), window, cx)
 8126        })
 8127        .unwrap();
 8128    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8129    cx.executor().start_waiting();
 8130    save.await;
 8131    assert_eq!(
 8132        editor.update(cx, |editor, cx| editor.text(cx)),
 8133        "one\ntwo\nthree\n"
 8134    );
 8135    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8136
 8137    // For non-dirty buffer, no formatting request should be sent
 8138    let save = editor
 8139        .update_in(cx, |editor, window, cx| {
 8140            editor.save(true, project.clone(), window, cx)
 8141        })
 8142        .unwrap();
 8143    let _pending_format_request = fake_server
 8144        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8145            panic!("Should not be invoked on non-dirty buffer");
 8146        })
 8147        .next();
 8148    cx.executor().start_waiting();
 8149    save.await;
 8150
 8151    // Set Rust language override and assert overridden tabsize is sent to language server
 8152    update_test_language_settings(cx, |settings| {
 8153        settings.languages.insert(
 8154            "Rust".into(),
 8155            LanguageSettingsContent {
 8156                tab_size: NonZeroU32::new(8),
 8157                ..Default::default()
 8158            },
 8159        );
 8160    });
 8161
 8162    editor.update_in(cx, |editor, window, cx| {
 8163        editor.set_text("somehting_new\n", window, cx)
 8164    });
 8165    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8166    let save = editor
 8167        .update_in(cx, |editor, window, cx| {
 8168            editor.save(true, project.clone(), window, cx)
 8169        })
 8170        .unwrap();
 8171    fake_server
 8172        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8173            assert_eq!(
 8174                params.text_document.uri,
 8175                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8176            );
 8177            assert_eq!(params.options.tab_size, 8);
 8178            Ok(Some(vec![]))
 8179        })
 8180        .next()
 8181        .await;
 8182    cx.executor().start_waiting();
 8183    save.await;
 8184}
 8185
 8186#[gpui::test]
 8187async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8188    init_test(cx, |settings| {
 8189        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8190            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8191        ))
 8192    });
 8193
 8194    let fs = FakeFs::new(cx.executor());
 8195    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8196
 8197    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8198
 8199    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8200    language_registry.add(Arc::new(Language::new(
 8201        LanguageConfig {
 8202            name: "Rust".into(),
 8203            matcher: LanguageMatcher {
 8204                path_suffixes: vec!["rs".to_string()],
 8205                ..Default::default()
 8206            },
 8207            ..LanguageConfig::default()
 8208        },
 8209        Some(tree_sitter_rust::LANGUAGE.into()),
 8210    )));
 8211    update_test_language_settings(cx, |settings| {
 8212        // Enable Prettier formatting for the same buffer, and ensure
 8213        // LSP is called instead of Prettier.
 8214        settings.defaults.prettier = Some(PrettierSettings {
 8215            allowed: true,
 8216            ..PrettierSettings::default()
 8217        });
 8218    });
 8219    let mut fake_servers = language_registry.register_fake_lsp(
 8220        "Rust",
 8221        FakeLspAdapter {
 8222            capabilities: lsp::ServerCapabilities {
 8223                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8224                ..Default::default()
 8225            },
 8226            ..Default::default()
 8227        },
 8228    );
 8229
 8230    let buffer = project
 8231        .update(cx, |project, cx| {
 8232            project.open_local_buffer(path!("/file.rs"), cx)
 8233        })
 8234        .await
 8235        .unwrap();
 8236
 8237    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8238    let (editor, cx) = cx.add_window_view(|window, cx| {
 8239        build_editor_with_project(project.clone(), buffer, window, cx)
 8240    });
 8241    editor.update_in(cx, |editor, window, cx| {
 8242        editor.set_text("one\ntwo\nthree\n", window, cx)
 8243    });
 8244
 8245    cx.executor().start_waiting();
 8246    let fake_server = fake_servers.next().await.unwrap();
 8247
 8248    let format = editor
 8249        .update_in(cx, |editor, window, cx| {
 8250            editor.perform_format(
 8251                project.clone(),
 8252                FormatTrigger::Manual,
 8253                FormatTarget::Buffers,
 8254                window,
 8255                cx,
 8256            )
 8257        })
 8258        .unwrap();
 8259    fake_server
 8260        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8261            assert_eq!(
 8262                params.text_document.uri,
 8263                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8264            );
 8265            assert_eq!(params.options.tab_size, 4);
 8266            Ok(Some(vec![lsp::TextEdit::new(
 8267                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8268                ", ".to_string(),
 8269            )]))
 8270        })
 8271        .next()
 8272        .await;
 8273    cx.executor().start_waiting();
 8274    format.await;
 8275    assert_eq!(
 8276        editor.update(cx, |editor, cx| editor.text(cx)),
 8277        "one, two\nthree\n"
 8278    );
 8279
 8280    editor.update_in(cx, |editor, window, cx| {
 8281        editor.set_text("one\ntwo\nthree\n", window, cx)
 8282    });
 8283    // Ensure we don't lock if formatting hangs.
 8284    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8285        move |params, _| async move {
 8286            assert_eq!(
 8287                params.text_document.uri,
 8288                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8289            );
 8290            futures::future::pending::<()>().await;
 8291            unreachable!()
 8292        },
 8293    );
 8294    let format = editor
 8295        .update_in(cx, |editor, window, cx| {
 8296            editor.perform_format(
 8297                project,
 8298                FormatTrigger::Manual,
 8299                FormatTarget::Buffers,
 8300                window,
 8301                cx,
 8302            )
 8303        })
 8304        .unwrap();
 8305    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8306    cx.executor().start_waiting();
 8307    format.await;
 8308    assert_eq!(
 8309        editor.update(cx, |editor, cx| editor.text(cx)),
 8310        "one\ntwo\nthree\n"
 8311    );
 8312}
 8313
 8314#[gpui::test]
 8315async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8316    init_test(cx, |settings| {
 8317        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8318            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8319        ))
 8320    });
 8321
 8322    let fs = FakeFs::new(cx.executor());
 8323    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8324
 8325    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8326
 8327    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8328    language_registry.add(Arc::new(Language::new(
 8329        LanguageConfig {
 8330            name: "TypeScript".into(),
 8331            matcher: LanguageMatcher {
 8332                path_suffixes: vec!["ts".to_string()],
 8333                ..Default::default()
 8334            },
 8335            ..LanguageConfig::default()
 8336        },
 8337        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8338    )));
 8339    update_test_language_settings(cx, |settings| {
 8340        settings.defaults.prettier = Some(PrettierSettings {
 8341            allowed: true,
 8342            ..PrettierSettings::default()
 8343        });
 8344    });
 8345    let mut fake_servers = language_registry.register_fake_lsp(
 8346        "TypeScript",
 8347        FakeLspAdapter {
 8348            capabilities: lsp::ServerCapabilities {
 8349                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8350                ..Default::default()
 8351            },
 8352            ..Default::default()
 8353        },
 8354    );
 8355
 8356    let buffer = project
 8357        .update(cx, |project, cx| {
 8358            project.open_local_buffer(path!("/file.ts"), cx)
 8359        })
 8360        .await
 8361        .unwrap();
 8362
 8363    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8364    let (editor, cx) = cx.add_window_view(|window, cx| {
 8365        build_editor_with_project(project.clone(), buffer, window, cx)
 8366    });
 8367    editor.update_in(cx, |editor, window, cx| {
 8368        editor.set_text(
 8369            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8370            window,
 8371            cx,
 8372        )
 8373    });
 8374
 8375    cx.executor().start_waiting();
 8376    let fake_server = fake_servers.next().await.unwrap();
 8377
 8378    let format = editor
 8379        .update_in(cx, |editor, window, cx| {
 8380            editor.perform_code_action_kind(
 8381                project.clone(),
 8382                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8383                window,
 8384                cx,
 8385            )
 8386        })
 8387        .unwrap();
 8388    fake_server
 8389        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8390            assert_eq!(
 8391                params.text_document.uri,
 8392                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8393            );
 8394            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8395                lsp::CodeAction {
 8396                    title: "Organize Imports".to_string(),
 8397                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8398                    edit: Some(lsp::WorkspaceEdit {
 8399                        changes: Some(
 8400                            [(
 8401                                params.text_document.uri.clone(),
 8402                                vec![lsp::TextEdit::new(
 8403                                    lsp::Range::new(
 8404                                        lsp::Position::new(1, 0),
 8405                                        lsp::Position::new(2, 0),
 8406                                    ),
 8407                                    "".to_string(),
 8408                                )],
 8409                            )]
 8410                            .into_iter()
 8411                            .collect(),
 8412                        ),
 8413                        ..Default::default()
 8414                    }),
 8415                    ..Default::default()
 8416                },
 8417            )]))
 8418        })
 8419        .next()
 8420        .await;
 8421    cx.executor().start_waiting();
 8422    format.await;
 8423    assert_eq!(
 8424        editor.update(cx, |editor, cx| editor.text(cx)),
 8425        "import { a } from 'module';\n\nconst x = a;\n"
 8426    );
 8427
 8428    editor.update_in(cx, |editor, window, cx| {
 8429        editor.set_text(
 8430            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8431            window,
 8432            cx,
 8433        )
 8434    });
 8435    // Ensure we don't lock if code action hangs.
 8436    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8437        move |params, _| async move {
 8438            assert_eq!(
 8439                params.text_document.uri,
 8440                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8441            );
 8442            futures::future::pending::<()>().await;
 8443            unreachable!()
 8444        },
 8445    );
 8446    let format = editor
 8447        .update_in(cx, |editor, window, cx| {
 8448            editor.perform_code_action_kind(
 8449                project,
 8450                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8451                window,
 8452                cx,
 8453            )
 8454        })
 8455        .unwrap();
 8456    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8457    cx.executor().start_waiting();
 8458    format.await;
 8459    assert_eq!(
 8460        editor.update(cx, |editor, cx| editor.text(cx)),
 8461        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8462    );
 8463}
 8464
 8465#[gpui::test]
 8466async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8467    init_test(cx, |_| {});
 8468
 8469    let mut cx = EditorLspTestContext::new_rust(
 8470        lsp::ServerCapabilities {
 8471            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8472            ..Default::default()
 8473        },
 8474        cx,
 8475    )
 8476    .await;
 8477
 8478    cx.set_state(indoc! {"
 8479        one.twoˇ
 8480    "});
 8481
 8482    // The format request takes a long time. When it completes, it inserts
 8483    // a newline and an indent before the `.`
 8484    cx.lsp
 8485        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8486            let executor = cx.background_executor().clone();
 8487            async move {
 8488                executor.timer(Duration::from_millis(100)).await;
 8489                Ok(Some(vec![lsp::TextEdit {
 8490                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8491                    new_text: "\n    ".into(),
 8492                }]))
 8493            }
 8494        });
 8495
 8496    // Submit a format request.
 8497    let format_1 = cx
 8498        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8499        .unwrap();
 8500    cx.executor().run_until_parked();
 8501
 8502    // Submit a second format request.
 8503    let format_2 = cx
 8504        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8505        .unwrap();
 8506    cx.executor().run_until_parked();
 8507
 8508    // Wait for both format requests to complete
 8509    cx.executor().advance_clock(Duration::from_millis(200));
 8510    cx.executor().start_waiting();
 8511    format_1.await.unwrap();
 8512    cx.executor().start_waiting();
 8513    format_2.await.unwrap();
 8514
 8515    // The formatting edits only happens once.
 8516    cx.assert_editor_state(indoc! {"
 8517        one
 8518            .twoˇ
 8519    "});
 8520}
 8521
 8522#[gpui::test]
 8523async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8524    init_test(cx, |settings| {
 8525        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8526    });
 8527
 8528    let mut cx = EditorLspTestContext::new_rust(
 8529        lsp::ServerCapabilities {
 8530            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8531            ..Default::default()
 8532        },
 8533        cx,
 8534    )
 8535    .await;
 8536
 8537    // Set up a buffer white some trailing whitespace and no trailing newline.
 8538    cx.set_state(
 8539        &[
 8540            "one ",   //
 8541            "twoˇ",   //
 8542            "three ", //
 8543            "four",   //
 8544        ]
 8545        .join("\n"),
 8546    );
 8547
 8548    // Submit a format request.
 8549    let format = cx
 8550        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8551        .unwrap();
 8552
 8553    // Record which buffer changes have been sent to the language server
 8554    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8555    cx.lsp
 8556        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8557            let buffer_changes = buffer_changes.clone();
 8558            move |params, _| {
 8559                buffer_changes.lock().extend(
 8560                    params
 8561                        .content_changes
 8562                        .into_iter()
 8563                        .map(|e| (e.range.unwrap(), e.text)),
 8564                );
 8565            }
 8566        });
 8567
 8568    // Handle formatting requests to the language server.
 8569    cx.lsp
 8570        .set_request_handler::<lsp::request::Formatting, _, _>({
 8571            let buffer_changes = buffer_changes.clone();
 8572            move |_, _| {
 8573                // When formatting is requested, trailing whitespace has already been stripped,
 8574                // and the trailing newline has already been added.
 8575                assert_eq!(
 8576                    &buffer_changes.lock()[1..],
 8577                    &[
 8578                        (
 8579                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8580                            "".into()
 8581                        ),
 8582                        (
 8583                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8584                            "".into()
 8585                        ),
 8586                        (
 8587                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8588                            "\n".into()
 8589                        ),
 8590                    ]
 8591                );
 8592
 8593                // Insert blank lines between each line of the buffer.
 8594                async move {
 8595                    Ok(Some(vec![
 8596                        lsp::TextEdit {
 8597                            range: lsp::Range::new(
 8598                                lsp::Position::new(1, 0),
 8599                                lsp::Position::new(1, 0),
 8600                            ),
 8601                            new_text: "\n".into(),
 8602                        },
 8603                        lsp::TextEdit {
 8604                            range: lsp::Range::new(
 8605                                lsp::Position::new(2, 0),
 8606                                lsp::Position::new(2, 0),
 8607                            ),
 8608                            new_text: "\n".into(),
 8609                        },
 8610                    ]))
 8611                }
 8612            }
 8613        });
 8614
 8615    // After formatting the buffer, the trailing whitespace is stripped,
 8616    // a newline is appended, and the edits provided by the language server
 8617    // have been applied.
 8618    format.await.unwrap();
 8619    cx.assert_editor_state(
 8620        &[
 8621            "one",   //
 8622            "",      //
 8623            "twoˇ",  //
 8624            "",      //
 8625            "three", //
 8626            "four",  //
 8627            "",      //
 8628        ]
 8629        .join("\n"),
 8630    );
 8631
 8632    // Undoing the formatting undoes the trailing whitespace removal, the
 8633    // trailing newline, and the LSP edits.
 8634    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8635    cx.assert_editor_state(
 8636        &[
 8637            "one ",   //
 8638            "twoˇ",   //
 8639            "three ", //
 8640            "four",   //
 8641        ]
 8642        .join("\n"),
 8643    );
 8644}
 8645
 8646#[gpui::test]
 8647async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8648    cx: &mut TestAppContext,
 8649) {
 8650    init_test(cx, |_| {});
 8651
 8652    cx.update(|cx| {
 8653        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8654            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8655                settings.auto_signature_help = Some(true);
 8656            });
 8657        });
 8658    });
 8659
 8660    let mut cx = EditorLspTestContext::new_rust(
 8661        lsp::ServerCapabilities {
 8662            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8663                ..Default::default()
 8664            }),
 8665            ..Default::default()
 8666        },
 8667        cx,
 8668    )
 8669    .await;
 8670
 8671    let language = Language::new(
 8672        LanguageConfig {
 8673            name: "Rust".into(),
 8674            brackets: BracketPairConfig {
 8675                pairs: vec![
 8676                    BracketPair {
 8677                        start: "{".to_string(),
 8678                        end: "}".to_string(),
 8679                        close: true,
 8680                        surround: true,
 8681                        newline: true,
 8682                    },
 8683                    BracketPair {
 8684                        start: "(".to_string(),
 8685                        end: ")".to_string(),
 8686                        close: true,
 8687                        surround: true,
 8688                        newline: true,
 8689                    },
 8690                    BracketPair {
 8691                        start: "/*".to_string(),
 8692                        end: " */".to_string(),
 8693                        close: true,
 8694                        surround: true,
 8695                        newline: true,
 8696                    },
 8697                    BracketPair {
 8698                        start: "[".to_string(),
 8699                        end: "]".to_string(),
 8700                        close: false,
 8701                        surround: false,
 8702                        newline: true,
 8703                    },
 8704                    BracketPair {
 8705                        start: "\"".to_string(),
 8706                        end: "\"".to_string(),
 8707                        close: true,
 8708                        surround: true,
 8709                        newline: false,
 8710                    },
 8711                    BracketPair {
 8712                        start: "<".to_string(),
 8713                        end: ">".to_string(),
 8714                        close: false,
 8715                        surround: true,
 8716                        newline: true,
 8717                    },
 8718                ],
 8719                ..Default::default()
 8720            },
 8721            autoclose_before: "})]".to_string(),
 8722            ..Default::default()
 8723        },
 8724        Some(tree_sitter_rust::LANGUAGE.into()),
 8725    );
 8726    let language = Arc::new(language);
 8727
 8728    cx.language_registry().add(language.clone());
 8729    cx.update_buffer(|buffer, cx| {
 8730        buffer.set_language(Some(language), cx);
 8731    });
 8732
 8733    cx.set_state(
 8734        &r#"
 8735            fn main() {
 8736                sampleˇ
 8737            }
 8738        "#
 8739        .unindent(),
 8740    );
 8741
 8742    cx.update_editor(|editor, window, cx| {
 8743        editor.handle_input("(", window, cx);
 8744    });
 8745    cx.assert_editor_state(
 8746        &"
 8747            fn main() {
 8748                sample(ˇ)
 8749            }
 8750        "
 8751        .unindent(),
 8752    );
 8753
 8754    let mocked_response = lsp::SignatureHelp {
 8755        signatures: vec![lsp::SignatureInformation {
 8756            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8757            documentation: None,
 8758            parameters: Some(vec![
 8759                lsp::ParameterInformation {
 8760                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8761                    documentation: None,
 8762                },
 8763                lsp::ParameterInformation {
 8764                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8765                    documentation: None,
 8766                },
 8767            ]),
 8768            active_parameter: None,
 8769        }],
 8770        active_signature: Some(0),
 8771        active_parameter: Some(0),
 8772    };
 8773    handle_signature_help_request(&mut cx, mocked_response).await;
 8774
 8775    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8776        .await;
 8777
 8778    cx.editor(|editor, _, _| {
 8779        let signature_help_state = editor.signature_help_state.popover().cloned();
 8780        assert_eq!(
 8781            signature_help_state.unwrap().label,
 8782            "param1: u8, param2: u8"
 8783        );
 8784    });
 8785}
 8786
 8787#[gpui::test]
 8788async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8789    init_test(cx, |_| {});
 8790
 8791    cx.update(|cx| {
 8792        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8793            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8794                settings.auto_signature_help = Some(false);
 8795                settings.show_signature_help_after_edits = Some(false);
 8796            });
 8797        });
 8798    });
 8799
 8800    let mut cx = EditorLspTestContext::new_rust(
 8801        lsp::ServerCapabilities {
 8802            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8803                ..Default::default()
 8804            }),
 8805            ..Default::default()
 8806        },
 8807        cx,
 8808    )
 8809    .await;
 8810
 8811    let language = Language::new(
 8812        LanguageConfig {
 8813            name: "Rust".into(),
 8814            brackets: BracketPairConfig {
 8815                pairs: vec![
 8816                    BracketPair {
 8817                        start: "{".to_string(),
 8818                        end: "}".to_string(),
 8819                        close: true,
 8820                        surround: true,
 8821                        newline: true,
 8822                    },
 8823                    BracketPair {
 8824                        start: "(".to_string(),
 8825                        end: ")".to_string(),
 8826                        close: true,
 8827                        surround: true,
 8828                        newline: true,
 8829                    },
 8830                    BracketPair {
 8831                        start: "/*".to_string(),
 8832                        end: " */".to_string(),
 8833                        close: true,
 8834                        surround: true,
 8835                        newline: true,
 8836                    },
 8837                    BracketPair {
 8838                        start: "[".to_string(),
 8839                        end: "]".to_string(),
 8840                        close: false,
 8841                        surround: false,
 8842                        newline: true,
 8843                    },
 8844                    BracketPair {
 8845                        start: "\"".to_string(),
 8846                        end: "\"".to_string(),
 8847                        close: true,
 8848                        surround: true,
 8849                        newline: false,
 8850                    },
 8851                    BracketPair {
 8852                        start: "<".to_string(),
 8853                        end: ">".to_string(),
 8854                        close: false,
 8855                        surround: true,
 8856                        newline: true,
 8857                    },
 8858                ],
 8859                ..Default::default()
 8860            },
 8861            autoclose_before: "})]".to_string(),
 8862            ..Default::default()
 8863        },
 8864        Some(tree_sitter_rust::LANGUAGE.into()),
 8865    );
 8866    let language = Arc::new(language);
 8867
 8868    cx.language_registry().add(language.clone());
 8869    cx.update_buffer(|buffer, cx| {
 8870        buffer.set_language(Some(language), cx);
 8871    });
 8872
 8873    // Ensure that signature_help is not called when no signature help is enabled.
 8874    cx.set_state(
 8875        &r#"
 8876            fn main() {
 8877                sampleˇ
 8878            }
 8879        "#
 8880        .unindent(),
 8881    );
 8882    cx.update_editor(|editor, window, cx| {
 8883        editor.handle_input("(", window, cx);
 8884    });
 8885    cx.assert_editor_state(
 8886        &"
 8887            fn main() {
 8888                sample(ˇ)
 8889            }
 8890        "
 8891        .unindent(),
 8892    );
 8893    cx.editor(|editor, _, _| {
 8894        assert!(editor.signature_help_state.task().is_none());
 8895    });
 8896
 8897    let mocked_response = lsp::SignatureHelp {
 8898        signatures: vec![lsp::SignatureInformation {
 8899            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8900            documentation: None,
 8901            parameters: Some(vec![
 8902                lsp::ParameterInformation {
 8903                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8904                    documentation: None,
 8905                },
 8906                lsp::ParameterInformation {
 8907                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8908                    documentation: None,
 8909                },
 8910            ]),
 8911            active_parameter: None,
 8912        }],
 8913        active_signature: Some(0),
 8914        active_parameter: Some(0),
 8915    };
 8916
 8917    // Ensure that signature_help is called when enabled afte edits
 8918    cx.update(|_, cx| {
 8919        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8920            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8921                settings.auto_signature_help = Some(false);
 8922                settings.show_signature_help_after_edits = Some(true);
 8923            });
 8924        });
 8925    });
 8926    cx.set_state(
 8927        &r#"
 8928            fn main() {
 8929                sampleˇ
 8930            }
 8931        "#
 8932        .unindent(),
 8933    );
 8934    cx.update_editor(|editor, window, cx| {
 8935        editor.handle_input("(", window, cx);
 8936    });
 8937    cx.assert_editor_state(
 8938        &"
 8939            fn main() {
 8940                sample(ˇ)
 8941            }
 8942        "
 8943        .unindent(),
 8944    );
 8945    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8946    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8947        .await;
 8948    cx.update_editor(|editor, _, _| {
 8949        let signature_help_state = editor.signature_help_state.popover().cloned();
 8950        assert!(signature_help_state.is_some());
 8951        assert_eq!(
 8952            signature_help_state.unwrap().label,
 8953            "param1: u8, param2: u8"
 8954        );
 8955        editor.signature_help_state = SignatureHelpState::default();
 8956    });
 8957
 8958    // Ensure that signature_help is called when auto signature help override is enabled
 8959    cx.update(|_, cx| {
 8960        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8961            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8962                settings.auto_signature_help = Some(true);
 8963                settings.show_signature_help_after_edits = Some(false);
 8964            });
 8965        });
 8966    });
 8967    cx.set_state(
 8968        &r#"
 8969            fn main() {
 8970                sampleˇ
 8971            }
 8972        "#
 8973        .unindent(),
 8974    );
 8975    cx.update_editor(|editor, window, cx| {
 8976        editor.handle_input("(", window, cx);
 8977    });
 8978    cx.assert_editor_state(
 8979        &"
 8980            fn main() {
 8981                sample(ˇ)
 8982            }
 8983        "
 8984        .unindent(),
 8985    );
 8986    handle_signature_help_request(&mut cx, mocked_response).await;
 8987    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8988        .await;
 8989    cx.editor(|editor, _, _| {
 8990        let signature_help_state = editor.signature_help_state.popover().cloned();
 8991        assert!(signature_help_state.is_some());
 8992        assert_eq!(
 8993            signature_help_state.unwrap().label,
 8994            "param1: u8, param2: u8"
 8995        );
 8996    });
 8997}
 8998
 8999#[gpui::test]
 9000async fn test_signature_help(cx: &mut TestAppContext) {
 9001    init_test(cx, |_| {});
 9002    cx.update(|cx| {
 9003        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9004            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9005                settings.auto_signature_help = Some(true);
 9006            });
 9007        });
 9008    });
 9009
 9010    let mut cx = EditorLspTestContext::new_rust(
 9011        lsp::ServerCapabilities {
 9012            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9013                ..Default::default()
 9014            }),
 9015            ..Default::default()
 9016        },
 9017        cx,
 9018    )
 9019    .await;
 9020
 9021    // A test that directly calls `show_signature_help`
 9022    cx.update_editor(|editor, window, cx| {
 9023        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9024    });
 9025
 9026    let mocked_response = lsp::SignatureHelp {
 9027        signatures: vec![lsp::SignatureInformation {
 9028            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9029            documentation: None,
 9030            parameters: Some(vec![
 9031                lsp::ParameterInformation {
 9032                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9033                    documentation: None,
 9034                },
 9035                lsp::ParameterInformation {
 9036                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9037                    documentation: None,
 9038                },
 9039            ]),
 9040            active_parameter: None,
 9041        }],
 9042        active_signature: Some(0),
 9043        active_parameter: Some(0),
 9044    };
 9045    handle_signature_help_request(&mut cx, mocked_response).await;
 9046
 9047    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9048        .await;
 9049
 9050    cx.editor(|editor, _, _| {
 9051        let signature_help_state = editor.signature_help_state.popover().cloned();
 9052        assert!(signature_help_state.is_some());
 9053        assert_eq!(
 9054            signature_help_state.unwrap().label,
 9055            "param1: u8, param2: u8"
 9056        );
 9057    });
 9058
 9059    // When exiting outside from inside the brackets, `signature_help` is closed.
 9060    cx.set_state(indoc! {"
 9061        fn main() {
 9062            sample(ˇ);
 9063        }
 9064
 9065        fn sample(param1: u8, param2: u8) {}
 9066    "});
 9067
 9068    cx.update_editor(|editor, window, cx| {
 9069        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9070    });
 9071
 9072    let mocked_response = lsp::SignatureHelp {
 9073        signatures: Vec::new(),
 9074        active_signature: None,
 9075        active_parameter: None,
 9076    };
 9077    handle_signature_help_request(&mut cx, mocked_response).await;
 9078
 9079    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9080        .await;
 9081
 9082    cx.editor(|editor, _, _| {
 9083        assert!(!editor.signature_help_state.is_shown());
 9084    });
 9085
 9086    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9087    cx.set_state(indoc! {"
 9088        fn main() {
 9089            sample(ˇ);
 9090        }
 9091
 9092        fn sample(param1: u8, param2: u8) {}
 9093    "});
 9094
 9095    let mocked_response = lsp::SignatureHelp {
 9096        signatures: vec![lsp::SignatureInformation {
 9097            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9098            documentation: None,
 9099            parameters: Some(vec![
 9100                lsp::ParameterInformation {
 9101                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9102                    documentation: None,
 9103                },
 9104                lsp::ParameterInformation {
 9105                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9106                    documentation: None,
 9107                },
 9108            ]),
 9109            active_parameter: None,
 9110        }],
 9111        active_signature: Some(0),
 9112        active_parameter: Some(0),
 9113    };
 9114    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9115    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9116        .await;
 9117    cx.editor(|editor, _, _| {
 9118        assert!(editor.signature_help_state.is_shown());
 9119    });
 9120
 9121    // Restore the popover with more parameter input
 9122    cx.set_state(indoc! {"
 9123        fn main() {
 9124            sample(param1, param2ˇ);
 9125        }
 9126
 9127        fn sample(param1: u8, param2: u8) {}
 9128    "});
 9129
 9130    let mocked_response = lsp::SignatureHelp {
 9131        signatures: vec![lsp::SignatureInformation {
 9132            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9133            documentation: None,
 9134            parameters: Some(vec![
 9135                lsp::ParameterInformation {
 9136                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9137                    documentation: None,
 9138                },
 9139                lsp::ParameterInformation {
 9140                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9141                    documentation: None,
 9142                },
 9143            ]),
 9144            active_parameter: None,
 9145        }],
 9146        active_signature: Some(0),
 9147        active_parameter: Some(1),
 9148    };
 9149    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9150    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9151        .await;
 9152
 9153    // When selecting a range, the popover is gone.
 9154    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9155    cx.update_editor(|editor, window, cx| {
 9156        editor.change_selections(None, window, cx, |s| {
 9157            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9158        })
 9159    });
 9160    cx.assert_editor_state(indoc! {"
 9161        fn main() {
 9162            sample(param1, «ˇparam2»);
 9163        }
 9164
 9165        fn sample(param1: u8, param2: u8) {}
 9166    "});
 9167    cx.editor(|editor, _, _| {
 9168        assert!(!editor.signature_help_state.is_shown());
 9169    });
 9170
 9171    // When unselecting again, the popover is back if within the brackets.
 9172    cx.update_editor(|editor, window, cx| {
 9173        editor.change_selections(None, window, cx, |s| {
 9174            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9175        })
 9176    });
 9177    cx.assert_editor_state(indoc! {"
 9178        fn main() {
 9179            sample(param1, ˇparam2);
 9180        }
 9181
 9182        fn sample(param1: u8, param2: u8) {}
 9183    "});
 9184    handle_signature_help_request(&mut cx, mocked_response).await;
 9185    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9186        .await;
 9187    cx.editor(|editor, _, _| {
 9188        assert!(editor.signature_help_state.is_shown());
 9189    });
 9190
 9191    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9192    cx.update_editor(|editor, window, cx| {
 9193        editor.change_selections(None, window, cx, |s| {
 9194            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9195            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9196        })
 9197    });
 9198    cx.assert_editor_state(indoc! {"
 9199        fn main() {
 9200            sample(param1, ˇparam2);
 9201        }
 9202
 9203        fn sample(param1: u8, param2: u8) {}
 9204    "});
 9205
 9206    let mocked_response = lsp::SignatureHelp {
 9207        signatures: vec![lsp::SignatureInformation {
 9208            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9209            documentation: None,
 9210            parameters: Some(vec![
 9211                lsp::ParameterInformation {
 9212                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9213                    documentation: None,
 9214                },
 9215                lsp::ParameterInformation {
 9216                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9217                    documentation: None,
 9218                },
 9219            ]),
 9220            active_parameter: None,
 9221        }],
 9222        active_signature: Some(0),
 9223        active_parameter: Some(1),
 9224    };
 9225    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9226    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9227        .await;
 9228    cx.update_editor(|editor, _, cx| {
 9229        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9230    });
 9231    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9232        .await;
 9233    cx.update_editor(|editor, window, cx| {
 9234        editor.change_selections(None, window, cx, |s| {
 9235            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9236        })
 9237    });
 9238    cx.assert_editor_state(indoc! {"
 9239        fn main() {
 9240            sample(param1, «ˇparam2»);
 9241        }
 9242
 9243        fn sample(param1: u8, param2: u8) {}
 9244    "});
 9245    cx.update_editor(|editor, window, cx| {
 9246        editor.change_selections(None, window, cx, |s| {
 9247            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9248        })
 9249    });
 9250    cx.assert_editor_state(indoc! {"
 9251        fn main() {
 9252            sample(param1, ˇparam2);
 9253        }
 9254
 9255        fn sample(param1: u8, param2: u8) {}
 9256    "});
 9257    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9258        .await;
 9259}
 9260
 9261#[gpui::test]
 9262async fn test_completion_mode(cx: &mut TestAppContext) {
 9263    init_test(cx, |_| {});
 9264    let mut cx = EditorLspTestContext::new_rust(
 9265        lsp::ServerCapabilities {
 9266            completion_provider: Some(lsp::CompletionOptions {
 9267                resolve_provider: Some(true),
 9268                ..Default::default()
 9269            }),
 9270            ..Default::default()
 9271        },
 9272        cx,
 9273    )
 9274    .await;
 9275
 9276    struct Run {
 9277        run_description: &'static str,
 9278        initial_state: String,
 9279        buffer_marked_text: String,
 9280        completion_text: &'static str,
 9281        expected_with_insert_mode: String,
 9282        expected_with_replace_mode: String,
 9283        expected_with_replace_subsequence_mode: String,
 9284        expected_with_replace_suffix_mode: String,
 9285    }
 9286
 9287    let runs = [
 9288        Run {
 9289            run_description: "Start of word matches completion text",
 9290            initial_state: "before ediˇ after".into(),
 9291            buffer_marked_text: "before <edi|> after".into(),
 9292            completion_text: "editor",
 9293            expected_with_insert_mode: "before editorˇ after".into(),
 9294            expected_with_replace_mode: "before editorˇ after".into(),
 9295            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9296            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9297        },
 9298        Run {
 9299            run_description: "Accept same text at the middle of the word",
 9300            initial_state: "before ediˇtor after".into(),
 9301            buffer_marked_text: "before <edi|tor> after".into(),
 9302            completion_text: "editor",
 9303            expected_with_insert_mode: "before editorˇtor after".into(),
 9304            expected_with_replace_mode: "before ediˇtor after".into(),
 9305            expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
 9306            expected_with_replace_suffix_mode: "before ediˇtor after".into(),
 9307        },
 9308        Run {
 9309            run_description: "End of word matches completion text -- cursor at end",
 9310            initial_state: "before torˇ after".into(),
 9311            buffer_marked_text: "before <tor|> after".into(),
 9312            completion_text: "editor",
 9313            expected_with_insert_mode: "before editorˇ after".into(),
 9314            expected_with_replace_mode: "before editorˇ after".into(),
 9315            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9316            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9317        },
 9318        Run {
 9319            run_description: "End of word matches completion text -- cursor at start",
 9320            initial_state: "before ˇtor after".into(),
 9321            buffer_marked_text: "before <|tor> after".into(),
 9322            completion_text: "editor",
 9323            expected_with_insert_mode: "before editorˇtor after".into(),
 9324            expected_with_replace_mode: "before editorˇ after".into(),
 9325            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9326            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9327        },
 9328        Run {
 9329            run_description: "Prepend text containing whitespace",
 9330            initial_state: "pˇfield: bool".into(),
 9331            buffer_marked_text: "<p|field>: bool".into(),
 9332            completion_text: "pub ",
 9333            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9334            expected_with_replace_mode: "pub ˇ: bool".into(),
 9335            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9336            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9337        },
 9338        Run {
 9339            run_description: "Add element to start of list",
 9340            initial_state: "[element_ˇelement_2]".into(),
 9341            buffer_marked_text: "[<element_|element_2>]".into(),
 9342            completion_text: "element_1",
 9343            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9344            expected_with_replace_mode: "[element_1ˇ]".into(),
 9345            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9346            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9347        },
 9348        Run {
 9349            run_description: "Add element to start of list -- first and second elements are equal",
 9350            initial_state: "[elˇelement]".into(),
 9351            buffer_marked_text: "[<el|element>]".into(),
 9352            completion_text: "element",
 9353            expected_with_insert_mode: "[elementˇelement]".into(),
 9354            expected_with_replace_mode: "[elˇement]".into(),
 9355            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9356            expected_with_replace_suffix_mode: "[elˇement]".into(),
 9357        },
 9358        Run {
 9359            run_description: "Ends with matching suffix",
 9360            initial_state: "SubˇError".into(),
 9361            buffer_marked_text: "<Sub|Error>".into(),
 9362            completion_text: "SubscriptionError",
 9363            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9364            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9365            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9366            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9367        },
 9368        Run {
 9369            run_description: "Suffix is a subsequence -- contiguous",
 9370            initial_state: "SubˇErr".into(),
 9371            buffer_marked_text: "<Sub|Err>".into(),
 9372            completion_text: "SubscriptionError",
 9373            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9374            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9375            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9376            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9377        },
 9378        Run {
 9379            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9380            initial_state: "Suˇscrirr".into(),
 9381            buffer_marked_text: "<Su|scrirr>".into(),
 9382            completion_text: "SubscriptionError",
 9383            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9384            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9385            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9386            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9387        },
 9388        Run {
 9389            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9390            initial_state: "foo(indˇix)".into(),
 9391            buffer_marked_text: "foo(<ind|ix>)".into(),
 9392            completion_text: "node_index",
 9393            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9394            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9395            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9396            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9397        },
 9398    ];
 9399
 9400    for run in runs {
 9401        let run_variations = [
 9402            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9403            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9404            (
 9405                LspInsertMode::ReplaceSubsequence,
 9406                run.expected_with_replace_subsequence_mode,
 9407            ),
 9408            (
 9409                LspInsertMode::ReplaceSuffix,
 9410                run.expected_with_replace_suffix_mode,
 9411            ),
 9412        ];
 9413
 9414        for (lsp_insert_mode, expected_text) in run_variations {
 9415            eprintln!(
 9416                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9417                run.run_description,
 9418            );
 9419
 9420            update_test_language_settings(&mut cx, |settings| {
 9421                settings.defaults.completions = Some(CompletionSettings {
 9422                    lsp_insert_mode,
 9423                    words: WordsCompletionMode::Disabled,
 9424                    lsp: true,
 9425                    lsp_fetch_timeout_ms: 0,
 9426                });
 9427            });
 9428
 9429            cx.set_state(&run.initial_state);
 9430            cx.update_editor(|editor, window, cx| {
 9431                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9432            });
 9433
 9434            let counter = Arc::new(AtomicUsize::new(0));
 9435            handle_completion_request_with_insert_and_replace(
 9436                &mut cx,
 9437                &run.buffer_marked_text,
 9438                vec![run.completion_text],
 9439                counter.clone(),
 9440            )
 9441            .await;
 9442            cx.condition(|editor, _| editor.context_menu_visible())
 9443                .await;
 9444            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9445
 9446            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9447                editor
 9448                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9449                    .unwrap()
 9450            });
 9451            cx.assert_editor_state(&expected_text);
 9452            handle_resolve_completion_request(&mut cx, None).await;
 9453            apply_additional_edits.await.unwrap();
 9454        }
 9455    }
 9456}
 9457
 9458#[gpui::test]
 9459async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9460    init_test(cx, |_| {});
 9461    let mut cx = EditorLspTestContext::new_rust(
 9462        lsp::ServerCapabilities {
 9463            completion_provider: Some(lsp::CompletionOptions {
 9464                resolve_provider: Some(true),
 9465                ..Default::default()
 9466            }),
 9467            ..Default::default()
 9468        },
 9469        cx,
 9470    )
 9471    .await;
 9472
 9473    let initial_state = "SubˇError";
 9474    let buffer_marked_text = "<Sub|Error>";
 9475    let completion_text = "SubscriptionError";
 9476    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9477    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9478
 9479    update_test_language_settings(&mut cx, |settings| {
 9480        settings.defaults.completions = Some(CompletionSettings {
 9481            words: WordsCompletionMode::Disabled,
 9482            // set the opposite here to ensure that the action is overriding the default behavior
 9483            lsp_insert_mode: LspInsertMode::Insert,
 9484            lsp: true,
 9485            lsp_fetch_timeout_ms: 0,
 9486        });
 9487    });
 9488
 9489    cx.set_state(initial_state);
 9490    cx.update_editor(|editor, window, cx| {
 9491        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9492    });
 9493
 9494    let counter = Arc::new(AtomicUsize::new(0));
 9495    handle_completion_request_with_insert_and_replace(
 9496        &mut cx,
 9497        &buffer_marked_text,
 9498        vec![completion_text],
 9499        counter.clone(),
 9500    )
 9501    .await;
 9502    cx.condition(|editor, _| editor.context_menu_visible())
 9503        .await;
 9504    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9505
 9506    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9507        editor
 9508            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9509            .unwrap()
 9510    });
 9511    cx.assert_editor_state(&expected_with_replace_mode);
 9512    handle_resolve_completion_request(&mut cx, None).await;
 9513    apply_additional_edits.await.unwrap();
 9514
 9515    update_test_language_settings(&mut cx, |settings| {
 9516        settings.defaults.completions = Some(CompletionSettings {
 9517            words: WordsCompletionMode::Disabled,
 9518            // set the opposite here to ensure that the action is overriding the default behavior
 9519            lsp_insert_mode: LspInsertMode::Replace,
 9520            lsp: true,
 9521            lsp_fetch_timeout_ms: 0,
 9522        });
 9523    });
 9524
 9525    cx.set_state(initial_state);
 9526    cx.update_editor(|editor, window, cx| {
 9527        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9528    });
 9529    handle_completion_request_with_insert_and_replace(
 9530        &mut cx,
 9531        &buffer_marked_text,
 9532        vec![completion_text],
 9533        counter.clone(),
 9534    )
 9535    .await;
 9536    cx.condition(|editor, _| editor.context_menu_visible())
 9537        .await;
 9538    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9539
 9540    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9541        editor
 9542            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9543            .unwrap()
 9544    });
 9545    cx.assert_editor_state(&expected_with_insert_mode);
 9546    handle_resolve_completion_request(&mut cx, None).await;
 9547    apply_additional_edits.await.unwrap();
 9548}
 9549
 9550#[gpui::test]
 9551async fn test_completion(cx: &mut TestAppContext) {
 9552    init_test(cx, |_| {});
 9553
 9554    let mut cx = EditorLspTestContext::new_rust(
 9555        lsp::ServerCapabilities {
 9556            completion_provider: Some(lsp::CompletionOptions {
 9557                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9558                resolve_provider: Some(true),
 9559                ..Default::default()
 9560            }),
 9561            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9562            ..Default::default()
 9563        },
 9564        cx,
 9565    )
 9566    .await;
 9567    let counter = Arc::new(AtomicUsize::new(0));
 9568
 9569    cx.set_state(indoc! {"
 9570        oneˇ
 9571        two
 9572        three
 9573    "});
 9574    cx.simulate_keystroke(".");
 9575    handle_completion_request(
 9576        &mut cx,
 9577        indoc! {"
 9578            one.|<>
 9579            two
 9580            three
 9581        "},
 9582        vec!["first_completion", "second_completion"],
 9583        counter.clone(),
 9584    )
 9585    .await;
 9586    cx.condition(|editor, _| editor.context_menu_visible())
 9587        .await;
 9588    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9589
 9590    let _handler = handle_signature_help_request(
 9591        &mut cx,
 9592        lsp::SignatureHelp {
 9593            signatures: vec![lsp::SignatureInformation {
 9594                label: "test signature".to_string(),
 9595                documentation: None,
 9596                parameters: Some(vec![lsp::ParameterInformation {
 9597                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9598                    documentation: None,
 9599                }]),
 9600                active_parameter: None,
 9601            }],
 9602            active_signature: None,
 9603            active_parameter: None,
 9604        },
 9605    );
 9606    cx.update_editor(|editor, window, cx| {
 9607        assert!(
 9608            !editor.signature_help_state.is_shown(),
 9609            "No signature help was called for"
 9610        );
 9611        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9612    });
 9613    cx.run_until_parked();
 9614    cx.update_editor(|editor, _, _| {
 9615        assert!(
 9616            !editor.signature_help_state.is_shown(),
 9617            "No signature help should be shown when completions menu is open"
 9618        );
 9619    });
 9620
 9621    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9622        editor.context_menu_next(&Default::default(), window, cx);
 9623        editor
 9624            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9625            .unwrap()
 9626    });
 9627    cx.assert_editor_state(indoc! {"
 9628        one.second_completionˇ
 9629        two
 9630        three
 9631    "});
 9632
 9633    handle_resolve_completion_request(
 9634        &mut cx,
 9635        Some(vec![
 9636            (
 9637                //This overlaps with the primary completion edit which is
 9638                //misbehavior from the LSP spec, test that we filter it out
 9639                indoc! {"
 9640                    one.second_ˇcompletion
 9641                    two
 9642                    threeˇ
 9643                "},
 9644                "overlapping additional edit",
 9645            ),
 9646            (
 9647                indoc! {"
 9648                    one.second_completion
 9649                    two
 9650                    threeˇ
 9651                "},
 9652                "\nadditional edit",
 9653            ),
 9654        ]),
 9655    )
 9656    .await;
 9657    apply_additional_edits.await.unwrap();
 9658    cx.assert_editor_state(indoc! {"
 9659        one.second_completionˇ
 9660        two
 9661        three
 9662        additional edit
 9663    "});
 9664
 9665    cx.set_state(indoc! {"
 9666        one.second_completion
 9667        twoˇ
 9668        threeˇ
 9669        additional edit
 9670    "});
 9671    cx.simulate_keystroke(" ");
 9672    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9673    cx.simulate_keystroke("s");
 9674    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9675
 9676    cx.assert_editor_state(indoc! {"
 9677        one.second_completion
 9678        two sˇ
 9679        three sˇ
 9680        additional edit
 9681    "});
 9682    handle_completion_request(
 9683        &mut cx,
 9684        indoc! {"
 9685            one.second_completion
 9686            two s
 9687            three <s|>
 9688            additional edit
 9689        "},
 9690        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9691        counter.clone(),
 9692    )
 9693    .await;
 9694    cx.condition(|editor, _| editor.context_menu_visible())
 9695        .await;
 9696    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9697
 9698    cx.simulate_keystroke("i");
 9699
 9700    handle_completion_request(
 9701        &mut cx,
 9702        indoc! {"
 9703            one.second_completion
 9704            two si
 9705            three <si|>
 9706            additional edit
 9707        "},
 9708        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9709        counter.clone(),
 9710    )
 9711    .await;
 9712    cx.condition(|editor, _| editor.context_menu_visible())
 9713        .await;
 9714    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9715
 9716    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9717        editor
 9718            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9719            .unwrap()
 9720    });
 9721    cx.assert_editor_state(indoc! {"
 9722        one.second_completion
 9723        two sixth_completionˇ
 9724        three sixth_completionˇ
 9725        additional edit
 9726    "});
 9727
 9728    apply_additional_edits.await.unwrap();
 9729
 9730    update_test_language_settings(&mut cx, |settings| {
 9731        settings.defaults.show_completions_on_input = Some(false);
 9732    });
 9733    cx.set_state("editorˇ");
 9734    cx.simulate_keystroke(".");
 9735    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9736    cx.simulate_keystrokes("c l o");
 9737    cx.assert_editor_state("editor.cloˇ");
 9738    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9739    cx.update_editor(|editor, window, cx| {
 9740        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9741    });
 9742    handle_completion_request(
 9743        &mut cx,
 9744        "editor.<clo|>",
 9745        vec!["close", "clobber"],
 9746        counter.clone(),
 9747    )
 9748    .await;
 9749    cx.condition(|editor, _| editor.context_menu_visible())
 9750        .await;
 9751    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9752
 9753    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9754        editor
 9755            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9756            .unwrap()
 9757    });
 9758    cx.assert_editor_state("editor.closeˇ");
 9759    handle_resolve_completion_request(&mut cx, None).await;
 9760    apply_additional_edits.await.unwrap();
 9761}
 9762
 9763#[gpui::test]
 9764async fn test_word_completion(cx: &mut TestAppContext) {
 9765    let lsp_fetch_timeout_ms = 10;
 9766    init_test(cx, |language_settings| {
 9767        language_settings.defaults.completions = Some(CompletionSettings {
 9768            words: WordsCompletionMode::Fallback,
 9769            lsp: true,
 9770            lsp_fetch_timeout_ms: 10,
 9771            lsp_insert_mode: LspInsertMode::Insert,
 9772        });
 9773    });
 9774
 9775    let mut cx = EditorLspTestContext::new_rust(
 9776        lsp::ServerCapabilities {
 9777            completion_provider: Some(lsp::CompletionOptions {
 9778                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9779                ..lsp::CompletionOptions::default()
 9780            }),
 9781            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9782            ..lsp::ServerCapabilities::default()
 9783        },
 9784        cx,
 9785    )
 9786    .await;
 9787
 9788    let throttle_completions = Arc::new(AtomicBool::new(false));
 9789
 9790    let lsp_throttle_completions = throttle_completions.clone();
 9791    let _completion_requests_handler =
 9792        cx.lsp
 9793            .server
 9794            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9795                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9796                let cx = cx.clone();
 9797                async move {
 9798                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9799                        cx.background_executor()
 9800                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9801                            .await;
 9802                    }
 9803                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9804                        lsp::CompletionItem {
 9805                            label: "first".into(),
 9806                            ..lsp::CompletionItem::default()
 9807                        },
 9808                        lsp::CompletionItem {
 9809                            label: "last".into(),
 9810                            ..lsp::CompletionItem::default()
 9811                        },
 9812                    ])))
 9813                }
 9814            });
 9815
 9816    cx.set_state(indoc! {"
 9817        oneˇ
 9818        two
 9819        three
 9820    "});
 9821    cx.simulate_keystroke(".");
 9822    cx.executor().run_until_parked();
 9823    cx.condition(|editor, _| editor.context_menu_visible())
 9824        .await;
 9825    cx.update_editor(|editor, window, cx| {
 9826        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9827        {
 9828            assert_eq!(
 9829                completion_menu_entries(&menu),
 9830                &["first", "last"],
 9831                "When LSP server is fast to reply, no fallback word completions are used"
 9832            );
 9833        } else {
 9834            panic!("expected completion menu to be open");
 9835        }
 9836        editor.cancel(&Cancel, window, cx);
 9837    });
 9838    cx.executor().run_until_parked();
 9839    cx.condition(|editor, _| !editor.context_menu_visible())
 9840        .await;
 9841
 9842    throttle_completions.store(true, atomic::Ordering::Release);
 9843    cx.simulate_keystroke(".");
 9844    cx.executor()
 9845        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9846    cx.executor().run_until_parked();
 9847    cx.condition(|editor, _| editor.context_menu_visible())
 9848        .await;
 9849    cx.update_editor(|editor, _, _| {
 9850        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9851        {
 9852            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9853                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9854        } else {
 9855            panic!("expected completion menu to be open");
 9856        }
 9857    });
 9858}
 9859
 9860#[gpui::test]
 9861async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9862    init_test(cx, |language_settings| {
 9863        language_settings.defaults.completions = Some(CompletionSettings {
 9864            words: WordsCompletionMode::Enabled,
 9865            lsp: true,
 9866            lsp_fetch_timeout_ms: 0,
 9867            lsp_insert_mode: LspInsertMode::Insert,
 9868        });
 9869    });
 9870
 9871    let mut cx = EditorLspTestContext::new_rust(
 9872        lsp::ServerCapabilities {
 9873            completion_provider: Some(lsp::CompletionOptions {
 9874                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9875                ..lsp::CompletionOptions::default()
 9876            }),
 9877            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9878            ..lsp::ServerCapabilities::default()
 9879        },
 9880        cx,
 9881    )
 9882    .await;
 9883
 9884    let _completion_requests_handler =
 9885        cx.lsp
 9886            .server
 9887            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9888                Ok(Some(lsp::CompletionResponse::Array(vec![
 9889                    lsp::CompletionItem {
 9890                        label: "first".into(),
 9891                        ..lsp::CompletionItem::default()
 9892                    },
 9893                    lsp::CompletionItem {
 9894                        label: "last".into(),
 9895                        ..lsp::CompletionItem::default()
 9896                    },
 9897                ])))
 9898            });
 9899
 9900    cx.set_state(indoc! {"ˇ
 9901        first
 9902        last
 9903        second
 9904    "});
 9905    cx.simulate_keystroke(".");
 9906    cx.executor().run_until_parked();
 9907    cx.condition(|editor, _| editor.context_menu_visible())
 9908        .await;
 9909    cx.update_editor(|editor, _, _| {
 9910        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9911        {
 9912            assert_eq!(
 9913                completion_menu_entries(&menu),
 9914                &["first", "last", "second"],
 9915                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9916            );
 9917        } else {
 9918            panic!("expected completion menu to be open");
 9919        }
 9920    });
 9921}
 9922
 9923#[gpui::test]
 9924async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9925    init_test(cx, |language_settings| {
 9926        language_settings.defaults.completions = Some(CompletionSettings {
 9927            words: WordsCompletionMode::Disabled,
 9928            lsp: true,
 9929            lsp_fetch_timeout_ms: 0,
 9930            lsp_insert_mode: LspInsertMode::Insert,
 9931        });
 9932    });
 9933
 9934    let mut cx = EditorLspTestContext::new_rust(
 9935        lsp::ServerCapabilities {
 9936            completion_provider: Some(lsp::CompletionOptions {
 9937                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9938                ..lsp::CompletionOptions::default()
 9939            }),
 9940            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9941            ..lsp::ServerCapabilities::default()
 9942        },
 9943        cx,
 9944    )
 9945    .await;
 9946
 9947    let _completion_requests_handler =
 9948        cx.lsp
 9949            .server
 9950            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9951                panic!("LSP completions should not be queried when dealing with word completions")
 9952            });
 9953
 9954    cx.set_state(indoc! {"ˇ
 9955        first
 9956        last
 9957        second
 9958    "});
 9959    cx.update_editor(|editor, window, cx| {
 9960        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9961    });
 9962    cx.executor().run_until_parked();
 9963    cx.condition(|editor, _| editor.context_menu_visible())
 9964        .await;
 9965    cx.update_editor(|editor, _, _| {
 9966        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9967        {
 9968            assert_eq!(
 9969                completion_menu_entries(&menu),
 9970                &["first", "last", "second"],
 9971                "`ShowWordCompletions` action should show word completions"
 9972            );
 9973        } else {
 9974            panic!("expected completion menu to be open");
 9975        }
 9976    });
 9977
 9978    cx.simulate_keystroke("l");
 9979    cx.executor().run_until_parked();
 9980    cx.condition(|editor, _| editor.context_menu_visible())
 9981        .await;
 9982    cx.update_editor(|editor, _, _| {
 9983        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9984        {
 9985            assert_eq!(
 9986                completion_menu_entries(&menu),
 9987                &["last"],
 9988                "After showing word completions, further editing should filter them and not query the LSP"
 9989            );
 9990        } else {
 9991            panic!("expected completion menu to be open");
 9992        }
 9993    });
 9994}
 9995
 9996#[gpui::test]
 9997async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9998    init_test(cx, |language_settings| {
 9999        language_settings.defaults.completions = Some(CompletionSettings {
10000            words: WordsCompletionMode::Fallback,
10001            lsp: false,
10002            lsp_fetch_timeout_ms: 0,
10003            lsp_insert_mode: LspInsertMode::Insert,
10004        });
10005    });
10006
10007    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10008
10009    cx.set_state(indoc! {"ˇ
10010        0_usize
10011        let
10012        33
10013        4.5f32
10014    "});
10015    cx.update_editor(|editor, window, cx| {
10016        editor.show_completions(&ShowCompletions::default(), window, cx);
10017    });
10018    cx.executor().run_until_parked();
10019    cx.condition(|editor, _| editor.context_menu_visible())
10020        .await;
10021    cx.update_editor(|editor, window, cx| {
10022        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10023        {
10024            assert_eq!(
10025                completion_menu_entries(&menu),
10026                &["let"],
10027                "With no digits in the completion query, no digits should be in the word completions"
10028            );
10029        } else {
10030            panic!("expected completion menu to be open");
10031        }
10032        editor.cancel(&Cancel, window, cx);
10033    });
10034
10035    cx.set_state(indoc! {"10036        0_usize
10037        let
10038        3
10039        33.35f32
10040    "});
10041    cx.update_editor(|editor, window, cx| {
10042        editor.show_completions(&ShowCompletions::default(), window, cx);
10043    });
10044    cx.executor().run_until_parked();
10045    cx.condition(|editor, _| editor.context_menu_visible())
10046        .await;
10047    cx.update_editor(|editor, _, _| {
10048        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10049        {
10050            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10051                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10052        } else {
10053            panic!("expected completion menu to be open");
10054        }
10055    });
10056}
10057
10058fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10059    let position = || lsp::Position {
10060        line: params.text_document_position.position.line,
10061        character: params.text_document_position.position.character,
10062    };
10063    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10064        range: lsp::Range {
10065            start: position(),
10066            end: position(),
10067        },
10068        new_text: text.to_string(),
10069    }))
10070}
10071
10072#[gpui::test]
10073async fn test_multiline_completion(cx: &mut TestAppContext) {
10074    init_test(cx, |_| {});
10075
10076    let fs = FakeFs::new(cx.executor());
10077    fs.insert_tree(
10078        path!("/a"),
10079        json!({
10080            "main.ts": "a",
10081        }),
10082    )
10083    .await;
10084
10085    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10086    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10087    let typescript_language = Arc::new(Language::new(
10088        LanguageConfig {
10089            name: "TypeScript".into(),
10090            matcher: LanguageMatcher {
10091                path_suffixes: vec!["ts".to_string()],
10092                ..LanguageMatcher::default()
10093            },
10094            line_comments: vec!["// ".into()],
10095            ..LanguageConfig::default()
10096        },
10097        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10098    ));
10099    language_registry.add(typescript_language.clone());
10100    let mut fake_servers = language_registry.register_fake_lsp(
10101        "TypeScript",
10102        FakeLspAdapter {
10103            capabilities: lsp::ServerCapabilities {
10104                completion_provider: Some(lsp::CompletionOptions {
10105                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10106                    ..lsp::CompletionOptions::default()
10107                }),
10108                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10109                ..lsp::ServerCapabilities::default()
10110            },
10111            // Emulate vtsls label generation
10112            label_for_completion: Some(Box::new(|item, _| {
10113                let text = if let Some(description) = item
10114                    .label_details
10115                    .as_ref()
10116                    .and_then(|label_details| label_details.description.as_ref())
10117                {
10118                    format!("{} {}", item.label, description)
10119                } else if let Some(detail) = &item.detail {
10120                    format!("{} {}", item.label, detail)
10121                } else {
10122                    item.label.clone()
10123                };
10124                let len = text.len();
10125                Some(language::CodeLabel {
10126                    text,
10127                    runs: Vec::new(),
10128                    filter_range: 0..len,
10129                })
10130            })),
10131            ..FakeLspAdapter::default()
10132        },
10133    );
10134    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10135    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10136    let worktree_id = workspace
10137        .update(cx, |workspace, _window, cx| {
10138            workspace.project().update(cx, |project, cx| {
10139                project.worktrees(cx).next().unwrap().read(cx).id()
10140            })
10141        })
10142        .unwrap();
10143    let _buffer = project
10144        .update(cx, |project, cx| {
10145            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10146        })
10147        .await
10148        .unwrap();
10149    let editor = workspace
10150        .update(cx, |workspace, window, cx| {
10151            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10152        })
10153        .unwrap()
10154        .await
10155        .unwrap()
10156        .downcast::<Editor>()
10157        .unwrap();
10158    let fake_server = fake_servers.next().await.unwrap();
10159
10160    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10161    let multiline_label_2 = "a\nb\nc\n";
10162    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10163    let multiline_description = "d\ne\nf\n";
10164    let multiline_detail_2 = "g\nh\ni\n";
10165
10166    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10167        move |params, _| async move {
10168            Ok(Some(lsp::CompletionResponse::Array(vec![
10169                lsp::CompletionItem {
10170                    label: multiline_label.to_string(),
10171                    text_edit: gen_text_edit(&params, "new_text_1"),
10172                    ..lsp::CompletionItem::default()
10173                },
10174                lsp::CompletionItem {
10175                    label: "single line label 1".to_string(),
10176                    detail: Some(multiline_detail.to_string()),
10177                    text_edit: gen_text_edit(&params, "new_text_2"),
10178                    ..lsp::CompletionItem::default()
10179                },
10180                lsp::CompletionItem {
10181                    label: "single line label 2".to_string(),
10182                    label_details: Some(lsp::CompletionItemLabelDetails {
10183                        description: Some(multiline_description.to_string()),
10184                        detail: None,
10185                    }),
10186                    text_edit: gen_text_edit(&params, "new_text_2"),
10187                    ..lsp::CompletionItem::default()
10188                },
10189                lsp::CompletionItem {
10190                    label: multiline_label_2.to_string(),
10191                    detail: Some(multiline_detail_2.to_string()),
10192                    text_edit: gen_text_edit(&params, "new_text_3"),
10193                    ..lsp::CompletionItem::default()
10194                },
10195                lsp::CompletionItem {
10196                    label: "Label with many     spaces and \t but without newlines".to_string(),
10197                    detail: Some(
10198                        "Details with many     spaces and \t but without newlines".to_string(),
10199                    ),
10200                    text_edit: gen_text_edit(&params, "new_text_4"),
10201                    ..lsp::CompletionItem::default()
10202                },
10203            ])))
10204        },
10205    );
10206
10207    editor.update_in(cx, |editor, window, cx| {
10208        cx.focus_self(window);
10209        editor.move_to_end(&MoveToEnd, window, cx);
10210        editor.handle_input(".", window, cx);
10211    });
10212    cx.run_until_parked();
10213    completion_handle.next().await.unwrap();
10214
10215    editor.update(cx, |editor, _| {
10216        assert!(editor.context_menu_visible());
10217        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10218        {
10219            let completion_labels = menu
10220                .completions
10221                .borrow()
10222                .iter()
10223                .map(|c| c.label.text.clone())
10224                .collect::<Vec<_>>();
10225            assert_eq!(
10226                completion_labels,
10227                &[
10228                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10229                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10230                    "single line label 2 d e f ",
10231                    "a b c g h i ",
10232                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10233                ],
10234                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10235            );
10236
10237            for completion in menu
10238                .completions
10239                .borrow()
10240                .iter() {
10241                    assert_eq!(
10242                        completion.label.filter_range,
10243                        0..completion.label.text.len(),
10244                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10245                    );
10246                }
10247        } else {
10248            panic!("expected completion menu to be open");
10249        }
10250    });
10251}
10252
10253#[gpui::test]
10254async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10255    init_test(cx, |_| {});
10256    let mut cx = EditorLspTestContext::new_rust(
10257        lsp::ServerCapabilities {
10258            completion_provider: Some(lsp::CompletionOptions {
10259                trigger_characters: Some(vec![".".to_string()]),
10260                ..Default::default()
10261            }),
10262            ..Default::default()
10263        },
10264        cx,
10265    )
10266    .await;
10267    cx.lsp
10268        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10269            Ok(Some(lsp::CompletionResponse::Array(vec![
10270                lsp::CompletionItem {
10271                    label: "first".into(),
10272                    ..Default::default()
10273                },
10274                lsp::CompletionItem {
10275                    label: "last".into(),
10276                    ..Default::default()
10277                },
10278            ])))
10279        });
10280    cx.set_state("variableˇ");
10281    cx.simulate_keystroke(".");
10282    cx.executor().run_until_parked();
10283
10284    cx.update_editor(|editor, _, _| {
10285        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10286        {
10287            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
10288        } else {
10289            panic!("expected completion menu to be open");
10290        }
10291    });
10292
10293    cx.update_editor(|editor, window, cx| {
10294        editor.move_page_down(&MovePageDown::default(), window, cx);
10295        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10296        {
10297            assert!(
10298                menu.selected_item == 1,
10299                "expected PageDown to select the last item from the context menu"
10300            );
10301        } else {
10302            panic!("expected completion menu to stay open after PageDown");
10303        }
10304    });
10305
10306    cx.update_editor(|editor, window, cx| {
10307        editor.move_page_up(&MovePageUp::default(), window, cx);
10308        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10309        {
10310            assert!(
10311                menu.selected_item == 0,
10312                "expected PageUp to select the first item from the context menu"
10313            );
10314        } else {
10315            panic!("expected completion menu to stay open after PageUp");
10316        }
10317    });
10318}
10319
10320#[gpui::test]
10321async fn test_completion_sort(cx: &mut TestAppContext) {
10322    init_test(cx, |_| {});
10323    let mut cx = EditorLspTestContext::new_rust(
10324        lsp::ServerCapabilities {
10325            completion_provider: Some(lsp::CompletionOptions {
10326                trigger_characters: Some(vec![".".to_string()]),
10327                ..Default::default()
10328            }),
10329            ..Default::default()
10330        },
10331        cx,
10332    )
10333    .await;
10334    cx.lsp
10335        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10336            Ok(Some(lsp::CompletionResponse::Array(vec![
10337                lsp::CompletionItem {
10338                    label: "Range".into(),
10339                    sort_text: Some("a".into()),
10340                    ..Default::default()
10341                },
10342                lsp::CompletionItem {
10343                    label: "r".into(),
10344                    sort_text: Some("b".into()),
10345                    ..Default::default()
10346                },
10347                lsp::CompletionItem {
10348                    label: "ret".into(),
10349                    sort_text: Some("c".into()),
10350                    ..Default::default()
10351                },
10352                lsp::CompletionItem {
10353                    label: "return".into(),
10354                    sort_text: Some("d".into()),
10355                    ..Default::default()
10356                },
10357                lsp::CompletionItem {
10358                    label: "slice".into(),
10359                    sort_text: Some("d".into()),
10360                    ..Default::default()
10361                },
10362            ])))
10363        });
10364    cx.set_state("");
10365    cx.executor().run_until_parked();
10366    cx.update_editor(|editor, window, cx| {
10367        editor.show_completions(
10368            &ShowCompletions {
10369                trigger: Some("r".into()),
10370            },
10371            window,
10372            cx,
10373        );
10374    });
10375    cx.executor().run_until_parked();
10376
10377    cx.update_editor(|editor, _, _| {
10378        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10379        {
10380            assert_eq!(
10381                completion_menu_entries(&menu),
10382                &["r", "ret", "Range", "return"]
10383            );
10384        } else {
10385            panic!("expected completion menu to be open");
10386        }
10387    });
10388}
10389
10390#[gpui::test]
10391async fn test_as_is_completions(cx: &mut TestAppContext) {
10392    init_test(cx, |_| {});
10393    let mut cx = EditorLspTestContext::new_rust(
10394        lsp::ServerCapabilities {
10395            completion_provider: Some(lsp::CompletionOptions {
10396                ..Default::default()
10397            }),
10398            ..Default::default()
10399        },
10400        cx,
10401    )
10402    .await;
10403    cx.lsp
10404        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10405            Ok(Some(lsp::CompletionResponse::Array(vec![
10406                lsp::CompletionItem {
10407                    label: "unsafe".into(),
10408                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10409                        range: lsp::Range {
10410                            start: lsp::Position {
10411                                line: 1,
10412                                character: 2,
10413                            },
10414                            end: lsp::Position {
10415                                line: 1,
10416                                character: 3,
10417                            },
10418                        },
10419                        new_text: "unsafe".to_string(),
10420                    })),
10421                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
10422                    ..Default::default()
10423                },
10424            ])))
10425        });
10426    cx.set_state("fn a() {}\n");
10427    cx.executor().run_until_parked();
10428    cx.update_editor(|editor, window, cx| {
10429        editor.show_completions(
10430            &ShowCompletions {
10431                trigger: Some("\n".into()),
10432            },
10433            window,
10434            cx,
10435        );
10436    });
10437    cx.executor().run_until_parked();
10438
10439    cx.update_editor(|editor, window, cx| {
10440        editor.confirm_completion(&Default::default(), window, cx)
10441    });
10442    cx.executor().run_until_parked();
10443    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
10444}
10445
10446#[gpui::test]
10447async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10448    init_test(cx, |_| {});
10449
10450    let mut cx = EditorLspTestContext::new_rust(
10451        lsp::ServerCapabilities {
10452            completion_provider: Some(lsp::CompletionOptions {
10453                trigger_characters: Some(vec![".".to_string()]),
10454                resolve_provider: Some(true),
10455                ..Default::default()
10456            }),
10457            ..Default::default()
10458        },
10459        cx,
10460    )
10461    .await;
10462
10463    cx.set_state("fn main() { let a = 2ˇ; }");
10464    cx.simulate_keystroke(".");
10465    let completion_item = lsp::CompletionItem {
10466        label: "Some".into(),
10467        kind: Some(lsp::CompletionItemKind::SNIPPET),
10468        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10469        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10470            kind: lsp::MarkupKind::Markdown,
10471            value: "```rust\nSome(2)\n```".to_string(),
10472        })),
10473        deprecated: Some(false),
10474        sort_text: Some("Some".to_string()),
10475        filter_text: Some("Some".to_string()),
10476        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10477        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10478            range: lsp::Range {
10479                start: lsp::Position {
10480                    line: 0,
10481                    character: 22,
10482                },
10483                end: lsp::Position {
10484                    line: 0,
10485                    character: 22,
10486                },
10487            },
10488            new_text: "Some(2)".to_string(),
10489        })),
10490        additional_text_edits: Some(vec![lsp::TextEdit {
10491            range: lsp::Range {
10492                start: lsp::Position {
10493                    line: 0,
10494                    character: 20,
10495                },
10496                end: lsp::Position {
10497                    line: 0,
10498                    character: 22,
10499                },
10500            },
10501            new_text: "".to_string(),
10502        }]),
10503        ..Default::default()
10504    };
10505
10506    let closure_completion_item = completion_item.clone();
10507    let counter = Arc::new(AtomicUsize::new(0));
10508    let counter_clone = counter.clone();
10509    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10510        let task_completion_item = closure_completion_item.clone();
10511        counter_clone.fetch_add(1, atomic::Ordering::Release);
10512        async move {
10513            Ok(Some(lsp::CompletionResponse::Array(vec![
10514                task_completion_item,
10515            ])))
10516        }
10517    });
10518
10519    cx.condition(|editor, _| editor.context_menu_visible())
10520        .await;
10521    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10522    assert!(request.next().await.is_some());
10523    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10524
10525    cx.simulate_keystrokes("S o m");
10526    cx.condition(|editor, _| editor.context_menu_visible())
10527        .await;
10528    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10529    assert!(request.next().await.is_some());
10530    assert!(request.next().await.is_some());
10531    assert!(request.next().await.is_some());
10532    request.close();
10533    assert!(request.next().await.is_none());
10534    assert_eq!(
10535        counter.load(atomic::Ordering::Acquire),
10536        4,
10537        "With the completions menu open, only one LSP request should happen per input"
10538    );
10539}
10540
10541#[gpui::test]
10542async fn test_toggle_comment(cx: &mut TestAppContext) {
10543    init_test(cx, |_| {});
10544    let mut cx = EditorTestContext::new(cx).await;
10545    let language = Arc::new(Language::new(
10546        LanguageConfig {
10547            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10548            ..Default::default()
10549        },
10550        Some(tree_sitter_rust::LANGUAGE.into()),
10551    ));
10552    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10553
10554    // If multiple selections intersect a line, the line is only toggled once.
10555    cx.set_state(indoc! {"
10556        fn a() {
10557            «//b();
10558            ˇ»// «c();
10559            //ˇ»  d();
10560        }
10561    "});
10562
10563    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10564
10565    cx.assert_editor_state(indoc! {"
10566        fn a() {
10567            «b();
10568            c();
10569            ˇ» d();
10570        }
10571    "});
10572
10573    // The comment prefix is inserted at the same column for every line in a
10574    // selection.
10575    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10576
10577    cx.assert_editor_state(indoc! {"
10578        fn a() {
10579            // «b();
10580            // c();
10581            ˇ»//  d();
10582        }
10583    "});
10584
10585    // If a selection ends at the beginning of a line, that line is not toggled.
10586    cx.set_selections_state(indoc! {"
10587        fn a() {
10588            // b();
10589            «// c();
10590        ˇ»    //  d();
10591        }
10592    "});
10593
10594    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10595
10596    cx.assert_editor_state(indoc! {"
10597        fn a() {
10598            // b();
10599            «c();
10600        ˇ»    //  d();
10601        }
10602    "});
10603
10604    // If a selection span a single line and is empty, the line is toggled.
10605    cx.set_state(indoc! {"
10606        fn a() {
10607            a();
10608            b();
10609        ˇ
10610        }
10611    "});
10612
10613    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10614
10615    cx.assert_editor_state(indoc! {"
10616        fn a() {
10617            a();
10618            b();
10619        //•ˇ
10620        }
10621    "});
10622
10623    // If a selection span multiple lines, empty lines are not toggled.
10624    cx.set_state(indoc! {"
10625        fn a() {
10626            «a();
10627
10628            c();ˇ»
10629        }
10630    "});
10631
10632    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10633
10634    cx.assert_editor_state(indoc! {"
10635        fn a() {
10636            // «a();
10637
10638            // c();ˇ»
10639        }
10640    "});
10641
10642    // If a selection includes multiple comment prefixes, all lines are uncommented.
10643    cx.set_state(indoc! {"
10644        fn a() {
10645            «// a();
10646            /// b();
10647            //! c();ˇ»
10648        }
10649    "});
10650
10651    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10652
10653    cx.assert_editor_state(indoc! {"
10654        fn a() {
10655            «a();
10656            b();
10657            c();ˇ»
10658        }
10659    "});
10660}
10661
10662#[gpui::test]
10663async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10664    init_test(cx, |_| {});
10665    let mut cx = EditorTestContext::new(cx).await;
10666    let language = Arc::new(Language::new(
10667        LanguageConfig {
10668            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10669            ..Default::default()
10670        },
10671        Some(tree_sitter_rust::LANGUAGE.into()),
10672    ));
10673    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10674
10675    let toggle_comments = &ToggleComments {
10676        advance_downwards: false,
10677        ignore_indent: true,
10678    };
10679
10680    // If multiple selections intersect a line, the line is only toggled once.
10681    cx.set_state(indoc! {"
10682        fn a() {
10683        //    «b();
10684        //    c();
10685        //    ˇ» d();
10686        }
10687    "});
10688
10689    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10690
10691    cx.assert_editor_state(indoc! {"
10692        fn a() {
10693            «b();
10694            c();
10695            ˇ» d();
10696        }
10697    "});
10698
10699    // The comment prefix is inserted at the beginning of each line
10700    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10701
10702    cx.assert_editor_state(indoc! {"
10703        fn a() {
10704        //    «b();
10705        //    c();
10706        //    ˇ» d();
10707        }
10708    "});
10709
10710    // If a selection ends at the beginning of a line, that line is not toggled.
10711    cx.set_selections_state(indoc! {"
10712        fn a() {
10713        //    b();
10714        //    «c();
10715        ˇ»//     d();
10716        }
10717    "});
10718
10719    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10720
10721    cx.assert_editor_state(indoc! {"
10722        fn a() {
10723        //    b();
10724            «c();
10725        ˇ»//     d();
10726        }
10727    "});
10728
10729    // If a selection span a single line and is empty, the line is toggled.
10730    cx.set_state(indoc! {"
10731        fn a() {
10732            a();
10733            b();
10734        ˇ
10735        }
10736    "});
10737
10738    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10739
10740    cx.assert_editor_state(indoc! {"
10741        fn a() {
10742            a();
10743            b();
10744        //ˇ
10745        }
10746    "});
10747
10748    // If a selection span multiple lines, empty lines are not toggled.
10749    cx.set_state(indoc! {"
10750        fn a() {
10751            «a();
10752
10753            c();ˇ»
10754        }
10755    "});
10756
10757    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10758
10759    cx.assert_editor_state(indoc! {"
10760        fn a() {
10761        //    «a();
10762
10763        //    c();ˇ»
10764        }
10765    "});
10766
10767    // If a selection includes multiple comment prefixes, all lines are uncommented.
10768    cx.set_state(indoc! {"
10769        fn a() {
10770        //    «a();
10771        ///    b();
10772        //!    c();ˇ»
10773        }
10774    "});
10775
10776    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10777
10778    cx.assert_editor_state(indoc! {"
10779        fn a() {
10780            «a();
10781            b();
10782            c();ˇ»
10783        }
10784    "});
10785}
10786
10787#[gpui::test]
10788async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10789    init_test(cx, |_| {});
10790
10791    let language = Arc::new(Language::new(
10792        LanguageConfig {
10793            line_comments: vec!["// ".into()],
10794            ..Default::default()
10795        },
10796        Some(tree_sitter_rust::LANGUAGE.into()),
10797    ));
10798
10799    let mut cx = EditorTestContext::new(cx).await;
10800
10801    cx.language_registry().add(language.clone());
10802    cx.update_buffer(|buffer, cx| {
10803        buffer.set_language(Some(language), cx);
10804    });
10805
10806    let toggle_comments = &ToggleComments {
10807        advance_downwards: true,
10808        ignore_indent: false,
10809    };
10810
10811    // Single cursor on one line -> advance
10812    // Cursor moves horizontally 3 characters as well on non-blank line
10813    cx.set_state(indoc!(
10814        "fn a() {
10815             ˇdog();
10816             cat();
10817        }"
10818    ));
10819    cx.update_editor(|editor, window, cx| {
10820        editor.toggle_comments(toggle_comments, window, cx);
10821    });
10822    cx.assert_editor_state(indoc!(
10823        "fn a() {
10824             // dog();
10825             catˇ();
10826        }"
10827    ));
10828
10829    // Single selection on one line -> don't advance
10830    cx.set_state(indoc!(
10831        "fn a() {
10832             «dog()ˇ»;
10833             cat();
10834        }"
10835    ));
10836    cx.update_editor(|editor, window, cx| {
10837        editor.toggle_comments(toggle_comments, window, cx);
10838    });
10839    cx.assert_editor_state(indoc!(
10840        "fn a() {
10841             // «dog()ˇ»;
10842             cat();
10843        }"
10844    ));
10845
10846    // Multiple cursors on one line -> advance
10847    cx.set_state(indoc!(
10848        "fn a() {
10849             ˇdˇog();
10850             cat();
10851        }"
10852    ));
10853    cx.update_editor(|editor, window, cx| {
10854        editor.toggle_comments(toggle_comments, window, cx);
10855    });
10856    cx.assert_editor_state(indoc!(
10857        "fn a() {
10858             // dog();
10859             catˇ(ˇ);
10860        }"
10861    ));
10862
10863    // Multiple cursors on one line, with selection -> don't advance
10864    cx.set_state(indoc!(
10865        "fn a() {
10866             ˇdˇog«()ˇ»;
10867             cat();
10868        }"
10869    ));
10870    cx.update_editor(|editor, window, cx| {
10871        editor.toggle_comments(toggle_comments, window, cx);
10872    });
10873    cx.assert_editor_state(indoc!(
10874        "fn a() {
10875             // ˇdˇog«()ˇ»;
10876             cat();
10877        }"
10878    ));
10879
10880    // Single cursor on one line -> advance
10881    // Cursor moves to column 0 on blank line
10882    cx.set_state(indoc!(
10883        "fn a() {
10884             ˇdog();
10885
10886             cat();
10887        }"
10888    ));
10889    cx.update_editor(|editor, window, cx| {
10890        editor.toggle_comments(toggle_comments, window, cx);
10891    });
10892    cx.assert_editor_state(indoc!(
10893        "fn a() {
10894             // dog();
10895        ˇ
10896             cat();
10897        }"
10898    ));
10899
10900    // Single cursor on one line -> advance
10901    // Cursor starts and ends at column 0
10902    cx.set_state(indoc!(
10903        "fn a() {
10904         ˇ    dog();
10905             cat();
10906        }"
10907    ));
10908    cx.update_editor(|editor, window, cx| {
10909        editor.toggle_comments(toggle_comments, window, cx);
10910    });
10911    cx.assert_editor_state(indoc!(
10912        "fn a() {
10913             // dog();
10914         ˇ    cat();
10915        }"
10916    ));
10917}
10918
10919#[gpui::test]
10920async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10921    init_test(cx, |_| {});
10922
10923    let mut cx = EditorTestContext::new(cx).await;
10924
10925    let html_language = Arc::new(
10926        Language::new(
10927            LanguageConfig {
10928                name: "HTML".into(),
10929                block_comment: Some(("<!-- ".into(), " -->".into())),
10930                ..Default::default()
10931            },
10932            Some(tree_sitter_html::LANGUAGE.into()),
10933        )
10934        .with_injection_query(
10935            r#"
10936            (script_element
10937                (raw_text) @injection.content
10938                (#set! injection.language "javascript"))
10939            "#,
10940        )
10941        .unwrap(),
10942    );
10943
10944    let javascript_language = Arc::new(Language::new(
10945        LanguageConfig {
10946            name: "JavaScript".into(),
10947            line_comments: vec!["// ".into()],
10948            ..Default::default()
10949        },
10950        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10951    ));
10952
10953    cx.language_registry().add(html_language.clone());
10954    cx.language_registry().add(javascript_language.clone());
10955    cx.update_buffer(|buffer, cx| {
10956        buffer.set_language(Some(html_language), cx);
10957    });
10958
10959    // Toggle comments for empty selections
10960    cx.set_state(
10961        &r#"
10962            <p>A</p>ˇ
10963            <p>B</p>ˇ
10964            <p>C</p>ˇ
10965        "#
10966        .unindent(),
10967    );
10968    cx.update_editor(|editor, window, cx| {
10969        editor.toggle_comments(&ToggleComments::default(), window, cx)
10970    });
10971    cx.assert_editor_state(
10972        &r#"
10973            <!-- <p>A</p>ˇ -->
10974            <!-- <p>B</p>ˇ -->
10975            <!-- <p>C</p>ˇ -->
10976        "#
10977        .unindent(),
10978    );
10979    cx.update_editor(|editor, window, cx| {
10980        editor.toggle_comments(&ToggleComments::default(), window, cx)
10981    });
10982    cx.assert_editor_state(
10983        &r#"
10984            <p>A</p>ˇ
10985            <p>B</p>ˇ
10986            <p>C</p>ˇ
10987        "#
10988        .unindent(),
10989    );
10990
10991    // Toggle comments for mixture of empty and non-empty selections, where
10992    // multiple selections occupy a given line.
10993    cx.set_state(
10994        &r#"
10995            <p>A«</p>
10996            <p>ˇ»B</p>ˇ
10997            <p>C«</p>
10998            <p>ˇ»D</p>ˇ
10999        "#
11000        .unindent(),
11001    );
11002
11003    cx.update_editor(|editor, window, cx| {
11004        editor.toggle_comments(&ToggleComments::default(), window, cx)
11005    });
11006    cx.assert_editor_state(
11007        &r#"
11008            <!-- <p>A«</p>
11009            <p>ˇ»B</p>ˇ -->
11010            <!-- <p>C«</p>
11011            <p>ˇ»D</p>ˇ -->
11012        "#
11013        .unindent(),
11014    );
11015    cx.update_editor(|editor, window, cx| {
11016        editor.toggle_comments(&ToggleComments::default(), window, cx)
11017    });
11018    cx.assert_editor_state(
11019        &r#"
11020            <p>A«</p>
11021            <p>ˇ»B</p>ˇ
11022            <p>C«</p>
11023            <p>ˇ»D</p>ˇ
11024        "#
11025        .unindent(),
11026    );
11027
11028    // Toggle comments when different languages are active for different
11029    // selections.
11030    cx.set_state(
11031        &r#"
11032            ˇ<script>
11033                ˇvar x = new Y();
11034            ˇ</script>
11035        "#
11036        .unindent(),
11037    );
11038    cx.executor().run_until_parked();
11039    cx.update_editor(|editor, window, cx| {
11040        editor.toggle_comments(&ToggleComments::default(), window, cx)
11041    });
11042    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11043    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11044    cx.assert_editor_state(
11045        &r#"
11046            <!-- ˇ<script> -->
11047                // ˇvar x = new Y();
11048            <!-- ˇ</script> -->
11049        "#
11050        .unindent(),
11051    );
11052}
11053
11054#[gpui::test]
11055fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11056    init_test(cx, |_| {});
11057
11058    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11059    let multibuffer = cx.new(|cx| {
11060        let mut multibuffer = MultiBuffer::new(ReadWrite);
11061        multibuffer.push_excerpts(
11062            buffer.clone(),
11063            [
11064                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11065                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11066            ],
11067            cx,
11068        );
11069        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11070        multibuffer
11071    });
11072
11073    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11074    editor.update_in(cx, |editor, window, cx| {
11075        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11076        editor.change_selections(None, window, cx, |s| {
11077            s.select_ranges([
11078                Point::new(0, 0)..Point::new(0, 0),
11079                Point::new(1, 0)..Point::new(1, 0),
11080            ])
11081        });
11082
11083        editor.handle_input("X", window, cx);
11084        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11085        assert_eq!(
11086            editor.selections.ranges(cx),
11087            [
11088                Point::new(0, 1)..Point::new(0, 1),
11089                Point::new(1, 1)..Point::new(1, 1),
11090            ]
11091        );
11092
11093        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11094        editor.change_selections(None, window, cx, |s| {
11095            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11096        });
11097        editor.backspace(&Default::default(), window, cx);
11098        assert_eq!(editor.text(cx), "Xa\nbbb");
11099        assert_eq!(
11100            editor.selections.ranges(cx),
11101            [Point::new(1, 0)..Point::new(1, 0)]
11102        );
11103
11104        editor.change_selections(None, window, cx, |s| {
11105            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11106        });
11107        editor.backspace(&Default::default(), window, cx);
11108        assert_eq!(editor.text(cx), "X\nbb");
11109        assert_eq!(
11110            editor.selections.ranges(cx),
11111            [Point::new(0, 1)..Point::new(0, 1)]
11112        );
11113    });
11114}
11115
11116#[gpui::test]
11117fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11118    init_test(cx, |_| {});
11119
11120    let markers = vec![('[', ']').into(), ('(', ')').into()];
11121    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11122        indoc! {"
11123            [aaaa
11124            (bbbb]
11125            cccc)",
11126        },
11127        markers.clone(),
11128    );
11129    let excerpt_ranges = markers.into_iter().map(|marker| {
11130        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11131        ExcerptRange::new(context.clone())
11132    });
11133    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11134    let multibuffer = cx.new(|cx| {
11135        let mut multibuffer = MultiBuffer::new(ReadWrite);
11136        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11137        multibuffer
11138    });
11139
11140    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11141    editor.update_in(cx, |editor, window, cx| {
11142        let (expected_text, selection_ranges) = marked_text_ranges(
11143            indoc! {"
11144                aaaa
11145                bˇbbb
11146                bˇbbˇb
11147                cccc"
11148            },
11149            true,
11150        );
11151        assert_eq!(editor.text(cx), expected_text);
11152        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11153
11154        editor.handle_input("X", window, cx);
11155
11156        let (expected_text, expected_selections) = marked_text_ranges(
11157            indoc! {"
11158                aaaa
11159                bXˇbbXb
11160                bXˇbbXˇb
11161                cccc"
11162            },
11163            false,
11164        );
11165        assert_eq!(editor.text(cx), expected_text);
11166        assert_eq!(editor.selections.ranges(cx), expected_selections);
11167
11168        editor.newline(&Newline, window, cx);
11169        let (expected_text, expected_selections) = marked_text_ranges(
11170            indoc! {"
11171                aaaa
11172                bX
11173                ˇbbX
11174                b
11175                bX
11176                ˇbbX
11177                ˇb
11178                cccc"
11179            },
11180            false,
11181        );
11182        assert_eq!(editor.text(cx), expected_text);
11183        assert_eq!(editor.selections.ranges(cx), expected_selections);
11184    });
11185}
11186
11187#[gpui::test]
11188fn test_refresh_selections(cx: &mut TestAppContext) {
11189    init_test(cx, |_| {});
11190
11191    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11192    let mut excerpt1_id = None;
11193    let multibuffer = cx.new(|cx| {
11194        let mut multibuffer = MultiBuffer::new(ReadWrite);
11195        excerpt1_id = multibuffer
11196            .push_excerpts(
11197                buffer.clone(),
11198                [
11199                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11200                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11201                ],
11202                cx,
11203            )
11204            .into_iter()
11205            .next();
11206        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11207        multibuffer
11208    });
11209
11210    let editor = cx.add_window(|window, cx| {
11211        let mut editor = build_editor(multibuffer.clone(), window, cx);
11212        let snapshot = editor.snapshot(window, cx);
11213        editor.change_selections(None, window, cx, |s| {
11214            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11215        });
11216        editor.begin_selection(
11217            Point::new(2, 1).to_display_point(&snapshot),
11218            true,
11219            1,
11220            window,
11221            cx,
11222        );
11223        assert_eq!(
11224            editor.selections.ranges(cx),
11225            [
11226                Point::new(1, 3)..Point::new(1, 3),
11227                Point::new(2, 1)..Point::new(2, 1),
11228            ]
11229        );
11230        editor
11231    });
11232
11233    // Refreshing selections is a no-op when excerpts haven't changed.
11234    _ = editor.update(cx, |editor, window, cx| {
11235        editor.change_selections(None, window, cx, |s| s.refresh());
11236        assert_eq!(
11237            editor.selections.ranges(cx),
11238            [
11239                Point::new(1, 3)..Point::new(1, 3),
11240                Point::new(2, 1)..Point::new(2, 1),
11241            ]
11242        );
11243    });
11244
11245    multibuffer.update(cx, |multibuffer, cx| {
11246        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11247    });
11248    _ = editor.update(cx, |editor, window, cx| {
11249        // Removing an excerpt causes the first selection to become degenerate.
11250        assert_eq!(
11251            editor.selections.ranges(cx),
11252            [
11253                Point::new(0, 0)..Point::new(0, 0),
11254                Point::new(0, 1)..Point::new(0, 1)
11255            ]
11256        );
11257
11258        // Refreshing selections will relocate the first selection to the original buffer
11259        // location.
11260        editor.change_selections(None, window, cx, |s| s.refresh());
11261        assert_eq!(
11262            editor.selections.ranges(cx),
11263            [
11264                Point::new(0, 1)..Point::new(0, 1),
11265                Point::new(0, 3)..Point::new(0, 3)
11266            ]
11267        );
11268        assert!(editor.selections.pending_anchor().is_some());
11269    });
11270}
11271
11272#[gpui::test]
11273fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11274    init_test(cx, |_| {});
11275
11276    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11277    let mut excerpt1_id = None;
11278    let multibuffer = cx.new(|cx| {
11279        let mut multibuffer = MultiBuffer::new(ReadWrite);
11280        excerpt1_id = multibuffer
11281            .push_excerpts(
11282                buffer.clone(),
11283                [
11284                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11285                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11286                ],
11287                cx,
11288            )
11289            .into_iter()
11290            .next();
11291        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11292        multibuffer
11293    });
11294
11295    let editor = cx.add_window(|window, cx| {
11296        let mut editor = build_editor(multibuffer.clone(), window, cx);
11297        let snapshot = editor.snapshot(window, cx);
11298        editor.begin_selection(
11299            Point::new(1, 3).to_display_point(&snapshot),
11300            false,
11301            1,
11302            window,
11303            cx,
11304        );
11305        assert_eq!(
11306            editor.selections.ranges(cx),
11307            [Point::new(1, 3)..Point::new(1, 3)]
11308        );
11309        editor
11310    });
11311
11312    multibuffer.update(cx, |multibuffer, cx| {
11313        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11314    });
11315    _ = editor.update(cx, |editor, window, cx| {
11316        assert_eq!(
11317            editor.selections.ranges(cx),
11318            [Point::new(0, 0)..Point::new(0, 0)]
11319        );
11320
11321        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11322        editor.change_selections(None, window, cx, |s| s.refresh());
11323        assert_eq!(
11324            editor.selections.ranges(cx),
11325            [Point::new(0, 3)..Point::new(0, 3)]
11326        );
11327        assert!(editor.selections.pending_anchor().is_some());
11328    });
11329}
11330
11331#[gpui::test]
11332async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11333    init_test(cx, |_| {});
11334
11335    let language = Arc::new(
11336        Language::new(
11337            LanguageConfig {
11338                brackets: BracketPairConfig {
11339                    pairs: vec![
11340                        BracketPair {
11341                            start: "{".to_string(),
11342                            end: "}".to_string(),
11343                            close: true,
11344                            surround: true,
11345                            newline: true,
11346                        },
11347                        BracketPair {
11348                            start: "/* ".to_string(),
11349                            end: " */".to_string(),
11350                            close: true,
11351                            surround: true,
11352                            newline: true,
11353                        },
11354                    ],
11355                    ..Default::default()
11356                },
11357                ..Default::default()
11358            },
11359            Some(tree_sitter_rust::LANGUAGE.into()),
11360        )
11361        .with_indents_query("")
11362        .unwrap(),
11363    );
11364
11365    let text = concat!(
11366        "{   }\n",     //
11367        "  x\n",       //
11368        "  /*   */\n", //
11369        "x\n",         //
11370        "{{} }\n",     //
11371    );
11372
11373    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11374    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11375    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11376    editor
11377        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11378        .await;
11379
11380    editor.update_in(cx, |editor, window, cx| {
11381        editor.change_selections(None, window, cx, |s| {
11382            s.select_display_ranges([
11383                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11384                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11385                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11386            ])
11387        });
11388        editor.newline(&Newline, window, cx);
11389
11390        assert_eq!(
11391            editor.buffer().read(cx).read(cx).text(),
11392            concat!(
11393                "{ \n",    // Suppress rustfmt
11394                "\n",      //
11395                "}\n",     //
11396                "  x\n",   //
11397                "  /* \n", //
11398                "  \n",    //
11399                "  */\n",  //
11400                "x\n",     //
11401                "{{} \n",  //
11402                "}\n",     //
11403            )
11404        );
11405    });
11406}
11407
11408#[gpui::test]
11409fn test_highlighted_ranges(cx: &mut TestAppContext) {
11410    init_test(cx, |_| {});
11411
11412    let editor = cx.add_window(|window, cx| {
11413        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11414        build_editor(buffer.clone(), window, cx)
11415    });
11416
11417    _ = editor.update(cx, |editor, window, cx| {
11418        struct Type1;
11419        struct Type2;
11420
11421        let buffer = editor.buffer.read(cx).snapshot(cx);
11422
11423        let anchor_range =
11424            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11425
11426        editor.highlight_background::<Type1>(
11427            &[
11428                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11429                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11430                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11431                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11432            ],
11433            |_| Hsla::red(),
11434            cx,
11435        );
11436        editor.highlight_background::<Type2>(
11437            &[
11438                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11439                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11440                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11441                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11442            ],
11443            |_| Hsla::green(),
11444            cx,
11445        );
11446
11447        let snapshot = editor.snapshot(window, cx);
11448        let mut highlighted_ranges = editor.background_highlights_in_range(
11449            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11450            &snapshot,
11451            cx.theme().colors(),
11452        );
11453        // Enforce a consistent ordering based on color without relying on the ordering of the
11454        // highlight's `TypeId` which is non-executor.
11455        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11456        assert_eq!(
11457            highlighted_ranges,
11458            &[
11459                (
11460                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11461                    Hsla::red(),
11462                ),
11463                (
11464                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11465                    Hsla::red(),
11466                ),
11467                (
11468                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11469                    Hsla::green(),
11470                ),
11471                (
11472                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11473                    Hsla::green(),
11474                ),
11475            ]
11476        );
11477        assert_eq!(
11478            editor.background_highlights_in_range(
11479                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11480                &snapshot,
11481                cx.theme().colors(),
11482            ),
11483            &[(
11484                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11485                Hsla::red(),
11486            )]
11487        );
11488    });
11489}
11490
11491#[gpui::test]
11492async fn test_following(cx: &mut TestAppContext) {
11493    init_test(cx, |_| {});
11494
11495    let fs = FakeFs::new(cx.executor());
11496    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11497
11498    let buffer = project.update(cx, |project, cx| {
11499        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11500        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11501    });
11502    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11503    let follower = cx.update(|cx| {
11504        cx.open_window(
11505            WindowOptions {
11506                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11507                    gpui::Point::new(px(0.), px(0.)),
11508                    gpui::Point::new(px(10.), px(80.)),
11509                ))),
11510                ..Default::default()
11511            },
11512            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11513        )
11514        .unwrap()
11515    });
11516
11517    let is_still_following = Rc::new(RefCell::new(true));
11518    let follower_edit_event_count = Rc::new(RefCell::new(0));
11519    let pending_update = Rc::new(RefCell::new(None));
11520    let leader_entity = leader.root(cx).unwrap();
11521    let follower_entity = follower.root(cx).unwrap();
11522    _ = follower.update(cx, {
11523        let update = pending_update.clone();
11524        let is_still_following = is_still_following.clone();
11525        let follower_edit_event_count = follower_edit_event_count.clone();
11526        |_, window, cx| {
11527            cx.subscribe_in(
11528                &leader_entity,
11529                window,
11530                move |_, leader, event, window, cx| {
11531                    leader.read(cx).add_event_to_update_proto(
11532                        event,
11533                        &mut update.borrow_mut(),
11534                        window,
11535                        cx,
11536                    );
11537                },
11538            )
11539            .detach();
11540
11541            cx.subscribe_in(
11542                &follower_entity,
11543                window,
11544                move |_, _, event: &EditorEvent, _window, _cx| {
11545                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11546                        *is_still_following.borrow_mut() = false;
11547                    }
11548
11549                    if let EditorEvent::BufferEdited = event {
11550                        *follower_edit_event_count.borrow_mut() += 1;
11551                    }
11552                },
11553            )
11554            .detach();
11555        }
11556    });
11557
11558    // Update the selections only
11559    _ = leader.update(cx, |leader, window, cx| {
11560        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11561    });
11562    follower
11563        .update(cx, |follower, window, cx| {
11564            follower.apply_update_proto(
11565                &project,
11566                pending_update.borrow_mut().take().unwrap(),
11567                window,
11568                cx,
11569            )
11570        })
11571        .unwrap()
11572        .await
11573        .unwrap();
11574    _ = follower.update(cx, |follower, _, cx| {
11575        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11576    });
11577    assert!(*is_still_following.borrow());
11578    assert_eq!(*follower_edit_event_count.borrow(), 0);
11579
11580    // Update the scroll position only
11581    _ = leader.update(cx, |leader, window, cx| {
11582        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11583    });
11584    follower
11585        .update(cx, |follower, window, cx| {
11586            follower.apply_update_proto(
11587                &project,
11588                pending_update.borrow_mut().take().unwrap(),
11589                window,
11590                cx,
11591            )
11592        })
11593        .unwrap()
11594        .await
11595        .unwrap();
11596    assert_eq!(
11597        follower
11598            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11599            .unwrap(),
11600        gpui::Point::new(1.5, 3.5)
11601    );
11602    assert!(*is_still_following.borrow());
11603    assert_eq!(*follower_edit_event_count.borrow(), 0);
11604
11605    // Update the selections and scroll position. The follower's scroll position is updated
11606    // via autoscroll, not via the leader's exact scroll position.
11607    _ = leader.update(cx, |leader, window, cx| {
11608        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11609        leader.request_autoscroll(Autoscroll::newest(), cx);
11610        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11611    });
11612    follower
11613        .update(cx, |follower, window, cx| {
11614            follower.apply_update_proto(
11615                &project,
11616                pending_update.borrow_mut().take().unwrap(),
11617                window,
11618                cx,
11619            )
11620        })
11621        .unwrap()
11622        .await
11623        .unwrap();
11624    _ = follower.update(cx, |follower, _, cx| {
11625        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11626        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11627    });
11628    assert!(*is_still_following.borrow());
11629
11630    // Creating a pending selection that precedes another selection
11631    _ = leader.update(cx, |leader, window, cx| {
11632        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11633        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11634    });
11635    follower
11636        .update(cx, |follower, window, cx| {
11637            follower.apply_update_proto(
11638                &project,
11639                pending_update.borrow_mut().take().unwrap(),
11640                window,
11641                cx,
11642            )
11643        })
11644        .unwrap()
11645        .await
11646        .unwrap();
11647    _ = follower.update(cx, |follower, _, cx| {
11648        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11649    });
11650    assert!(*is_still_following.borrow());
11651
11652    // Extend the pending selection so that it surrounds another selection
11653    _ = leader.update(cx, |leader, window, cx| {
11654        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11655    });
11656    follower
11657        .update(cx, |follower, window, cx| {
11658            follower.apply_update_proto(
11659                &project,
11660                pending_update.borrow_mut().take().unwrap(),
11661                window,
11662                cx,
11663            )
11664        })
11665        .unwrap()
11666        .await
11667        .unwrap();
11668    _ = follower.update(cx, |follower, _, cx| {
11669        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11670    });
11671
11672    // Scrolling locally breaks the follow
11673    _ = follower.update(cx, |follower, window, cx| {
11674        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11675        follower.set_scroll_anchor(
11676            ScrollAnchor {
11677                anchor: top_anchor,
11678                offset: gpui::Point::new(0.0, 0.5),
11679            },
11680            window,
11681            cx,
11682        );
11683    });
11684    assert!(!(*is_still_following.borrow()));
11685}
11686
11687#[gpui::test]
11688async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11689    init_test(cx, |_| {});
11690
11691    let fs = FakeFs::new(cx.executor());
11692    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11693    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11694    let pane = workspace
11695        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11696        .unwrap();
11697
11698    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11699
11700    let leader = pane.update_in(cx, |_, window, cx| {
11701        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11702        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11703    });
11704
11705    // Start following the editor when it has no excerpts.
11706    let mut state_message =
11707        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11708    let workspace_entity = workspace.root(cx).unwrap();
11709    let follower_1 = cx
11710        .update_window(*workspace.deref(), |_, window, cx| {
11711            Editor::from_state_proto(
11712                workspace_entity,
11713                ViewId {
11714                    creator: Default::default(),
11715                    id: 0,
11716                },
11717                &mut state_message,
11718                window,
11719                cx,
11720            )
11721        })
11722        .unwrap()
11723        .unwrap()
11724        .await
11725        .unwrap();
11726
11727    let update_message = Rc::new(RefCell::new(None));
11728    follower_1.update_in(cx, {
11729        let update = update_message.clone();
11730        |_, window, cx| {
11731            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11732                leader.read(cx).add_event_to_update_proto(
11733                    event,
11734                    &mut update.borrow_mut(),
11735                    window,
11736                    cx,
11737                );
11738            })
11739            .detach();
11740        }
11741    });
11742
11743    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11744        (
11745            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11746            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11747        )
11748    });
11749
11750    // Insert some excerpts.
11751    leader.update(cx, |leader, cx| {
11752        leader.buffer.update(cx, |multibuffer, cx| {
11753            let excerpt_ids = multibuffer.push_excerpts(
11754                buffer_1.clone(),
11755                [
11756                    ExcerptRange::new(1..6),
11757                    ExcerptRange::new(12..15),
11758                    ExcerptRange::new(0..3),
11759                ],
11760                cx,
11761            );
11762            multibuffer.insert_excerpts_after(
11763                excerpt_ids[0],
11764                buffer_2.clone(),
11765                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11766                cx,
11767            );
11768        });
11769    });
11770
11771    // Apply the update of adding the excerpts.
11772    follower_1
11773        .update_in(cx, |follower, window, cx| {
11774            follower.apply_update_proto(
11775                &project,
11776                update_message.borrow().clone().unwrap(),
11777                window,
11778                cx,
11779            )
11780        })
11781        .await
11782        .unwrap();
11783    assert_eq!(
11784        follower_1.update(cx, |editor, cx| editor.text(cx)),
11785        leader.update(cx, |editor, cx| editor.text(cx))
11786    );
11787    update_message.borrow_mut().take();
11788
11789    // Start following separately after it already has excerpts.
11790    let mut state_message =
11791        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11792    let workspace_entity = workspace.root(cx).unwrap();
11793    let follower_2 = cx
11794        .update_window(*workspace.deref(), |_, window, cx| {
11795            Editor::from_state_proto(
11796                workspace_entity,
11797                ViewId {
11798                    creator: Default::default(),
11799                    id: 0,
11800                },
11801                &mut state_message,
11802                window,
11803                cx,
11804            )
11805        })
11806        .unwrap()
11807        .unwrap()
11808        .await
11809        .unwrap();
11810    assert_eq!(
11811        follower_2.update(cx, |editor, cx| editor.text(cx)),
11812        leader.update(cx, |editor, cx| editor.text(cx))
11813    );
11814
11815    // Remove some excerpts.
11816    leader.update(cx, |leader, cx| {
11817        leader.buffer.update(cx, |multibuffer, cx| {
11818            let excerpt_ids = multibuffer.excerpt_ids();
11819            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11820            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11821        });
11822    });
11823
11824    // Apply the update of removing the excerpts.
11825    follower_1
11826        .update_in(cx, |follower, window, cx| {
11827            follower.apply_update_proto(
11828                &project,
11829                update_message.borrow().clone().unwrap(),
11830                window,
11831                cx,
11832            )
11833        })
11834        .await
11835        .unwrap();
11836    follower_2
11837        .update_in(cx, |follower, window, cx| {
11838            follower.apply_update_proto(
11839                &project,
11840                update_message.borrow().clone().unwrap(),
11841                window,
11842                cx,
11843            )
11844        })
11845        .await
11846        .unwrap();
11847    update_message.borrow_mut().take();
11848    assert_eq!(
11849        follower_1.update(cx, |editor, cx| editor.text(cx)),
11850        leader.update(cx, |editor, cx| editor.text(cx))
11851    );
11852}
11853
11854#[gpui::test]
11855async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11856    init_test(cx, |_| {});
11857
11858    let mut cx = EditorTestContext::new(cx).await;
11859    let lsp_store =
11860        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11861
11862    cx.set_state(indoc! {"
11863        ˇfn func(abc def: i32) -> u32 {
11864        }
11865    "});
11866
11867    cx.update(|_, cx| {
11868        lsp_store.update(cx, |lsp_store, cx| {
11869            lsp_store
11870                .update_diagnostics(
11871                    LanguageServerId(0),
11872                    lsp::PublishDiagnosticsParams {
11873                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11874                        version: None,
11875                        diagnostics: vec![
11876                            lsp::Diagnostic {
11877                                range: lsp::Range::new(
11878                                    lsp::Position::new(0, 11),
11879                                    lsp::Position::new(0, 12),
11880                                ),
11881                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11882                                ..Default::default()
11883                            },
11884                            lsp::Diagnostic {
11885                                range: lsp::Range::new(
11886                                    lsp::Position::new(0, 12),
11887                                    lsp::Position::new(0, 15),
11888                                ),
11889                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11890                                ..Default::default()
11891                            },
11892                            lsp::Diagnostic {
11893                                range: lsp::Range::new(
11894                                    lsp::Position::new(0, 25),
11895                                    lsp::Position::new(0, 28),
11896                                ),
11897                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11898                                ..Default::default()
11899                            },
11900                        ],
11901                    },
11902                    &[],
11903                    cx,
11904                )
11905                .unwrap()
11906        });
11907    });
11908
11909    executor.run_until_parked();
11910
11911    cx.update_editor(|editor, window, cx| {
11912        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11913    });
11914
11915    cx.assert_editor_state(indoc! {"
11916        fn func(abc def: i32) -> ˇu32 {
11917        }
11918    "});
11919
11920    cx.update_editor(|editor, window, cx| {
11921        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11922    });
11923
11924    cx.assert_editor_state(indoc! {"
11925        fn func(abc ˇdef: i32) -> u32 {
11926        }
11927    "});
11928
11929    cx.update_editor(|editor, window, cx| {
11930        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11931    });
11932
11933    cx.assert_editor_state(indoc! {"
11934        fn func(abcˇ def: i32) -> u32 {
11935        }
11936    "});
11937
11938    cx.update_editor(|editor, window, cx| {
11939        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11940    });
11941
11942    cx.assert_editor_state(indoc! {"
11943        fn func(abc def: i32) -> ˇu32 {
11944        }
11945    "});
11946}
11947
11948#[gpui::test]
11949async fn cycle_through_same_place_diagnostics(
11950    executor: BackgroundExecutor,
11951    cx: &mut TestAppContext,
11952) {
11953    init_test(cx, |_| {});
11954
11955    let mut cx = EditorTestContext::new(cx).await;
11956    let lsp_store =
11957        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11958
11959    cx.set_state(indoc! {"
11960        ˇfn func(abc def: i32) -> u32 {
11961        }
11962    "});
11963
11964    cx.update(|_, cx| {
11965        lsp_store.update(cx, |lsp_store, cx| {
11966            lsp_store
11967                .update_diagnostics(
11968                    LanguageServerId(0),
11969                    lsp::PublishDiagnosticsParams {
11970                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11971                        version: None,
11972                        diagnostics: vec![
11973                            lsp::Diagnostic {
11974                                range: lsp::Range::new(
11975                                    lsp::Position::new(0, 11),
11976                                    lsp::Position::new(0, 12),
11977                                ),
11978                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11979                                ..Default::default()
11980                            },
11981                            lsp::Diagnostic {
11982                                range: lsp::Range::new(
11983                                    lsp::Position::new(0, 12),
11984                                    lsp::Position::new(0, 15),
11985                                ),
11986                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11987                                ..Default::default()
11988                            },
11989                            lsp::Diagnostic {
11990                                range: lsp::Range::new(
11991                                    lsp::Position::new(0, 12),
11992                                    lsp::Position::new(0, 15),
11993                                ),
11994                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11995                                ..Default::default()
11996                            },
11997                            lsp::Diagnostic {
11998                                range: lsp::Range::new(
11999                                    lsp::Position::new(0, 25),
12000                                    lsp::Position::new(0, 28),
12001                                ),
12002                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12003                                ..Default::default()
12004                            },
12005                        ],
12006                    },
12007                    &[],
12008                    cx,
12009                )
12010                .unwrap()
12011        });
12012    });
12013    executor.run_until_parked();
12014
12015    //// Backward
12016
12017    // Fourth diagnostic
12018    cx.update_editor(|editor, window, cx| {
12019        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12020    });
12021    cx.assert_editor_state(indoc! {"
12022        fn func(abc def: i32) -> ˇu32 {
12023        }
12024    "});
12025
12026    // Third diagnostic
12027    cx.update_editor(|editor, window, cx| {
12028        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12029    });
12030    cx.assert_editor_state(indoc! {"
12031        fn func(abc ˇdef: i32) -> u32 {
12032        }
12033    "});
12034
12035    // Second diagnostic, same place
12036    cx.update_editor(|editor, window, cx| {
12037        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12038    });
12039    cx.assert_editor_state(indoc! {"
12040        fn func(abc ˇdef: i32) -> u32 {
12041        }
12042    "});
12043
12044    // First diagnostic
12045    cx.update_editor(|editor, window, cx| {
12046        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12047    });
12048    cx.assert_editor_state(indoc! {"
12049        fn func(abcˇ def: i32) -> u32 {
12050        }
12051    "});
12052
12053    // Wrapped over, fourth diagnostic
12054    cx.update_editor(|editor, window, cx| {
12055        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12056    });
12057    cx.assert_editor_state(indoc! {"
12058        fn func(abc def: i32) -> ˇu32 {
12059        }
12060    "});
12061
12062    cx.update_editor(|editor, window, cx| {
12063        editor.move_to_beginning(&MoveToBeginning, window, cx);
12064    });
12065    cx.assert_editor_state(indoc! {"
12066        ˇfn func(abc def: i32) -> u32 {
12067        }
12068    "});
12069
12070    //// Forward
12071
12072    // First diagnostic
12073    cx.update_editor(|editor, window, cx| {
12074        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12075    });
12076    cx.assert_editor_state(indoc! {"
12077        fn func(abcˇ def: i32) -> u32 {
12078        }
12079    "});
12080
12081    // Second diagnostic
12082    cx.update_editor(|editor, window, cx| {
12083        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12084    });
12085    cx.assert_editor_state(indoc! {"
12086        fn func(abc ˇdef: i32) -> u32 {
12087        }
12088    "});
12089
12090    // Third diagnostic, same place
12091    cx.update_editor(|editor, window, cx| {
12092        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12093    });
12094    cx.assert_editor_state(indoc! {"
12095        fn func(abc ˇdef: i32) -> u32 {
12096        }
12097    "});
12098
12099    // Fourth diagnostic
12100    cx.update_editor(|editor, window, cx| {
12101        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12102    });
12103    cx.assert_editor_state(indoc! {"
12104        fn func(abc def: i32) -> ˇu32 {
12105        }
12106    "});
12107
12108    // Wrapped around, first diagnostic
12109    cx.update_editor(|editor, window, cx| {
12110        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12111    });
12112    cx.assert_editor_state(indoc! {"
12113        fn func(abcˇ def: i32) -> u32 {
12114        }
12115    "});
12116}
12117
12118#[gpui::test]
12119async fn active_diagnostics_dismiss_after_invalidation(
12120    executor: BackgroundExecutor,
12121    cx: &mut TestAppContext,
12122) {
12123    init_test(cx, |_| {});
12124
12125    let mut cx = EditorTestContext::new(cx).await;
12126    let lsp_store =
12127        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12128
12129    cx.set_state(indoc! {"
12130        ˇfn func(abc def: i32) -> u32 {
12131        }
12132    "});
12133
12134    let message = "Something's wrong!";
12135    cx.update(|_, cx| {
12136        lsp_store.update(cx, |lsp_store, cx| {
12137            lsp_store
12138                .update_diagnostics(
12139                    LanguageServerId(0),
12140                    lsp::PublishDiagnosticsParams {
12141                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12142                        version: None,
12143                        diagnostics: vec![lsp::Diagnostic {
12144                            range: lsp::Range::new(
12145                                lsp::Position::new(0, 11),
12146                                lsp::Position::new(0, 12),
12147                            ),
12148                            severity: Some(lsp::DiagnosticSeverity::ERROR),
12149                            message: message.to_string(),
12150                            ..Default::default()
12151                        }],
12152                    },
12153                    &[],
12154                    cx,
12155                )
12156                .unwrap()
12157        });
12158    });
12159    executor.run_until_parked();
12160
12161    cx.update_editor(|editor, window, cx| {
12162        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12163        assert_eq!(
12164            editor
12165                .active_diagnostics
12166                .as_ref()
12167                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
12168            Some(message),
12169            "Should have a diagnostics group activated"
12170        );
12171    });
12172    cx.assert_editor_state(indoc! {"
12173        fn func(abcˇ def: i32) -> u32 {
12174        }
12175    "});
12176
12177    cx.update(|_, cx| {
12178        lsp_store.update(cx, |lsp_store, cx| {
12179            lsp_store
12180                .update_diagnostics(
12181                    LanguageServerId(0),
12182                    lsp::PublishDiagnosticsParams {
12183                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12184                        version: None,
12185                        diagnostics: Vec::new(),
12186                    },
12187                    &[],
12188                    cx,
12189                )
12190                .unwrap()
12191        });
12192    });
12193    executor.run_until_parked();
12194    cx.update_editor(|editor, _, _| {
12195        assert_eq!(
12196            editor.active_diagnostics, None,
12197            "After no diagnostics set to the editor, no diagnostics should be active"
12198        );
12199    });
12200    cx.assert_editor_state(indoc! {"
12201        fn func(abcˇ def: i32) -> u32 {
12202        }
12203    "});
12204
12205    cx.update_editor(|editor, window, cx| {
12206        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12207        assert_eq!(
12208            editor.active_diagnostics, None,
12209            "Should be no diagnostics to go to and activate"
12210        );
12211    });
12212    cx.assert_editor_state(indoc! {"
12213        fn func(abcˇ def: i32) -> u32 {
12214        }
12215    "});
12216}
12217
12218#[gpui::test]
12219async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12220    init_test(cx, |_| {});
12221
12222    let mut cx = EditorTestContext::new(cx).await;
12223
12224    cx.set_state(indoc! {"
12225        fn func(abˇc def: i32) -> u32 {
12226        }
12227    "});
12228    let lsp_store =
12229        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12230
12231    cx.update(|_, cx| {
12232        lsp_store.update(cx, |lsp_store, cx| {
12233            lsp_store.update_diagnostics(
12234                LanguageServerId(0),
12235                lsp::PublishDiagnosticsParams {
12236                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12237                    version: None,
12238                    diagnostics: vec![lsp::Diagnostic {
12239                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12240                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12241                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12242                        ..Default::default()
12243                    }],
12244                },
12245                &[],
12246                cx,
12247            )
12248        })
12249    }).unwrap();
12250    cx.run_until_parked();
12251    cx.update_editor(|editor, window, cx| {
12252        hover_popover::hover(editor, &Default::default(), window, cx)
12253    });
12254    cx.run_until_parked();
12255    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12256}
12257
12258#[gpui::test]
12259async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12260    init_test(cx, |_| {});
12261
12262    let mut cx = EditorTestContext::new(cx).await;
12263
12264    let diff_base = r#"
12265        use some::mod;
12266
12267        const A: u32 = 42;
12268
12269        fn main() {
12270            println!("hello");
12271
12272            println!("world");
12273        }
12274        "#
12275    .unindent();
12276
12277    // Edits are modified, removed, modified, added
12278    cx.set_state(
12279        &r#"
12280        use some::modified;
12281
12282        ˇ
12283        fn main() {
12284            println!("hello there");
12285
12286            println!("around the");
12287            println!("world");
12288        }
12289        "#
12290        .unindent(),
12291    );
12292
12293    cx.set_head_text(&diff_base);
12294    executor.run_until_parked();
12295
12296    cx.update_editor(|editor, window, cx| {
12297        //Wrap around the bottom of the buffer
12298        for _ in 0..3 {
12299            editor.go_to_next_hunk(&GoToHunk, window, cx);
12300        }
12301    });
12302
12303    cx.assert_editor_state(
12304        &r#"
12305        ˇuse some::modified;
12306
12307
12308        fn main() {
12309            println!("hello there");
12310
12311            println!("around the");
12312            println!("world");
12313        }
12314        "#
12315        .unindent(),
12316    );
12317
12318    cx.update_editor(|editor, window, cx| {
12319        //Wrap around the top of the buffer
12320        for _ in 0..2 {
12321            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12322        }
12323    });
12324
12325    cx.assert_editor_state(
12326        &r#"
12327        use some::modified;
12328
12329
12330        fn main() {
12331        ˇ    println!("hello there");
12332
12333            println!("around the");
12334            println!("world");
12335        }
12336        "#
12337        .unindent(),
12338    );
12339
12340    cx.update_editor(|editor, window, cx| {
12341        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12342    });
12343
12344    cx.assert_editor_state(
12345        &r#"
12346        use some::modified;
12347
12348        ˇ
12349        fn main() {
12350            println!("hello there");
12351
12352            println!("around the");
12353            println!("world");
12354        }
12355        "#
12356        .unindent(),
12357    );
12358
12359    cx.update_editor(|editor, window, cx| {
12360        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12361    });
12362
12363    cx.assert_editor_state(
12364        &r#"
12365        ˇuse some::modified;
12366
12367
12368        fn main() {
12369            println!("hello there");
12370
12371            println!("around the");
12372            println!("world");
12373        }
12374        "#
12375        .unindent(),
12376    );
12377
12378    cx.update_editor(|editor, window, cx| {
12379        for _ in 0..2 {
12380            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12381        }
12382    });
12383
12384    cx.assert_editor_state(
12385        &r#"
12386        use some::modified;
12387
12388
12389        fn main() {
12390        ˇ    println!("hello there");
12391
12392            println!("around the");
12393            println!("world");
12394        }
12395        "#
12396        .unindent(),
12397    );
12398
12399    cx.update_editor(|editor, window, cx| {
12400        editor.fold(&Fold, window, cx);
12401    });
12402
12403    cx.update_editor(|editor, window, cx| {
12404        editor.go_to_next_hunk(&GoToHunk, window, cx);
12405    });
12406
12407    cx.assert_editor_state(
12408        &r#"
12409        ˇuse some::modified;
12410
12411
12412        fn main() {
12413            println!("hello there");
12414
12415            println!("around the");
12416            println!("world");
12417        }
12418        "#
12419        .unindent(),
12420    );
12421}
12422
12423#[test]
12424fn test_split_words() {
12425    fn split(text: &str) -> Vec<&str> {
12426        split_words(text).collect()
12427    }
12428
12429    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12430    assert_eq!(split("hello_world"), &["hello_", "world"]);
12431    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12432    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12433    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12434    assert_eq!(split("helloworld"), &["helloworld"]);
12435
12436    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12437}
12438
12439#[gpui::test]
12440async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12441    init_test(cx, |_| {});
12442
12443    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12444    let mut assert = |before, after| {
12445        let _state_context = cx.set_state(before);
12446        cx.run_until_parked();
12447        cx.update_editor(|editor, window, cx| {
12448            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12449        });
12450        cx.run_until_parked();
12451        cx.assert_editor_state(after);
12452    };
12453
12454    // Outside bracket jumps to outside of matching bracket
12455    assert("console.logˇ(var);", "console.log(var)ˇ;");
12456    assert("console.log(var)ˇ;", "console.logˇ(var);");
12457
12458    // Inside bracket jumps to inside of matching bracket
12459    assert("console.log(ˇvar);", "console.log(varˇ);");
12460    assert("console.log(varˇ);", "console.log(ˇvar);");
12461
12462    // When outside a bracket and inside, favor jumping to the inside bracket
12463    assert(
12464        "console.log('foo', [1, 2, 3]ˇ);",
12465        "console.log(ˇ'foo', [1, 2, 3]);",
12466    );
12467    assert(
12468        "console.log(ˇ'foo', [1, 2, 3]);",
12469        "console.log('foo', [1, 2, 3]ˇ);",
12470    );
12471
12472    // Bias forward if two options are equally likely
12473    assert(
12474        "let result = curried_fun()ˇ();",
12475        "let result = curried_fun()()ˇ;",
12476    );
12477
12478    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12479    assert(
12480        indoc! {"
12481            function test() {
12482                console.log('test')ˇ
12483            }"},
12484        indoc! {"
12485            function test() {
12486                console.logˇ('test')
12487            }"},
12488    );
12489}
12490
12491#[gpui::test]
12492async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12493    init_test(cx, |_| {});
12494
12495    let fs = FakeFs::new(cx.executor());
12496    fs.insert_tree(
12497        path!("/a"),
12498        json!({
12499            "main.rs": "fn main() { let a = 5; }",
12500            "other.rs": "// Test file",
12501        }),
12502    )
12503    .await;
12504    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12505
12506    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12507    language_registry.add(Arc::new(Language::new(
12508        LanguageConfig {
12509            name: "Rust".into(),
12510            matcher: LanguageMatcher {
12511                path_suffixes: vec!["rs".to_string()],
12512                ..Default::default()
12513            },
12514            brackets: BracketPairConfig {
12515                pairs: vec![BracketPair {
12516                    start: "{".to_string(),
12517                    end: "}".to_string(),
12518                    close: true,
12519                    surround: true,
12520                    newline: true,
12521                }],
12522                disabled_scopes_by_bracket_ix: Vec::new(),
12523            },
12524            ..Default::default()
12525        },
12526        Some(tree_sitter_rust::LANGUAGE.into()),
12527    )));
12528    let mut fake_servers = language_registry.register_fake_lsp(
12529        "Rust",
12530        FakeLspAdapter {
12531            capabilities: lsp::ServerCapabilities {
12532                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12533                    first_trigger_character: "{".to_string(),
12534                    more_trigger_character: None,
12535                }),
12536                ..Default::default()
12537            },
12538            ..Default::default()
12539        },
12540    );
12541
12542    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12543
12544    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12545
12546    let worktree_id = workspace
12547        .update(cx, |workspace, _, cx| {
12548            workspace.project().update(cx, |project, cx| {
12549                project.worktrees(cx).next().unwrap().read(cx).id()
12550            })
12551        })
12552        .unwrap();
12553
12554    let buffer = project
12555        .update(cx, |project, cx| {
12556            project.open_local_buffer(path!("/a/main.rs"), cx)
12557        })
12558        .await
12559        .unwrap();
12560    let editor_handle = workspace
12561        .update(cx, |workspace, window, cx| {
12562            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12563        })
12564        .unwrap()
12565        .await
12566        .unwrap()
12567        .downcast::<Editor>()
12568        .unwrap();
12569
12570    cx.executor().start_waiting();
12571    let fake_server = fake_servers.next().await.unwrap();
12572
12573    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12574        |params, _| async move {
12575            assert_eq!(
12576                params.text_document_position.text_document.uri,
12577                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12578            );
12579            assert_eq!(
12580                params.text_document_position.position,
12581                lsp::Position::new(0, 21),
12582            );
12583
12584            Ok(Some(vec![lsp::TextEdit {
12585                new_text: "]".to_string(),
12586                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12587            }]))
12588        },
12589    );
12590
12591    editor_handle.update_in(cx, |editor, window, cx| {
12592        window.focus(&editor.focus_handle(cx));
12593        editor.change_selections(None, window, cx, |s| {
12594            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12595        });
12596        editor.handle_input("{", window, cx);
12597    });
12598
12599    cx.executor().run_until_parked();
12600
12601    buffer.update(cx, |buffer, _| {
12602        assert_eq!(
12603            buffer.text(),
12604            "fn main() { let a = {5}; }",
12605            "No extra braces from on type formatting should appear in the buffer"
12606        )
12607    });
12608}
12609
12610#[gpui::test]
12611async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12612    init_test(cx, |_| {});
12613
12614    let fs = FakeFs::new(cx.executor());
12615    fs.insert_tree(
12616        path!("/a"),
12617        json!({
12618            "main.rs": "fn main() { let a = 5; }",
12619            "other.rs": "// Test file",
12620        }),
12621    )
12622    .await;
12623
12624    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12625
12626    let server_restarts = Arc::new(AtomicUsize::new(0));
12627    let closure_restarts = Arc::clone(&server_restarts);
12628    let language_server_name = "test language server";
12629    let language_name: LanguageName = "Rust".into();
12630
12631    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12632    language_registry.add(Arc::new(Language::new(
12633        LanguageConfig {
12634            name: language_name.clone(),
12635            matcher: LanguageMatcher {
12636                path_suffixes: vec!["rs".to_string()],
12637                ..Default::default()
12638            },
12639            ..Default::default()
12640        },
12641        Some(tree_sitter_rust::LANGUAGE.into()),
12642    )));
12643    let mut fake_servers = language_registry.register_fake_lsp(
12644        "Rust",
12645        FakeLspAdapter {
12646            name: language_server_name,
12647            initialization_options: Some(json!({
12648                "testOptionValue": true
12649            })),
12650            initializer: Some(Box::new(move |fake_server| {
12651                let task_restarts = Arc::clone(&closure_restarts);
12652                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12653                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12654                    futures::future::ready(Ok(()))
12655                });
12656            })),
12657            ..Default::default()
12658        },
12659    );
12660
12661    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12662    let _buffer = project
12663        .update(cx, |project, cx| {
12664            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12665        })
12666        .await
12667        .unwrap();
12668    let _fake_server = fake_servers.next().await.unwrap();
12669    update_test_language_settings(cx, |language_settings| {
12670        language_settings.languages.insert(
12671            language_name.clone(),
12672            LanguageSettingsContent {
12673                tab_size: NonZeroU32::new(8),
12674                ..Default::default()
12675            },
12676        );
12677    });
12678    cx.executor().run_until_parked();
12679    assert_eq!(
12680        server_restarts.load(atomic::Ordering::Acquire),
12681        0,
12682        "Should not restart LSP server on an unrelated change"
12683    );
12684
12685    update_test_project_settings(cx, |project_settings| {
12686        project_settings.lsp.insert(
12687            "Some other server name".into(),
12688            LspSettings {
12689                binary: None,
12690                settings: None,
12691                initialization_options: Some(json!({
12692                    "some other init value": false
12693                })),
12694                enable_lsp_tasks: false,
12695            },
12696        );
12697    });
12698    cx.executor().run_until_parked();
12699    assert_eq!(
12700        server_restarts.load(atomic::Ordering::Acquire),
12701        0,
12702        "Should not restart LSP server on an unrelated LSP settings change"
12703    );
12704
12705    update_test_project_settings(cx, |project_settings| {
12706        project_settings.lsp.insert(
12707            language_server_name.into(),
12708            LspSettings {
12709                binary: None,
12710                settings: None,
12711                initialization_options: Some(json!({
12712                    "anotherInitValue": false
12713                })),
12714                enable_lsp_tasks: false,
12715            },
12716        );
12717    });
12718    cx.executor().run_until_parked();
12719    assert_eq!(
12720        server_restarts.load(atomic::Ordering::Acquire),
12721        1,
12722        "Should restart LSP server on a related LSP settings change"
12723    );
12724
12725    update_test_project_settings(cx, |project_settings| {
12726        project_settings.lsp.insert(
12727            language_server_name.into(),
12728            LspSettings {
12729                binary: None,
12730                settings: None,
12731                initialization_options: Some(json!({
12732                    "anotherInitValue": false
12733                })),
12734                enable_lsp_tasks: false,
12735            },
12736        );
12737    });
12738    cx.executor().run_until_parked();
12739    assert_eq!(
12740        server_restarts.load(atomic::Ordering::Acquire),
12741        1,
12742        "Should not restart LSP server on a related LSP settings change that is the same"
12743    );
12744
12745    update_test_project_settings(cx, |project_settings| {
12746        project_settings.lsp.insert(
12747            language_server_name.into(),
12748            LspSettings {
12749                binary: None,
12750                settings: None,
12751                initialization_options: None,
12752                enable_lsp_tasks: false,
12753            },
12754        );
12755    });
12756    cx.executor().run_until_parked();
12757    assert_eq!(
12758        server_restarts.load(atomic::Ordering::Acquire),
12759        2,
12760        "Should restart LSP server on another related LSP settings change"
12761    );
12762}
12763
12764#[gpui::test]
12765async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12766    init_test(cx, |_| {});
12767
12768    let mut cx = EditorLspTestContext::new_rust(
12769        lsp::ServerCapabilities {
12770            completion_provider: Some(lsp::CompletionOptions {
12771                trigger_characters: Some(vec![".".to_string()]),
12772                resolve_provider: Some(true),
12773                ..Default::default()
12774            }),
12775            ..Default::default()
12776        },
12777        cx,
12778    )
12779    .await;
12780
12781    cx.set_state("fn main() { let a = 2ˇ; }");
12782    cx.simulate_keystroke(".");
12783    let completion_item = lsp::CompletionItem {
12784        label: "some".into(),
12785        kind: Some(lsp::CompletionItemKind::SNIPPET),
12786        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12787        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12788            kind: lsp::MarkupKind::Markdown,
12789            value: "```rust\nSome(2)\n```".to_string(),
12790        })),
12791        deprecated: Some(false),
12792        sort_text: Some("fffffff2".to_string()),
12793        filter_text: Some("some".to_string()),
12794        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12795        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12796            range: lsp::Range {
12797                start: lsp::Position {
12798                    line: 0,
12799                    character: 22,
12800                },
12801                end: lsp::Position {
12802                    line: 0,
12803                    character: 22,
12804                },
12805            },
12806            new_text: "Some(2)".to_string(),
12807        })),
12808        additional_text_edits: Some(vec![lsp::TextEdit {
12809            range: lsp::Range {
12810                start: lsp::Position {
12811                    line: 0,
12812                    character: 20,
12813                },
12814                end: lsp::Position {
12815                    line: 0,
12816                    character: 22,
12817                },
12818            },
12819            new_text: "".to_string(),
12820        }]),
12821        ..Default::default()
12822    };
12823
12824    let closure_completion_item = completion_item.clone();
12825    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12826        let task_completion_item = closure_completion_item.clone();
12827        async move {
12828            Ok(Some(lsp::CompletionResponse::Array(vec![
12829                task_completion_item,
12830            ])))
12831        }
12832    });
12833
12834    request.next().await;
12835
12836    cx.condition(|editor, _| editor.context_menu_visible())
12837        .await;
12838    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12839        editor
12840            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12841            .unwrap()
12842    });
12843    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12844
12845    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12846        let task_completion_item = completion_item.clone();
12847        async move { Ok(task_completion_item) }
12848    })
12849    .next()
12850    .await
12851    .unwrap();
12852    apply_additional_edits.await.unwrap();
12853    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12854}
12855
12856#[gpui::test]
12857async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12858    init_test(cx, |_| {});
12859
12860    let mut cx = EditorLspTestContext::new_rust(
12861        lsp::ServerCapabilities {
12862            completion_provider: Some(lsp::CompletionOptions {
12863                trigger_characters: Some(vec![".".to_string()]),
12864                resolve_provider: Some(true),
12865                ..Default::default()
12866            }),
12867            ..Default::default()
12868        },
12869        cx,
12870    )
12871    .await;
12872
12873    cx.set_state("fn main() { let a = 2ˇ; }");
12874    cx.simulate_keystroke(".");
12875
12876    let item1 = lsp::CompletionItem {
12877        label: "method id()".to_string(),
12878        filter_text: Some("id".to_string()),
12879        detail: None,
12880        documentation: None,
12881        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12882            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12883            new_text: ".id".to_string(),
12884        })),
12885        ..lsp::CompletionItem::default()
12886    };
12887
12888    let item2 = lsp::CompletionItem {
12889        label: "other".to_string(),
12890        filter_text: Some("other".to_string()),
12891        detail: None,
12892        documentation: None,
12893        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12894            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12895            new_text: ".other".to_string(),
12896        })),
12897        ..lsp::CompletionItem::default()
12898    };
12899
12900    let item1 = item1.clone();
12901    cx.set_request_handler::<lsp::request::Completion, _, _>({
12902        let item1 = item1.clone();
12903        move |_, _, _| {
12904            let item1 = item1.clone();
12905            let item2 = item2.clone();
12906            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12907        }
12908    })
12909    .next()
12910    .await;
12911
12912    cx.condition(|editor, _| editor.context_menu_visible())
12913        .await;
12914    cx.update_editor(|editor, _, _| {
12915        let context_menu = editor.context_menu.borrow_mut();
12916        let context_menu = context_menu
12917            .as_ref()
12918            .expect("Should have the context menu deployed");
12919        match context_menu {
12920            CodeContextMenu::Completions(completions_menu) => {
12921                let completions = completions_menu.completions.borrow_mut();
12922                assert_eq!(
12923                    completions
12924                        .iter()
12925                        .map(|completion| &completion.label.text)
12926                        .collect::<Vec<_>>(),
12927                    vec!["method id()", "other"]
12928                )
12929            }
12930            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12931        }
12932    });
12933
12934    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12935        let item1 = item1.clone();
12936        move |_, item_to_resolve, _| {
12937            let item1 = item1.clone();
12938            async move {
12939                if item1 == item_to_resolve {
12940                    Ok(lsp::CompletionItem {
12941                        label: "method id()".to_string(),
12942                        filter_text: Some("id".to_string()),
12943                        detail: Some("Now resolved!".to_string()),
12944                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12945                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12946                            range: lsp::Range::new(
12947                                lsp::Position::new(0, 22),
12948                                lsp::Position::new(0, 22),
12949                            ),
12950                            new_text: ".id".to_string(),
12951                        })),
12952                        ..lsp::CompletionItem::default()
12953                    })
12954                } else {
12955                    Ok(item_to_resolve)
12956                }
12957            }
12958        }
12959    })
12960    .next()
12961    .await
12962    .unwrap();
12963    cx.run_until_parked();
12964
12965    cx.update_editor(|editor, window, cx| {
12966        editor.context_menu_next(&Default::default(), window, cx);
12967    });
12968
12969    cx.update_editor(|editor, _, _| {
12970        let context_menu = editor.context_menu.borrow_mut();
12971        let context_menu = context_menu
12972            .as_ref()
12973            .expect("Should have the context menu deployed");
12974        match context_menu {
12975            CodeContextMenu::Completions(completions_menu) => {
12976                let completions = completions_menu.completions.borrow_mut();
12977                assert_eq!(
12978                    completions
12979                        .iter()
12980                        .map(|completion| &completion.label.text)
12981                        .collect::<Vec<_>>(),
12982                    vec!["method id() Now resolved!", "other"],
12983                    "Should update first completion label, but not second as the filter text did not match."
12984                );
12985            }
12986            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12987        }
12988    });
12989}
12990
12991#[gpui::test]
12992async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12993    init_test(cx, |_| {});
12994
12995    let mut cx = EditorLspTestContext::new_rust(
12996        lsp::ServerCapabilities {
12997            completion_provider: Some(lsp::CompletionOptions {
12998                trigger_characters: Some(vec![".".to_string()]),
12999                resolve_provider: Some(true),
13000                ..Default::default()
13001            }),
13002            ..Default::default()
13003        },
13004        cx,
13005    )
13006    .await;
13007
13008    cx.set_state("fn main() { let a = 2ˇ; }");
13009    cx.simulate_keystroke(".");
13010
13011    let unresolved_item_1 = lsp::CompletionItem {
13012        label: "id".to_string(),
13013        filter_text: Some("id".to_string()),
13014        detail: None,
13015        documentation: None,
13016        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13017            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13018            new_text: ".id".to_string(),
13019        })),
13020        ..lsp::CompletionItem::default()
13021    };
13022    let resolved_item_1 = lsp::CompletionItem {
13023        additional_text_edits: Some(vec![lsp::TextEdit {
13024            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13025            new_text: "!!".to_string(),
13026        }]),
13027        ..unresolved_item_1.clone()
13028    };
13029    let unresolved_item_2 = lsp::CompletionItem {
13030        label: "other".to_string(),
13031        filter_text: Some("other".to_string()),
13032        detail: None,
13033        documentation: None,
13034        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13035            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13036            new_text: ".other".to_string(),
13037        })),
13038        ..lsp::CompletionItem::default()
13039    };
13040    let resolved_item_2 = lsp::CompletionItem {
13041        additional_text_edits: Some(vec![lsp::TextEdit {
13042            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13043            new_text: "??".to_string(),
13044        }]),
13045        ..unresolved_item_2.clone()
13046    };
13047
13048    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13049    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13050    cx.lsp
13051        .server
13052        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13053            let unresolved_item_1 = unresolved_item_1.clone();
13054            let resolved_item_1 = resolved_item_1.clone();
13055            let unresolved_item_2 = unresolved_item_2.clone();
13056            let resolved_item_2 = resolved_item_2.clone();
13057            let resolve_requests_1 = resolve_requests_1.clone();
13058            let resolve_requests_2 = resolve_requests_2.clone();
13059            move |unresolved_request, _| {
13060                let unresolved_item_1 = unresolved_item_1.clone();
13061                let resolved_item_1 = resolved_item_1.clone();
13062                let unresolved_item_2 = unresolved_item_2.clone();
13063                let resolved_item_2 = resolved_item_2.clone();
13064                let resolve_requests_1 = resolve_requests_1.clone();
13065                let resolve_requests_2 = resolve_requests_2.clone();
13066                async move {
13067                    if unresolved_request == unresolved_item_1 {
13068                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13069                        Ok(resolved_item_1.clone())
13070                    } else if unresolved_request == unresolved_item_2 {
13071                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13072                        Ok(resolved_item_2.clone())
13073                    } else {
13074                        panic!("Unexpected completion item {unresolved_request:?}")
13075                    }
13076                }
13077            }
13078        })
13079        .detach();
13080
13081    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13082        let unresolved_item_1 = unresolved_item_1.clone();
13083        let unresolved_item_2 = unresolved_item_2.clone();
13084        async move {
13085            Ok(Some(lsp::CompletionResponse::Array(vec![
13086                unresolved_item_1,
13087                unresolved_item_2,
13088            ])))
13089        }
13090    })
13091    .next()
13092    .await;
13093
13094    cx.condition(|editor, _| editor.context_menu_visible())
13095        .await;
13096    cx.update_editor(|editor, _, _| {
13097        let context_menu = editor.context_menu.borrow_mut();
13098        let context_menu = context_menu
13099            .as_ref()
13100            .expect("Should have the context menu deployed");
13101        match context_menu {
13102            CodeContextMenu::Completions(completions_menu) => {
13103                let completions = completions_menu.completions.borrow_mut();
13104                assert_eq!(
13105                    completions
13106                        .iter()
13107                        .map(|completion| &completion.label.text)
13108                        .collect::<Vec<_>>(),
13109                    vec!["id", "other"]
13110                )
13111            }
13112            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13113        }
13114    });
13115    cx.run_until_parked();
13116
13117    cx.update_editor(|editor, window, cx| {
13118        editor.context_menu_next(&ContextMenuNext, window, cx);
13119    });
13120    cx.run_until_parked();
13121    cx.update_editor(|editor, window, cx| {
13122        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13123    });
13124    cx.run_until_parked();
13125    cx.update_editor(|editor, window, cx| {
13126        editor.context_menu_next(&ContextMenuNext, window, cx);
13127    });
13128    cx.run_until_parked();
13129    cx.update_editor(|editor, window, cx| {
13130        editor
13131            .compose_completion(&ComposeCompletion::default(), window, cx)
13132            .expect("No task returned")
13133    })
13134    .await
13135    .expect("Completion failed");
13136    cx.run_until_parked();
13137
13138    cx.update_editor(|editor, _, cx| {
13139        assert_eq!(
13140            resolve_requests_1.load(atomic::Ordering::Acquire),
13141            1,
13142            "Should always resolve once despite multiple selections"
13143        );
13144        assert_eq!(
13145            resolve_requests_2.load(atomic::Ordering::Acquire),
13146            1,
13147            "Should always resolve once after multiple selections and applying the completion"
13148        );
13149        assert_eq!(
13150            editor.text(cx),
13151            "fn main() { let a = ??.other; }",
13152            "Should use resolved data when applying the completion"
13153        );
13154    });
13155}
13156
13157#[gpui::test]
13158async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13159    init_test(cx, |_| {});
13160
13161    let item_0 = lsp::CompletionItem {
13162        label: "abs".into(),
13163        insert_text: Some("abs".into()),
13164        data: Some(json!({ "very": "special"})),
13165        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13166        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13167            lsp::InsertReplaceEdit {
13168                new_text: "abs".to_string(),
13169                insert: lsp::Range::default(),
13170                replace: lsp::Range::default(),
13171            },
13172        )),
13173        ..lsp::CompletionItem::default()
13174    };
13175    let items = iter::once(item_0.clone())
13176        .chain((11..51).map(|i| lsp::CompletionItem {
13177            label: format!("item_{}", i),
13178            insert_text: Some(format!("item_{}", i)),
13179            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13180            ..lsp::CompletionItem::default()
13181        }))
13182        .collect::<Vec<_>>();
13183
13184    let default_commit_characters = vec!["?".to_string()];
13185    let default_data = json!({ "default": "data"});
13186    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13187    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13188    let default_edit_range = lsp::Range {
13189        start: lsp::Position {
13190            line: 0,
13191            character: 5,
13192        },
13193        end: lsp::Position {
13194            line: 0,
13195            character: 5,
13196        },
13197    };
13198
13199    let mut cx = EditorLspTestContext::new_rust(
13200        lsp::ServerCapabilities {
13201            completion_provider: Some(lsp::CompletionOptions {
13202                trigger_characters: Some(vec![".".to_string()]),
13203                resolve_provider: Some(true),
13204                ..Default::default()
13205            }),
13206            ..Default::default()
13207        },
13208        cx,
13209    )
13210    .await;
13211
13212    cx.set_state("fn main() { let a = 2ˇ; }");
13213    cx.simulate_keystroke(".");
13214
13215    let completion_data = default_data.clone();
13216    let completion_characters = default_commit_characters.clone();
13217    let completion_items = items.clone();
13218    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13219        let default_data = completion_data.clone();
13220        let default_commit_characters = completion_characters.clone();
13221        let items = completion_items.clone();
13222        async move {
13223            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13224                items,
13225                item_defaults: Some(lsp::CompletionListItemDefaults {
13226                    data: Some(default_data.clone()),
13227                    commit_characters: Some(default_commit_characters.clone()),
13228                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13229                        default_edit_range,
13230                    )),
13231                    insert_text_format: Some(default_insert_text_format),
13232                    insert_text_mode: Some(default_insert_text_mode),
13233                }),
13234                ..lsp::CompletionList::default()
13235            })))
13236        }
13237    })
13238    .next()
13239    .await;
13240
13241    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13242    cx.lsp
13243        .server
13244        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13245            let closure_resolved_items = resolved_items.clone();
13246            move |item_to_resolve, _| {
13247                let closure_resolved_items = closure_resolved_items.clone();
13248                async move {
13249                    closure_resolved_items.lock().push(item_to_resolve.clone());
13250                    Ok(item_to_resolve)
13251                }
13252            }
13253        })
13254        .detach();
13255
13256    cx.condition(|editor, _| editor.context_menu_visible())
13257        .await;
13258    cx.run_until_parked();
13259    cx.update_editor(|editor, _, _| {
13260        let menu = editor.context_menu.borrow_mut();
13261        match menu.as_ref().expect("should have the completions menu") {
13262            CodeContextMenu::Completions(completions_menu) => {
13263                assert_eq!(
13264                    completions_menu
13265                        .entries
13266                        .borrow()
13267                        .iter()
13268                        .map(|mat| mat.string.clone())
13269                        .collect::<Vec<String>>(),
13270                    items
13271                        .iter()
13272                        .map(|completion| completion.label.clone())
13273                        .collect::<Vec<String>>()
13274                );
13275            }
13276            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13277        }
13278    });
13279    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13280    // with 4 from the end.
13281    assert_eq!(
13282        *resolved_items.lock(),
13283        [&items[0..16], &items[items.len() - 4..items.len()]]
13284            .concat()
13285            .iter()
13286            .cloned()
13287            .map(|mut item| {
13288                if item.data.is_none() {
13289                    item.data = Some(default_data.clone());
13290                }
13291                item
13292            })
13293            .collect::<Vec<lsp::CompletionItem>>(),
13294        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13295    );
13296    resolved_items.lock().clear();
13297
13298    cx.update_editor(|editor, window, cx| {
13299        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13300    });
13301    cx.run_until_parked();
13302    // Completions that have already been resolved are skipped.
13303    assert_eq!(
13304        *resolved_items.lock(),
13305        items[items.len() - 16..items.len() - 4]
13306            .iter()
13307            .cloned()
13308            .map(|mut item| {
13309                if item.data.is_none() {
13310                    item.data = Some(default_data.clone());
13311                }
13312                item
13313            })
13314            .collect::<Vec<lsp::CompletionItem>>()
13315    );
13316    resolved_items.lock().clear();
13317}
13318
13319#[gpui::test]
13320async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13321    init_test(cx, |_| {});
13322
13323    let mut cx = EditorLspTestContext::new(
13324        Language::new(
13325            LanguageConfig {
13326                matcher: LanguageMatcher {
13327                    path_suffixes: vec!["jsx".into()],
13328                    ..Default::default()
13329                },
13330                overrides: [(
13331                    "element".into(),
13332                    LanguageConfigOverride {
13333                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13334                        ..Default::default()
13335                    },
13336                )]
13337                .into_iter()
13338                .collect(),
13339                ..Default::default()
13340            },
13341            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13342        )
13343        .with_override_query("(jsx_self_closing_element) @element")
13344        .unwrap(),
13345        lsp::ServerCapabilities {
13346            completion_provider: Some(lsp::CompletionOptions {
13347                trigger_characters: Some(vec![":".to_string()]),
13348                ..Default::default()
13349            }),
13350            ..Default::default()
13351        },
13352        cx,
13353    )
13354    .await;
13355
13356    cx.lsp
13357        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13358            Ok(Some(lsp::CompletionResponse::Array(vec![
13359                lsp::CompletionItem {
13360                    label: "bg-blue".into(),
13361                    ..Default::default()
13362                },
13363                lsp::CompletionItem {
13364                    label: "bg-red".into(),
13365                    ..Default::default()
13366                },
13367                lsp::CompletionItem {
13368                    label: "bg-yellow".into(),
13369                    ..Default::default()
13370                },
13371            ])))
13372        });
13373
13374    cx.set_state(r#"<p class="bgˇ" />"#);
13375
13376    // Trigger completion when typing a dash, because the dash is an extra
13377    // word character in the 'element' scope, which contains the cursor.
13378    cx.simulate_keystroke("-");
13379    cx.executor().run_until_parked();
13380    cx.update_editor(|editor, _, _| {
13381        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13382        {
13383            assert_eq!(
13384                completion_menu_entries(&menu),
13385                &["bg-red", "bg-blue", "bg-yellow"]
13386            );
13387        } else {
13388            panic!("expected completion menu to be open");
13389        }
13390    });
13391
13392    cx.simulate_keystroke("l");
13393    cx.executor().run_until_parked();
13394    cx.update_editor(|editor, _, _| {
13395        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13396        {
13397            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13398        } else {
13399            panic!("expected completion menu to be open");
13400        }
13401    });
13402
13403    // When filtering completions, consider the character after the '-' to
13404    // be the start of a subword.
13405    cx.set_state(r#"<p class="yelˇ" />"#);
13406    cx.simulate_keystroke("l");
13407    cx.executor().run_until_parked();
13408    cx.update_editor(|editor, _, _| {
13409        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13410        {
13411            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13412        } else {
13413            panic!("expected completion menu to be open");
13414        }
13415    });
13416}
13417
13418fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13419    let entries = menu.entries.borrow();
13420    entries.iter().map(|mat| mat.string.clone()).collect()
13421}
13422
13423#[gpui::test]
13424async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13425    init_test(cx, |settings| {
13426        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13427            FormatterList(vec![Formatter::Prettier].into()),
13428        ))
13429    });
13430
13431    let fs = FakeFs::new(cx.executor());
13432    fs.insert_file(path!("/file.ts"), Default::default()).await;
13433
13434    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13435    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13436
13437    language_registry.add(Arc::new(Language::new(
13438        LanguageConfig {
13439            name: "TypeScript".into(),
13440            matcher: LanguageMatcher {
13441                path_suffixes: vec!["ts".to_string()],
13442                ..Default::default()
13443            },
13444            ..Default::default()
13445        },
13446        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13447    )));
13448    update_test_language_settings(cx, |settings| {
13449        settings.defaults.prettier = Some(PrettierSettings {
13450            allowed: true,
13451            ..PrettierSettings::default()
13452        });
13453    });
13454
13455    let test_plugin = "test_plugin";
13456    let _ = language_registry.register_fake_lsp(
13457        "TypeScript",
13458        FakeLspAdapter {
13459            prettier_plugins: vec![test_plugin],
13460            ..Default::default()
13461        },
13462    );
13463
13464    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13465    let buffer = project
13466        .update(cx, |project, cx| {
13467            project.open_local_buffer(path!("/file.ts"), cx)
13468        })
13469        .await
13470        .unwrap();
13471
13472    let buffer_text = "one\ntwo\nthree\n";
13473    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13474    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13475    editor.update_in(cx, |editor, window, cx| {
13476        editor.set_text(buffer_text, window, cx)
13477    });
13478
13479    editor
13480        .update_in(cx, |editor, window, cx| {
13481            editor.perform_format(
13482                project.clone(),
13483                FormatTrigger::Manual,
13484                FormatTarget::Buffers,
13485                window,
13486                cx,
13487            )
13488        })
13489        .unwrap()
13490        .await;
13491    assert_eq!(
13492        editor.update(cx, |editor, cx| editor.text(cx)),
13493        buffer_text.to_string() + prettier_format_suffix,
13494        "Test prettier formatting was not applied to the original buffer text",
13495    );
13496
13497    update_test_language_settings(cx, |settings| {
13498        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13499    });
13500    let format = editor.update_in(cx, |editor, window, cx| {
13501        editor.perform_format(
13502            project.clone(),
13503            FormatTrigger::Manual,
13504            FormatTarget::Buffers,
13505            window,
13506            cx,
13507        )
13508    });
13509    format.await.unwrap();
13510    assert_eq!(
13511        editor.update(cx, |editor, cx| editor.text(cx)),
13512        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13513        "Autoformatting (via test prettier) was not applied to the original buffer text",
13514    );
13515}
13516
13517#[gpui::test]
13518async fn test_addition_reverts(cx: &mut TestAppContext) {
13519    init_test(cx, |_| {});
13520    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13521    let base_text = indoc! {r#"
13522        struct Row;
13523        struct Row1;
13524        struct Row2;
13525
13526        struct Row4;
13527        struct Row5;
13528        struct Row6;
13529
13530        struct Row8;
13531        struct Row9;
13532        struct Row10;"#};
13533
13534    // When addition hunks are not adjacent to carets, no hunk revert is performed
13535    assert_hunk_revert(
13536        indoc! {r#"struct Row;
13537                   struct Row1;
13538                   struct Row1.1;
13539                   struct Row1.2;
13540                   struct Row2;ˇ
13541
13542                   struct Row4;
13543                   struct Row5;
13544                   struct Row6;
13545
13546                   struct Row8;
13547                   ˇstruct Row9;
13548                   struct Row9.1;
13549                   struct Row9.2;
13550                   struct Row9.3;
13551                   struct Row10;"#},
13552        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13553        indoc! {r#"struct Row;
13554                   struct Row1;
13555                   struct Row1.1;
13556                   struct Row1.2;
13557                   struct Row2;ˇ
13558
13559                   struct Row4;
13560                   struct Row5;
13561                   struct Row6;
13562
13563                   struct Row8;
13564                   ˇstruct Row9;
13565                   struct Row9.1;
13566                   struct Row9.2;
13567                   struct Row9.3;
13568                   struct Row10;"#},
13569        base_text,
13570        &mut cx,
13571    );
13572    // Same for selections
13573    assert_hunk_revert(
13574        indoc! {r#"struct Row;
13575                   struct Row1;
13576                   struct Row2;
13577                   struct Row2.1;
13578                   struct Row2.2;
13579                   «ˇ
13580                   struct Row4;
13581                   struct» Row5;
13582                   «struct Row6;
13583                   ˇ»
13584                   struct Row9.1;
13585                   struct Row9.2;
13586                   struct Row9.3;
13587                   struct Row8;
13588                   struct Row9;
13589                   struct Row10;"#},
13590        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13591        indoc! {r#"struct Row;
13592                   struct Row1;
13593                   struct Row2;
13594                   struct Row2.1;
13595                   struct Row2.2;
13596                   «ˇ
13597                   struct Row4;
13598                   struct» Row5;
13599                   «struct Row6;
13600                   ˇ»
13601                   struct Row9.1;
13602                   struct Row9.2;
13603                   struct Row9.3;
13604                   struct Row8;
13605                   struct Row9;
13606                   struct Row10;"#},
13607        base_text,
13608        &mut cx,
13609    );
13610
13611    // When carets and selections intersect the addition hunks, those are reverted.
13612    // Adjacent carets got merged.
13613    assert_hunk_revert(
13614        indoc! {r#"struct Row;
13615                   ˇ// something on the top
13616                   struct Row1;
13617                   struct Row2;
13618                   struct Roˇw3.1;
13619                   struct Row2.2;
13620                   struct Row2.3;ˇ
13621
13622                   struct Row4;
13623                   struct ˇRow5.1;
13624                   struct Row5.2;
13625                   struct «Rowˇ»5.3;
13626                   struct Row5;
13627                   struct Row6;
13628                   ˇ
13629                   struct Row9.1;
13630                   struct «Rowˇ»9.2;
13631                   struct «ˇRow»9.3;
13632                   struct Row8;
13633                   struct Row9;
13634                   «ˇ// something on bottom»
13635                   struct Row10;"#},
13636        vec![
13637            DiffHunkStatusKind::Added,
13638            DiffHunkStatusKind::Added,
13639            DiffHunkStatusKind::Added,
13640            DiffHunkStatusKind::Added,
13641            DiffHunkStatusKind::Added,
13642        ],
13643        indoc! {r#"struct Row;
13644                   ˇstruct Row1;
13645                   struct Row2;
13646                   ˇ
13647                   struct Row4;
13648                   ˇstruct Row5;
13649                   struct Row6;
13650                   ˇ
13651                   ˇstruct Row8;
13652                   struct Row9;
13653                   ˇstruct Row10;"#},
13654        base_text,
13655        &mut cx,
13656    );
13657}
13658
13659#[gpui::test]
13660async fn test_modification_reverts(cx: &mut TestAppContext) {
13661    init_test(cx, |_| {});
13662    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13663    let base_text = indoc! {r#"
13664        struct Row;
13665        struct Row1;
13666        struct Row2;
13667
13668        struct Row4;
13669        struct Row5;
13670        struct Row6;
13671
13672        struct Row8;
13673        struct Row9;
13674        struct Row10;"#};
13675
13676    // Modification hunks behave the same as the addition ones.
13677    assert_hunk_revert(
13678        indoc! {r#"struct Row;
13679                   struct Row1;
13680                   struct Row33;
13681                   ˇ
13682                   struct Row4;
13683                   struct Row5;
13684                   struct Row6;
13685                   ˇ
13686                   struct Row99;
13687                   struct Row9;
13688                   struct Row10;"#},
13689        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13690        indoc! {r#"struct Row;
13691                   struct Row1;
13692                   struct Row33;
13693                   ˇ
13694                   struct Row4;
13695                   struct Row5;
13696                   struct Row6;
13697                   ˇ
13698                   struct Row99;
13699                   struct Row9;
13700                   struct Row10;"#},
13701        base_text,
13702        &mut cx,
13703    );
13704    assert_hunk_revert(
13705        indoc! {r#"struct Row;
13706                   struct Row1;
13707                   struct Row33;
13708                   «ˇ
13709                   struct Row4;
13710                   struct» Row5;
13711                   «struct Row6;
13712                   ˇ»
13713                   struct Row99;
13714                   struct Row9;
13715                   struct Row10;"#},
13716        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13717        indoc! {r#"struct Row;
13718                   struct Row1;
13719                   struct Row33;
13720                   «ˇ
13721                   struct Row4;
13722                   struct» Row5;
13723                   «struct Row6;
13724                   ˇ»
13725                   struct Row99;
13726                   struct Row9;
13727                   struct Row10;"#},
13728        base_text,
13729        &mut cx,
13730    );
13731
13732    assert_hunk_revert(
13733        indoc! {r#"ˇstruct Row1.1;
13734                   struct Row1;
13735                   «ˇstr»uct Row22;
13736
13737                   struct ˇRow44;
13738                   struct Row5;
13739                   struct «Rˇ»ow66;ˇ
13740
13741                   «struˇ»ct Row88;
13742                   struct Row9;
13743                   struct Row1011;ˇ"#},
13744        vec![
13745            DiffHunkStatusKind::Modified,
13746            DiffHunkStatusKind::Modified,
13747            DiffHunkStatusKind::Modified,
13748            DiffHunkStatusKind::Modified,
13749            DiffHunkStatusKind::Modified,
13750            DiffHunkStatusKind::Modified,
13751        ],
13752        indoc! {r#"struct Row;
13753                   ˇstruct Row1;
13754                   struct Row2;
13755                   ˇ
13756                   struct Row4;
13757                   ˇstruct Row5;
13758                   struct Row6;
13759                   ˇ
13760                   struct Row8;
13761                   ˇstruct Row9;
13762                   struct Row10;ˇ"#},
13763        base_text,
13764        &mut cx,
13765    );
13766}
13767
13768#[gpui::test]
13769async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13770    init_test(cx, |_| {});
13771    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13772    let base_text = indoc! {r#"
13773        one
13774
13775        two
13776        three
13777        "#};
13778
13779    cx.set_head_text(base_text);
13780    cx.set_state("\nˇ\n");
13781    cx.executor().run_until_parked();
13782    cx.update_editor(|editor, _window, cx| {
13783        editor.expand_selected_diff_hunks(cx);
13784    });
13785    cx.executor().run_until_parked();
13786    cx.update_editor(|editor, window, cx| {
13787        editor.backspace(&Default::default(), window, cx);
13788    });
13789    cx.run_until_parked();
13790    cx.assert_state_with_diff(
13791        indoc! {r#"
13792
13793        - two
13794        - threeˇ
13795        +
13796        "#}
13797        .to_string(),
13798    );
13799}
13800
13801#[gpui::test]
13802async fn test_deletion_reverts(cx: &mut TestAppContext) {
13803    init_test(cx, |_| {});
13804    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13805    let base_text = indoc! {r#"struct Row;
13806struct Row1;
13807struct Row2;
13808
13809struct Row4;
13810struct Row5;
13811struct Row6;
13812
13813struct Row8;
13814struct Row9;
13815struct Row10;"#};
13816
13817    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13818    assert_hunk_revert(
13819        indoc! {r#"struct Row;
13820                   struct Row2;
13821
13822                   ˇstruct Row4;
13823                   struct Row5;
13824                   struct Row6;
13825                   ˇ
13826                   struct Row8;
13827                   struct Row10;"#},
13828        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13829        indoc! {r#"struct Row;
13830                   struct Row2;
13831
13832                   ˇstruct Row4;
13833                   struct Row5;
13834                   struct Row6;
13835                   ˇ
13836                   struct Row8;
13837                   struct Row10;"#},
13838        base_text,
13839        &mut cx,
13840    );
13841    assert_hunk_revert(
13842        indoc! {r#"struct Row;
13843                   struct Row2;
13844
13845                   «ˇstruct Row4;
13846                   struct» Row5;
13847                   «struct Row6;
13848                   ˇ»
13849                   struct Row8;
13850                   struct Row10;"#},
13851        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13852        indoc! {r#"struct Row;
13853                   struct Row2;
13854
13855                   «ˇstruct Row4;
13856                   struct» Row5;
13857                   «struct Row6;
13858                   ˇ»
13859                   struct Row8;
13860                   struct Row10;"#},
13861        base_text,
13862        &mut cx,
13863    );
13864
13865    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13866    assert_hunk_revert(
13867        indoc! {r#"struct Row;
13868                   ˇstruct Row2;
13869
13870                   struct Row4;
13871                   struct Row5;
13872                   struct Row6;
13873
13874                   struct Row8;ˇ
13875                   struct Row10;"#},
13876        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13877        indoc! {r#"struct Row;
13878                   struct Row1;
13879                   ˇstruct Row2;
13880
13881                   struct Row4;
13882                   struct Row5;
13883                   struct Row6;
13884
13885                   struct Row8;ˇ
13886                   struct Row9;
13887                   struct Row10;"#},
13888        base_text,
13889        &mut cx,
13890    );
13891    assert_hunk_revert(
13892        indoc! {r#"struct Row;
13893                   struct Row2«ˇ;
13894                   struct Row4;
13895                   struct» Row5;
13896                   «struct Row6;
13897
13898                   struct Row8;ˇ»
13899                   struct Row10;"#},
13900        vec![
13901            DiffHunkStatusKind::Deleted,
13902            DiffHunkStatusKind::Deleted,
13903            DiffHunkStatusKind::Deleted,
13904        ],
13905        indoc! {r#"struct Row;
13906                   struct Row1;
13907                   struct Row2«ˇ;
13908
13909                   struct Row4;
13910                   struct» Row5;
13911                   «struct Row6;
13912
13913                   struct Row8;ˇ»
13914                   struct Row9;
13915                   struct Row10;"#},
13916        base_text,
13917        &mut cx,
13918    );
13919}
13920
13921#[gpui::test]
13922async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13923    init_test(cx, |_| {});
13924
13925    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13926    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13927    let base_text_3 =
13928        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13929
13930    let text_1 = edit_first_char_of_every_line(base_text_1);
13931    let text_2 = edit_first_char_of_every_line(base_text_2);
13932    let text_3 = edit_first_char_of_every_line(base_text_3);
13933
13934    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13935    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13936    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13937
13938    let multibuffer = cx.new(|cx| {
13939        let mut multibuffer = MultiBuffer::new(ReadWrite);
13940        multibuffer.push_excerpts(
13941            buffer_1.clone(),
13942            [
13943                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13944                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13945                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13946            ],
13947            cx,
13948        );
13949        multibuffer.push_excerpts(
13950            buffer_2.clone(),
13951            [
13952                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13953                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13954                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13955            ],
13956            cx,
13957        );
13958        multibuffer.push_excerpts(
13959            buffer_3.clone(),
13960            [
13961                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13962                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13963                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13964            ],
13965            cx,
13966        );
13967        multibuffer
13968    });
13969
13970    let fs = FakeFs::new(cx.executor());
13971    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13972    let (editor, cx) = cx
13973        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13974    editor.update_in(cx, |editor, _window, cx| {
13975        for (buffer, diff_base) in [
13976            (buffer_1.clone(), base_text_1),
13977            (buffer_2.clone(), base_text_2),
13978            (buffer_3.clone(), base_text_3),
13979        ] {
13980            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13981            editor
13982                .buffer
13983                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13984        }
13985    });
13986    cx.executor().run_until_parked();
13987
13988    editor.update_in(cx, |editor, window, cx| {
13989        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}");
13990        editor.select_all(&SelectAll, window, cx);
13991        editor.git_restore(&Default::default(), window, cx);
13992    });
13993    cx.executor().run_until_parked();
13994
13995    // When all ranges are selected, all buffer hunks are reverted.
13996    editor.update(cx, |editor, cx| {
13997        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");
13998    });
13999    buffer_1.update(cx, |buffer, _| {
14000        assert_eq!(buffer.text(), base_text_1);
14001    });
14002    buffer_2.update(cx, |buffer, _| {
14003        assert_eq!(buffer.text(), base_text_2);
14004    });
14005    buffer_3.update(cx, |buffer, _| {
14006        assert_eq!(buffer.text(), base_text_3);
14007    });
14008
14009    editor.update_in(cx, |editor, window, cx| {
14010        editor.undo(&Default::default(), window, cx);
14011    });
14012
14013    editor.update_in(cx, |editor, window, cx| {
14014        editor.change_selections(None, window, cx, |s| {
14015            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14016        });
14017        editor.git_restore(&Default::default(), window, cx);
14018    });
14019
14020    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14021    // but not affect buffer_2 and its related excerpts.
14022    editor.update(cx, |editor, cx| {
14023        assert_eq!(
14024            editor.text(cx),
14025            "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}"
14026        );
14027    });
14028    buffer_1.update(cx, |buffer, _| {
14029        assert_eq!(buffer.text(), base_text_1);
14030    });
14031    buffer_2.update(cx, |buffer, _| {
14032        assert_eq!(
14033            buffer.text(),
14034            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14035        );
14036    });
14037    buffer_3.update(cx, |buffer, _| {
14038        assert_eq!(
14039            buffer.text(),
14040            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14041        );
14042    });
14043
14044    fn edit_first_char_of_every_line(text: &str) -> String {
14045        text.split('\n')
14046            .map(|line| format!("X{}", &line[1..]))
14047            .collect::<Vec<_>>()
14048            .join("\n")
14049    }
14050}
14051
14052#[gpui::test]
14053async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14054    init_test(cx, |_| {});
14055
14056    let cols = 4;
14057    let rows = 10;
14058    let sample_text_1 = sample_text(rows, cols, 'a');
14059    assert_eq!(
14060        sample_text_1,
14061        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14062    );
14063    let sample_text_2 = sample_text(rows, cols, 'l');
14064    assert_eq!(
14065        sample_text_2,
14066        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14067    );
14068    let sample_text_3 = sample_text(rows, cols, 'v');
14069    assert_eq!(
14070        sample_text_3,
14071        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14072    );
14073
14074    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14075    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14076    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14077
14078    let multi_buffer = cx.new(|cx| {
14079        let mut multibuffer = MultiBuffer::new(ReadWrite);
14080        multibuffer.push_excerpts(
14081            buffer_1.clone(),
14082            [
14083                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14084                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14085                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14086            ],
14087            cx,
14088        );
14089        multibuffer.push_excerpts(
14090            buffer_2.clone(),
14091            [
14092                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14093                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14094                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14095            ],
14096            cx,
14097        );
14098        multibuffer.push_excerpts(
14099            buffer_3.clone(),
14100            [
14101                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14102                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14103                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14104            ],
14105            cx,
14106        );
14107        multibuffer
14108    });
14109
14110    let fs = FakeFs::new(cx.executor());
14111    fs.insert_tree(
14112        "/a",
14113        json!({
14114            "main.rs": sample_text_1,
14115            "other.rs": sample_text_2,
14116            "lib.rs": sample_text_3,
14117        }),
14118    )
14119    .await;
14120    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14121    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14122    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14123    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14124        Editor::new(
14125            EditorMode::Full,
14126            multi_buffer,
14127            Some(project.clone()),
14128            window,
14129            cx,
14130        )
14131    });
14132    let multibuffer_item_id = workspace
14133        .update(cx, |workspace, window, cx| {
14134            assert!(
14135                workspace.active_item(cx).is_none(),
14136                "active item should be None before the first item is added"
14137            );
14138            workspace.add_item_to_active_pane(
14139                Box::new(multi_buffer_editor.clone()),
14140                None,
14141                true,
14142                window,
14143                cx,
14144            );
14145            let active_item = workspace
14146                .active_item(cx)
14147                .expect("should have an active item after adding the multi buffer");
14148            assert!(
14149                !active_item.is_singleton(cx),
14150                "A multi buffer was expected to active after adding"
14151            );
14152            active_item.item_id()
14153        })
14154        .unwrap();
14155    cx.executor().run_until_parked();
14156
14157    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14158        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14159            s.select_ranges(Some(1..2))
14160        });
14161        editor.open_excerpts(&OpenExcerpts, window, cx);
14162    });
14163    cx.executor().run_until_parked();
14164    let first_item_id = workspace
14165        .update(cx, |workspace, window, cx| {
14166            let active_item = workspace
14167                .active_item(cx)
14168                .expect("should have an active item after navigating into the 1st buffer");
14169            let first_item_id = active_item.item_id();
14170            assert_ne!(
14171                first_item_id, multibuffer_item_id,
14172                "Should navigate into the 1st buffer and activate it"
14173            );
14174            assert!(
14175                active_item.is_singleton(cx),
14176                "New active item should be a singleton buffer"
14177            );
14178            assert_eq!(
14179                active_item
14180                    .act_as::<Editor>(cx)
14181                    .expect("should have navigated into an editor for the 1st buffer")
14182                    .read(cx)
14183                    .text(cx),
14184                sample_text_1
14185            );
14186
14187            workspace
14188                .go_back(workspace.active_pane().downgrade(), window, cx)
14189                .detach_and_log_err(cx);
14190
14191            first_item_id
14192        })
14193        .unwrap();
14194    cx.executor().run_until_parked();
14195    workspace
14196        .update(cx, |workspace, _, cx| {
14197            let active_item = workspace
14198                .active_item(cx)
14199                .expect("should have an active item after navigating back");
14200            assert_eq!(
14201                active_item.item_id(),
14202                multibuffer_item_id,
14203                "Should navigate back to the multi buffer"
14204            );
14205            assert!(!active_item.is_singleton(cx));
14206        })
14207        .unwrap();
14208
14209    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14210        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14211            s.select_ranges(Some(39..40))
14212        });
14213        editor.open_excerpts(&OpenExcerpts, window, cx);
14214    });
14215    cx.executor().run_until_parked();
14216    let second_item_id = workspace
14217        .update(cx, |workspace, window, cx| {
14218            let active_item = workspace
14219                .active_item(cx)
14220                .expect("should have an active item after navigating into the 2nd buffer");
14221            let second_item_id = active_item.item_id();
14222            assert_ne!(
14223                second_item_id, multibuffer_item_id,
14224                "Should navigate away from the multibuffer"
14225            );
14226            assert_ne!(
14227                second_item_id, first_item_id,
14228                "Should navigate into the 2nd buffer and activate it"
14229            );
14230            assert!(
14231                active_item.is_singleton(cx),
14232                "New active item should be a singleton buffer"
14233            );
14234            assert_eq!(
14235                active_item
14236                    .act_as::<Editor>(cx)
14237                    .expect("should have navigated into an editor")
14238                    .read(cx)
14239                    .text(cx),
14240                sample_text_2
14241            );
14242
14243            workspace
14244                .go_back(workspace.active_pane().downgrade(), window, cx)
14245                .detach_and_log_err(cx);
14246
14247            second_item_id
14248        })
14249        .unwrap();
14250    cx.executor().run_until_parked();
14251    workspace
14252        .update(cx, |workspace, _, cx| {
14253            let active_item = workspace
14254                .active_item(cx)
14255                .expect("should have an active item after navigating back from the 2nd buffer");
14256            assert_eq!(
14257                active_item.item_id(),
14258                multibuffer_item_id,
14259                "Should navigate back from the 2nd buffer to the multi buffer"
14260            );
14261            assert!(!active_item.is_singleton(cx));
14262        })
14263        .unwrap();
14264
14265    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14266        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14267            s.select_ranges(Some(70..70))
14268        });
14269        editor.open_excerpts(&OpenExcerpts, window, cx);
14270    });
14271    cx.executor().run_until_parked();
14272    workspace
14273        .update(cx, |workspace, window, cx| {
14274            let active_item = workspace
14275                .active_item(cx)
14276                .expect("should have an active item after navigating into the 3rd buffer");
14277            let third_item_id = active_item.item_id();
14278            assert_ne!(
14279                third_item_id, multibuffer_item_id,
14280                "Should navigate into the 3rd buffer and activate it"
14281            );
14282            assert_ne!(third_item_id, first_item_id);
14283            assert_ne!(third_item_id, second_item_id);
14284            assert!(
14285                active_item.is_singleton(cx),
14286                "New active item should be a singleton buffer"
14287            );
14288            assert_eq!(
14289                active_item
14290                    .act_as::<Editor>(cx)
14291                    .expect("should have navigated into an editor")
14292                    .read(cx)
14293                    .text(cx),
14294                sample_text_3
14295            );
14296
14297            workspace
14298                .go_back(workspace.active_pane().downgrade(), window, cx)
14299                .detach_and_log_err(cx);
14300        })
14301        .unwrap();
14302    cx.executor().run_until_parked();
14303    workspace
14304        .update(cx, |workspace, _, cx| {
14305            let active_item = workspace
14306                .active_item(cx)
14307                .expect("should have an active item after navigating back from the 3rd buffer");
14308            assert_eq!(
14309                active_item.item_id(),
14310                multibuffer_item_id,
14311                "Should navigate back from the 3rd buffer to the multi buffer"
14312            );
14313            assert!(!active_item.is_singleton(cx));
14314        })
14315        .unwrap();
14316}
14317
14318#[gpui::test]
14319async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14320    init_test(cx, |_| {});
14321
14322    let mut cx = EditorTestContext::new(cx).await;
14323
14324    let diff_base = r#"
14325        use some::mod;
14326
14327        const A: u32 = 42;
14328
14329        fn main() {
14330            println!("hello");
14331
14332            println!("world");
14333        }
14334        "#
14335    .unindent();
14336
14337    cx.set_state(
14338        &r#"
14339        use some::modified;
14340
14341        ˇ
14342        fn main() {
14343            println!("hello there");
14344
14345            println!("around the");
14346            println!("world");
14347        }
14348        "#
14349        .unindent(),
14350    );
14351
14352    cx.set_head_text(&diff_base);
14353    executor.run_until_parked();
14354
14355    cx.update_editor(|editor, window, cx| {
14356        editor.go_to_next_hunk(&GoToHunk, window, cx);
14357        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14358    });
14359    executor.run_until_parked();
14360    cx.assert_state_with_diff(
14361        r#"
14362          use some::modified;
14363
14364
14365          fn main() {
14366        -     println!("hello");
14367        + ˇ    println!("hello there");
14368
14369              println!("around the");
14370              println!("world");
14371          }
14372        "#
14373        .unindent(),
14374    );
14375
14376    cx.update_editor(|editor, window, cx| {
14377        for _ in 0..2 {
14378            editor.go_to_next_hunk(&GoToHunk, window, cx);
14379            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14380        }
14381    });
14382    executor.run_until_parked();
14383    cx.assert_state_with_diff(
14384        r#"
14385        - use some::mod;
14386        + ˇuse some::modified;
14387
14388
14389          fn main() {
14390        -     println!("hello");
14391        +     println!("hello there");
14392
14393        +     println!("around the");
14394              println!("world");
14395          }
14396        "#
14397        .unindent(),
14398    );
14399
14400    cx.update_editor(|editor, window, cx| {
14401        editor.go_to_next_hunk(&GoToHunk, window, cx);
14402        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14403    });
14404    executor.run_until_parked();
14405    cx.assert_state_with_diff(
14406        r#"
14407        - use some::mod;
14408        + use some::modified;
14409
14410        - const A: u32 = 42;
14411          ˇ
14412          fn main() {
14413        -     println!("hello");
14414        +     println!("hello there");
14415
14416        +     println!("around the");
14417              println!("world");
14418          }
14419        "#
14420        .unindent(),
14421    );
14422
14423    cx.update_editor(|editor, window, cx| {
14424        editor.cancel(&Cancel, window, cx);
14425    });
14426
14427    cx.assert_state_with_diff(
14428        r#"
14429          use some::modified;
14430
14431          ˇ
14432          fn main() {
14433              println!("hello there");
14434
14435              println!("around the");
14436              println!("world");
14437          }
14438        "#
14439        .unindent(),
14440    );
14441}
14442
14443#[gpui::test]
14444async fn test_diff_base_change_with_expanded_diff_hunks(
14445    executor: BackgroundExecutor,
14446    cx: &mut TestAppContext,
14447) {
14448    init_test(cx, |_| {});
14449
14450    let mut cx = EditorTestContext::new(cx).await;
14451
14452    let diff_base = r#"
14453        use some::mod1;
14454        use some::mod2;
14455
14456        const A: u32 = 42;
14457        const B: u32 = 42;
14458        const C: u32 = 42;
14459
14460        fn main() {
14461            println!("hello");
14462
14463            println!("world");
14464        }
14465        "#
14466    .unindent();
14467
14468    cx.set_state(
14469        &r#"
14470        use some::mod2;
14471
14472        const A: u32 = 42;
14473        const C: u32 = 42;
14474
14475        fn main(ˇ) {
14476            //println!("hello");
14477
14478            println!("world");
14479            //
14480            //
14481        }
14482        "#
14483        .unindent(),
14484    );
14485
14486    cx.set_head_text(&diff_base);
14487    executor.run_until_parked();
14488
14489    cx.update_editor(|editor, window, cx| {
14490        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14491    });
14492    executor.run_until_parked();
14493    cx.assert_state_with_diff(
14494        r#"
14495        - use some::mod1;
14496          use some::mod2;
14497
14498          const A: u32 = 42;
14499        - const B: u32 = 42;
14500          const C: u32 = 42;
14501
14502          fn main(ˇ) {
14503        -     println!("hello");
14504        +     //println!("hello");
14505
14506              println!("world");
14507        +     //
14508        +     //
14509          }
14510        "#
14511        .unindent(),
14512    );
14513
14514    cx.set_head_text("new diff base!");
14515    executor.run_until_parked();
14516    cx.assert_state_with_diff(
14517        r#"
14518        - new diff base!
14519        + use some::mod2;
14520        +
14521        + const A: u32 = 42;
14522        + const C: u32 = 42;
14523        +
14524        + fn main(ˇ) {
14525        +     //println!("hello");
14526        +
14527        +     println!("world");
14528        +     //
14529        +     //
14530        + }
14531        "#
14532        .unindent(),
14533    );
14534}
14535
14536#[gpui::test]
14537async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14538    init_test(cx, |_| {});
14539
14540    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14541    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14542    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14543    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14544    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14545    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14546
14547    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14548    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14549    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14550
14551    let multi_buffer = cx.new(|cx| {
14552        let mut multibuffer = MultiBuffer::new(ReadWrite);
14553        multibuffer.push_excerpts(
14554            buffer_1.clone(),
14555            [
14556                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14557                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14558                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14559            ],
14560            cx,
14561        );
14562        multibuffer.push_excerpts(
14563            buffer_2.clone(),
14564            [
14565                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14566                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14567                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14568            ],
14569            cx,
14570        );
14571        multibuffer.push_excerpts(
14572            buffer_3.clone(),
14573            [
14574                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14575                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14576                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14577            ],
14578            cx,
14579        );
14580        multibuffer
14581    });
14582
14583    let editor =
14584        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14585    editor
14586        .update(cx, |editor, _window, cx| {
14587            for (buffer, diff_base) in [
14588                (buffer_1.clone(), file_1_old),
14589                (buffer_2.clone(), file_2_old),
14590                (buffer_3.clone(), file_3_old),
14591            ] {
14592                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14593                editor
14594                    .buffer
14595                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14596            }
14597        })
14598        .unwrap();
14599
14600    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14601    cx.run_until_parked();
14602
14603    cx.assert_editor_state(
14604        &"
14605            ˇaaa
14606            ccc
14607            ddd
14608
14609            ggg
14610            hhh
14611
14612
14613            lll
14614            mmm
14615            NNN
14616
14617            qqq
14618            rrr
14619
14620            uuu
14621            111
14622            222
14623            333
14624
14625            666
14626            777
14627
14628            000
14629            !!!"
14630        .unindent(),
14631    );
14632
14633    cx.update_editor(|editor, window, cx| {
14634        editor.select_all(&SelectAll, window, cx);
14635        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14636    });
14637    cx.executor().run_until_parked();
14638
14639    cx.assert_state_with_diff(
14640        "
14641            «aaa
14642          - bbb
14643            ccc
14644            ddd
14645
14646            ggg
14647            hhh
14648
14649
14650            lll
14651            mmm
14652          - nnn
14653          + NNN
14654
14655            qqq
14656            rrr
14657
14658            uuu
14659            111
14660            222
14661            333
14662
14663          + 666
14664            777
14665
14666            000
14667            !!!ˇ»"
14668            .unindent(),
14669    );
14670}
14671
14672#[gpui::test]
14673async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14674    init_test(cx, |_| {});
14675
14676    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14677    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14678
14679    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14680    let multi_buffer = cx.new(|cx| {
14681        let mut multibuffer = MultiBuffer::new(ReadWrite);
14682        multibuffer.push_excerpts(
14683            buffer.clone(),
14684            [
14685                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14686                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14687                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14688            ],
14689            cx,
14690        );
14691        multibuffer
14692    });
14693
14694    let editor =
14695        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14696    editor
14697        .update(cx, |editor, _window, cx| {
14698            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14699            editor
14700                .buffer
14701                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14702        })
14703        .unwrap();
14704
14705    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14706    cx.run_until_parked();
14707
14708    cx.update_editor(|editor, window, cx| {
14709        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14710    });
14711    cx.executor().run_until_parked();
14712
14713    // When the start of a hunk coincides with the start of its excerpt,
14714    // the hunk is expanded. When the start of a a hunk is earlier than
14715    // the start of its excerpt, the hunk is not expanded.
14716    cx.assert_state_with_diff(
14717        "
14718            ˇaaa
14719          - bbb
14720          + BBB
14721
14722          - ddd
14723          - eee
14724          + DDD
14725          + EEE
14726            fff
14727
14728            iii
14729        "
14730        .unindent(),
14731    );
14732}
14733
14734#[gpui::test]
14735async fn test_edits_around_expanded_insertion_hunks(
14736    executor: BackgroundExecutor,
14737    cx: &mut TestAppContext,
14738) {
14739    init_test(cx, |_| {});
14740
14741    let mut cx = EditorTestContext::new(cx).await;
14742
14743    let diff_base = r#"
14744        use some::mod1;
14745        use some::mod2;
14746
14747        const A: u32 = 42;
14748
14749        fn main() {
14750            println!("hello");
14751
14752            println!("world");
14753        }
14754        "#
14755    .unindent();
14756    executor.run_until_parked();
14757    cx.set_state(
14758        &r#"
14759        use some::mod1;
14760        use some::mod2;
14761
14762        const A: u32 = 42;
14763        const B: u32 = 42;
14764        const C: u32 = 42;
14765        ˇ
14766
14767        fn main() {
14768            println!("hello");
14769
14770            println!("world");
14771        }
14772        "#
14773        .unindent(),
14774    );
14775
14776    cx.set_head_text(&diff_base);
14777    executor.run_until_parked();
14778
14779    cx.update_editor(|editor, window, cx| {
14780        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14781    });
14782    executor.run_until_parked();
14783
14784    cx.assert_state_with_diff(
14785        r#"
14786        use some::mod1;
14787        use some::mod2;
14788
14789        const A: u32 = 42;
14790      + const B: u32 = 42;
14791      + const C: u32 = 42;
14792      + ˇ
14793
14794        fn main() {
14795            println!("hello");
14796
14797            println!("world");
14798        }
14799      "#
14800        .unindent(),
14801    );
14802
14803    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14804    executor.run_until_parked();
14805
14806    cx.assert_state_with_diff(
14807        r#"
14808        use some::mod1;
14809        use some::mod2;
14810
14811        const A: u32 = 42;
14812      + const B: u32 = 42;
14813      + const C: u32 = 42;
14814      + const D: u32 = 42;
14815      + ˇ
14816
14817        fn main() {
14818            println!("hello");
14819
14820            println!("world");
14821        }
14822      "#
14823        .unindent(),
14824    );
14825
14826    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14827    executor.run_until_parked();
14828
14829    cx.assert_state_with_diff(
14830        r#"
14831        use some::mod1;
14832        use some::mod2;
14833
14834        const A: u32 = 42;
14835      + const B: u32 = 42;
14836      + const C: u32 = 42;
14837      + const D: u32 = 42;
14838      + const E: u32 = 42;
14839      + ˇ
14840
14841        fn main() {
14842            println!("hello");
14843
14844            println!("world");
14845        }
14846      "#
14847        .unindent(),
14848    );
14849
14850    cx.update_editor(|editor, window, cx| {
14851        editor.delete_line(&DeleteLine, window, cx);
14852    });
14853    executor.run_until_parked();
14854
14855    cx.assert_state_with_diff(
14856        r#"
14857        use some::mod1;
14858        use some::mod2;
14859
14860        const A: u32 = 42;
14861      + const B: u32 = 42;
14862      + const C: u32 = 42;
14863      + const D: u32 = 42;
14864      + const E: u32 = 42;
14865        ˇ
14866        fn main() {
14867            println!("hello");
14868
14869            println!("world");
14870        }
14871      "#
14872        .unindent(),
14873    );
14874
14875    cx.update_editor(|editor, window, cx| {
14876        editor.move_up(&MoveUp, window, cx);
14877        editor.delete_line(&DeleteLine, window, cx);
14878        editor.move_up(&MoveUp, window, cx);
14879        editor.delete_line(&DeleteLine, window, cx);
14880        editor.move_up(&MoveUp, window, cx);
14881        editor.delete_line(&DeleteLine, window, cx);
14882    });
14883    executor.run_until_parked();
14884    cx.assert_state_with_diff(
14885        r#"
14886        use some::mod1;
14887        use some::mod2;
14888
14889        const A: u32 = 42;
14890      + const B: u32 = 42;
14891        ˇ
14892        fn main() {
14893            println!("hello");
14894
14895            println!("world");
14896        }
14897      "#
14898        .unindent(),
14899    );
14900
14901    cx.update_editor(|editor, window, cx| {
14902        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14903        editor.delete_line(&DeleteLine, window, cx);
14904    });
14905    executor.run_until_parked();
14906    cx.assert_state_with_diff(
14907        r#"
14908        ˇ
14909        fn main() {
14910            println!("hello");
14911
14912            println!("world");
14913        }
14914      "#
14915        .unindent(),
14916    );
14917}
14918
14919#[gpui::test]
14920async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14921    init_test(cx, |_| {});
14922
14923    let mut cx = EditorTestContext::new(cx).await;
14924    cx.set_head_text(indoc! { "
14925        one
14926        two
14927        three
14928        four
14929        five
14930        "
14931    });
14932    cx.set_state(indoc! { "
14933        one
14934        ˇthree
14935        five
14936    "});
14937    cx.run_until_parked();
14938    cx.update_editor(|editor, window, cx| {
14939        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14940    });
14941    cx.assert_state_with_diff(
14942        indoc! { "
14943        one
14944      - two
14945        ˇthree
14946      - four
14947        five
14948    "}
14949        .to_string(),
14950    );
14951    cx.update_editor(|editor, window, cx| {
14952        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14953    });
14954
14955    cx.assert_state_with_diff(
14956        indoc! { "
14957        one
14958        ˇthree
14959        five
14960    "}
14961        .to_string(),
14962    );
14963
14964    cx.set_state(indoc! { "
14965        one
14966        ˇTWO
14967        three
14968        four
14969        five
14970    "});
14971    cx.run_until_parked();
14972    cx.update_editor(|editor, window, cx| {
14973        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14974    });
14975
14976    cx.assert_state_with_diff(
14977        indoc! { "
14978            one
14979          - two
14980          + ˇTWO
14981            three
14982            four
14983            five
14984        "}
14985        .to_string(),
14986    );
14987    cx.update_editor(|editor, window, cx| {
14988        editor.move_up(&Default::default(), window, cx);
14989        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14990    });
14991    cx.assert_state_with_diff(
14992        indoc! { "
14993            one
14994            ˇTWO
14995            three
14996            four
14997            five
14998        "}
14999        .to_string(),
15000    );
15001}
15002
15003#[gpui::test]
15004async fn test_edits_around_expanded_deletion_hunks(
15005    executor: BackgroundExecutor,
15006    cx: &mut TestAppContext,
15007) {
15008    init_test(cx, |_| {});
15009
15010    let mut cx = EditorTestContext::new(cx).await;
15011
15012    let diff_base = r#"
15013        use some::mod1;
15014        use some::mod2;
15015
15016        const A: u32 = 42;
15017        const B: u32 = 42;
15018        const C: u32 = 42;
15019
15020
15021        fn main() {
15022            println!("hello");
15023
15024            println!("world");
15025        }
15026    "#
15027    .unindent();
15028    executor.run_until_parked();
15029    cx.set_state(
15030        &r#"
15031        use some::mod1;
15032        use some::mod2;
15033
15034        ˇconst B: u32 = 42;
15035        const C: u32 = 42;
15036
15037
15038        fn main() {
15039            println!("hello");
15040
15041            println!("world");
15042        }
15043        "#
15044        .unindent(),
15045    );
15046
15047    cx.set_head_text(&diff_base);
15048    executor.run_until_parked();
15049
15050    cx.update_editor(|editor, window, cx| {
15051        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15052    });
15053    executor.run_until_parked();
15054
15055    cx.assert_state_with_diff(
15056        r#"
15057        use some::mod1;
15058        use some::mod2;
15059
15060      - const A: u32 = 42;
15061        ˇconst B: u32 = 42;
15062        const C: u32 = 42;
15063
15064
15065        fn main() {
15066            println!("hello");
15067
15068            println!("world");
15069        }
15070      "#
15071        .unindent(),
15072    );
15073
15074    cx.update_editor(|editor, window, cx| {
15075        editor.delete_line(&DeleteLine, window, cx);
15076    });
15077    executor.run_until_parked();
15078    cx.assert_state_with_diff(
15079        r#"
15080        use some::mod1;
15081        use some::mod2;
15082
15083      - const A: u32 = 42;
15084      - const B: u32 = 42;
15085        ˇconst C: u32 = 42;
15086
15087
15088        fn main() {
15089            println!("hello");
15090
15091            println!("world");
15092        }
15093      "#
15094        .unindent(),
15095    );
15096
15097    cx.update_editor(|editor, window, cx| {
15098        editor.delete_line(&DeleteLine, window, cx);
15099    });
15100    executor.run_until_parked();
15101    cx.assert_state_with_diff(
15102        r#"
15103        use some::mod1;
15104        use some::mod2;
15105
15106      - const A: u32 = 42;
15107      - const B: u32 = 42;
15108      - const C: u32 = 42;
15109        ˇ
15110
15111        fn main() {
15112            println!("hello");
15113
15114            println!("world");
15115        }
15116      "#
15117        .unindent(),
15118    );
15119
15120    cx.update_editor(|editor, window, cx| {
15121        editor.handle_input("replacement", window, cx);
15122    });
15123    executor.run_until_parked();
15124    cx.assert_state_with_diff(
15125        r#"
15126        use some::mod1;
15127        use some::mod2;
15128
15129      - const A: u32 = 42;
15130      - const B: u32 = 42;
15131      - const C: u32 = 42;
15132      -
15133      + replacementˇ
15134
15135        fn main() {
15136            println!("hello");
15137
15138            println!("world");
15139        }
15140      "#
15141        .unindent(),
15142    );
15143}
15144
15145#[gpui::test]
15146async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15147    init_test(cx, |_| {});
15148
15149    let mut cx = EditorTestContext::new(cx).await;
15150
15151    let base_text = r#"
15152        one
15153        two
15154        three
15155        four
15156        five
15157    "#
15158    .unindent();
15159    executor.run_until_parked();
15160    cx.set_state(
15161        &r#"
15162        one
15163        two
15164        fˇour
15165        five
15166        "#
15167        .unindent(),
15168    );
15169
15170    cx.set_head_text(&base_text);
15171    executor.run_until_parked();
15172
15173    cx.update_editor(|editor, window, cx| {
15174        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15175    });
15176    executor.run_until_parked();
15177
15178    cx.assert_state_with_diff(
15179        r#"
15180          one
15181          two
15182        - three
15183          fˇour
15184          five
15185        "#
15186        .unindent(),
15187    );
15188
15189    cx.update_editor(|editor, window, cx| {
15190        editor.backspace(&Backspace, window, cx);
15191        editor.backspace(&Backspace, window, cx);
15192    });
15193    executor.run_until_parked();
15194    cx.assert_state_with_diff(
15195        r#"
15196          one
15197          two
15198        - threeˇ
15199        - four
15200        + our
15201          five
15202        "#
15203        .unindent(),
15204    );
15205}
15206
15207#[gpui::test]
15208async fn test_edit_after_expanded_modification_hunk(
15209    executor: BackgroundExecutor,
15210    cx: &mut TestAppContext,
15211) {
15212    init_test(cx, |_| {});
15213
15214    let mut cx = EditorTestContext::new(cx).await;
15215
15216    let diff_base = r#"
15217        use some::mod1;
15218        use some::mod2;
15219
15220        const A: u32 = 42;
15221        const B: u32 = 42;
15222        const C: u32 = 42;
15223        const D: u32 = 42;
15224
15225
15226        fn main() {
15227            println!("hello");
15228
15229            println!("world");
15230        }"#
15231    .unindent();
15232
15233    cx.set_state(
15234        &r#"
15235        use some::mod1;
15236        use some::mod2;
15237
15238        const A: u32 = 42;
15239        const B: u32 = 42;
15240        const C: u32 = 43ˇ
15241        const D: u32 = 42;
15242
15243
15244        fn main() {
15245            println!("hello");
15246
15247            println!("world");
15248        }"#
15249        .unindent(),
15250    );
15251
15252    cx.set_head_text(&diff_base);
15253    executor.run_until_parked();
15254    cx.update_editor(|editor, window, cx| {
15255        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15256    });
15257    executor.run_until_parked();
15258
15259    cx.assert_state_with_diff(
15260        r#"
15261        use some::mod1;
15262        use some::mod2;
15263
15264        const A: u32 = 42;
15265        const B: u32 = 42;
15266      - const C: u32 = 42;
15267      + const C: u32 = 43ˇ
15268        const D: u32 = 42;
15269
15270
15271        fn main() {
15272            println!("hello");
15273
15274            println!("world");
15275        }"#
15276        .unindent(),
15277    );
15278
15279    cx.update_editor(|editor, window, cx| {
15280        editor.handle_input("\nnew_line\n", window, cx);
15281    });
15282    executor.run_until_parked();
15283
15284    cx.assert_state_with_diff(
15285        r#"
15286        use some::mod1;
15287        use some::mod2;
15288
15289        const A: u32 = 42;
15290        const B: u32 = 42;
15291      - const C: u32 = 42;
15292      + const C: u32 = 43
15293      + new_line
15294      + ˇ
15295        const D: u32 = 42;
15296
15297
15298        fn main() {
15299            println!("hello");
15300
15301            println!("world");
15302        }"#
15303        .unindent(),
15304    );
15305}
15306
15307#[gpui::test]
15308async fn test_stage_and_unstage_added_file_hunk(
15309    executor: BackgroundExecutor,
15310    cx: &mut TestAppContext,
15311) {
15312    init_test(cx, |_| {});
15313
15314    let mut cx = EditorTestContext::new(cx).await;
15315    cx.update_editor(|editor, _, cx| {
15316        editor.set_expand_all_diff_hunks(cx);
15317    });
15318
15319    let working_copy = r#"
15320            ˇfn main() {
15321                println!("hello, world!");
15322            }
15323        "#
15324    .unindent();
15325
15326    cx.set_state(&working_copy);
15327    executor.run_until_parked();
15328
15329    cx.assert_state_with_diff(
15330        r#"
15331            + ˇfn main() {
15332            +     println!("hello, world!");
15333            + }
15334        "#
15335        .unindent(),
15336    );
15337    cx.assert_index_text(None);
15338
15339    cx.update_editor(|editor, window, cx| {
15340        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15341    });
15342    executor.run_until_parked();
15343    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15344    cx.assert_state_with_diff(
15345        r#"
15346            + ˇfn main() {
15347            +     println!("hello, world!");
15348            + }
15349        "#
15350        .unindent(),
15351    );
15352
15353    cx.update_editor(|editor, window, cx| {
15354        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15355    });
15356    executor.run_until_parked();
15357    cx.assert_index_text(None);
15358}
15359
15360async fn setup_indent_guides_editor(
15361    text: &str,
15362    cx: &mut TestAppContext,
15363) -> (BufferId, EditorTestContext) {
15364    init_test(cx, |_| {});
15365
15366    let mut cx = EditorTestContext::new(cx).await;
15367
15368    let buffer_id = cx.update_editor(|editor, window, cx| {
15369        editor.set_text(text, window, cx);
15370        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15371
15372        buffer_ids[0]
15373    });
15374
15375    (buffer_id, cx)
15376}
15377
15378fn assert_indent_guides(
15379    range: Range<u32>,
15380    expected: Vec<IndentGuide>,
15381    active_indices: Option<Vec<usize>>,
15382    cx: &mut EditorTestContext,
15383) {
15384    let indent_guides = cx.update_editor(|editor, window, cx| {
15385        let snapshot = editor.snapshot(window, cx).display_snapshot;
15386        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15387            editor,
15388            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15389            true,
15390            &snapshot,
15391            cx,
15392        );
15393
15394        indent_guides.sort_by(|a, b| {
15395            a.depth.cmp(&b.depth).then(
15396                a.start_row
15397                    .cmp(&b.start_row)
15398                    .then(a.end_row.cmp(&b.end_row)),
15399            )
15400        });
15401        indent_guides
15402    });
15403
15404    if let Some(expected) = active_indices {
15405        let active_indices = cx.update_editor(|editor, window, cx| {
15406            let snapshot = editor.snapshot(window, cx).display_snapshot;
15407            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15408        });
15409
15410        assert_eq!(
15411            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15412            expected,
15413            "Active indent guide indices do not match"
15414        );
15415    }
15416
15417    assert_eq!(indent_guides, expected, "Indent guides do not match");
15418}
15419
15420fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15421    IndentGuide {
15422        buffer_id,
15423        start_row: MultiBufferRow(start_row),
15424        end_row: MultiBufferRow(end_row),
15425        depth,
15426        tab_size: 4,
15427        settings: IndentGuideSettings {
15428            enabled: true,
15429            line_width: 1,
15430            active_line_width: 1,
15431            ..Default::default()
15432        },
15433    }
15434}
15435
15436#[gpui::test]
15437async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15438    let (buffer_id, mut cx) = setup_indent_guides_editor(
15439        &"
15440    fn main() {
15441        let a = 1;
15442    }"
15443        .unindent(),
15444        cx,
15445    )
15446    .await;
15447
15448    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15449}
15450
15451#[gpui::test]
15452async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15453    let (buffer_id, mut cx) = setup_indent_guides_editor(
15454        &"
15455    fn main() {
15456        let a = 1;
15457        let b = 2;
15458    }"
15459        .unindent(),
15460        cx,
15461    )
15462    .await;
15463
15464    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15465}
15466
15467#[gpui::test]
15468async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15469    let (buffer_id, mut cx) = setup_indent_guides_editor(
15470        &"
15471    fn main() {
15472        let a = 1;
15473        if a == 3 {
15474            let b = 2;
15475        } else {
15476            let c = 3;
15477        }
15478    }"
15479        .unindent(),
15480        cx,
15481    )
15482    .await;
15483
15484    assert_indent_guides(
15485        0..8,
15486        vec![
15487            indent_guide(buffer_id, 1, 6, 0),
15488            indent_guide(buffer_id, 3, 3, 1),
15489            indent_guide(buffer_id, 5, 5, 1),
15490        ],
15491        None,
15492        &mut cx,
15493    );
15494}
15495
15496#[gpui::test]
15497async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15498    let (buffer_id, mut cx) = setup_indent_guides_editor(
15499        &"
15500    fn main() {
15501        let a = 1;
15502            let b = 2;
15503        let c = 3;
15504    }"
15505        .unindent(),
15506        cx,
15507    )
15508    .await;
15509
15510    assert_indent_guides(
15511        0..5,
15512        vec![
15513            indent_guide(buffer_id, 1, 3, 0),
15514            indent_guide(buffer_id, 2, 2, 1),
15515        ],
15516        None,
15517        &mut cx,
15518    );
15519}
15520
15521#[gpui::test]
15522async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15523    let (buffer_id, mut cx) = setup_indent_guides_editor(
15524        &"
15525        fn main() {
15526            let a = 1;
15527
15528            let c = 3;
15529        }"
15530        .unindent(),
15531        cx,
15532    )
15533    .await;
15534
15535    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15536}
15537
15538#[gpui::test]
15539async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15540    let (buffer_id, mut cx) = setup_indent_guides_editor(
15541        &"
15542        fn main() {
15543            let a = 1;
15544
15545            let c = 3;
15546
15547            if a == 3 {
15548                let b = 2;
15549            } else {
15550                let c = 3;
15551            }
15552        }"
15553        .unindent(),
15554        cx,
15555    )
15556    .await;
15557
15558    assert_indent_guides(
15559        0..11,
15560        vec![
15561            indent_guide(buffer_id, 1, 9, 0),
15562            indent_guide(buffer_id, 6, 6, 1),
15563            indent_guide(buffer_id, 8, 8, 1),
15564        ],
15565        None,
15566        &mut cx,
15567    );
15568}
15569
15570#[gpui::test]
15571async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15572    let (buffer_id, mut cx) = setup_indent_guides_editor(
15573        &"
15574        fn main() {
15575            let a = 1;
15576
15577            let c = 3;
15578
15579            if a == 3 {
15580                let b = 2;
15581            } else {
15582                let c = 3;
15583            }
15584        }"
15585        .unindent(),
15586        cx,
15587    )
15588    .await;
15589
15590    assert_indent_guides(
15591        1..11,
15592        vec![
15593            indent_guide(buffer_id, 1, 9, 0),
15594            indent_guide(buffer_id, 6, 6, 1),
15595            indent_guide(buffer_id, 8, 8, 1),
15596        ],
15597        None,
15598        &mut cx,
15599    );
15600}
15601
15602#[gpui::test]
15603async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15604    let (buffer_id, mut cx) = setup_indent_guides_editor(
15605        &"
15606        fn main() {
15607            let a = 1;
15608
15609            let c = 3;
15610
15611            if a == 3 {
15612                let b = 2;
15613            } else {
15614                let c = 3;
15615            }
15616        }"
15617        .unindent(),
15618        cx,
15619    )
15620    .await;
15621
15622    assert_indent_guides(
15623        1..10,
15624        vec![
15625            indent_guide(buffer_id, 1, 9, 0),
15626            indent_guide(buffer_id, 6, 6, 1),
15627            indent_guide(buffer_id, 8, 8, 1),
15628        ],
15629        None,
15630        &mut cx,
15631    );
15632}
15633
15634#[gpui::test]
15635async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15636    let (buffer_id, mut cx) = setup_indent_guides_editor(
15637        &"
15638        block1
15639            block2
15640                block3
15641                    block4
15642            block2
15643        block1
15644        block1"
15645            .unindent(),
15646        cx,
15647    )
15648    .await;
15649
15650    assert_indent_guides(
15651        1..10,
15652        vec![
15653            indent_guide(buffer_id, 1, 4, 0),
15654            indent_guide(buffer_id, 2, 3, 1),
15655            indent_guide(buffer_id, 3, 3, 2),
15656        ],
15657        None,
15658        &mut cx,
15659    );
15660}
15661
15662#[gpui::test]
15663async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15664    let (buffer_id, mut cx) = setup_indent_guides_editor(
15665        &"
15666        block1
15667            block2
15668                block3
15669
15670        block1
15671        block1"
15672            .unindent(),
15673        cx,
15674    )
15675    .await;
15676
15677    assert_indent_guides(
15678        0..6,
15679        vec![
15680            indent_guide(buffer_id, 1, 2, 0),
15681            indent_guide(buffer_id, 2, 2, 1),
15682        ],
15683        None,
15684        &mut cx,
15685    );
15686}
15687
15688#[gpui::test]
15689async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15690    let (buffer_id, mut cx) = setup_indent_guides_editor(
15691        &"
15692        block1
15693
15694
15695
15696            block2
15697        "
15698        .unindent(),
15699        cx,
15700    )
15701    .await;
15702
15703    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15704}
15705
15706#[gpui::test]
15707async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15708    let (buffer_id, mut cx) = setup_indent_guides_editor(
15709        &"
15710        def a:
15711        \tb = 3
15712        \tif True:
15713        \t\tc = 4
15714        \t\td = 5
15715        \tprint(b)
15716        "
15717        .unindent(),
15718        cx,
15719    )
15720    .await;
15721
15722    assert_indent_guides(
15723        0..6,
15724        vec![
15725            indent_guide(buffer_id, 1, 6, 0),
15726            indent_guide(buffer_id, 3, 4, 1),
15727        ],
15728        None,
15729        &mut cx,
15730    );
15731}
15732
15733#[gpui::test]
15734async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15735    let (buffer_id, mut cx) = setup_indent_guides_editor(
15736        &"
15737    fn main() {
15738        let a = 1;
15739    }"
15740        .unindent(),
15741        cx,
15742    )
15743    .await;
15744
15745    cx.update_editor(|editor, window, cx| {
15746        editor.change_selections(None, window, cx, |s| {
15747            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15748        });
15749    });
15750
15751    assert_indent_guides(
15752        0..3,
15753        vec![indent_guide(buffer_id, 1, 1, 0)],
15754        Some(vec![0]),
15755        &mut cx,
15756    );
15757}
15758
15759#[gpui::test]
15760async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15761    let (buffer_id, mut cx) = setup_indent_guides_editor(
15762        &"
15763    fn main() {
15764        if 1 == 2 {
15765            let a = 1;
15766        }
15767    }"
15768        .unindent(),
15769        cx,
15770    )
15771    .await;
15772
15773    cx.update_editor(|editor, window, cx| {
15774        editor.change_selections(None, window, cx, |s| {
15775            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15776        });
15777    });
15778
15779    assert_indent_guides(
15780        0..4,
15781        vec![
15782            indent_guide(buffer_id, 1, 3, 0),
15783            indent_guide(buffer_id, 2, 2, 1),
15784        ],
15785        Some(vec![1]),
15786        &mut cx,
15787    );
15788
15789    cx.update_editor(|editor, window, cx| {
15790        editor.change_selections(None, window, cx, |s| {
15791            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15792        });
15793    });
15794
15795    assert_indent_guides(
15796        0..4,
15797        vec![
15798            indent_guide(buffer_id, 1, 3, 0),
15799            indent_guide(buffer_id, 2, 2, 1),
15800        ],
15801        Some(vec![1]),
15802        &mut cx,
15803    );
15804
15805    cx.update_editor(|editor, window, cx| {
15806        editor.change_selections(None, window, cx, |s| {
15807            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15808        });
15809    });
15810
15811    assert_indent_guides(
15812        0..4,
15813        vec![
15814            indent_guide(buffer_id, 1, 3, 0),
15815            indent_guide(buffer_id, 2, 2, 1),
15816        ],
15817        Some(vec![0]),
15818        &mut cx,
15819    );
15820}
15821
15822#[gpui::test]
15823async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15824    let (buffer_id, mut cx) = setup_indent_guides_editor(
15825        &"
15826    fn main() {
15827        let a = 1;
15828
15829        let b = 2;
15830    }"
15831        .unindent(),
15832        cx,
15833    )
15834    .await;
15835
15836    cx.update_editor(|editor, window, cx| {
15837        editor.change_selections(None, window, cx, |s| {
15838            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15839        });
15840    });
15841
15842    assert_indent_guides(
15843        0..5,
15844        vec![indent_guide(buffer_id, 1, 3, 0)],
15845        Some(vec![0]),
15846        &mut cx,
15847    );
15848}
15849
15850#[gpui::test]
15851async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15852    let (buffer_id, mut cx) = setup_indent_guides_editor(
15853        &"
15854    def m:
15855        a = 1
15856        pass"
15857            .unindent(),
15858        cx,
15859    )
15860    .await;
15861
15862    cx.update_editor(|editor, window, cx| {
15863        editor.change_selections(None, window, cx, |s| {
15864            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15865        });
15866    });
15867
15868    assert_indent_guides(
15869        0..3,
15870        vec![indent_guide(buffer_id, 1, 2, 0)],
15871        Some(vec![0]),
15872        &mut cx,
15873    );
15874}
15875
15876#[gpui::test]
15877async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15878    init_test(cx, |_| {});
15879    let mut cx = EditorTestContext::new(cx).await;
15880    let text = indoc! {
15881        "
15882        impl A {
15883            fn b() {
15884                0;
15885                3;
15886                5;
15887                6;
15888                7;
15889            }
15890        }
15891        "
15892    };
15893    let base_text = indoc! {
15894        "
15895        impl A {
15896            fn b() {
15897                0;
15898                1;
15899                2;
15900                3;
15901                4;
15902            }
15903            fn c() {
15904                5;
15905                6;
15906                7;
15907            }
15908        }
15909        "
15910    };
15911
15912    cx.update_editor(|editor, window, cx| {
15913        editor.set_text(text, window, cx);
15914
15915        editor.buffer().update(cx, |multibuffer, cx| {
15916            let buffer = multibuffer.as_singleton().unwrap();
15917            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15918
15919            multibuffer.set_all_diff_hunks_expanded(cx);
15920            multibuffer.add_diff(diff, cx);
15921
15922            buffer.read(cx).remote_id()
15923        })
15924    });
15925    cx.run_until_parked();
15926
15927    cx.assert_state_with_diff(
15928        indoc! { "
15929          impl A {
15930              fn b() {
15931                  0;
15932        -         1;
15933        -         2;
15934                  3;
15935        -         4;
15936        -     }
15937        -     fn c() {
15938                  5;
15939                  6;
15940                  7;
15941              }
15942          }
15943          ˇ"
15944        }
15945        .to_string(),
15946    );
15947
15948    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15949        editor
15950            .snapshot(window, cx)
15951            .buffer_snapshot
15952            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15953            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15954            .collect::<Vec<_>>()
15955    });
15956    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15957    assert_eq!(
15958        actual_guides,
15959        vec![
15960            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15961            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15962            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15963        ]
15964    );
15965}
15966
15967#[gpui::test]
15968async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15969    init_test(cx, |_| {});
15970    let mut cx = EditorTestContext::new(cx).await;
15971
15972    let diff_base = r#"
15973        a
15974        b
15975        c
15976        "#
15977    .unindent();
15978
15979    cx.set_state(
15980        &r#"
15981        ˇA
15982        b
15983        C
15984        "#
15985        .unindent(),
15986    );
15987    cx.set_head_text(&diff_base);
15988    cx.update_editor(|editor, window, cx| {
15989        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15990    });
15991    executor.run_until_parked();
15992
15993    let both_hunks_expanded = r#"
15994        - a
15995        + ˇA
15996          b
15997        - c
15998        + C
15999        "#
16000    .unindent();
16001
16002    cx.assert_state_with_diff(both_hunks_expanded.clone());
16003
16004    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16005        let snapshot = editor.snapshot(window, cx);
16006        let hunks = editor
16007            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16008            .collect::<Vec<_>>();
16009        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16010        let buffer_id = hunks[0].buffer_id;
16011        hunks
16012            .into_iter()
16013            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16014            .collect::<Vec<_>>()
16015    });
16016    assert_eq!(hunk_ranges.len(), 2);
16017
16018    cx.update_editor(|editor, _, cx| {
16019        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16020    });
16021    executor.run_until_parked();
16022
16023    let second_hunk_expanded = r#"
16024          ˇA
16025          b
16026        - c
16027        + C
16028        "#
16029    .unindent();
16030
16031    cx.assert_state_with_diff(second_hunk_expanded);
16032
16033    cx.update_editor(|editor, _, cx| {
16034        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16035    });
16036    executor.run_until_parked();
16037
16038    cx.assert_state_with_diff(both_hunks_expanded.clone());
16039
16040    cx.update_editor(|editor, _, cx| {
16041        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16042    });
16043    executor.run_until_parked();
16044
16045    let first_hunk_expanded = r#"
16046        - a
16047        + ˇA
16048          b
16049          C
16050        "#
16051    .unindent();
16052
16053    cx.assert_state_with_diff(first_hunk_expanded);
16054
16055    cx.update_editor(|editor, _, cx| {
16056        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16057    });
16058    executor.run_until_parked();
16059
16060    cx.assert_state_with_diff(both_hunks_expanded);
16061
16062    cx.set_state(
16063        &r#"
16064        ˇA
16065        b
16066        "#
16067        .unindent(),
16068    );
16069    cx.run_until_parked();
16070
16071    // TODO this cursor position seems bad
16072    cx.assert_state_with_diff(
16073        r#"
16074        - ˇa
16075        + A
16076          b
16077        "#
16078        .unindent(),
16079    );
16080
16081    cx.update_editor(|editor, window, cx| {
16082        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16083    });
16084
16085    cx.assert_state_with_diff(
16086        r#"
16087            - ˇa
16088            + A
16089              b
16090            - c
16091            "#
16092        .unindent(),
16093    );
16094
16095    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16096        let snapshot = editor.snapshot(window, cx);
16097        let hunks = editor
16098            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16099            .collect::<Vec<_>>();
16100        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16101        let buffer_id = hunks[0].buffer_id;
16102        hunks
16103            .into_iter()
16104            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16105            .collect::<Vec<_>>()
16106    });
16107    assert_eq!(hunk_ranges.len(), 2);
16108
16109    cx.update_editor(|editor, _, cx| {
16110        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16111    });
16112    executor.run_until_parked();
16113
16114    cx.assert_state_with_diff(
16115        r#"
16116        - ˇa
16117        + A
16118          b
16119        "#
16120        .unindent(),
16121    );
16122}
16123
16124#[gpui::test]
16125async fn test_toggle_deletion_hunk_at_start_of_file(
16126    executor: BackgroundExecutor,
16127    cx: &mut TestAppContext,
16128) {
16129    init_test(cx, |_| {});
16130    let mut cx = EditorTestContext::new(cx).await;
16131
16132    let diff_base = r#"
16133        a
16134        b
16135        c
16136        "#
16137    .unindent();
16138
16139    cx.set_state(
16140        &r#"
16141        ˇb
16142        c
16143        "#
16144        .unindent(),
16145    );
16146    cx.set_head_text(&diff_base);
16147    cx.update_editor(|editor, window, cx| {
16148        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16149    });
16150    executor.run_until_parked();
16151
16152    let hunk_expanded = r#"
16153        - a
16154          ˇb
16155          c
16156        "#
16157    .unindent();
16158
16159    cx.assert_state_with_diff(hunk_expanded.clone());
16160
16161    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16162        let snapshot = editor.snapshot(window, cx);
16163        let hunks = editor
16164            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16165            .collect::<Vec<_>>();
16166        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16167        let buffer_id = hunks[0].buffer_id;
16168        hunks
16169            .into_iter()
16170            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16171            .collect::<Vec<_>>()
16172    });
16173    assert_eq!(hunk_ranges.len(), 1);
16174
16175    cx.update_editor(|editor, _, cx| {
16176        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16177    });
16178    executor.run_until_parked();
16179
16180    let hunk_collapsed = r#"
16181          ˇb
16182          c
16183        "#
16184    .unindent();
16185
16186    cx.assert_state_with_diff(hunk_collapsed);
16187
16188    cx.update_editor(|editor, _, cx| {
16189        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16190    });
16191    executor.run_until_parked();
16192
16193    cx.assert_state_with_diff(hunk_expanded.clone());
16194}
16195
16196#[gpui::test]
16197async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16198    init_test(cx, |_| {});
16199
16200    let fs = FakeFs::new(cx.executor());
16201    fs.insert_tree(
16202        path!("/test"),
16203        json!({
16204            ".git": {},
16205            "file-1": "ONE\n",
16206            "file-2": "TWO\n",
16207            "file-3": "THREE\n",
16208        }),
16209    )
16210    .await;
16211
16212    fs.set_head_for_repo(
16213        path!("/test/.git").as_ref(),
16214        &[
16215            ("file-1".into(), "one\n".into()),
16216            ("file-2".into(), "two\n".into()),
16217            ("file-3".into(), "three\n".into()),
16218        ],
16219    );
16220
16221    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16222    let mut buffers = vec![];
16223    for i in 1..=3 {
16224        let buffer = project
16225            .update(cx, |project, cx| {
16226                let path = format!(path!("/test/file-{}"), i);
16227                project.open_local_buffer(path, cx)
16228            })
16229            .await
16230            .unwrap();
16231        buffers.push(buffer);
16232    }
16233
16234    let multibuffer = cx.new(|cx| {
16235        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16236        multibuffer.set_all_diff_hunks_expanded(cx);
16237        for buffer in &buffers {
16238            let snapshot = buffer.read(cx).snapshot();
16239            multibuffer.set_excerpts_for_path(
16240                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16241                buffer.clone(),
16242                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16243                DEFAULT_MULTIBUFFER_CONTEXT,
16244                cx,
16245            );
16246        }
16247        multibuffer
16248    });
16249
16250    let editor = cx.add_window(|window, cx| {
16251        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16252    });
16253    cx.run_until_parked();
16254
16255    let snapshot = editor
16256        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16257        .unwrap();
16258    let hunks = snapshot
16259        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16260        .map(|hunk| match hunk {
16261            DisplayDiffHunk::Unfolded {
16262                display_row_range, ..
16263            } => display_row_range,
16264            DisplayDiffHunk::Folded { .. } => unreachable!(),
16265        })
16266        .collect::<Vec<_>>();
16267    assert_eq!(
16268        hunks,
16269        [
16270            DisplayRow(2)..DisplayRow(4),
16271            DisplayRow(7)..DisplayRow(9),
16272            DisplayRow(12)..DisplayRow(14),
16273        ]
16274    );
16275}
16276
16277#[gpui::test]
16278async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16279    init_test(cx, |_| {});
16280
16281    let mut cx = EditorTestContext::new(cx).await;
16282    cx.set_head_text(indoc! { "
16283        one
16284        two
16285        three
16286        four
16287        five
16288        "
16289    });
16290    cx.set_index_text(indoc! { "
16291        one
16292        two
16293        three
16294        four
16295        five
16296        "
16297    });
16298    cx.set_state(indoc! {"
16299        one
16300        TWO
16301        ˇTHREE
16302        FOUR
16303        five
16304    "});
16305    cx.run_until_parked();
16306    cx.update_editor(|editor, window, cx| {
16307        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16308    });
16309    cx.run_until_parked();
16310    cx.assert_index_text(Some(indoc! {"
16311        one
16312        TWO
16313        THREE
16314        FOUR
16315        five
16316    "}));
16317    cx.set_state(indoc! { "
16318        one
16319        TWO
16320        ˇTHREE-HUNDRED
16321        FOUR
16322        five
16323    "});
16324    cx.run_until_parked();
16325    cx.update_editor(|editor, window, cx| {
16326        let snapshot = editor.snapshot(window, cx);
16327        let hunks = editor
16328            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16329            .collect::<Vec<_>>();
16330        assert_eq!(hunks.len(), 1);
16331        assert_eq!(
16332            hunks[0].status(),
16333            DiffHunkStatus {
16334                kind: DiffHunkStatusKind::Modified,
16335                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16336            }
16337        );
16338
16339        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16340    });
16341    cx.run_until_parked();
16342    cx.assert_index_text(Some(indoc! {"
16343        one
16344        TWO
16345        THREE-HUNDRED
16346        FOUR
16347        five
16348    "}));
16349}
16350
16351#[gpui::test]
16352fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16353    init_test(cx, |_| {});
16354
16355    let editor = cx.add_window(|window, cx| {
16356        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16357        build_editor(buffer, window, cx)
16358    });
16359
16360    let render_args = Arc::new(Mutex::new(None));
16361    let snapshot = editor
16362        .update(cx, |editor, window, cx| {
16363            let snapshot = editor.buffer().read(cx).snapshot(cx);
16364            let range =
16365                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16366
16367            struct RenderArgs {
16368                row: MultiBufferRow,
16369                folded: bool,
16370                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16371            }
16372
16373            let crease = Crease::inline(
16374                range,
16375                FoldPlaceholder::test(),
16376                {
16377                    let toggle_callback = render_args.clone();
16378                    move |row, folded, callback, _window, _cx| {
16379                        *toggle_callback.lock() = Some(RenderArgs {
16380                            row,
16381                            folded,
16382                            callback,
16383                        });
16384                        div()
16385                    }
16386                },
16387                |_row, _folded, _window, _cx| div(),
16388            );
16389
16390            editor.insert_creases(Some(crease), cx);
16391            let snapshot = editor.snapshot(window, cx);
16392            let _div = snapshot.render_crease_toggle(
16393                MultiBufferRow(1),
16394                false,
16395                cx.entity().clone(),
16396                window,
16397                cx,
16398            );
16399            snapshot
16400        })
16401        .unwrap();
16402
16403    let render_args = render_args.lock().take().unwrap();
16404    assert_eq!(render_args.row, MultiBufferRow(1));
16405    assert!(!render_args.folded);
16406    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16407
16408    cx.update_window(*editor, |_, window, cx| {
16409        (render_args.callback)(true, window, cx)
16410    })
16411    .unwrap();
16412    let snapshot = editor
16413        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16414        .unwrap();
16415    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16416
16417    cx.update_window(*editor, |_, window, cx| {
16418        (render_args.callback)(false, window, cx)
16419    })
16420    .unwrap();
16421    let snapshot = editor
16422        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16423        .unwrap();
16424    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16425}
16426
16427#[gpui::test]
16428async fn test_input_text(cx: &mut TestAppContext) {
16429    init_test(cx, |_| {});
16430    let mut cx = EditorTestContext::new(cx).await;
16431
16432    cx.set_state(
16433        &r#"ˇone
16434        two
16435
16436        three
16437        fourˇ
16438        five
16439
16440        siˇx"#
16441            .unindent(),
16442    );
16443
16444    cx.dispatch_action(HandleInput(String::new()));
16445    cx.assert_editor_state(
16446        &r#"ˇone
16447        two
16448
16449        three
16450        fourˇ
16451        five
16452
16453        siˇx"#
16454            .unindent(),
16455    );
16456
16457    cx.dispatch_action(HandleInput("AAAA".to_string()));
16458    cx.assert_editor_state(
16459        &r#"AAAAˇone
16460        two
16461
16462        three
16463        fourAAAAˇ
16464        five
16465
16466        siAAAAˇx"#
16467            .unindent(),
16468    );
16469}
16470
16471#[gpui::test]
16472async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16473    init_test(cx, |_| {});
16474
16475    let mut cx = EditorTestContext::new(cx).await;
16476    cx.set_state(
16477        r#"let foo = 1;
16478let foo = 2;
16479let foo = 3;
16480let fooˇ = 4;
16481let foo = 5;
16482let foo = 6;
16483let foo = 7;
16484let foo = 8;
16485let foo = 9;
16486let foo = 10;
16487let foo = 11;
16488let foo = 12;
16489let foo = 13;
16490let foo = 14;
16491let foo = 15;"#,
16492    );
16493
16494    cx.update_editor(|e, window, cx| {
16495        assert_eq!(
16496            e.next_scroll_position,
16497            NextScrollCursorCenterTopBottom::Center,
16498            "Default next scroll direction is center",
16499        );
16500
16501        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16502        assert_eq!(
16503            e.next_scroll_position,
16504            NextScrollCursorCenterTopBottom::Top,
16505            "After center, next scroll direction should be top",
16506        );
16507
16508        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16509        assert_eq!(
16510            e.next_scroll_position,
16511            NextScrollCursorCenterTopBottom::Bottom,
16512            "After top, next scroll direction should be bottom",
16513        );
16514
16515        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16516        assert_eq!(
16517            e.next_scroll_position,
16518            NextScrollCursorCenterTopBottom::Center,
16519            "After bottom, scrolling should start over",
16520        );
16521
16522        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16523        assert_eq!(
16524            e.next_scroll_position,
16525            NextScrollCursorCenterTopBottom::Top,
16526            "Scrolling continues if retriggered fast enough"
16527        );
16528    });
16529
16530    cx.executor()
16531        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16532    cx.executor().run_until_parked();
16533    cx.update_editor(|e, _, _| {
16534        assert_eq!(
16535            e.next_scroll_position,
16536            NextScrollCursorCenterTopBottom::Center,
16537            "If scrolling is not triggered fast enough, it should reset"
16538        );
16539    });
16540}
16541
16542#[gpui::test]
16543async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16544    init_test(cx, |_| {});
16545    let mut cx = EditorLspTestContext::new_rust(
16546        lsp::ServerCapabilities {
16547            definition_provider: Some(lsp::OneOf::Left(true)),
16548            references_provider: Some(lsp::OneOf::Left(true)),
16549            ..lsp::ServerCapabilities::default()
16550        },
16551        cx,
16552    )
16553    .await;
16554
16555    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16556        let go_to_definition = cx
16557            .lsp
16558            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16559                move |params, _| async move {
16560                    if empty_go_to_definition {
16561                        Ok(None)
16562                    } else {
16563                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16564                            uri: params.text_document_position_params.text_document.uri,
16565                            range: lsp::Range::new(
16566                                lsp::Position::new(4, 3),
16567                                lsp::Position::new(4, 6),
16568                            ),
16569                        })))
16570                    }
16571                },
16572            );
16573        let references = cx
16574            .lsp
16575            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16576                Ok(Some(vec![lsp::Location {
16577                    uri: params.text_document_position.text_document.uri,
16578                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16579                }]))
16580            });
16581        (go_to_definition, references)
16582    };
16583
16584    cx.set_state(
16585        &r#"fn one() {
16586            let mut a = ˇtwo();
16587        }
16588
16589        fn two() {}"#
16590            .unindent(),
16591    );
16592    set_up_lsp_handlers(false, &mut cx);
16593    let navigated = cx
16594        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16595        .await
16596        .expect("Failed to navigate to definition");
16597    assert_eq!(
16598        navigated,
16599        Navigated::Yes,
16600        "Should have navigated to definition from the GetDefinition response"
16601    );
16602    cx.assert_editor_state(
16603        &r#"fn one() {
16604            let mut a = two();
16605        }
16606
16607        fn «twoˇ»() {}"#
16608            .unindent(),
16609    );
16610
16611    let editors = cx.update_workspace(|workspace, _, cx| {
16612        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16613    });
16614    cx.update_editor(|_, _, test_editor_cx| {
16615        assert_eq!(
16616            editors.len(),
16617            1,
16618            "Initially, only one, test, editor should be open in the workspace"
16619        );
16620        assert_eq!(
16621            test_editor_cx.entity(),
16622            editors.last().expect("Asserted len is 1").clone()
16623        );
16624    });
16625
16626    set_up_lsp_handlers(true, &mut cx);
16627    let navigated = cx
16628        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16629        .await
16630        .expect("Failed to navigate to lookup references");
16631    assert_eq!(
16632        navigated,
16633        Navigated::Yes,
16634        "Should have navigated to references as a fallback after empty GoToDefinition response"
16635    );
16636    // We should not change the selections in the existing file,
16637    // if opening another milti buffer with the references
16638    cx.assert_editor_state(
16639        &r#"fn one() {
16640            let mut a = two();
16641        }
16642
16643        fn «twoˇ»() {}"#
16644            .unindent(),
16645    );
16646    let editors = cx.update_workspace(|workspace, _, cx| {
16647        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16648    });
16649    cx.update_editor(|_, _, test_editor_cx| {
16650        assert_eq!(
16651            editors.len(),
16652            2,
16653            "After falling back to references search, we open a new editor with the results"
16654        );
16655        let references_fallback_text = editors
16656            .into_iter()
16657            .find(|new_editor| *new_editor != test_editor_cx.entity())
16658            .expect("Should have one non-test editor now")
16659            .read(test_editor_cx)
16660            .text(test_editor_cx);
16661        assert_eq!(
16662            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16663            "Should use the range from the references response and not the GoToDefinition one"
16664        );
16665    });
16666}
16667
16668#[gpui::test]
16669async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16670    init_test(cx, |_| {});
16671    cx.update(|cx| {
16672        let mut editor_settings = EditorSettings::get_global(cx).clone();
16673        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16674        EditorSettings::override_global(editor_settings, cx);
16675    });
16676    let mut cx = EditorLspTestContext::new_rust(
16677        lsp::ServerCapabilities {
16678            definition_provider: Some(lsp::OneOf::Left(true)),
16679            references_provider: Some(lsp::OneOf::Left(true)),
16680            ..lsp::ServerCapabilities::default()
16681        },
16682        cx,
16683    )
16684    .await;
16685    let original_state = r#"fn one() {
16686        let mut a = ˇtwo();
16687    }
16688
16689    fn two() {}"#
16690        .unindent();
16691    cx.set_state(&original_state);
16692
16693    let mut go_to_definition = cx
16694        .lsp
16695        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16696            move |_, _| async move { Ok(None) },
16697        );
16698    let _references = cx
16699        .lsp
16700        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16701            panic!("Should not call for references with no go to definition fallback")
16702        });
16703
16704    let navigated = cx
16705        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16706        .await
16707        .expect("Failed to navigate to lookup references");
16708    go_to_definition
16709        .next()
16710        .await
16711        .expect("Should have called the go_to_definition handler");
16712
16713    assert_eq!(
16714        navigated,
16715        Navigated::No,
16716        "Should have navigated to references as a fallback after empty GoToDefinition response"
16717    );
16718    cx.assert_editor_state(&original_state);
16719    let editors = cx.update_workspace(|workspace, _, cx| {
16720        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16721    });
16722    cx.update_editor(|_, _, _| {
16723        assert_eq!(
16724            editors.len(),
16725            1,
16726            "After unsuccessful fallback, no other editor should have been opened"
16727        );
16728    });
16729}
16730
16731#[gpui::test]
16732async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16733    init_test(cx, |_| {});
16734
16735    let language = Arc::new(Language::new(
16736        LanguageConfig::default(),
16737        Some(tree_sitter_rust::LANGUAGE.into()),
16738    ));
16739
16740    let text = r#"
16741        #[cfg(test)]
16742        mod tests() {
16743            #[test]
16744            fn runnable_1() {
16745                let a = 1;
16746            }
16747
16748            #[test]
16749            fn runnable_2() {
16750                let a = 1;
16751                let b = 2;
16752            }
16753        }
16754    "#
16755    .unindent();
16756
16757    let fs = FakeFs::new(cx.executor());
16758    fs.insert_file("/file.rs", Default::default()).await;
16759
16760    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16761    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16762    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16763    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16764    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16765
16766    let editor = cx.new_window_entity(|window, cx| {
16767        Editor::new(
16768            EditorMode::Full,
16769            multi_buffer,
16770            Some(project.clone()),
16771            window,
16772            cx,
16773        )
16774    });
16775
16776    editor.update_in(cx, |editor, window, cx| {
16777        let snapshot = editor.buffer().read(cx).snapshot(cx);
16778        editor.tasks.insert(
16779            (buffer.read(cx).remote_id(), 3),
16780            RunnableTasks {
16781                templates: vec![],
16782                offset: snapshot.anchor_before(43),
16783                column: 0,
16784                extra_variables: HashMap::default(),
16785                context_range: BufferOffset(43)..BufferOffset(85),
16786            },
16787        );
16788        editor.tasks.insert(
16789            (buffer.read(cx).remote_id(), 8),
16790            RunnableTasks {
16791                templates: vec![],
16792                offset: snapshot.anchor_before(86),
16793                column: 0,
16794                extra_variables: HashMap::default(),
16795                context_range: BufferOffset(86)..BufferOffset(191),
16796            },
16797        );
16798
16799        // Test finding task when cursor is inside function body
16800        editor.change_selections(None, window, cx, |s| {
16801            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16802        });
16803        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16804        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16805
16806        // Test finding task when cursor is on function name
16807        editor.change_selections(None, window, cx, |s| {
16808            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16809        });
16810        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16811        assert_eq!(row, 8, "Should find task when cursor is on function name");
16812    });
16813}
16814
16815#[gpui::test]
16816async fn test_folding_buffers(cx: &mut TestAppContext) {
16817    init_test(cx, |_| {});
16818
16819    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16820    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16821    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16822
16823    let fs = FakeFs::new(cx.executor());
16824    fs.insert_tree(
16825        path!("/a"),
16826        json!({
16827            "first.rs": sample_text_1,
16828            "second.rs": sample_text_2,
16829            "third.rs": sample_text_3,
16830        }),
16831    )
16832    .await;
16833    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16834    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16835    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16836    let worktree = project.update(cx, |project, cx| {
16837        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16838        assert_eq!(worktrees.len(), 1);
16839        worktrees.pop().unwrap()
16840    });
16841    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16842
16843    let buffer_1 = project
16844        .update(cx, |project, cx| {
16845            project.open_buffer((worktree_id, "first.rs"), cx)
16846        })
16847        .await
16848        .unwrap();
16849    let buffer_2 = project
16850        .update(cx, |project, cx| {
16851            project.open_buffer((worktree_id, "second.rs"), cx)
16852        })
16853        .await
16854        .unwrap();
16855    let buffer_3 = project
16856        .update(cx, |project, cx| {
16857            project.open_buffer((worktree_id, "third.rs"), cx)
16858        })
16859        .await
16860        .unwrap();
16861
16862    let multi_buffer = cx.new(|cx| {
16863        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16864        multi_buffer.push_excerpts(
16865            buffer_1.clone(),
16866            [
16867                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16868                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16869                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16870            ],
16871            cx,
16872        );
16873        multi_buffer.push_excerpts(
16874            buffer_2.clone(),
16875            [
16876                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16877                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16878                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16879            ],
16880            cx,
16881        );
16882        multi_buffer.push_excerpts(
16883            buffer_3.clone(),
16884            [
16885                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16886                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16887                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16888            ],
16889            cx,
16890        );
16891        multi_buffer
16892    });
16893    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16894        Editor::new(
16895            EditorMode::Full,
16896            multi_buffer.clone(),
16897            Some(project.clone()),
16898            window,
16899            cx,
16900        )
16901    });
16902
16903    assert_eq!(
16904        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16905        "\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",
16906    );
16907
16908    multi_buffer_editor.update(cx, |editor, cx| {
16909        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16910    });
16911    assert_eq!(
16912        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16913        "\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",
16914        "After folding the first buffer, its text should not be displayed"
16915    );
16916
16917    multi_buffer_editor.update(cx, |editor, cx| {
16918        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16919    });
16920    assert_eq!(
16921        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16922        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16923        "After folding the second buffer, its text should not be displayed"
16924    );
16925
16926    multi_buffer_editor.update(cx, |editor, cx| {
16927        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16928    });
16929    assert_eq!(
16930        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16931        "\n\n\n\n\n",
16932        "After folding the third buffer, its text should not be displayed"
16933    );
16934
16935    // Emulate selection inside the fold logic, that should work
16936    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16937        editor
16938            .snapshot(window, cx)
16939            .next_line_boundary(Point::new(0, 4));
16940    });
16941
16942    multi_buffer_editor.update(cx, |editor, cx| {
16943        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16944    });
16945    assert_eq!(
16946        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16947        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16948        "After unfolding the second buffer, its text should be displayed"
16949    );
16950
16951    // Typing inside of buffer 1 causes that buffer to be unfolded.
16952    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16953        assert_eq!(
16954            multi_buffer
16955                .read(cx)
16956                .snapshot(cx)
16957                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16958                .collect::<String>(),
16959            "bbbb"
16960        );
16961        editor.change_selections(None, window, cx, |selections| {
16962            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16963        });
16964        editor.handle_input("B", window, cx);
16965    });
16966
16967    assert_eq!(
16968        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16969        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16970        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16971    );
16972
16973    multi_buffer_editor.update(cx, |editor, cx| {
16974        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16975    });
16976    assert_eq!(
16977        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16978        "\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",
16979        "After unfolding the all buffers, all original text should be displayed"
16980    );
16981}
16982
16983#[gpui::test]
16984async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16985    init_test(cx, |_| {});
16986
16987    let sample_text_1 = "1111\n2222\n3333".to_string();
16988    let sample_text_2 = "4444\n5555\n6666".to_string();
16989    let sample_text_3 = "7777\n8888\n9999".to_string();
16990
16991    let fs = FakeFs::new(cx.executor());
16992    fs.insert_tree(
16993        path!("/a"),
16994        json!({
16995            "first.rs": sample_text_1,
16996            "second.rs": sample_text_2,
16997            "third.rs": sample_text_3,
16998        }),
16999    )
17000    .await;
17001    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17002    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17003    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17004    let worktree = project.update(cx, |project, cx| {
17005        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17006        assert_eq!(worktrees.len(), 1);
17007        worktrees.pop().unwrap()
17008    });
17009    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17010
17011    let buffer_1 = project
17012        .update(cx, |project, cx| {
17013            project.open_buffer((worktree_id, "first.rs"), cx)
17014        })
17015        .await
17016        .unwrap();
17017    let buffer_2 = project
17018        .update(cx, |project, cx| {
17019            project.open_buffer((worktree_id, "second.rs"), cx)
17020        })
17021        .await
17022        .unwrap();
17023    let buffer_3 = project
17024        .update(cx, |project, cx| {
17025            project.open_buffer((worktree_id, "third.rs"), cx)
17026        })
17027        .await
17028        .unwrap();
17029
17030    let multi_buffer = cx.new(|cx| {
17031        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17032        multi_buffer.push_excerpts(
17033            buffer_1.clone(),
17034            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17035            cx,
17036        );
17037        multi_buffer.push_excerpts(
17038            buffer_2.clone(),
17039            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17040            cx,
17041        );
17042        multi_buffer.push_excerpts(
17043            buffer_3.clone(),
17044            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17045            cx,
17046        );
17047        multi_buffer
17048    });
17049
17050    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17051        Editor::new(
17052            EditorMode::Full,
17053            multi_buffer,
17054            Some(project.clone()),
17055            window,
17056            cx,
17057        )
17058    });
17059
17060    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17061    assert_eq!(
17062        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17063        full_text,
17064    );
17065
17066    multi_buffer_editor.update(cx, |editor, cx| {
17067        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17068    });
17069    assert_eq!(
17070        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17071        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17072        "After folding the first buffer, its text should not be displayed"
17073    );
17074
17075    multi_buffer_editor.update(cx, |editor, cx| {
17076        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17077    });
17078
17079    assert_eq!(
17080        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17081        "\n\n\n\n\n\n7777\n8888\n9999",
17082        "After folding the second buffer, its text should not be displayed"
17083    );
17084
17085    multi_buffer_editor.update(cx, |editor, cx| {
17086        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17087    });
17088    assert_eq!(
17089        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17090        "\n\n\n\n\n",
17091        "After folding the third buffer, its text should not be displayed"
17092    );
17093
17094    multi_buffer_editor.update(cx, |editor, cx| {
17095        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17096    });
17097    assert_eq!(
17098        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17099        "\n\n\n\n4444\n5555\n6666\n\n",
17100        "After unfolding the second buffer, its text should be displayed"
17101    );
17102
17103    multi_buffer_editor.update(cx, |editor, cx| {
17104        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17105    });
17106    assert_eq!(
17107        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17108        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17109        "After unfolding the first buffer, its text should be displayed"
17110    );
17111
17112    multi_buffer_editor.update(cx, |editor, cx| {
17113        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17114    });
17115    assert_eq!(
17116        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17117        full_text,
17118        "After unfolding all buffers, all original text should be displayed"
17119    );
17120}
17121
17122#[gpui::test]
17123async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17124    init_test(cx, |_| {});
17125
17126    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17127
17128    let fs = FakeFs::new(cx.executor());
17129    fs.insert_tree(
17130        path!("/a"),
17131        json!({
17132            "main.rs": sample_text,
17133        }),
17134    )
17135    .await;
17136    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17137    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17138    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17139    let worktree = project.update(cx, |project, cx| {
17140        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17141        assert_eq!(worktrees.len(), 1);
17142        worktrees.pop().unwrap()
17143    });
17144    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17145
17146    let buffer_1 = project
17147        .update(cx, |project, cx| {
17148            project.open_buffer((worktree_id, "main.rs"), cx)
17149        })
17150        .await
17151        .unwrap();
17152
17153    let multi_buffer = cx.new(|cx| {
17154        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17155        multi_buffer.push_excerpts(
17156            buffer_1.clone(),
17157            [ExcerptRange::new(
17158                Point::new(0, 0)
17159                    ..Point::new(
17160                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17161                        0,
17162                    ),
17163            )],
17164            cx,
17165        );
17166        multi_buffer
17167    });
17168    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17169        Editor::new(
17170            EditorMode::Full,
17171            multi_buffer,
17172            Some(project.clone()),
17173            window,
17174            cx,
17175        )
17176    });
17177
17178    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17179    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17180        enum TestHighlight {}
17181        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17182        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17183        editor.highlight_text::<TestHighlight>(
17184            vec![highlight_range.clone()],
17185            HighlightStyle::color(Hsla::green()),
17186            cx,
17187        );
17188        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17189    });
17190
17191    let full_text = format!("\n\n{sample_text}");
17192    assert_eq!(
17193        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17194        full_text,
17195    );
17196}
17197
17198#[gpui::test]
17199async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17200    init_test(cx, |_| {});
17201    cx.update(|cx| {
17202        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17203            "keymaps/default-linux.json",
17204            cx,
17205        )
17206        .unwrap();
17207        cx.bind_keys(default_key_bindings);
17208    });
17209
17210    let (editor, cx) = cx.add_window_view(|window, cx| {
17211        let multi_buffer = MultiBuffer::build_multi(
17212            [
17213                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17214                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17215                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17216                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17217            ],
17218            cx,
17219        );
17220        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17221
17222        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17223        // fold all but the second buffer, so that we test navigating between two
17224        // adjacent folded buffers, as well as folded buffers at the start and
17225        // end the multibuffer
17226        editor.fold_buffer(buffer_ids[0], cx);
17227        editor.fold_buffer(buffer_ids[2], cx);
17228        editor.fold_buffer(buffer_ids[3], cx);
17229
17230        editor
17231    });
17232    cx.simulate_resize(size(px(1000.), px(1000.)));
17233
17234    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17235    cx.assert_excerpts_with_selections(indoc! {"
17236        [EXCERPT]
17237        ˇ[FOLDED]
17238        [EXCERPT]
17239        a1
17240        b1
17241        [EXCERPT]
17242        [FOLDED]
17243        [EXCERPT]
17244        [FOLDED]
17245        "
17246    });
17247    cx.simulate_keystroke("down");
17248    cx.assert_excerpts_with_selections(indoc! {"
17249        [EXCERPT]
17250        [FOLDED]
17251        [EXCERPT]
17252        ˇa1
17253        b1
17254        [EXCERPT]
17255        [FOLDED]
17256        [EXCERPT]
17257        [FOLDED]
17258        "
17259    });
17260    cx.simulate_keystroke("down");
17261    cx.assert_excerpts_with_selections(indoc! {"
17262        [EXCERPT]
17263        [FOLDED]
17264        [EXCERPT]
17265        a1
17266        ˇb1
17267        [EXCERPT]
17268        [FOLDED]
17269        [EXCERPT]
17270        [FOLDED]
17271        "
17272    });
17273    cx.simulate_keystroke("down");
17274    cx.assert_excerpts_with_selections(indoc! {"
17275        [EXCERPT]
17276        [FOLDED]
17277        [EXCERPT]
17278        a1
17279        b1
17280        ˇ[EXCERPT]
17281        [FOLDED]
17282        [EXCERPT]
17283        [FOLDED]
17284        "
17285    });
17286    cx.simulate_keystroke("down");
17287    cx.assert_excerpts_with_selections(indoc! {"
17288        [EXCERPT]
17289        [FOLDED]
17290        [EXCERPT]
17291        a1
17292        b1
17293        [EXCERPT]
17294        ˇ[FOLDED]
17295        [EXCERPT]
17296        [FOLDED]
17297        "
17298    });
17299    for _ in 0..5 {
17300        cx.simulate_keystroke("down");
17301        cx.assert_excerpts_with_selections(indoc! {"
17302            [EXCERPT]
17303            [FOLDED]
17304            [EXCERPT]
17305            a1
17306            b1
17307            [EXCERPT]
17308            [FOLDED]
17309            [EXCERPT]
17310            ˇ[FOLDED]
17311            "
17312        });
17313    }
17314
17315    cx.simulate_keystroke("up");
17316    cx.assert_excerpts_with_selections(indoc! {"
17317        [EXCERPT]
17318        [FOLDED]
17319        [EXCERPT]
17320        a1
17321        b1
17322        [EXCERPT]
17323        ˇ[FOLDED]
17324        [EXCERPT]
17325        [FOLDED]
17326        "
17327    });
17328    cx.simulate_keystroke("up");
17329    cx.assert_excerpts_with_selections(indoc! {"
17330        [EXCERPT]
17331        [FOLDED]
17332        [EXCERPT]
17333        a1
17334        b1
17335        ˇ[EXCERPT]
17336        [FOLDED]
17337        [EXCERPT]
17338        [FOLDED]
17339        "
17340    });
17341    cx.simulate_keystroke("up");
17342    cx.assert_excerpts_with_selections(indoc! {"
17343        [EXCERPT]
17344        [FOLDED]
17345        [EXCERPT]
17346        a1
17347        ˇb1
17348        [EXCERPT]
17349        [FOLDED]
17350        [EXCERPT]
17351        [FOLDED]
17352        "
17353    });
17354    cx.simulate_keystroke("up");
17355    cx.assert_excerpts_with_selections(indoc! {"
17356        [EXCERPT]
17357        [FOLDED]
17358        [EXCERPT]
17359        ˇa1
17360        b1
17361        [EXCERPT]
17362        [FOLDED]
17363        [EXCERPT]
17364        [FOLDED]
17365        "
17366    });
17367    for _ in 0..5 {
17368        cx.simulate_keystroke("up");
17369        cx.assert_excerpts_with_selections(indoc! {"
17370            [EXCERPT]
17371            ˇ[FOLDED]
17372            [EXCERPT]
17373            a1
17374            b1
17375            [EXCERPT]
17376            [FOLDED]
17377            [EXCERPT]
17378            [FOLDED]
17379            "
17380        });
17381    }
17382}
17383
17384#[gpui::test]
17385async fn test_inline_completion_text(cx: &mut TestAppContext) {
17386    init_test(cx, |_| {});
17387
17388    // Simple insertion
17389    assert_highlighted_edits(
17390        "Hello, world!",
17391        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17392        true,
17393        cx,
17394        |highlighted_edits, cx| {
17395            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17396            assert_eq!(highlighted_edits.highlights.len(), 1);
17397            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17398            assert_eq!(
17399                highlighted_edits.highlights[0].1.background_color,
17400                Some(cx.theme().status().created_background)
17401            );
17402        },
17403    )
17404    .await;
17405
17406    // Replacement
17407    assert_highlighted_edits(
17408        "This is a test.",
17409        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17410        false,
17411        cx,
17412        |highlighted_edits, cx| {
17413            assert_eq!(highlighted_edits.text, "That is a test.");
17414            assert_eq!(highlighted_edits.highlights.len(), 1);
17415            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17416            assert_eq!(
17417                highlighted_edits.highlights[0].1.background_color,
17418                Some(cx.theme().status().created_background)
17419            );
17420        },
17421    )
17422    .await;
17423
17424    // Multiple edits
17425    assert_highlighted_edits(
17426        "Hello, world!",
17427        vec![
17428            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17429            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17430        ],
17431        false,
17432        cx,
17433        |highlighted_edits, cx| {
17434            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17435            assert_eq!(highlighted_edits.highlights.len(), 2);
17436            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17437            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17438            assert_eq!(
17439                highlighted_edits.highlights[0].1.background_color,
17440                Some(cx.theme().status().created_background)
17441            );
17442            assert_eq!(
17443                highlighted_edits.highlights[1].1.background_color,
17444                Some(cx.theme().status().created_background)
17445            );
17446        },
17447    )
17448    .await;
17449
17450    // Multiple lines with edits
17451    assert_highlighted_edits(
17452        "First line\nSecond line\nThird line\nFourth line",
17453        vec![
17454            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17455            (
17456                Point::new(2, 0)..Point::new(2, 10),
17457                "New third line".to_string(),
17458            ),
17459            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17460        ],
17461        false,
17462        cx,
17463        |highlighted_edits, cx| {
17464            assert_eq!(
17465                highlighted_edits.text,
17466                "Second modified\nNew third line\nFourth updated line"
17467            );
17468            assert_eq!(highlighted_edits.highlights.len(), 3);
17469            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17470            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17471            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17472            for highlight in &highlighted_edits.highlights {
17473                assert_eq!(
17474                    highlight.1.background_color,
17475                    Some(cx.theme().status().created_background)
17476                );
17477            }
17478        },
17479    )
17480    .await;
17481}
17482
17483#[gpui::test]
17484async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17485    init_test(cx, |_| {});
17486
17487    // Deletion
17488    assert_highlighted_edits(
17489        "Hello, world!",
17490        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17491        true,
17492        cx,
17493        |highlighted_edits, cx| {
17494            assert_eq!(highlighted_edits.text, "Hello, world!");
17495            assert_eq!(highlighted_edits.highlights.len(), 1);
17496            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17497            assert_eq!(
17498                highlighted_edits.highlights[0].1.background_color,
17499                Some(cx.theme().status().deleted_background)
17500            );
17501        },
17502    )
17503    .await;
17504
17505    // Insertion
17506    assert_highlighted_edits(
17507        "Hello, world!",
17508        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17509        true,
17510        cx,
17511        |highlighted_edits, cx| {
17512            assert_eq!(highlighted_edits.highlights.len(), 1);
17513            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17514            assert_eq!(
17515                highlighted_edits.highlights[0].1.background_color,
17516                Some(cx.theme().status().created_background)
17517            );
17518        },
17519    )
17520    .await;
17521}
17522
17523async fn assert_highlighted_edits(
17524    text: &str,
17525    edits: Vec<(Range<Point>, String)>,
17526    include_deletions: bool,
17527    cx: &mut TestAppContext,
17528    assertion_fn: impl Fn(HighlightedText, &App),
17529) {
17530    let window = cx.add_window(|window, cx| {
17531        let buffer = MultiBuffer::build_simple(text, cx);
17532        Editor::new(EditorMode::Full, buffer, None, window, cx)
17533    });
17534    let cx = &mut VisualTestContext::from_window(*window, cx);
17535
17536    let (buffer, snapshot) = window
17537        .update(cx, |editor, _window, cx| {
17538            (
17539                editor.buffer().clone(),
17540                editor.buffer().read(cx).snapshot(cx),
17541            )
17542        })
17543        .unwrap();
17544
17545    let edits = edits
17546        .into_iter()
17547        .map(|(range, edit)| {
17548            (
17549                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17550                edit,
17551            )
17552        })
17553        .collect::<Vec<_>>();
17554
17555    let text_anchor_edits = edits
17556        .clone()
17557        .into_iter()
17558        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17559        .collect::<Vec<_>>();
17560
17561    let edit_preview = window
17562        .update(cx, |_, _window, cx| {
17563            buffer
17564                .read(cx)
17565                .as_singleton()
17566                .unwrap()
17567                .read(cx)
17568                .preview_edits(text_anchor_edits.into(), cx)
17569        })
17570        .unwrap()
17571        .await;
17572
17573    cx.update(|_window, cx| {
17574        let highlighted_edits = inline_completion_edit_text(
17575            &snapshot.as_singleton().unwrap().2,
17576            &edits,
17577            &edit_preview,
17578            include_deletions,
17579            cx,
17580        );
17581        assertion_fn(highlighted_edits, cx)
17582    });
17583}
17584
17585#[track_caller]
17586fn assert_breakpoint(
17587    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17588    path: &Arc<Path>,
17589    expected: Vec<(u32, Breakpoint)>,
17590) {
17591    if expected.len() == 0usize {
17592        assert!(!breakpoints.contains_key(path), "{}", path.display());
17593    } else {
17594        let mut breakpoint = breakpoints
17595            .get(path)
17596            .unwrap()
17597            .into_iter()
17598            .map(|breakpoint| {
17599                (
17600                    breakpoint.row,
17601                    Breakpoint {
17602                        message: breakpoint.message.clone(),
17603                        state: breakpoint.state,
17604                        condition: breakpoint.condition.clone(),
17605                        hit_condition: breakpoint.hit_condition.clone(),
17606                    },
17607                )
17608            })
17609            .collect::<Vec<_>>();
17610
17611        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17612
17613        assert_eq!(expected, breakpoint);
17614    }
17615}
17616
17617fn add_log_breakpoint_at_cursor(
17618    editor: &mut Editor,
17619    log_message: &str,
17620    window: &mut Window,
17621    cx: &mut Context<Editor>,
17622) {
17623    let (anchor, bp) = editor
17624        .breakpoint_at_cursor_head(window, cx)
17625        .unwrap_or_else(|| {
17626            let cursor_position: Point = editor.selections.newest(cx).head();
17627
17628            let breakpoint_position = editor
17629                .snapshot(window, cx)
17630                .display_snapshot
17631                .buffer_snapshot
17632                .anchor_before(Point::new(cursor_position.row, 0));
17633
17634            (breakpoint_position, Breakpoint::new_log(&log_message))
17635        });
17636
17637    editor.edit_breakpoint_at_anchor(
17638        anchor,
17639        bp,
17640        BreakpointEditAction::EditLogMessage(log_message.into()),
17641        cx,
17642    );
17643}
17644
17645#[gpui::test]
17646async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17647    init_test(cx, |_| {});
17648
17649    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17650    let fs = FakeFs::new(cx.executor());
17651    fs.insert_tree(
17652        path!("/a"),
17653        json!({
17654            "main.rs": sample_text,
17655        }),
17656    )
17657    .await;
17658    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17659    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17660    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17661
17662    let fs = FakeFs::new(cx.executor());
17663    fs.insert_tree(
17664        path!("/a"),
17665        json!({
17666            "main.rs": sample_text,
17667        }),
17668    )
17669    .await;
17670    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17671    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17672    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17673    let worktree_id = workspace
17674        .update(cx, |workspace, _window, cx| {
17675            workspace.project().update(cx, |project, cx| {
17676                project.worktrees(cx).next().unwrap().read(cx).id()
17677            })
17678        })
17679        .unwrap();
17680
17681    let buffer = project
17682        .update(cx, |project, cx| {
17683            project.open_buffer((worktree_id, "main.rs"), cx)
17684        })
17685        .await
17686        .unwrap();
17687
17688    let (editor, cx) = cx.add_window_view(|window, cx| {
17689        Editor::new(
17690            EditorMode::Full,
17691            MultiBuffer::build_from_buffer(buffer, cx),
17692            Some(project.clone()),
17693            window,
17694            cx,
17695        )
17696    });
17697
17698    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17699    let abs_path = project.read_with(cx, |project, cx| {
17700        project
17701            .absolute_path(&project_path, cx)
17702            .map(|path_buf| Arc::from(path_buf.to_owned()))
17703            .unwrap()
17704    });
17705
17706    // assert we can add breakpoint on the first line
17707    editor.update_in(cx, |editor, window, cx| {
17708        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17709        editor.move_to_end(&MoveToEnd, window, cx);
17710        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17711    });
17712
17713    let breakpoints = editor.update(cx, |editor, cx| {
17714        editor
17715            .breakpoint_store()
17716            .as_ref()
17717            .unwrap()
17718            .read(cx)
17719            .all_breakpoints(cx)
17720            .clone()
17721    });
17722
17723    assert_eq!(1, breakpoints.len());
17724    assert_breakpoint(
17725        &breakpoints,
17726        &abs_path,
17727        vec![
17728            (0, Breakpoint::new_standard()),
17729            (3, Breakpoint::new_standard()),
17730        ],
17731    );
17732
17733    editor.update_in(cx, |editor, window, cx| {
17734        editor.move_to_beginning(&MoveToBeginning, window, cx);
17735        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17736    });
17737
17738    let breakpoints = editor.update(cx, |editor, cx| {
17739        editor
17740            .breakpoint_store()
17741            .as_ref()
17742            .unwrap()
17743            .read(cx)
17744            .all_breakpoints(cx)
17745            .clone()
17746    });
17747
17748    assert_eq!(1, breakpoints.len());
17749    assert_breakpoint(
17750        &breakpoints,
17751        &abs_path,
17752        vec![(3, Breakpoint::new_standard())],
17753    );
17754
17755    editor.update_in(cx, |editor, window, cx| {
17756        editor.move_to_end(&MoveToEnd, window, cx);
17757        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17758    });
17759
17760    let breakpoints = editor.update(cx, |editor, cx| {
17761        editor
17762            .breakpoint_store()
17763            .as_ref()
17764            .unwrap()
17765            .read(cx)
17766            .all_breakpoints(cx)
17767            .clone()
17768    });
17769
17770    assert_eq!(0, breakpoints.len());
17771    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17772}
17773
17774#[gpui::test]
17775async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17776    init_test(cx, |_| {});
17777
17778    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17779
17780    let fs = FakeFs::new(cx.executor());
17781    fs.insert_tree(
17782        path!("/a"),
17783        json!({
17784            "main.rs": sample_text,
17785        }),
17786    )
17787    .await;
17788    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17789    let (workspace, cx) =
17790        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17791
17792    let worktree_id = workspace.update(cx, |workspace, cx| {
17793        workspace.project().update(cx, |project, cx| {
17794            project.worktrees(cx).next().unwrap().read(cx).id()
17795        })
17796    });
17797
17798    let buffer = project
17799        .update(cx, |project, cx| {
17800            project.open_buffer((worktree_id, "main.rs"), cx)
17801        })
17802        .await
17803        .unwrap();
17804
17805    let (editor, cx) = cx.add_window_view(|window, cx| {
17806        Editor::new(
17807            EditorMode::Full,
17808            MultiBuffer::build_from_buffer(buffer, cx),
17809            Some(project.clone()),
17810            window,
17811            cx,
17812        )
17813    });
17814
17815    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17816    let abs_path = project.read_with(cx, |project, cx| {
17817        project
17818            .absolute_path(&project_path, cx)
17819            .map(|path_buf| Arc::from(path_buf.to_owned()))
17820            .unwrap()
17821    });
17822
17823    editor.update_in(cx, |editor, window, cx| {
17824        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17825    });
17826
17827    let breakpoints = editor.update(cx, |editor, cx| {
17828        editor
17829            .breakpoint_store()
17830            .as_ref()
17831            .unwrap()
17832            .read(cx)
17833            .all_breakpoints(cx)
17834            .clone()
17835    });
17836
17837    assert_breakpoint(
17838        &breakpoints,
17839        &abs_path,
17840        vec![(0, Breakpoint::new_log("hello world"))],
17841    );
17842
17843    // Removing a log message from a log breakpoint should remove it
17844    editor.update_in(cx, |editor, window, cx| {
17845        add_log_breakpoint_at_cursor(editor, "", window, cx);
17846    });
17847
17848    let breakpoints = editor.update(cx, |editor, cx| {
17849        editor
17850            .breakpoint_store()
17851            .as_ref()
17852            .unwrap()
17853            .read(cx)
17854            .all_breakpoints(cx)
17855            .clone()
17856    });
17857
17858    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17859
17860    editor.update_in(cx, |editor, window, cx| {
17861        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17862        editor.move_to_end(&MoveToEnd, window, cx);
17863        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17864        // Not adding a log message to a standard breakpoint shouldn't remove it
17865        add_log_breakpoint_at_cursor(editor, "", window, cx);
17866    });
17867
17868    let breakpoints = editor.update(cx, |editor, cx| {
17869        editor
17870            .breakpoint_store()
17871            .as_ref()
17872            .unwrap()
17873            .read(cx)
17874            .all_breakpoints(cx)
17875            .clone()
17876    });
17877
17878    assert_breakpoint(
17879        &breakpoints,
17880        &abs_path,
17881        vec![
17882            (0, Breakpoint::new_standard()),
17883            (3, Breakpoint::new_standard()),
17884        ],
17885    );
17886
17887    editor.update_in(cx, |editor, window, cx| {
17888        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17889    });
17890
17891    let breakpoints = editor.update(cx, |editor, cx| {
17892        editor
17893            .breakpoint_store()
17894            .as_ref()
17895            .unwrap()
17896            .read(cx)
17897            .all_breakpoints(cx)
17898            .clone()
17899    });
17900
17901    assert_breakpoint(
17902        &breakpoints,
17903        &abs_path,
17904        vec![
17905            (0, Breakpoint::new_standard()),
17906            (3, Breakpoint::new_log("hello world")),
17907        ],
17908    );
17909
17910    editor.update_in(cx, |editor, window, cx| {
17911        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17912    });
17913
17914    let breakpoints = editor.update(cx, |editor, cx| {
17915        editor
17916            .breakpoint_store()
17917            .as_ref()
17918            .unwrap()
17919            .read(cx)
17920            .all_breakpoints(cx)
17921            .clone()
17922    });
17923
17924    assert_breakpoint(
17925        &breakpoints,
17926        &abs_path,
17927        vec![
17928            (0, Breakpoint::new_standard()),
17929            (3, Breakpoint::new_log("hello Earth!!")),
17930        ],
17931    );
17932}
17933
17934/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17935/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17936/// or when breakpoints were placed out of order. This tests for a regression too
17937#[gpui::test]
17938async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17939    init_test(cx, |_| {});
17940
17941    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17942    let fs = FakeFs::new(cx.executor());
17943    fs.insert_tree(
17944        path!("/a"),
17945        json!({
17946            "main.rs": sample_text,
17947        }),
17948    )
17949    .await;
17950    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17951    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17952    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17953
17954    let fs = FakeFs::new(cx.executor());
17955    fs.insert_tree(
17956        path!("/a"),
17957        json!({
17958            "main.rs": sample_text,
17959        }),
17960    )
17961    .await;
17962    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17963    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17964    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17965    let worktree_id = workspace
17966        .update(cx, |workspace, _window, cx| {
17967            workspace.project().update(cx, |project, cx| {
17968                project.worktrees(cx).next().unwrap().read(cx).id()
17969            })
17970        })
17971        .unwrap();
17972
17973    let buffer = project
17974        .update(cx, |project, cx| {
17975            project.open_buffer((worktree_id, "main.rs"), cx)
17976        })
17977        .await
17978        .unwrap();
17979
17980    let (editor, cx) = cx.add_window_view(|window, cx| {
17981        Editor::new(
17982            EditorMode::Full,
17983            MultiBuffer::build_from_buffer(buffer, cx),
17984            Some(project.clone()),
17985            window,
17986            cx,
17987        )
17988    });
17989
17990    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17991    let abs_path = project.read_with(cx, |project, cx| {
17992        project
17993            .absolute_path(&project_path, cx)
17994            .map(|path_buf| Arc::from(path_buf.to_owned()))
17995            .unwrap()
17996    });
17997
17998    // assert we can add breakpoint on the first line
17999    editor.update_in(cx, |editor, window, cx| {
18000        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18001        editor.move_to_end(&MoveToEnd, window, cx);
18002        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18003        editor.move_up(&MoveUp, window, cx);
18004        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18005    });
18006
18007    let breakpoints = editor.update(cx, |editor, cx| {
18008        editor
18009            .breakpoint_store()
18010            .as_ref()
18011            .unwrap()
18012            .read(cx)
18013            .all_breakpoints(cx)
18014            .clone()
18015    });
18016
18017    assert_eq!(1, breakpoints.len());
18018    assert_breakpoint(
18019        &breakpoints,
18020        &abs_path,
18021        vec![
18022            (0, Breakpoint::new_standard()),
18023            (2, Breakpoint::new_standard()),
18024            (3, Breakpoint::new_standard()),
18025        ],
18026    );
18027
18028    editor.update_in(cx, |editor, window, cx| {
18029        editor.move_to_beginning(&MoveToBeginning, window, cx);
18030        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18031        editor.move_to_end(&MoveToEnd, window, cx);
18032        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18033        // Disabling a breakpoint that doesn't exist should do nothing
18034        editor.move_up(&MoveUp, window, cx);
18035        editor.move_up(&MoveUp, window, cx);
18036        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18037    });
18038
18039    let breakpoints = editor.update(cx, |editor, cx| {
18040        editor
18041            .breakpoint_store()
18042            .as_ref()
18043            .unwrap()
18044            .read(cx)
18045            .all_breakpoints(cx)
18046            .clone()
18047    });
18048
18049    let disable_breakpoint = {
18050        let mut bp = Breakpoint::new_standard();
18051        bp.state = BreakpointState::Disabled;
18052        bp
18053    };
18054
18055    assert_eq!(1, breakpoints.len());
18056    assert_breakpoint(
18057        &breakpoints,
18058        &abs_path,
18059        vec![
18060            (0, disable_breakpoint.clone()),
18061            (2, Breakpoint::new_standard()),
18062            (3, disable_breakpoint.clone()),
18063        ],
18064    );
18065
18066    editor.update_in(cx, |editor, window, cx| {
18067        editor.move_to_beginning(&MoveToBeginning, window, cx);
18068        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18069        editor.move_to_end(&MoveToEnd, window, cx);
18070        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18071        editor.move_up(&MoveUp, window, cx);
18072        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18073    });
18074
18075    let breakpoints = editor.update(cx, |editor, cx| {
18076        editor
18077            .breakpoint_store()
18078            .as_ref()
18079            .unwrap()
18080            .read(cx)
18081            .all_breakpoints(cx)
18082            .clone()
18083    });
18084
18085    assert_eq!(1, breakpoints.len());
18086    assert_breakpoint(
18087        &breakpoints,
18088        &abs_path,
18089        vec![
18090            (0, Breakpoint::new_standard()),
18091            (2, disable_breakpoint),
18092            (3, Breakpoint::new_standard()),
18093        ],
18094    );
18095}
18096
18097#[gpui::test]
18098async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18099    init_test(cx, |_| {});
18100    let capabilities = lsp::ServerCapabilities {
18101        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18102            prepare_provider: Some(true),
18103            work_done_progress_options: Default::default(),
18104        })),
18105        ..Default::default()
18106    };
18107    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18108
18109    cx.set_state(indoc! {"
18110        struct Fˇoo {}
18111    "});
18112
18113    cx.update_editor(|editor, _, cx| {
18114        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18115        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18116        editor.highlight_background::<DocumentHighlightRead>(
18117            &[highlight_range],
18118            |c| c.editor_document_highlight_read_background,
18119            cx,
18120        );
18121    });
18122
18123    let mut prepare_rename_handler = cx
18124        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18125            move |_, _, _| async move {
18126                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18127                    start: lsp::Position {
18128                        line: 0,
18129                        character: 7,
18130                    },
18131                    end: lsp::Position {
18132                        line: 0,
18133                        character: 10,
18134                    },
18135                })))
18136            },
18137        );
18138    let prepare_rename_task = cx
18139        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18140        .expect("Prepare rename was not started");
18141    prepare_rename_handler.next().await.unwrap();
18142    prepare_rename_task.await.expect("Prepare rename failed");
18143
18144    let mut rename_handler =
18145        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18146            let edit = lsp::TextEdit {
18147                range: lsp::Range {
18148                    start: lsp::Position {
18149                        line: 0,
18150                        character: 7,
18151                    },
18152                    end: lsp::Position {
18153                        line: 0,
18154                        character: 10,
18155                    },
18156                },
18157                new_text: "FooRenamed".to_string(),
18158            };
18159            Ok(Some(lsp::WorkspaceEdit::new(
18160                // Specify the same edit twice
18161                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18162            )))
18163        });
18164    let rename_task = cx
18165        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18166        .expect("Confirm rename was not started");
18167    rename_handler.next().await.unwrap();
18168    rename_task.await.expect("Confirm rename failed");
18169    cx.run_until_parked();
18170
18171    // Despite two edits, only one is actually applied as those are identical
18172    cx.assert_editor_state(indoc! {"
18173        struct FooRenamedˇ {}
18174    "});
18175}
18176
18177#[gpui::test]
18178async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18179    init_test(cx, |_| {});
18180    // These capabilities indicate that the server does not support prepare rename.
18181    let capabilities = lsp::ServerCapabilities {
18182        rename_provider: Some(lsp::OneOf::Left(true)),
18183        ..Default::default()
18184    };
18185    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18186
18187    cx.set_state(indoc! {"
18188        struct Fˇoo {}
18189    "});
18190
18191    cx.update_editor(|editor, _window, cx| {
18192        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18193        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18194        editor.highlight_background::<DocumentHighlightRead>(
18195            &[highlight_range],
18196            |c| c.editor_document_highlight_read_background,
18197            cx,
18198        );
18199    });
18200
18201    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18202        .expect("Prepare rename was not started")
18203        .await
18204        .expect("Prepare rename failed");
18205
18206    let mut rename_handler =
18207        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18208            let edit = lsp::TextEdit {
18209                range: lsp::Range {
18210                    start: lsp::Position {
18211                        line: 0,
18212                        character: 7,
18213                    },
18214                    end: lsp::Position {
18215                        line: 0,
18216                        character: 10,
18217                    },
18218                },
18219                new_text: "FooRenamed".to_string(),
18220            };
18221            Ok(Some(lsp::WorkspaceEdit::new(
18222                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18223            )))
18224        });
18225    let rename_task = cx
18226        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18227        .expect("Confirm rename was not started");
18228    rename_handler.next().await.unwrap();
18229    rename_task.await.expect("Confirm rename failed");
18230    cx.run_until_parked();
18231
18232    // Correct range is renamed, as `surrounding_word` is used to find it.
18233    cx.assert_editor_state(indoc! {"
18234        struct FooRenamedˇ {}
18235    "});
18236}
18237
18238#[gpui::test]
18239async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18240    init_test(cx, |_| {});
18241    let mut cx = EditorTestContext::new(cx).await;
18242
18243    let language = Arc::new(
18244        Language::new(
18245            LanguageConfig::default(),
18246            Some(tree_sitter_html::LANGUAGE.into()),
18247        )
18248        .with_brackets_query(
18249            r#"
18250            ("<" @open "/>" @close)
18251            ("</" @open ">" @close)
18252            ("<" @open ">" @close)
18253            ("\"" @open "\"" @close)
18254            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18255        "#,
18256        )
18257        .unwrap(),
18258    );
18259    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18260
18261    cx.set_state(indoc! {"
18262        <span>ˇ</span>
18263    "});
18264    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18265    cx.assert_editor_state(indoc! {"
18266        <span>
18267        ˇ
18268        </span>
18269    "});
18270
18271    cx.set_state(indoc! {"
18272        <span><span></span>ˇ</span>
18273    "});
18274    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18275    cx.assert_editor_state(indoc! {"
18276        <span><span></span>
18277        ˇ</span>
18278    "});
18279
18280    cx.set_state(indoc! {"
18281        <span>ˇ
18282        </span>
18283    "});
18284    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18285    cx.assert_editor_state(indoc! {"
18286        <span>
18287        ˇ
18288        </span>
18289    "});
18290}
18291
18292#[gpui::test(iterations = 10)]
18293async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18294    init_test(cx, |_| {});
18295
18296    let fs = FakeFs::new(cx.executor());
18297    fs.insert_tree(
18298        path!("/dir"),
18299        json!({
18300            "a.ts": "a",
18301        }),
18302    )
18303    .await;
18304
18305    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18306    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18307    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18308
18309    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18310    language_registry.add(Arc::new(Language::new(
18311        LanguageConfig {
18312            name: "TypeScript".into(),
18313            matcher: LanguageMatcher {
18314                path_suffixes: vec!["ts".to_string()],
18315                ..Default::default()
18316            },
18317            ..Default::default()
18318        },
18319        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18320    )));
18321    let mut fake_language_servers = language_registry.register_fake_lsp(
18322        "TypeScript",
18323        FakeLspAdapter {
18324            capabilities: lsp::ServerCapabilities {
18325                code_lens_provider: Some(lsp::CodeLensOptions {
18326                    resolve_provider: Some(true),
18327                }),
18328                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18329                    commands: vec!["_the/command".to_string()],
18330                    ..lsp::ExecuteCommandOptions::default()
18331                }),
18332                ..lsp::ServerCapabilities::default()
18333            },
18334            ..FakeLspAdapter::default()
18335        },
18336    );
18337
18338    let (buffer, _handle) = project
18339        .update(cx, |p, cx| {
18340            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18341        })
18342        .await
18343        .unwrap();
18344    cx.executor().run_until_parked();
18345
18346    let fake_server = fake_language_servers.next().await.unwrap();
18347
18348    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18349    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18350    drop(buffer_snapshot);
18351    let actions = cx
18352        .update_window(*workspace, |_, window, cx| {
18353            project.code_actions(&buffer, anchor..anchor, window, cx)
18354        })
18355        .unwrap();
18356
18357    fake_server
18358        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18359            Ok(Some(vec![
18360                lsp::CodeLens {
18361                    range: lsp::Range::default(),
18362                    command: Some(lsp::Command {
18363                        title: "Code lens command".to_owned(),
18364                        command: "_the/command".to_owned(),
18365                        arguments: None,
18366                    }),
18367                    data: None,
18368                },
18369                lsp::CodeLens {
18370                    range: lsp::Range::default(),
18371                    command: Some(lsp::Command {
18372                        title: "Command not in capabilities".to_owned(),
18373                        command: "not in capabilities".to_owned(),
18374                        arguments: None,
18375                    }),
18376                    data: None,
18377                },
18378                lsp::CodeLens {
18379                    range: lsp::Range {
18380                        start: lsp::Position {
18381                            line: 1,
18382                            character: 1,
18383                        },
18384                        end: lsp::Position {
18385                            line: 1,
18386                            character: 1,
18387                        },
18388                    },
18389                    command: Some(lsp::Command {
18390                        title: "Command not in range".to_owned(),
18391                        command: "_the/command".to_owned(),
18392                        arguments: None,
18393                    }),
18394                    data: None,
18395                },
18396            ]))
18397        })
18398        .next()
18399        .await;
18400
18401    let actions = actions.await.unwrap();
18402    assert_eq!(
18403        actions.len(),
18404        1,
18405        "Should have only one valid action for the 0..0 range"
18406    );
18407    let action = actions[0].clone();
18408    let apply = project.update(cx, |project, cx| {
18409        project.apply_code_action(buffer.clone(), action, true, cx)
18410    });
18411
18412    // Resolving the code action does not populate its edits. In absence of
18413    // edits, we must execute the given command.
18414    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18415        |mut lens, _| async move {
18416            let lens_command = lens.command.as_mut().expect("should have a command");
18417            assert_eq!(lens_command.title, "Code lens command");
18418            lens_command.arguments = Some(vec![json!("the-argument")]);
18419            Ok(lens)
18420        },
18421    );
18422
18423    // While executing the command, the language server sends the editor
18424    // a `workspaceEdit` request.
18425    fake_server
18426        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18427            let fake = fake_server.clone();
18428            move |params, _| {
18429                assert_eq!(params.command, "_the/command");
18430                let fake = fake.clone();
18431                async move {
18432                    fake.server
18433                        .request::<lsp::request::ApplyWorkspaceEdit>(
18434                            lsp::ApplyWorkspaceEditParams {
18435                                label: None,
18436                                edit: lsp::WorkspaceEdit {
18437                                    changes: Some(
18438                                        [(
18439                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18440                                            vec![lsp::TextEdit {
18441                                                range: lsp::Range::new(
18442                                                    lsp::Position::new(0, 0),
18443                                                    lsp::Position::new(0, 0),
18444                                                ),
18445                                                new_text: "X".into(),
18446                                            }],
18447                                        )]
18448                                        .into_iter()
18449                                        .collect(),
18450                                    ),
18451                                    ..Default::default()
18452                                },
18453                            },
18454                        )
18455                        .await
18456                        .unwrap();
18457                    Ok(Some(json!(null)))
18458                }
18459            }
18460        })
18461        .next()
18462        .await;
18463
18464    // Applying the code lens command returns a project transaction containing the edits
18465    // sent by the language server in its `workspaceEdit` request.
18466    let transaction = apply.await.unwrap();
18467    assert!(transaction.0.contains_key(&buffer));
18468    buffer.update(cx, |buffer, cx| {
18469        assert_eq!(buffer.text(), "Xa");
18470        buffer.undo(cx);
18471        assert_eq!(buffer.text(), "a");
18472    });
18473}
18474
18475#[gpui::test]
18476async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18477    init_test(cx, |_| {});
18478
18479    let fs = FakeFs::new(cx.executor());
18480    let main_text = r#"fn main() {
18481println!("1");
18482println!("2");
18483println!("3");
18484println!("4");
18485println!("5");
18486}"#;
18487    let lib_text = "mod foo {}";
18488    fs.insert_tree(
18489        path!("/a"),
18490        json!({
18491            "lib.rs": lib_text,
18492            "main.rs": main_text,
18493        }),
18494    )
18495    .await;
18496
18497    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18498    let (workspace, cx) =
18499        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18500    let worktree_id = workspace.update(cx, |workspace, cx| {
18501        workspace.project().update(cx, |project, cx| {
18502            project.worktrees(cx).next().unwrap().read(cx).id()
18503        })
18504    });
18505
18506    let expected_ranges = vec![
18507        Point::new(0, 0)..Point::new(0, 0),
18508        Point::new(1, 0)..Point::new(1, 1),
18509        Point::new(2, 0)..Point::new(2, 2),
18510        Point::new(3, 0)..Point::new(3, 3),
18511    ];
18512
18513    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18514    let editor_1 = workspace
18515        .update_in(cx, |workspace, window, cx| {
18516            workspace.open_path(
18517                (worktree_id, "main.rs"),
18518                Some(pane_1.downgrade()),
18519                true,
18520                window,
18521                cx,
18522            )
18523        })
18524        .unwrap()
18525        .await
18526        .downcast::<Editor>()
18527        .unwrap();
18528    pane_1.update(cx, |pane, cx| {
18529        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18530        open_editor.update(cx, |editor, cx| {
18531            assert_eq!(
18532                editor.display_text(cx),
18533                main_text,
18534                "Original main.rs text on initial open",
18535            );
18536            assert_eq!(
18537                editor
18538                    .selections
18539                    .all::<Point>(cx)
18540                    .into_iter()
18541                    .map(|s| s.range())
18542                    .collect::<Vec<_>>(),
18543                vec![Point::zero()..Point::zero()],
18544                "Default selections on initial open",
18545            );
18546        })
18547    });
18548    editor_1.update_in(cx, |editor, window, cx| {
18549        editor.change_selections(None, window, cx, |s| {
18550            s.select_ranges(expected_ranges.clone());
18551        });
18552    });
18553
18554    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18555        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18556    });
18557    let editor_2 = workspace
18558        .update_in(cx, |workspace, window, cx| {
18559            workspace.open_path(
18560                (worktree_id, "main.rs"),
18561                Some(pane_2.downgrade()),
18562                true,
18563                window,
18564                cx,
18565            )
18566        })
18567        .unwrap()
18568        .await
18569        .downcast::<Editor>()
18570        .unwrap();
18571    pane_2.update(cx, |pane, cx| {
18572        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18573        open_editor.update(cx, |editor, cx| {
18574            assert_eq!(
18575                editor.display_text(cx),
18576                main_text,
18577                "Original main.rs text on initial open in another panel",
18578            );
18579            assert_eq!(
18580                editor
18581                    .selections
18582                    .all::<Point>(cx)
18583                    .into_iter()
18584                    .map(|s| s.range())
18585                    .collect::<Vec<_>>(),
18586                vec![Point::zero()..Point::zero()],
18587                "Default selections on initial open in another panel",
18588            );
18589        })
18590    });
18591
18592    editor_2.update_in(cx, |editor, window, cx| {
18593        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18594    });
18595
18596    let _other_editor_1 = workspace
18597        .update_in(cx, |workspace, window, cx| {
18598            workspace.open_path(
18599                (worktree_id, "lib.rs"),
18600                Some(pane_1.downgrade()),
18601                true,
18602                window,
18603                cx,
18604            )
18605        })
18606        .unwrap()
18607        .await
18608        .downcast::<Editor>()
18609        .unwrap();
18610    pane_1
18611        .update_in(cx, |pane, window, cx| {
18612            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18613                .unwrap()
18614        })
18615        .await
18616        .unwrap();
18617    drop(editor_1);
18618    pane_1.update(cx, |pane, cx| {
18619        pane.active_item()
18620            .unwrap()
18621            .downcast::<Editor>()
18622            .unwrap()
18623            .update(cx, |editor, cx| {
18624                assert_eq!(
18625                    editor.display_text(cx),
18626                    lib_text,
18627                    "Other file should be open and active",
18628                );
18629            });
18630        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18631    });
18632
18633    let _other_editor_2 = workspace
18634        .update_in(cx, |workspace, window, cx| {
18635            workspace.open_path(
18636                (worktree_id, "lib.rs"),
18637                Some(pane_2.downgrade()),
18638                true,
18639                window,
18640                cx,
18641            )
18642        })
18643        .unwrap()
18644        .await
18645        .downcast::<Editor>()
18646        .unwrap();
18647    pane_2
18648        .update_in(cx, |pane, window, cx| {
18649            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18650                .unwrap()
18651        })
18652        .await
18653        .unwrap();
18654    drop(editor_2);
18655    pane_2.update(cx, |pane, cx| {
18656        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18657        open_editor.update(cx, |editor, cx| {
18658            assert_eq!(
18659                editor.display_text(cx),
18660                lib_text,
18661                "Other file should be open and active in another panel too",
18662            );
18663        });
18664        assert_eq!(
18665            pane.items().count(),
18666            1,
18667            "No other editors should be open in another pane",
18668        );
18669    });
18670
18671    let _editor_1_reopened = workspace
18672        .update_in(cx, |workspace, window, cx| {
18673            workspace.open_path(
18674                (worktree_id, "main.rs"),
18675                Some(pane_1.downgrade()),
18676                true,
18677                window,
18678                cx,
18679            )
18680        })
18681        .unwrap()
18682        .await
18683        .downcast::<Editor>()
18684        .unwrap();
18685    let _editor_2_reopened = workspace
18686        .update_in(cx, |workspace, window, cx| {
18687            workspace.open_path(
18688                (worktree_id, "main.rs"),
18689                Some(pane_2.downgrade()),
18690                true,
18691                window,
18692                cx,
18693            )
18694        })
18695        .unwrap()
18696        .await
18697        .downcast::<Editor>()
18698        .unwrap();
18699    pane_1.update(cx, |pane, cx| {
18700        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18701        open_editor.update(cx, |editor, cx| {
18702            assert_eq!(
18703                editor.display_text(cx),
18704                main_text,
18705                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18706            );
18707            assert_eq!(
18708                editor
18709                    .selections
18710                    .all::<Point>(cx)
18711                    .into_iter()
18712                    .map(|s| s.range())
18713                    .collect::<Vec<_>>(),
18714                expected_ranges,
18715                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18716            );
18717        })
18718    });
18719    pane_2.update(cx, |pane, cx| {
18720        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18721        open_editor.update(cx, |editor, cx| {
18722            assert_eq!(
18723                editor.display_text(cx),
18724                r#"fn main() {
18725⋯rintln!("1");
18726⋯intln!("2");
18727⋯ntln!("3");
18728println!("4");
18729println!("5");
18730}"#,
18731                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18732            );
18733            assert_eq!(
18734                editor
18735                    .selections
18736                    .all::<Point>(cx)
18737                    .into_iter()
18738                    .map(|s| s.range())
18739                    .collect::<Vec<_>>(),
18740                vec![Point::zero()..Point::zero()],
18741                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18742            );
18743        })
18744    });
18745}
18746
18747#[gpui::test]
18748async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18749    init_test(cx, |_| {});
18750
18751    let fs = FakeFs::new(cx.executor());
18752    let main_text = r#"fn main() {
18753println!("1");
18754println!("2");
18755println!("3");
18756println!("4");
18757println!("5");
18758}"#;
18759    let lib_text = "mod foo {}";
18760    fs.insert_tree(
18761        path!("/a"),
18762        json!({
18763            "lib.rs": lib_text,
18764            "main.rs": main_text,
18765        }),
18766    )
18767    .await;
18768
18769    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18770    let (workspace, cx) =
18771        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18772    let worktree_id = workspace.update(cx, |workspace, cx| {
18773        workspace.project().update(cx, |project, cx| {
18774            project.worktrees(cx).next().unwrap().read(cx).id()
18775        })
18776    });
18777
18778    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18779    let editor = workspace
18780        .update_in(cx, |workspace, window, cx| {
18781            workspace.open_path(
18782                (worktree_id, "main.rs"),
18783                Some(pane.downgrade()),
18784                true,
18785                window,
18786                cx,
18787            )
18788        })
18789        .unwrap()
18790        .await
18791        .downcast::<Editor>()
18792        .unwrap();
18793    pane.update(cx, |pane, cx| {
18794        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18795        open_editor.update(cx, |editor, cx| {
18796            assert_eq!(
18797                editor.display_text(cx),
18798                main_text,
18799                "Original main.rs text on initial open",
18800            );
18801        })
18802    });
18803    editor.update_in(cx, |editor, window, cx| {
18804        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18805    });
18806
18807    cx.update_global(|store: &mut SettingsStore, cx| {
18808        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18809            s.restore_on_file_reopen = Some(false);
18810        });
18811    });
18812    editor.update_in(cx, |editor, window, cx| {
18813        editor.fold_ranges(
18814            vec![
18815                Point::new(1, 0)..Point::new(1, 1),
18816                Point::new(2, 0)..Point::new(2, 2),
18817                Point::new(3, 0)..Point::new(3, 3),
18818            ],
18819            false,
18820            window,
18821            cx,
18822        );
18823    });
18824    pane.update_in(cx, |pane, window, cx| {
18825        pane.close_all_items(&CloseAllItems::default(), window, cx)
18826            .unwrap()
18827    })
18828    .await
18829    .unwrap();
18830    pane.update(cx, |pane, _| {
18831        assert!(pane.active_item().is_none());
18832    });
18833    cx.update_global(|store: &mut SettingsStore, cx| {
18834        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18835            s.restore_on_file_reopen = Some(true);
18836        });
18837    });
18838
18839    let _editor_reopened = workspace
18840        .update_in(cx, |workspace, window, cx| {
18841            workspace.open_path(
18842                (worktree_id, "main.rs"),
18843                Some(pane.downgrade()),
18844                true,
18845                window,
18846                cx,
18847            )
18848        })
18849        .unwrap()
18850        .await
18851        .downcast::<Editor>()
18852        .unwrap();
18853    pane.update(cx, |pane, cx| {
18854        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18855        open_editor.update(cx, |editor, cx| {
18856            assert_eq!(
18857                editor.display_text(cx),
18858                main_text,
18859                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18860            );
18861        })
18862    });
18863}
18864
18865fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18866    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18867    point..point
18868}
18869
18870fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18871    let (text, ranges) = marked_text_ranges(marked_text, true);
18872    assert_eq!(editor.text(cx), text);
18873    assert_eq!(
18874        editor.selections.ranges(cx),
18875        ranges,
18876        "Assert selections are {}",
18877        marked_text
18878    );
18879}
18880
18881pub fn handle_signature_help_request(
18882    cx: &mut EditorLspTestContext,
18883    mocked_response: lsp::SignatureHelp,
18884) -> impl Future<Output = ()> + use<> {
18885    let mut request =
18886        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18887            let mocked_response = mocked_response.clone();
18888            async move { Ok(Some(mocked_response)) }
18889        });
18890
18891    async move {
18892        request.next().await;
18893    }
18894}
18895
18896/// Handle completion request passing a marked string specifying where the completion
18897/// should be triggered from using '|' character, what range should be replaced, and what completions
18898/// should be returned using '<' and '>' to delimit the range.
18899///
18900/// Also see `handle_completion_request_with_insert_and_replace`.
18901#[track_caller]
18902pub fn handle_completion_request(
18903    cx: &mut EditorLspTestContext,
18904    marked_string: &str,
18905    completions: Vec<&'static str>,
18906    counter: Arc<AtomicUsize>,
18907) -> impl Future<Output = ()> {
18908    let complete_from_marker: TextRangeMarker = '|'.into();
18909    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18910    let (_, mut marked_ranges) = marked_text_ranges_by(
18911        marked_string,
18912        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18913    );
18914
18915    let complete_from_position =
18916        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18917    let replace_range =
18918        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18919
18920    let mut request =
18921        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18922            let completions = completions.clone();
18923            counter.fetch_add(1, atomic::Ordering::Release);
18924            async move {
18925                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18926                assert_eq!(
18927                    params.text_document_position.position,
18928                    complete_from_position
18929                );
18930                Ok(Some(lsp::CompletionResponse::Array(
18931                    completions
18932                        .iter()
18933                        .map(|completion_text| lsp::CompletionItem {
18934                            label: completion_text.to_string(),
18935                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18936                                range: replace_range,
18937                                new_text: completion_text.to_string(),
18938                            })),
18939                            ..Default::default()
18940                        })
18941                        .collect(),
18942                )))
18943            }
18944        });
18945
18946    async move {
18947        request.next().await;
18948    }
18949}
18950
18951/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18952/// given instead, which also contains an `insert` range.
18953///
18954/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18955/// that is, `replace_range.start..cursor_pos`.
18956pub fn handle_completion_request_with_insert_and_replace(
18957    cx: &mut EditorLspTestContext,
18958    marked_string: &str,
18959    completions: Vec<&'static str>,
18960    counter: Arc<AtomicUsize>,
18961) -> impl Future<Output = ()> {
18962    let complete_from_marker: TextRangeMarker = '|'.into();
18963    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18964    let (_, mut marked_ranges) = marked_text_ranges_by(
18965        marked_string,
18966        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18967    );
18968
18969    let complete_from_position =
18970        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18971    let replace_range =
18972        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18973
18974    let mut request =
18975        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18976            let completions = completions.clone();
18977            counter.fetch_add(1, atomic::Ordering::Release);
18978            async move {
18979                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18980                assert_eq!(
18981                    params.text_document_position.position, complete_from_position,
18982                    "marker `|` position doesn't match",
18983                );
18984                Ok(Some(lsp::CompletionResponse::Array(
18985                    completions
18986                        .iter()
18987                        .map(|completion_text| lsp::CompletionItem {
18988                            label: completion_text.to_string(),
18989                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18990                                lsp::InsertReplaceEdit {
18991                                    insert: lsp::Range {
18992                                        start: replace_range.start,
18993                                        end: complete_from_position,
18994                                    },
18995                                    replace: replace_range,
18996                                    new_text: completion_text.to_string(),
18997                                },
18998                            )),
18999                            ..Default::default()
19000                        })
19001                        .collect(),
19002                )))
19003            }
19004        });
19005
19006    async move {
19007        request.next().await;
19008    }
19009}
19010
19011fn handle_resolve_completion_request(
19012    cx: &mut EditorLspTestContext,
19013    edits: Option<Vec<(&'static str, &'static str)>>,
19014) -> impl Future<Output = ()> {
19015    let edits = edits.map(|edits| {
19016        edits
19017            .iter()
19018            .map(|(marked_string, new_text)| {
19019                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19020                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19021                lsp::TextEdit::new(replace_range, new_text.to_string())
19022            })
19023            .collect::<Vec<_>>()
19024    });
19025
19026    let mut request =
19027        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19028            let edits = edits.clone();
19029            async move {
19030                Ok(lsp::CompletionItem {
19031                    additional_text_edits: edits,
19032                    ..Default::default()
19033                })
19034            }
19035        });
19036
19037    async move {
19038        request.next().await;
19039    }
19040}
19041
19042pub(crate) fn update_test_language_settings(
19043    cx: &mut TestAppContext,
19044    f: impl Fn(&mut AllLanguageSettingsContent),
19045) {
19046    cx.update(|cx| {
19047        SettingsStore::update_global(cx, |store, cx| {
19048            store.update_user_settings::<AllLanguageSettings>(cx, f);
19049        });
19050    });
19051}
19052
19053pub(crate) fn update_test_project_settings(
19054    cx: &mut TestAppContext,
19055    f: impl Fn(&mut ProjectSettings),
19056) {
19057    cx.update(|cx| {
19058        SettingsStore::update_global(cx, |store, cx| {
19059            store.update_user_settings::<ProjectSettings>(cx, f);
19060        });
19061    });
19062}
19063
19064pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19065    cx.update(|cx| {
19066        assets::Assets.load_test_fonts(cx);
19067        let store = SettingsStore::test(cx);
19068        cx.set_global(store);
19069        theme::init(theme::LoadThemes::JustBase, cx);
19070        release_channel::init(SemanticVersion::default(), cx);
19071        client::init_settings(cx);
19072        language::init(cx);
19073        Project::init_settings(cx);
19074        workspace::init_settings(cx);
19075        crate::init(cx);
19076    });
19077
19078    update_test_language_settings(cx, f);
19079}
19080
19081#[track_caller]
19082fn assert_hunk_revert(
19083    not_reverted_text_with_selections: &str,
19084    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19085    expected_reverted_text_with_selections: &str,
19086    base_text: &str,
19087    cx: &mut EditorLspTestContext,
19088) {
19089    cx.set_state(not_reverted_text_with_selections);
19090    cx.set_head_text(base_text);
19091    cx.executor().run_until_parked();
19092
19093    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19094        let snapshot = editor.snapshot(window, cx);
19095        let reverted_hunk_statuses = snapshot
19096            .buffer_snapshot
19097            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19098            .map(|hunk| hunk.status().kind)
19099            .collect::<Vec<_>>();
19100
19101        editor.git_restore(&Default::default(), window, cx);
19102        reverted_hunk_statuses
19103    });
19104    cx.executor().run_until_parked();
19105    cx.assert_editor_state(expected_reverted_text_with_selections);
19106    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19107}