editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   16    VisualTestContext, WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319#[gpui::test]
 1320fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1321    init_test(cx, |_| {});
 1322
 1323    let editor = cx.add_window(|window, cx| {
 1324        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1325        build_editor(buffer.clone(), window, cx)
 1326    });
 1327
 1328    assert_eq!('🟥'.len_utf8(), 4);
 1329    assert_eq!('α'.len_utf8(), 2);
 1330
 1331    _ = editor.update(cx, |editor, window, cx| {
 1332        editor.fold_creases(
 1333            vec![
 1334                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1337            ],
 1338            true,
 1339            window,
 1340            cx,
 1341        );
 1342        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1343
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧".len())]
 1353        );
 1354        editor.move_right(&MoveRight, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(0, "🟥🟧⋯".len())]
 1358        );
 1359
 1360        editor.move_down(&MoveDown, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯e".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "ab".len())]
 1374        );
 1375        editor.move_left(&MoveLeft, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(1, "a".len())]
 1379        );
 1380
 1381        editor.move_down(&MoveDown, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "α".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯".len())]
 1395        );
 1396        editor.move_right(&MoveRight, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(2, "αβ⋯ε".len())]
 1400        );
 1401
 1402        editor.move_up(&MoveUp, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(1, "ab⋯e".len())]
 1406        );
 1407        editor.move_down(&MoveDown, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβ⋯ε".len())]
 1411        );
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(1, "ab⋯e".len())]
 1416        );
 1417
 1418        editor.move_up(&MoveUp, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥🟧".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "🟥".len())]
 1427        );
 1428        editor.move_left(&MoveLeft, window, cx);
 1429        assert_eq!(
 1430            editor.selections.display_ranges(cx),
 1431            &[empty_range(0, "".len())]
 1432        );
 1433    });
 1434}
 1435
 1436#[gpui::test]
 1437fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1438    init_test(cx, |_| {});
 1439
 1440    let editor = cx.add_window(|window, cx| {
 1441        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1442        build_editor(buffer.clone(), window, cx)
 1443    });
 1444    _ = editor.update(cx, |editor, window, cx| {
 1445        editor.change_selections(None, window, cx, |s| {
 1446            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1447        });
 1448
 1449        // moving above start of document should move selection to start of document,
 1450        // but the next move down should still be at the original goal_x
 1451        editor.move_up(&MoveUp, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(0, "".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(1, "abcd".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(3, "abcd".len())]
 1473        );
 1474
 1475        editor.move_down(&MoveDown, window, cx);
 1476        assert_eq!(
 1477            editor.selections.display_ranges(cx),
 1478            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1479        );
 1480
 1481        // moving past end of document should not change goal_x
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_down(&MoveDown, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(5, "".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(3, "abcd".len())]
 1504        );
 1505
 1506        editor.move_up(&MoveUp, window, cx);
 1507        assert_eq!(
 1508            editor.selections.display_ranges(cx),
 1509            &[empty_range(2, "αβγ".len())]
 1510        );
 1511    });
 1512}
 1513
 1514#[gpui::test]
 1515fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1516    init_test(cx, |_| {});
 1517    let move_to_beg = MoveToBeginningOfLine {
 1518        stop_at_soft_wraps: true,
 1519        stop_at_indent: true,
 1520    };
 1521
 1522    let delete_to_beg = DeleteToBeginningOfLine {
 1523        stop_at_indent: false,
 1524    };
 1525
 1526    let move_to_end = MoveToEndOfLine {
 1527        stop_at_soft_wraps: true,
 1528    };
 1529
 1530    let editor = cx.add_window(|window, cx| {
 1531        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1532        build_editor(buffer, window, cx)
 1533    });
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.change_selections(None, window, cx, |s| {
 1536            s.select_display_ranges([
 1537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1538                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1539            ]);
 1540        });
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1571                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1572            ]
 1573        );
 1574    });
 1575
 1576    _ = editor.update(cx, |editor, window, cx| {
 1577        editor.move_to_end_of_line(&move_to_end, window, cx);
 1578        assert_eq!(
 1579            editor.selections.display_ranges(cx),
 1580            &[
 1581                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1582                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1583            ]
 1584        );
 1585    });
 1586
 1587    // Moving to the end of line again is a no-op.
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_to_end_of_line(&move_to_end, window, cx);
 1590        assert_eq!(
 1591            editor.selections.display_ranges(cx),
 1592            &[
 1593                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1594                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1595            ]
 1596        );
 1597    });
 1598
 1599    _ = editor.update(cx, |editor, window, cx| {
 1600        editor.move_left(&MoveLeft, window, cx);
 1601        editor.select_to_beginning_of_line(
 1602            &SelectToBeginningOfLine {
 1603                stop_at_soft_wraps: true,
 1604                stop_at_indent: true,
 1605            },
 1606            window,
 1607            cx,
 1608        );
 1609        assert_eq!(
 1610            editor.selections.display_ranges(cx),
 1611            &[
 1612                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1613                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1614            ]
 1615        );
 1616    });
 1617
 1618    _ = editor.update(cx, |editor, window, cx| {
 1619        editor.select_to_beginning_of_line(
 1620            &SelectToBeginningOfLine {
 1621                stop_at_soft_wraps: true,
 1622                stop_at_indent: true,
 1623            },
 1624            window,
 1625            cx,
 1626        );
 1627        assert_eq!(
 1628            editor.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635
 1636    _ = editor.update(cx, |editor, window, cx| {
 1637        editor.select_to_beginning_of_line(
 1638            &SelectToBeginningOfLine {
 1639                stop_at_soft_wraps: true,
 1640                stop_at_indent: true,
 1641            },
 1642            window,
 1643            cx,
 1644        );
 1645        assert_eq!(
 1646            editor.selections.display_ranges(cx),
 1647            &[
 1648                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1649                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1650            ]
 1651        );
 1652    });
 1653
 1654    _ = editor.update(cx, |editor, window, cx| {
 1655        editor.select_to_end_of_line(
 1656            &SelectToEndOfLine {
 1657                stop_at_soft_wraps: true,
 1658            },
 1659            window,
 1660            cx,
 1661        );
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "ab\n  de");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1678                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1679            ]
 1680        );
 1681    });
 1682
 1683    _ = editor.update(cx, |editor, window, cx| {
 1684        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1685        assert_eq!(editor.display_text(cx), "\n");
 1686        assert_eq!(
 1687            editor.selections.display_ranges(cx),
 1688            &[
 1689                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1691            ]
 1692        );
 1693    });
 1694}
 1695
 1696#[gpui::test]
 1697fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1698    init_test(cx, |_| {});
 1699    let move_to_beg = MoveToBeginningOfLine {
 1700        stop_at_soft_wraps: false,
 1701        stop_at_indent: false,
 1702    };
 1703
 1704    let move_to_end = MoveToEndOfLine {
 1705        stop_at_soft_wraps: false,
 1706    };
 1707
 1708    let editor = cx.add_window(|window, cx| {
 1709        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1710        build_editor(buffer, window, cx)
 1711    });
 1712
 1713    _ = editor.update(cx, |editor, window, cx| {
 1714        editor.set_wrap_width(Some(140.0.into()), cx);
 1715
 1716        // We expect the following lines after wrapping
 1717        // ```
 1718        // thequickbrownfox
 1719        // jumpedoverthelazydo
 1720        // gs
 1721        // ```
 1722        // The final `gs` was soft-wrapped onto a new line.
 1723        assert_eq!(
 1724            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1725            editor.display_text(cx),
 1726        );
 1727
 1728        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1729        // Start the cursor at the `k` on the first line
 1730        editor.change_selections(None, window, cx, |s| {
 1731            s.select_display_ranges([
 1732                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1733            ]);
 1734        });
 1735
 1736        // Moving to the beginning of the line should put us at the beginning of the line.
 1737        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1738        assert_eq!(
 1739            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1740            editor.selections.display_ranges(cx)
 1741        );
 1742
 1743        // Moving to the end of the line should put us at the end of the line.
 1744        editor.move_to_end_of_line(&move_to_end, window, cx);
 1745        assert_eq!(
 1746            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1747            editor.selections.display_ranges(cx)
 1748        );
 1749
 1750        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1751        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1752        editor.change_selections(None, window, cx, |s| {
 1753            s.select_display_ranges([
 1754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1755            ]);
 1756        });
 1757
 1758        // Moving to the beginning of the line should put us at the start of the second line of
 1759        // display text, i.e., the `j`.
 1760        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the beginning of the line again should be a no-op.
 1767        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772
 1773        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1774        // next display line.
 1775        editor.move_to_end_of_line(&move_to_end, window, cx);
 1776        assert_eq!(
 1777            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1778            editor.selections.display_ranges(cx)
 1779        );
 1780
 1781        // Moving to the end of the line again should be a no-op.
 1782        editor.move_to_end_of_line(&move_to_end, window, cx);
 1783        assert_eq!(
 1784            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1785            editor.selections.display_ranges(cx)
 1786        );
 1787    });
 1788}
 1789
 1790#[gpui::test]
 1791fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1792    init_test(cx, |_| {});
 1793
 1794    let move_to_beg = MoveToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let select_to_beg = SelectToBeginningOfLine {
 1800        stop_at_soft_wraps: true,
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let delete_to_beg = DeleteToBeginningOfLine {
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let move_to_end = MoveToEndOfLine {
 1809        stop_at_soft_wraps: false,
 1810    };
 1811
 1812    let editor = cx.add_window(|window, cx| {
 1813        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1814        build_editor(buffer, window, cx)
 1815    });
 1816
 1817    _ = editor.update(cx, |editor, window, cx| {
 1818        editor.change_selections(None, window, cx, |s| {
 1819            s.select_display_ranges([
 1820                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1821                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1822            ]);
 1823        });
 1824
 1825        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1826        // and the second cursor at the first non-whitespace character in the line.
 1827        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1828        assert_eq!(
 1829            editor.selections.display_ranges(cx),
 1830            &[
 1831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1833            ]
 1834        );
 1835
 1836        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1837        // and should move the second cursor to the beginning of the line.
 1838        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1839        assert_eq!(
 1840            editor.selections.display_ranges(cx),
 1841            &[
 1842                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1843                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1844            ]
 1845        );
 1846
 1847        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1848        // and should move the second cursor back to the first non-whitespace character in the line.
 1849        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1850        assert_eq!(
 1851            editor.selections.display_ranges(cx),
 1852            &[
 1853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1855            ]
 1856        );
 1857
 1858        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1859        // and to the first non-whitespace character in the line for the second cursor.
 1860        editor.move_to_end_of_line(&move_to_end, window, cx);
 1861        editor.move_left(&MoveLeft, window, cx);
 1862        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[
 1866                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1867                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1868            ]
 1869        );
 1870
 1871        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1872        // and should select to the beginning of the line for the second cursor.
 1873        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[
 1877                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1878                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1879            ]
 1880        );
 1881
 1882        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1883        // and should delete to the first non-whitespace character in the line for the second cursor.
 1884        editor.move_to_end_of_line(&move_to_end, window, cx);
 1885        editor.move_left(&MoveLeft, window, cx);
 1886        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1887        assert_eq!(editor.text(cx), "c\n  f");
 1888    });
 1889}
 1890
 1891#[gpui::test]
 1892fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1893    init_test(cx, |_| {});
 1894
 1895    let editor = cx.add_window(|window, cx| {
 1896        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1897        build_editor(buffer, window, cx)
 1898    });
 1899    _ = editor.update(cx, |editor, window, cx| {
 1900        editor.change_selections(None, window, cx, |s| {
 1901            s.select_display_ranges([
 1902                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1903                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1904            ])
 1905        });
 1906
 1907        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1908        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1923        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1930
 1931        editor.move_right(&MoveRight, window, cx);
 1932        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1933        assert_selection_ranges(
 1934            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1935            editor,
 1936            cx,
 1937        );
 1938
 1939        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1940        assert_selection_ranges(
 1941            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1942            editor,
 1943            cx,
 1944        );
 1945
 1946        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1947        assert_selection_ranges(
 1948            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1949            editor,
 1950            cx,
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1957    init_test(cx, |_| {});
 1958
 1959    let editor = cx.add_window(|window, cx| {
 1960        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1961        build_editor(buffer, window, cx)
 1962    });
 1963
 1964    _ = editor.update(cx, |editor, window, cx| {
 1965        editor.set_wrap_width(Some(140.0.into()), cx);
 1966        assert_eq!(
 1967            editor.display_text(cx),
 1968            "use one::{\n    two::three::\n    four::five\n};"
 1969        );
 1970
 1971        editor.change_selections(None, window, cx, |s| {
 1972            s.select_display_ranges([
 1973                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1974            ]);
 1975        });
 1976
 1977        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1978        assert_eq!(
 1979            editor.selections.display_ranges(cx),
 1980            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1981        );
 1982
 1983        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1984        assert_eq!(
 1985            editor.selections.display_ranges(cx),
 1986            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1987        );
 1988
 1989        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1990        assert_eq!(
 1991            editor.selections.display_ranges(cx),
 1992            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1993        );
 1994
 1995        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1996        assert_eq!(
 1997            editor.selections.display_ranges(cx),
 1998            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1999        );
 2000
 2001        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2002        assert_eq!(
 2003            editor.selections.display_ranges(cx),
 2004            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2005        );
 2006
 2007        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2008        assert_eq!(
 2009            editor.selections.display_ranges(cx),
 2010            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2011        );
 2012    });
 2013}
 2014
 2015#[gpui::test]
 2016async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2017    init_test(cx, |_| {});
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2028
 2029    cx.set_state(
 2030        &r#"ˇone
 2031        two
 2032
 2033        three
 2034        fourˇ
 2035        five
 2036
 2037        six"#
 2038            .unindent(),
 2039    );
 2040
 2041    cx.update_editor(|editor, window, cx| {
 2042        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2043    });
 2044    cx.assert_editor_state(
 2045        &r#"one
 2046        two
 2047        ˇ
 2048        three
 2049        four
 2050        five
 2051        ˇ
 2052        six"#
 2053            .unindent(),
 2054    );
 2055
 2056    cx.update_editor(|editor, window, cx| {
 2057        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"one
 2061        two
 2062
 2063        three
 2064        four
 2065        five
 2066        ˇ
 2067        sixˇ"#
 2068            .unindent(),
 2069    );
 2070
 2071    cx.update_editor(|editor, window, cx| {
 2072        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2073    });
 2074    cx.assert_editor_state(
 2075        &r#"one
 2076        two
 2077
 2078        three
 2079        four
 2080        five
 2081
 2082        sixˇ"#
 2083            .unindent(),
 2084    );
 2085
 2086    cx.update_editor(|editor, window, cx| {
 2087        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2088    });
 2089    cx.assert_editor_state(
 2090        &r#"one
 2091        two
 2092
 2093        three
 2094        four
 2095        five
 2096        ˇ
 2097        six"#
 2098            .unindent(),
 2099    );
 2100
 2101    cx.update_editor(|editor, window, cx| {
 2102        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2103    });
 2104    cx.assert_editor_state(
 2105        &r#"one
 2106        two
 2107        ˇ
 2108        three
 2109        four
 2110        five
 2111
 2112        six"#
 2113            .unindent(),
 2114    );
 2115
 2116    cx.update_editor(|editor, window, cx| {
 2117        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2118    });
 2119    cx.assert_editor_state(
 2120        &r#"ˇone
 2121        two
 2122
 2123        three
 2124        four
 2125        five
 2126
 2127        six"#
 2128            .unindent(),
 2129    );
 2130}
 2131
 2132#[gpui::test]
 2133async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135    let mut cx = EditorTestContext::new(cx).await;
 2136    let line_height = cx.editor(|editor, window, _| {
 2137        editor
 2138            .style()
 2139            .unwrap()
 2140            .text
 2141            .line_height_in_pixels(window.rem_size())
 2142    });
 2143    let window = cx.window;
 2144    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2145
 2146    cx.set_state(
 2147        r#"ˇone
 2148        two
 2149        three
 2150        four
 2151        five
 2152        six
 2153        seven
 2154        eight
 2155        nine
 2156        ten
 2157        "#,
 2158    );
 2159
 2160    cx.update_editor(|editor, window, cx| {
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 0.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 3.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 6.)
 2174        );
 2175        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2176        assert_eq!(
 2177            editor.snapshot(window, cx).scroll_position(),
 2178            gpui::Point::new(0., 3.)
 2179        );
 2180
 2181        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 1.)
 2185        );
 2186        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2187        assert_eq!(
 2188            editor.snapshot(window, cx).scroll_position(),
 2189            gpui::Point::new(0., 3.)
 2190        );
 2191    });
 2192}
 2193
 2194#[gpui::test]
 2195async fn test_autoscroll(cx: &mut TestAppContext) {
 2196    init_test(cx, |_| {});
 2197    let mut cx = EditorTestContext::new(cx).await;
 2198
 2199    let line_height = cx.update_editor(|editor, window, cx| {
 2200        editor.set_vertical_scroll_margin(2, cx);
 2201        editor
 2202            .style()
 2203            .unwrap()
 2204            .text
 2205            .line_height_in_pixels(window.rem_size())
 2206    });
 2207    let window = cx.window;
 2208    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2209
 2210    cx.set_state(
 2211        r#"ˇone
 2212            two
 2213            three
 2214            four
 2215            five
 2216            six
 2217            seven
 2218            eight
 2219            nine
 2220            ten
 2221        "#,
 2222    );
 2223    cx.update_editor(|editor, window, cx| {
 2224        assert_eq!(
 2225            editor.snapshot(window, cx).scroll_position(),
 2226            gpui::Point::new(0., 0.0)
 2227        );
 2228    });
 2229
 2230    // Add a cursor below the visible area. Since both cursors cannot fit
 2231    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2232    // allows the vertical scroll margin below that cursor.
 2233    cx.update_editor(|editor, window, cx| {
 2234        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2235            selections.select_ranges([
 2236                Point::new(0, 0)..Point::new(0, 0),
 2237                Point::new(6, 0)..Point::new(6, 0),
 2238            ]);
 2239        })
 2240    });
 2241    cx.update_editor(|editor, window, cx| {
 2242        assert_eq!(
 2243            editor.snapshot(window, cx).scroll_position(),
 2244            gpui::Point::new(0., 3.0)
 2245        );
 2246    });
 2247
 2248    // Move down. The editor cursor scrolls down to track the newest cursor.
 2249    cx.update_editor(|editor, window, cx| {
 2250        editor.move_down(&Default::default(), window, cx);
 2251    });
 2252    cx.update_editor(|editor, window, cx| {
 2253        assert_eq!(
 2254            editor.snapshot(window, cx).scroll_position(),
 2255            gpui::Point::new(0., 4.0)
 2256        );
 2257    });
 2258
 2259    // Add a cursor above the visible area. Since both cursors fit on screen,
 2260    // the editor scrolls to show both.
 2261    cx.update_editor(|editor, window, cx| {
 2262        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2263            selections.select_ranges([
 2264                Point::new(1, 0)..Point::new(1, 0),
 2265                Point::new(6, 0)..Point::new(6, 0),
 2266            ]);
 2267        })
 2268    });
 2269    cx.update_editor(|editor, window, cx| {
 2270        assert_eq!(
 2271            editor.snapshot(window, cx).scroll_position(),
 2272            gpui::Point::new(0., 1.0)
 2273        );
 2274    });
 2275}
 2276
 2277#[gpui::test]
 2278async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2279    init_test(cx, |_| {});
 2280    let mut cx = EditorTestContext::new(cx).await;
 2281
 2282    let line_height = cx.editor(|editor, window, _cx| {
 2283        editor
 2284            .style()
 2285            .unwrap()
 2286            .text
 2287            .line_height_in_pixels(window.rem_size())
 2288    });
 2289    let window = cx.window;
 2290    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2291    cx.set_state(
 2292        &r#"
 2293        ˇone
 2294        two
 2295        threeˇ
 2296        four
 2297        five
 2298        six
 2299        seven
 2300        eight
 2301        nine
 2302        ten
 2303        "#
 2304        .unindent(),
 2305    );
 2306
 2307    cx.update_editor(|editor, window, cx| {
 2308        editor.move_page_down(&MovePageDown::default(), window, cx)
 2309    });
 2310    cx.assert_editor_state(
 2311        &r#"
 2312        one
 2313        two
 2314        three
 2315        ˇfour
 2316        five
 2317        sixˇ
 2318        seven
 2319        eight
 2320        nine
 2321        ten
 2322        "#
 2323        .unindent(),
 2324    );
 2325
 2326    cx.update_editor(|editor, window, cx| {
 2327        editor.move_page_down(&MovePageDown::default(), window, cx)
 2328    });
 2329    cx.assert_editor_state(
 2330        &r#"
 2331        one
 2332        two
 2333        three
 2334        four
 2335        five
 2336        six
 2337        ˇseven
 2338        eight
 2339        nineˇ
 2340        ten
 2341        "#
 2342        .unindent(),
 2343    );
 2344
 2345    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2346    cx.assert_editor_state(
 2347        &r#"
 2348        one
 2349        two
 2350        three
 2351        ˇfour
 2352        five
 2353        sixˇ
 2354        seven
 2355        eight
 2356        nine
 2357        ten
 2358        "#
 2359        .unindent(),
 2360    );
 2361
 2362    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2363    cx.assert_editor_state(
 2364        &r#"
 2365        ˇone
 2366        two
 2367        threeˇ
 2368        four
 2369        five
 2370        six
 2371        seven
 2372        eight
 2373        nine
 2374        ten
 2375        "#
 2376        .unindent(),
 2377    );
 2378
 2379    // Test select collapsing
 2380    cx.update_editor(|editor, window, cx| {
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384    });
 2385    cx.assert_editor_state(
 2386        &r#"
 2387        one
 2388        two
 2389        three
 2390        four
 2391        five
 2392        six
 2393        seven
 2394        eight
 2395        nine
 2396        ˇten
 2397        ˇ"#
 2398        .unindent(),
 2399    );
 2400}
 2401
 2402#[gpui::test]
 2403async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2404    init_test(cx, |_| {});
 2405    let mut cx = EditorTestContext::new(cx).await;
 2406    cx.set_state("one «two threeˇ» four");
 2407    cx.update_editor(|editor, window, cx| {
 2408        editor.delete_to_beginning_of_line(
 2409            &DeleteToBeginningOfLine {
 2410                stop_at_indent: false,
 2411            },
 2412            window,
 2413            cx,
 2414        );
 2415        assert_eq!(editor.text(cx), " four");
 2416    });
 2417}
 2418
 2419#[gpui::test]
 2420fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2421    init_test(cx, |_| {});
 2422
 2423    let editor = cx.add_window(|window, cx| {
 2424        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2425        build_editor(buffer.clone(), window, cx)
 2426    });
 2427
 2428    _ = editor.update(cx, |editor, window, cx| {
 2429        editor.change_selections(None, window, cx, |s| {
 2430            s.select_display_ranges([
 2431                // an empty selection - the preceding word fragment is deleted
 2432                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2433                // characters selected - they are deleted
 2434                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2435            ])
 2436        });
 2437        editor.delete_to_previous_word_start(
 2438            &DeleteToPreviousWordStart {
 2439                ignore_newlines: false,
 2440            },
 2441            window,
 2442            cx,
 2443        );
 2444        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2445    });
 2446
 2447    _ = editor.update(cx, |editor, window, cx| {
 2448        editor.change_selections(None, window, cx, |s| {
 2449            s.select_display_ranges([
 2450                // an empty selection - the following word fragment is deleted
 2451                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2452                // characters selected - they are deleted
 2453                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2454            ])
 2455        });
 2456        editor.delete_to_next_word_end(
 2457            &DeleteToNextWordEnd {
 2458                ignore_newlines: false,
 2459            },
 2460            window,
 2461            cx,
 2462        );
 2463        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2464    });
 2465}
 2466
 2467#[gpui::test]
 2468fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2469    init_test(cx, |_| {});
 2470
 2471    let editor = cx.add_window(|window, cx| {
 2472        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2473        build_editor(buffer.clone(), window, cx)
 2474    });
 2475    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2476        ignore_newlines: false,
 2477    };
 2478    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2479        ignore_newlines: true,
 2480    };
 2481
 2482    _ = editor.update(cx, |editor, window, cx| {
 2483        editor.change_selections(None, window, cx, |s| {
 2484            s.select_display_ranges([
 2485                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2486            ])
 2487        });
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2500    });
 2501}
 2502
 2503#[gpui::test]
 2504fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2505    init_test(cx, |_| {});
 2506
 2507    let editor = cx.add_window(|window, cx| {
 2508        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2509        build_editor(buffer.clone(), window, cx)
 2510    });
 2511    let del_to_next_word_end = DeleteToNextWordEnd {
 2512        ignore_newlines: false,
 2513    };
 2514    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2515        ignore_newlines: true,
 2516    };
 2517
 2518    _ = editor.update(cx, |editor, window, cx| {
 2519        editor.change_selections(None, window, cx, |s| {
 2520            s.select_display_ranges([
 2521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2522            ])
 2523        });
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "one\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "\n   two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(
 2536            editor.buffer.read(cx).read(cx).text(),
 2537            "two\nthree\n   four"
 2538        );
 2539        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2541        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2545    });
 2546}
 2547
 2548#[gpui::test]
 2549fn test_newline(cx: &mut TestAppContext) {
 2550    init_test(cx, |_| {});
 2551
 2552    let editor = cx.add_window(|window, cx| {
 2553        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2554        build_editor(buffer.clone(), window, cx)
 2555    });
 2556
 2557    _ = editor.update(cx, |editor, window, cx| {
 2558        editor.change_selections(None, window, cx, |s| {
 2559            s.select_display_ranges([
 2560                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2562                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2563            ])
 2564        });
 2565
 2566        editor.newline(&Newline, window, cx);
 2567        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2568    });
 2569}
 2570
 2571#[gpui::test]
 2572fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|window, cx| {
 2576        let buffer = MultiBuffer::build_simple(
 2577            "
 2578                a
 2579                b(
 2580                    X
 2581                )
 2582                c(
 2583                    X
 2584                )
 2585            "
 2586            .unindent()
 2587            .as_str(),
 2588            cx,
 2589        );
 2590        let mut editor = build_editor(buffer.clone(), window, cx);
 2591        editor.change_selections(None, window, cx, |s| {
 2592            s.select_ranges([
 2593                Point::new(2, 4)..Point::new(2, 5),
 2594                Point::new(5, 4)..Point::new(5, 5),
 2595            ])
 2596        });
 2597        editor
 2598    });
 2599
 2600    _ = editor.update(cx, |editor, window, cx| {
 2601        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2602        editor.buffer.update(cx, |buffer, cx| {
 2603            buffer.edit(
 2604                [
 2605                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2606                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2607                ],
 2608                None,
 2609                cx,
 2610            );
 2611            assert_eq!(
 2612                buffer.read(cx).text(),
 2613                "
 2614                    a
 2615                    b()
 2616                    c()
 2617                "
 2618                .unindent()
 2619            );
 2620        });
 2621        assert_eq!(
 2622            editor.selections.ranges(cx),
 2623            &[
 2624                Point::new(1, 2)..Point::new(1, 2),
 2625                Point::new(2, 2)..Point::new(2, 2),
 2626            ],
 2627        );
 2628
 2629        editor.newline(&Newline, window, cx);
 2630        assert_eq!(
 2631            editor.text(cx),
 2632            "
 2633                a
 2634                b(
 2635                )
 2636                c(
 2637                )
 2638            "
 2639            .unindent()
 2640        );
 2641
 2642        // The selections are moved after the inserted newlines
 2643        assert_eq!(
 2644            editor.selections.ranges(cx),
 2645            &[
 2646                Point::new(2, 0)..Point::new(2, 0),
 2647                Point::new(4, 0)..Point::new(4, 0),
 2648            ],
 2649        );
 2650    });
 2651}
 2652
 2653#[gpui::test]
 2654async fn test_newline_above(cx: &mut TestAppContext) {
 2655    init_test(cx, |settings| {
 2656        settings.defaults.tab_size = NonZeroU32::new(4)
 2657    });
 2658
 2659    let language = Arc::new(
 2660        Language::new(
 2661            LanguageConfig::default(),
 2662            Some(tree_sitter_rust::LANGUAGE.into()),
 2663        )
 2664        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2665        .unwrap(),
 2666    );
 2667
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670    cx.set_state(indoc! {"
 2671        const a: ˇA = (
 2672 2673                «const_functionˇ»(ˇ),
 2674                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2675 2676        ˇ);ˇ
 2677    "});
 2678
 2679    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        ˇ
 2682        const a: A = (
 2683            ˇ
 2684            (
 2685                ˇ
 2686                ˇ
 2687                const_function(),
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                something_else,
 2693                ˇ
 2694            )
 2695            ˇ
 2696            ˇ
 2697        );
 2698    "});
 2699}
 2700
 2701#[gpui::test]
 2702async fn test_newline_below(cx: &mut TestAppContext) {
 2703    init_test(cx, |settings| {
 2704        settings.defaults.tab_size = NonZeroU32::new(4)
 2705    });
 2706
 2707    let language = Arc::new(
 2708        Language::new(
 2709            LanguageConfig::default(),
 2710            Some(tree_sitter_rust::LANGUAGE.into()),
 2711        )
 2712        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2713        .unwrap(),
 2714    );
 2715
 2716    let mut cx = EditorTestContext::new(cx).await;
 2717    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2718    cx.set_state(indoc! {"
 2719        const a: ˇA = (
 2720 2721                «const_functionˇ»(ˇ),
 2722                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2723 2724        ˇ);ˇ
 2725    "});
 2726
 2727    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2728    cx.assert_editor_state(indoc! {"
 2729        const a: A = (
 2730            ˇ
 2731            (
 2732                ˇ
 2733                const_function(),
 2734                ˇ
 2735                ˇ
 2736                something_else,
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741            )
 2742            ˇ
 2743        );
 2744        ˇ
 2745        ˇ
 2746    "});
 2747}
 2748
 2749#[gpui::test]
 2750async fn test_newline_comments(cx: &mut TestAppContext) {
 2751    init_test(cx, |settings| {
 2752        settings.defaults.tab_size = NonZeroU32::new(4)
 2753    });
 2754
 2755    let language = Arc::new(Language::new(
 2756        LanguageConfig {
 2757            line_comments: vec!["//".into()],
 2758            ..LanguageConfig::default()
 2759        },
 2760        None,
 2761    ));
 2762    {
 2763        let mut cx = EditorTestContext::new(cx).await;
 2764        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2765        cx.set_state(indoc! {"
 2766        // Fooˇ
 2767    "});
 2768
 2769        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2770        cx.assert_editor_state(indoc! {"
 2771        // Foo
 2772        //ˇ
 2773    "});
 2774        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2775        cx.set_state(indoc! {"
 2776        ˇ// Foo
 2777    "});
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(indoc! {"
 2780
 2781        ˇ// Foo
 2782    "});
 2783    }
 2784    // Ensure that comment continuations can be disabled.
 2785    update_test_language_settings(cx, |settings| {
 2786        settings.defaults.extend_comment_on_newline = Some(false);
 2787    });
 2788    let mut cx = EditorTestContext::new(cx).await;
 2789    cx.set_state(indoc! {"
 2790        // Fooˇ
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        ˇ
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2801    init_test(cx, |_| {});
 2802
 2803    let editor = cx.add_window(|window, cx| {
 2804        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2805        let mut editor = build_editor(buffer.clone(), window, cx);
 2806        editor.change_selections(None, window, cx, |s| {
 2807            s.select_ranges([3..4, 11..12, 19..20])
 2808        });
 2809        editor
 2810    });
 2811
 2812    _ = editor.update(cx, |editor, window, cx| {
 2813        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2814        editor.buffer.update(cx, |buffer, cx| {
 2815            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2816            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2817        });
 2818        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2819
 2820        editor.insert("Z", window, cx);
 2821        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2822
 2823        // The selections are moved after the inserted characters
 2824        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2825    });
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_tab(cx: &mut TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.tab_size = NonZeroU32::new(3)
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835    cx.set_state(indoc! {"
 2836        ˇabˇc
 2837        ˇ🏀ˇ🏀ˇefg
 2838 2839    "});
 2840    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2841    cx.assert_editor_state(indoc! {"
 2842           ˇab ˇc
 2843           ˇ🏀  ˇ🏀  ˇefg
 2844        d  ˇ
 2845    "});
 2846
 2847    cx.set_state(indoc! {"
 2848        a
 2849        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        a
 2854           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2855    "});
 2856}
 2857
 2858#[gpui::test]
 2859async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2860    init_test(cx, |_| {});
 2861
 2862    let mut cx = EditorTestContext::new(cx).await;
 2863    let language = Arc::new(
 2864        Language::new(
 2865            LanguageConfig::default(),
 2866            Some(tree_sitter_rust::LANGUAGE.into()),
 2867        )
 2868        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2869        .unwrap(),
 2870    );
 2871    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2872
 2873    // when all cursors are to the left of the suggested indent, then auto-indent all.
 2874    cx.set_state(indoc! {"
 2875        const a: B = (
 2876            c(
 2877        ˇ
 2878        ˇ    )
 2879        );
 2880    "});
 2881    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2882    cx.assert_editor_state(indoc! {"
 2883        const a: B = (
 2884            c(
 2885                ˇ
 2886            ˇ)
 2887        );
 2888    "});
 2889
 2890    // cursors that are already at the suggested indent level do not move
 2891    // until other cursors that are to the left of the suggested indent
 2892    // auto-indent.
 2893    cx.set_state(indoc! {"
 2894        ˇ
 2895        const a: B = (
 2896            c(
 2897                d(
 2898        ˇ
 2899                )
 2900        ˇ
 2901        ˇ    )
 2902        );
 2903    "});
 2904    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2905    cx.assert_editor_state(indoc! {"
 2906        ˇ
 2907        const a: B = (
 2908            c(
 2909                d(
 2910                    ˇ
 2911                )
 2912                ˇ
 2913            ˇ)
 2914        );
 2915    "});
 2916    // once all multi-cursors are at the suggested
 2917    // indent level, they all insert a soft tab together.
 2918    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2919    cx.assert_editor_state(indoc! {"
 2920            ˇ
 2921        const a: B = (
 2922            c(
 2923                d(
 2924                        ˇ
 2925                )
 2926                    ˇ
 2927                ˇ)
 2928        );
 2929    "});
 2930
 2931    // handle auto-indent when there are multiple cursors on the same line
 2932    cx.set_state(indoc! {"
 2933        const a: B = (
 2934            c(
 2935        ˇ    ˇ
 2936        ˇ    )
 2937        );
 2938    "});
 2939    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2940    cx.assert_editor_state(indoc! {"
 2941        const a: B = (
 2942            c(
 2943                ˇ
 2944            ˇ)
 2945        );
 2946    "});
 2947}
 2948
 2949#[gpui::test]
 2950async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2951    init_test(cx, |settings| {
 2952        settings.defaults.tab_size = NonZeroU32::new(3)
 2953    });
 2954
 2955    let mut cx = EditorTestContext::new(cx).await;
 2956    cx.set_state(indoc! {"
 2957         ˇ
 2958        \t ˇ
 2959        \t  ˇ
 2960        \t   ˇ
 2961         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2962    "});
 2963
 2964    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2965    cx.assert_editor_state(indoc! {"
 2966           ˇ
 2967        \t   ˇ
 2968        \t   ˇ
 2969        \t      ˇ
 2970         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2971    "});
 2972}
 2973
 2974#[gpui::test]
 2975async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2976    init_test(cx, |settings| {
 2977        settings.defaults.tab_size = NonZeroU32::new(4)
 2978    });
 2979
 2980    let language = Arc::new(
 2981        Language::new(
 2982            LanguageConfig::default(),
 2983            Some(tree_sitter_rust::LANGUAGE.into()),
 2984        )
 2985        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2986        .unwrap(),
 2987    );
 2988
 2989    let mut cx = EditorTestContext::new(cx).await;
 2990    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2991    cx.set_state(indoc! {"
 2992        fn a() {
 2993            if b {
 2994        \t ˇc
 2995            }
 2996        }
 2997    "});
 2998
 2999    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3000    cx.assert_editor_state(indoc! {"
 3001        fn a() {
 3002            if b {
 3003                ˇc
 3004            }
 3005        }
 3006    "});
 3007}
 3008
 3009#[gpui::test]
 3010async fn test_indent_outdent(cx: &mut TestAppContext) {
 3011    init_test(cx, |settings| {
 3012        settings.defaults.tab_size = NonZeroU32::new(4);
 3013    });
 3014
 3015    let mut cx = EditorTestContext::new(cx).await;
 3016
 3017    cx.set_state(indoc! {"
 3018          «oneˇ» «twoˇ»
 3019        three
 3020         four
 3021    "});
 3022    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3023    cx.assert_editor_state(indoc! {"
 3024            «oneˇ» «twoˇ»
 3025        three
 3026         four
 3027    "});
 3028
 3029    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3030    cx.assert_editor_state(indoc! {"
 3031        «oneˇ» «twoˇ»
 3032        three
 3033         four
 3034    "});
 3035
 3036    // select across line ending
 3037    cx.set_state(indoc! {"
 3038        one two
 3039        t«hree
 3040        ˇ» four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        one two
 3045            t«hree
 3046        ˇ» four
 3047    "});
 3048
 3049    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3050    cx.assert_editor_state(indoc! {"
 3051        one two
 3052        t«hree
 3053        ˇ» four
 3054    "});
 3055
 3056    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3057    cx.set_state(indoc! {"
 3058        one two
 3059        ˇthree
 3060            four
 3061    "});
 3062    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3063    cx.assert_editor_state(indoc! {"
 3064        one two
 3065            ˇthree
 3066            four
 3067    "});
 3068
 3069    cx.set_state(indoc! {"
 3070        one two
 3071        ˇ    three
 3072            four
 3073    "});
 3074    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3075    cx.assert_editor_state(indoc! {"
 3076        one two
 3077        ˇthree
 3078            four
 3079    "});
 3080}
 3081
 3082#[gpui::test]
 3083async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3084    init_test(cx, |settings| {
 3085        settings.defaults.hard_tabs = Some(true);
 3086    });
 3087
 3088    let mut cx = EditorTestContext::new(cx).await;
 3089
 3090    // select two ranges on one line
 3091    cx.set_state(indoc! {"
 3092        «oneˇ» «twoˇ»
 3093        three
 3094        four
 3095    "});
 3096    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3097    cx.assert_editor_state(indoc! {"
 3098        \t«oneˇ» «twoˇ»
 3099        three
 3100        four
 3101    "});
 3102    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3103    cx.assert_editor_state(indoc! {"
 3104        \t\t«oneˇ» «twoˇ»
 3105        three
 3106        four
 3107    "});
 3108    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        \t«oneˇ» «twoˇ»
 3111        three
 3112        four
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        «oneˇ» «twoˇ»
 3117        three
 3118        four
 3119    "});
 3120
 3121    // select across a line ending
 3122    cx.set_state(indoc! {"
 3123        one two
 3124        t«hree
 3125        ˇ»four
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        one two
 3130        \tt«hree
 3131        ˇ»four
 3132    "});
 3133    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3134    cx.assert_editor_state(indoc! {"
 3135        one two
 3136        \t\tt«hree
 3137        ˇ»four
 3138    "});
 3139    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3140    cx.assert_editor_state(indoc! {"
 3141        one two
 3142        \tt«hree
 3143        ˇ»four
 3144    "});
 3145    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3146    cx.assert_editor_state(indoc! {"
 3147        one two
 3148        t«hree
 3149        ˇ»four
 3150    "});
 3151
 3152    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3153    cx.set_state(indoc! {"
 3154        one two
 3155        ˇthree
 3156        four
 3157    "});
 3158    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3159    cx.assert_editor_state(indoc! {"
 3160        one two
 3161        ˇthree
 3162        four
 3163    "});
 3164    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3165    cx.assert_editor_state(indoc! {"
 3166        one two
 3167        \tˇthree
 3168        four
 3169    "});
 3170    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3171    cx.assert_editor_state(indoc! {"
 3172        one two
 3173        ˇthree
 3174        four
 3175    "});
 3176}
 3177
 3178#[gpui::test]
 3179fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3180    init_test(cx, |settings| {
 3181        settings.languages.extend([
 3182            (
 3183                "TOML".into(),
 3184                LanguageSettingsContent {
 3185                    tab_size: NonZeroU32::new(2),
 3186                    ..Default::default()
 3187                },
 3188            ),
 3189            (
 3190                "Rust".into(),
 3191                LanguageSettingsContent {
 3192                    tab_size: NonZeroU32::new(4),
 3193                    ..Default::default()
 3194                },
 3195            ),
 3196        ]);
 3197    });
 3198
 3199    let toml_language = Arc::new(Language::new(
 3200        LanguageConfig {
 3201            name: "TOML".into(),
 3202            ..Default::default()
 3203        },
 3204        None,
 3205    ));
 3206    let rust_language = Arc::new(Language::new(
 3207        LanguageConfig {
 3208            name: "Rust".into(),
 3209            ..Default::default()
 3210        },
 3211        None,
 3212    ));
 3213
 3214    let toml_buffer =
 3215        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3216    let rust_buffer =
 3217        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3218    let multibuffer = cx.new(|cx| {
 3219        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3220        multibuffer.push_excerpts(
 3221            toml_buffer.clone(),
 3222            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3223            cx,
 3224        );
 3225        multibuffer.push_excerpts(
 3226            rust_buffer.clone(),
 3227            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3228            cx,
 3229        );
 3230        multibuffer
 3231    });
 3232
 3233    cx.add_window(|window, cx| {
 3234        let mut editor = build_editor(multibuffer, window, cx);
 3235
 3236        assert_eq!(
 3237            editor.text(cx),
 3238            indoc! {"
 3239                a = 1
 3240                b = 2
 3241
 3242                const c: usize = 3;
 3243            "}
 3244        );
 3245
 3246        select_ranges(
 3247            &mut editor,
 3248            indoc! {"
 3249                «aˇ» = 1
 3250                b = 2
 3251
 3252                «const c:ˇ» usize = 3;
 3253            "},
 3254            window,
 3255            cx,
 3256        );
 3257
 3258        editor.tab(&Tab, window, cx);
 3259        assert_text_with_selections(
 3260            &mut editor,
 3261            indoc! {"
 3262                  «aˇ» = 1
 3263                b = 2
 3264
 3265                    «const c:ˇ» usize = 3;
 3266            "},
 3267            cx,
 3268        );
 3269        editor.backtab(&Backtab, window, cx);
 3270        assert_text_with_selections(
 3271            &mut editor,
 3272            indoc! {"
 3273                «aˇ» = 1
 3274                b = 2
 3275
 3276                «const c:ˇ» usize = 3;
 3277            "},
 3278            cx,
 3279        );
 3280
 3281        editor
 3282    });
 3283}
 3284
 3285#[gpui::test]
 3286async fn test_backspace(cx: &mut TestAppContext) {
 3287    init_test(cx, |_| {});
 3288
 3289    let mut cx = EditorTestContext::new(cx).await;
 3290
 3291    // Basic backspace
 3292    cx.set_state(indoc! {"
 3293        onˇe two three
 3294        fou«rˇ» five six
 3295        seven «ˇeight nine
 3296        »ten
 3297    "});
 3298    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3299    cx.assert_editor_state(indoc! {"
 3300        oˇe two three
 3301        fouˇ five six
 3302        seven ˇten
 3303    "});
 3304
 3305    // Test backspace inside and around indents
 3306    cx.set_state(indoc! {"
 3307        zero
 3308            ˇone
 3309                ˇtwo
 3310            ˇ ˇ ˇ  three
 3311        ˇ  ˇ  four
 3312    "});
 3313    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3314    cx.assert_editor_state(indoc! {"
 3315        zero
 3316        ˇone
 3317            ˇtwo
 3318        ˇ  threeˇ  four
 3319    "});
 3320}
 3321
 3322#[gpui::test]
 3323async fn test_delete(cx: &mut TestAppContext) {
 3324    init_test(cx, |_| {});
 3325
 3326    let mut cx = EditorTestContext::new(cx).await;
 3327    cx.set_state(indoc! {"
 3328        onˇe two three
 3329        fou«rˇ» five six
 3330        seven «ˇeight nine
 3331        »ten
 3332    "});
 3333    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3334    cx.assert_editor_state(indoc! {"
 3335        onˇ two three
 3336        fouˇ five six
 3337        seven ˇten
 3338    "});
 3339}
 3340
 3341#[gpui::test]
 3342fn test_delete_line(cx: &mut TestAppContext) {
 3343    init_test(cx, |_| {});
 3344
 3345    let editor = cx.add_window(|window, cx| {
 3346        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3347        build_editor(buffer, window, cx)
 3348    });
 3349    _ = editor.update(cx, |editor, window, cx| {
 3350        editor.change_selections(None, window, cx, |s| {
 3351            s.select_display_ranges([
 3352                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3353                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3354                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3355            ])
 3356        });
 3357        editor.delete_line(&DeleteLine, window, cx);
 3358        assert_eq!(editor.display_text(cx), "ghi");
 3359        assert_eq!(
 3360            editor.selections.display_ranges(cx),
 3361            vec![
 3362                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3363                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3364            ]
 3365        );
 3366    });
 3367
 3368    let editor = cx.add_window(|window, cx| {
 3369        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3370        build_editor(buffer, window, cx)
 3371    });
 3372    _ = editor.update(cx, |editor, window, cx| {
 3373        editor.change_selections(None, window, cx, |s| {
 3374            s.select_display_ranges([
 3375                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3376            ])
 3377        });
 3378        editor.delete_line(&DeleteLine, window, cx);
 3379        assert_eq!(editor.display_text(cx), "ghi\n");
 3380        assert_eq!(
 3381            editor.selections.display_ranges(cx),
 3382            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3383        );
 3384    });
 3385}
 3386
 3387#[gpui::test]
 3388fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3389    init_test(cx, |_| {});
 3390
 3391    cx.add_window(|window, cx| {
 3392        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3393        let mut editor = build_editor(buffer.clone(), window, cx);
 3394        let buffer = buffer.read(cx).as_singleton().unwrap();
 3395
 3396        assert_eq!(
 3397            editor.selections.ranges::<Point>(cx),
 3398            &[Point::new(0, 0)..Point::new(0, 0)]
 3399        );
 3400
 3401        // When on single line, replace newline at end by space
 3402        editor.join_lines(&JoinLines, window, cx);
 3403        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3404        assert_eq!(
 3405            editor.selections.ranges::<Point>(cx),
 3406            &[Point::new(0, 3)..Point::new(0, 3)]
 3407        );
 3408
 3409        // When multiple lines are selected, remove newlines that are spanned by the selection
 3410        editor.change_selections(None, window, cx, |s| {
 3411            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3412        });
 3413        editor.join_lines(&JoinLines, window, cx);
 3414        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3415        assert_eq!(
 3416            editor.selections.ranges::<Point>(cx),
 3417            &[Point::new(0, 11)..Point::new(0, 11)]
 3418        );
 3419
 3420        // Undo should be transactional
 3421        editor.undo(&Undo, window, cx);
 3422        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3423        assert_eq!(
 3424            editor.selections.ranges::<Point>(cx),
 3425            &[Point::new(0, 5)..Point::new(2, 2)]
 3426        );
 3427
 3428        // When joining an empty line don't insert a space
 3429        editor.change_selections(None, window, cx, |s| {
 3430            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3431        });
 3432        editor.join_lines(&JoinLines, window, cx);
 3433        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3434        assert_eq!(
 3435            editor.selections.ranges::<Point>(cx),
 3436            [Point::new(2, 3)..Point::new(2, 3)]
 3437        );
 3438
 3439        // We can remove trailing newlines
 3440        editor.join_lines(&JoinLines, window, cx);
 3441        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3442        assert_eq!(
 3443            editor.selections.ranges::<Point>(cx),
 3444            [Point::new(2, 3)..Point::new(2, 3)]
 3445        );
 3446
 3447        // We don't blow up on the last line
 3448        editor.join_lines(&JoinLines, window, cx);
 3449        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3450        assert_eq!(
 3451            editor.selections.ranges::<Point>(cx),
 3452            [Point::new(2, 3)..Point::new(2, 3)]
 3453        );
 3454
 3455        // reset to test indentation
 3456        editor.buffer.update(cx, |buffer, cx| {
 3457            buffer.edit(
 3458                [
 3459                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3460                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3461                ],
 3462                None,
 3463                cx,
 3464            )
 3465        });
 3466
 3467        // We remove any leading spaces
 3468        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3469        editor.change_selections(None, window, cx, |s| {
 3470            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3471        });
 3472        editor.join_lines(&JoinLines, window, cx);
 3473        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3474
 3475        // We don't insert a space for a line containing only spaces
 3476        editor.join_lines(&JoinLines, window, cx);
 3477        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3478
 3479        // We ignore any leading tabs
 3480        editor.join_lines(&JoinLines, window, cx);
 3481        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3482
 3483        editor
 3484    });
 3485}
 3486
 3487#[gpui::test]
 3488fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3489    init_test(cx, |_| {});
 3490
 3491    cx.add_window(|window, cx| {
 3492        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3493        let mut editor = build_editor(buffer.clone(), window, cx);
 3494        let buffer = buffer.read(cx).as_singleton().unwrap();
 3495
 3496        editor.change_selections(None, window, cx, |s| {
 3497            s.select_ranges([
 3498                Point::new(0, 2)..Point::new(1, 1),
 3499                Point::new(1, 2)..Point::new(1, 2),
 3500                Point::new(3, 1)..Point::new(3, 2),
 3501            ])
 3502        });
 3503
 3504        editor.join_lines(&JoinLines, window, cx);
 3505        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3506
 3507        assert_eq!(
 3508            editor.selections.ranges::<Point>(cx),
 3509            [
 3510                Point::new(0, 7)..Point::new(0, 7),
 3511                Point::new(1, 3)..Point::new(1, 3)
 3512            ]
 3513        );
 3514        editor
 3515    });
 3516}
 3517
 3518#[gpui::test]
 3519async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3520    init_test(cx, |_| {});
 3521
 3522    let mut cx = EditorTestContext::new(cx).await;
 3523
 3524    let diff_base = r#"
 3525        Line 0
 3526        Line 1
 3527        Line 2
 3528        Line 3
 3529        "#
 3530    .unindent();
 3531
 3532    cx.set_state(
 3533        &r#"
 3534        ˇLine 0
 3535        Line 1
 3536        Line 2
 3537        Line 3
 3538        "#
 3539        .unindent(),
 3540    );
 3541
 3542    cx.set_head_text(&diff_base);
 3543    executor.run_until_parked();
 3544
 3545    // Join lines
 3546    cx.update_editor(|editor, window, cx| {
 3547        editor.join_lines(&JoinLines, window, cx);
 3548    });
 3549    executor.run_until_parked();
 3550
 3551    cx.assert_editor_state(
 3552        &r#"
 3553        Line 0ˇ Line 1
 3554        Line 2
 3555        Line 3
 3556        "#
 3557        .unindent(),
 3558    );
 3559    // Join again
 3560    cx.update_editor(|editor, window, cx| {
 3561        editor.join_lines(&JoinLines, window, cx);
 3562    });
 3563    executor.run_until_parked();
 3564
 3565    cx.assert_editor_state(
 3566        &r#"
 3567        Line 0 Line 1ˇ Line 2
 3568        Line 3
 3569        "#
 3570        .unindent(),
 3571    );
 3572}
 3573
 3574#[gpui::test]
 3575async fn test_custom_newlines_cause_no_false_positive_diffs(
 3576    executor: BackgroundExecutor,
 3577    cx: &mut TestAppContext,
 3578) {
 3579    init_test(cx, |_| {});
 3580    let mut cx = EditorTestContext::new(cx).await;
 3581    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3582    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3583    executor.run_until_parked();
 3584
 3585    cx.update_editor(|editor, window, cx| {
 3586        let snapshot = editor.snapshot(window, cx);
 3587        assert_eq!(
 3588            snapshot
 3589                .buffer_snapshot
 3590                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3591                .collect::<Vec<_>>(),
 3592            Vec::new(),
 3593            "Should not have any diffs for files with custom newlines"
 3594        );
 3595    });
 3596}
 3597
 3598#[gpui::test]
 3599async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3600    init_test(cx, |_| {});
 3601
 3602    let mut cx = EditorTestContext::new(cx).await;
 3603
 3604    // Test sort_lines_case_insensitive()
 3605    cx.set_state(indoc! {"
 3606        «z
 3607        y
 3608        x
 3609        Z
 3610        Y
 3611        Xˇ»
 3612    "});
 3613    cx.update_editor(|e, window, cx| {
 3614        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3615    });
 3616    cx.assert_editor_state(indoc! {"
 3617        «x
 3618        X
 3619        y
 3620        Y
 3621        z
 3622        Zˇ»
 3623    "});
 3624
 3625    // Test reverse_lines()
 3626    cx.set_state(indoc! {"
 3627        «5
 3628        4
 3629        3
 3630        2
 3631        1ˇ»
 3632    "});
 3633    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3634    cx.assert_editor_state(indoc! {"
 3635        «1
 3636        2
 3637        3
 3638        4
 3639        5ˇ»
 3640    "});
 3641
 3642    // Skip testing shuffle_line()
 3643
 3644    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3645    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3646
 3647    // Don't manipulate when cursor is on single line, but expand the selection
 3648    cx.set_state(indoc! {"
 3649        ddˇdd
 3650        ccc
 3651        bb
 3652        a
 3653    "});
 3654    cx.update_editor(|e, window, cx| {
 3655        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3656    });
 3657    cx.assert_editor_state(indoc! {"
 3658        «ddddˇ»
 3659        ccc
 3660        bb
 3661        a
 3662    "});
 3663
 3664    // Basic manipulate case
 3665    // Start selection moves to column 0
 3666    // End of selection shrinks to fit shorter line
 3667    cx.set_state(indoc! {"
 3668        dd«d
 3669        ccc
 3670        bb
 3671        aaaaaˇ»
 3672    "});
 3673    cx.update_editor(|e, window, cx| {
 3674        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «aaaaa
 3678        bb
 3679        ccc
 3680        dddˇ»
 3681    "});
 3682
 3683    // Manipulate case with newlines
 3684    cx.set_state(indoc! {"
 3685        dd«d
 3686        ccc
 3687
 3688        bb
 3689        aaaaa
 3690
 3691        ˇ»
 3692    "});
 3693    cx.update_editor(|e, window, cx| {
 3694        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3695    });
 3696    cx.assert_editor_state(indoc! {"
 3697        «
 3698
 3699        aaaaa
 3700        bb
 3701        ccc
 3702        dddˇ»
 3703
 3704    "});
 3705
 3706    // Adding new line
 3707    cx.set_state(indoc! {"
 3708        aa«a
 3709        bbˇ»b
 3710    "});
 3711    cx.update_editor(|e, window, cx| {
 3712        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3713    });
 3714    cx.assert_editor_state(indoc! {"
 3715        «aaa
 3716        bbb
 3717        added_lineˇ»
 3718    "});
 3719
 3720    // Removing line
 3721    cx.set_state(indoc! {"
 3722        aa«a
 3723        bbbˇ»
 3724    "});
 3725    cx.update_editor(|e, window, cx| {
 3726        e.manipulate_lines(window, cx, |lines| {
 3727            lines.pop();
 3728        })
 3729    });
 3730    cx.assert_editor_state(indoc! {"
 3731        «aaaˇ»
 3732    "});
 3733
 3734    // Removing all lines
 3735    cx.set_state(indoc! {"
 3736        aa«a
 3737        bbbˇ»
 3738    "});
 3739    cx.update_editor(|e, window, cx| {
 3740        e.manipulate_lines(window, cx, |lines| {
 3741            lines.drain(..);
 3742        })
 3743    });
 3744    cx.assert_editor_state(indoc! {"
 3745        ˇ
 3746    "});
 3747}
 3748
 3749#[gpui::test]
 3750async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3751    init_test(cx, |_| {});
 3752
 3753    let mut cx = EditorTestContext::new(cx).await;
 3754
 3755    // Consider continuous selection as single selection
 3756    cx.set_state(indoc! {"
 3757        Aaa«aa
 3758        cˇ»c«c
 3759        bb
 3760        aaaˇ»aa
 3761    "});
 3762    cx.update_editor(|e, window, cx| {
 3763        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3764    });
 3765    cx.assert_editor_state(indoc! {"
 3766        «Aaaaa
 3767        ccc
 3768        bb
 3769        aaaaaˇ»
 3770    "});
 3771
 3772    cx.set_state(indoc! {"
 3773        Aaa«aa
 3774        cˇ»c«c
 3775        bb
 3776        aaaˇ»aa
 3777    "});
 3778    cx.update_editor(|e, window, cx| {
 3779        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3780    });
 3781    cx.assert_editor_state(indoc! {"
 3782        «Aaaaa
 3783        ccc
 3784        bbˇ»
 3785    "});
 3786
 3787    // Consider non continuous selection as distinct dedup operations
 3788    cx.set_state(indoc! {"
 3789        «aaaaa
 3790        bb
 3791        aaaaa
 3792        aaaaaˇ»
 3793
 3794        aaa«aaˇ»
 3795    "});
 3796    cx.update_editor(|e, window, cx| {
 3797        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3798    });
 3799    cx.assert_editor_state(indoc! {"
 3800        «aaaaa
 3801        bbˇ»
 3802
 3803        «aaaaaˇ»
 3804    "});
 3805}
 3806
 3807#[gpui::test]
 3808async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3809    init_test(cx, |_| {});
 3810
 3811    let mut cx = EditorTestContext::new(cx).await;
 3812
 3813    cx.set_state(indoc! {"
 3814        «Aaa
 3815        aAa
 3816        Aaaˇ»
 3817    "});
 3818    cx.update_editor(|e, window, cx| {
 3819        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3820    });
 3821    cx.assert_editor_state(indoc! {"
 3822        «Aaa
 3823        aAaˇ»
 3824    "});
 3825
 3826    cx.set_state(indoc! {"
 3827        «Aaa
 3828        aAa
 3829        aaAˇ»
 3830    "});
 3831    cx.update_editor(|e, window, cx| {
 3832        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3833    });
 3834    cx.assert_editor_state(indoc! {"
 3835        «Aaaˇ»
 3836    "});
 3837}
 3838
 3839#[gpui::test]
 3840async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3841    init_test(cx, |_| {});
 3842
 3843    let mut cx = EditorTestContext::new(cx).await;
 3844
 3845    // Manipulate with multiple selections on a single line
 3846    cx.set_state(indoc! {"
 3847        dd«dd
 3848        cˇ»c«c
 3849        bb
 3850        aaaˇ»aa
 3851    "});
 3852    cx.update_editor(|e, window, cx| {
 3853        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3854    });
 3855    cx.assert_editor_state(indoc! {"
 3856        «aaaaa
 3857        bb
 3858        ccc
 3859        ddddˇ»
 3860    "});
 3861
 3862    // Manipulate with multiple disjoin selections
 3863    cx.set_state(indoc! {"
 3864 3865        4
 3866        3
 3867        2
 3868        1ˇ»
 3869
 3870        dd«dd
 3871        ccc
 3872        bb
 3873        aaaˇ»aa
 3874    "});
 3875    cx.update_editor(|e, window, cx| {
 3876        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3877    });
 3878    cx.assert_editor_state(indoc! {"
 3879        «1
 3880        2
 3881        3
 3882        4
 3883        5ˇ»
 3884
 3885        «aaaaa
 3886        bb
 3887        ccc
 3888        ddddˇ»
 3889    "});
 3890
 3891    // Adding lines on each selection
 3892    cx.set_state(indoc! {"
 3893 3894        1ˇ»
 3895
 3896        bb«bb
 3897        aaaˇ»aa
 3898    "});
 3899    cx.update_editor(|e, window, cx| {
 3900        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3901    });
 3902    cx.assert_editor_state(indoc! {"
 3903        «2
 3904        1
 3905        added lineˇ»
 3906
 3907        «bbbb
 3908        aaaaa
 3909        added lineˇ»
 3910    "});
 3911
 3912    // Removing lines on each selection
 3913    cx.set_state(indoc! {"
 3914 3915        1ˇ»
 3916
 3917        bb«bb
 3918        aaaˇ»aa
 3919    "});
 3920    cx.update_editor(|e, window, cx| {
 3921        e.manipulate_lines(window, cx, |lines| {
 3922            lines.pop();
 3923        })
 3924    });
 3925    cx.assert_editor_state(indoc! {"
 3926        «2ˇ»
 3927
 3928        «bbbbˇ»
 3929    "});
 3930}
 3931
 3932#[gpui::test]
 3933async fn test_toggle_case(cx: &mut TestAppContext) {
 3934    init_test(cx, |_| {});
 3935
 3936    let mut cx = EditorTestContext::new(cx).await;
 3937
 3938    // If all lower case -> upper case
 3939    cx.set_state(indoc! {"
 3940        «hello worldˇ»
 3941    "});
 3942    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3943    cx.assert_editor_state(indoc! {"
 3944        «HELLO WORLDˇ»
 3945    "});
 3946
 3947    // If all upper case -> lower case
 3948    cx.set_state(indoc! {"
 3949        «HELLO WORLDˇ»
 3950    "});
 3951    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3952    cx.assert_editor_state(indoc! {"
 3953        «hello worldˇ»
 3954    "});
 3955
 3956    // If any upper case characters are identified -> lower case
 3957    // This matches JetBrains IDEs
 3958    cx.set_state(indoc! {"
 3959        «hEllo worldˇ»
 3960    "});
 3961    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3962    cx.assert_editor_state(indoc! {"
 3963        «hello worldˇ»
 3964    "});
 3965}
 3966
 3967#[gpui::test]
 3968async fn test_manipulate_text(cx: &mut TestAppContext) {
 3969    init_test(cx, |_| {});
 3970
 3971    let mut cx = EditorTestContext::new(cx).await;
 3972
 3973    // Test convert_to_upper_case()
 3974    cx.set_state(indoc! {"
 3975        «hello worldˇ»
 3976    "});
 3977    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3978    cx.assert_editor_state(indoc! {"
 3979        «HELLO WORLDˇ»
 3980    "});
 3981
 3982    // Test convert_to_lower_case()
 3983    cx.set_state(indoc! {"
 3984        «HELLO WORLDˇ»
 3985    "});
 3986    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3987    cx.assert_editor_state(indoc! {"
 3988        «hello worldˇ»
 3989    "});
 3990
 3991    // Test multiple line, single selection case
 3992    cx.set_state(indoc! {"
 3993        «The quick brown
 3994        fox jumps over
 3995        the lazy dogˇ»
 3996    "});
 3997    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3998    cx.assert_editor_state(indoc! {"
 3999        «The Quick Brown
 4000        Fox Jumps Over
 4001        The Lazy Dogˇ»
 4002    "});
 4003
 4004    // Test multiple line, single selection case
 4005    cx.set_state(indoc! {"
 4006        «The quick brown
 4007        fox jumps over
 4008        the lazy dogˇ»
 4009    "});
 4010    cx.update_editor(|e, window, cx| {
 4011        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 4012    });
 4013    cx.assert_editor_state(indoc! {"
 4014        «TheQuickBrown
 4015        FoxJumpsOver
 4016        TheLazyDogˇ»
 4017    "});
 4018
 4019    // From here on out, test more complex cases of manipulate_text()
 4020
 4021    // Test no selection case - should affect words cursors are in
 4022    // Cursor at beginning, middle, and end of word
 4023    cx.set_state(indoc! {"
 4024        ˇhello big beauˇtiful worldˇ
 4025    "});
 4026    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4027    cx.assert_editor_state(indoc! {"
 4028        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4029    "});
 4030
 4031    // Test multiple selections on a single line and across multiple lines
 4032    cx.set_state(indoc! {"
 4033        «Theˇ» quick «brown
 4034        foxˇ» jumps «overˇ»
 4035        the «lazyˇ» dog
 4036    "});
 4037    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4038    cx.assert_editor_state(indoc! {"
 4039        «THEˇ» quick «BROWN
 4040        FOXˇ» jumps «OVERˇ»
 4041        the «LAZYˇ» dog
 4042    "});
 4043
 4044    // Test case where text length grows
 4045    cx.set_state(indoc! {"
 4046        «tschüߡ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4049    cx.assert_editor_state(indoc! {"
 4050        «TSCHÜSSˇ»
 4051    "});
 4052
 4053    // Test to make sure we don't crash when text shrinks
 4054    cx.set_state(indoc! {"
 4055        aaa_bbbˇ
 4056    "});
 4057    cx.update_editor(|e, window, cx| {
 4058        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4059    });
 4060    cx.assert_editor_state(indoc! {"
 4061        «aaaBbbˇ»
 4062    "});
 4063
 4064    // Test to make sure we all aware of the fact that each word can grow and shrink
 4065    // Final selections should be aware of this fact
 4066    cx.set_state(indoc! {"
 4067        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4068    "});
 4069    cx.update_editor(|e, window, cx| {
 4070        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4071    });
 4072    cx.assert_editor_state(indoc! {"
 4073        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4074    "});
 4075
 4076    cx.set_state(indoc! {"
 4077        «hElLo, WoRld!ˇ»
 4078    "});
 4079    cx.update_editor(|e, window, cx| {
 4080        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4081    });
 4082    cx.assert_editor_state(indoc! {"
 4083        «HeLlO, wOrLD!ˇ»
 4084    "});
 4085}
 4086
 4087#[gpui::test]
 4088fn test_duplicate_line(cx: &mut TestAppContext) {
 4089    init_test(cx, |_| {});
 4090
 4091    let editor = cx.add_window(|window, cx| {
 4092        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4093        build_editor(buffer, window, cx)
 4094    });
 4095    _ = editor.update(cx, |editor, window, cx| {
 4096        editor.change_selections(None, window, cx, |s| {
 4097            s.select_display_ranges([
 4098                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4099                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4100                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4101                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4102            ])
 4103        });
 4104        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4105        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4106        assert_eq!(
 4107            editor.selections.display_ranges(cx),
 4108            vec![
 4109                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4110                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4111                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4112                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4113            ]
 4114        );
 4115    });
 4116
 4117    let editor = cx.add_window(|window, cx| {
 4118        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4119        build_editor(buffer, window, cx)
 4120    });
 4121    _ = editor.update(cx, |editor, window, cx| {
 4122        editor.change_selections(None, window, cx, |s| {
 4123            s.select_display_ranges([
 4124                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4125                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4126            ])
 4127        });
 4128        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4129        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4130        assert_eq!(
 4131            editor.selections.display_ranges(cx),
 4132            vec![
 4133                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4134                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4135            ]
 4136        );
 4137    });
 4138
 4139    // With `move_upwards` the selections stay in place, except for
 4140    // the lines inserted above them
 4141    let editor = cx.add_window(|window, cx| {
 4142        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4143        build_editor(buffer, window, cx)
 4144    });
 4145    _ = editor.update(cx, |editor, window, cx| {
 4146        editor.change_selections(None, window, cx, |s| {
 4147            s.select_display_ranges([
 4148                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4149                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4150                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4151                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4152            ])
 4153        });
 4154        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4155        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4156        assert_eq!(
 4157            editor.selections.display_ranges(cx),
 4158            vec![
 4159                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4160                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4161                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4162                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4163            ]
 4164        );
 4165    });
 4166
 4167    let editor = cx.add_window(|window, cx| {
 4168        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4169        build_editor(buffer, window, cx)
 4170    });
 4171    _ = editor.update(cx, |editor, window, cx| {
 4172        editor.change_selections(None, window, cx, |s| {
 4173            s.select_display_ranges([
 4174                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4175                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4176            ])
 4177        });
 4178        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4179        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4180        assert_eq!(
 4181            editor.selections.display_ranges(cx),
 4182            vec![
 4183                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4184                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4185            ]
 4186        );
 4187    });
 4188
 4189    let editor = cx.add_window(|window, cx| {
 4190        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4191        build_editor(buffer, window, cx)
 4192    });
 4193    _ = editor.update(cx, |editor, window, cx| {
 4194        editor.change_selections(None, window, cx, |s| {
 4195            s.select_display_ranges([
 4196                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4197                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4198            ])
 4199        });
 4200        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4201        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4202        assert_eq!(
 4203            editor.selections.display_ranges(cx),
 4204            vec![
 4205                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4206                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4207            ]
 4208        );
 4209    });
 4210}
 4211
 4212#[gpui::test]
 4213fn test_move_line_up_down(cx: &mut TestAppContext) {
 4214    init_test(cx, |_| {});
 4215
 4216    let editor = cx.add_window(|window, cx| {
 4217        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4218        build_editor(buffer, window, cx)
 4219    });
 4220    _ = editor.update(cx, |editor, window, cx| {
 4221        editor.fold_creases(
 4222            vec![
 4223                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4224                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4225                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4226            ],
 4227            true,
 4228            window,
 4229            cx,
 4230        );
 4231        editor.change_selections(None, window, cx, |s| {
 4232            s.select_display_ranges([
 4233                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4234                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4235                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4236                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4237            ])
 4238        });
 4239        assert_eq!(
 4240            editor.display_text(cx),
 4241            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4242        );
 4243
 4244        editor.move_line_up(&MoveLineUp, window, cx);
 4245        assert_eq!(
 4246            editor.display_text(cx),
 4247            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4248        );
 4249        assert_eq!(
 4250            editor.selections.display_ranges(cx),
 4251            vec![
 4252                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4253                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4254                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4255                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4256            ]
 4257        );
 4258    });
 4259
 4260    _ = editor.update(cx, |editor, window, cx| {
 4261        editor.move_line_down(&MoveLineDown, window, cx);
 4262        assert_eq!(
 4263            editor.display_text(cx),
 4264            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4265        );
 4266        assert_eq!(
 4267            editor.selections.display_ranges(cx),
 4268            vec![
 4269                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4270                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4271                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4272                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4273            ]
 4274        );
 4275    });
 4276
 4277    _ = editor.update(cx, |editor, window, cx| {
 4278        editor.move_line_down(&MoveLineDown, window, cx);
 4279        assert_eq!(
 4280            editor.display_text(cx),
 4281            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4282        );
 4283        assert_eq!(
 4284            editor.selections.display_ranges(cx),
 4285            vec![
 4286                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4287                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4288                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4289                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4290            ]
 4291        );
 4292    });
 4293
 4294    _ = editor.update(cx, |editor, window, cx| {
 4295        editor.move_line_up(&MoveLineUp, window, cx);
 4296        assert_eq!(
 4297            editor.display_text(cx),
 4298            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4299        );
 4300        assert_eq!(
 4301            editor.selections.display_ranges(cx),
 4302            vec![
 4303                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4304                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4305                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4306                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4307            ]
 4308        );
 4309    });
 4310}
 4311
 4312#[gpui::test]
 4313fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4314    init_test(cx, |_| {});
 4315
 4316    let editor = cx.add_window(|window, cx| {
 4317        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4318        build_editor(buffer, window, cx)
 4319    });
 4320    _ = editor.update(cx, |editor, window, cx| {
 4321        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4322        editor.insert_blocks(
 4323            [BlockProperties {
 4324                style: BlockStyle::Fixed,
 4325                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4326                height: Some(1),
 4327                render: Arc::new(|_| div().into_any()),
 4328                priority: 0,
 4329            }],
 4330            Some(Autoscroll::fit()),
 4331            cx,
 4332        );
 4333        editor.change_selections(None, window, cx, |s| {
 4334            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4335        });
 4336        editor.move_line_down(&MoveLineDown, window, cx);
 4337    });
 4338}
 4339
 4340#[gpui::test]
 4341async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4342    init_test(cx, |_| {});
 4343
 4344    let mut cx = EditorTestContext::new(cx).await;
 4345    cx.set_state(
 4346        &"
 4347            ˇzero
 4348            one
 4349            two
 4350            three
 4351            four
 4352            five
 4353        "
 4354        .unindent(),
 4355    );
 4356
 4357    // Create a four-line block that replaces three lines of text.
 4358    cx.update_editor(|editor, window, cx| {
 4359        let snapshot = editor.snapshot(window, cx);
 4360        let snapshot = &snapshot.buffer_snapshot;
 4361        let placement = BlockPlacement::Replace(
 4362            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4363        );
 4364        editor.insert_blocks(
 4365            [BlockProperties {
 4366                placement,
 4367                height: Some(4),
 4368                style: BlockStyle::Sticky,
 4369                render: Arc::new(|_| gpui::div().into_any_element()),
 4370                priority: 0,
 4371            }],
 4372            None,
 4373            cx,
 4374        );
 4375    });
 4376
 4377    // Move down so that the cursor touches the block.
 4378    cx.update_editor(|editor, window, cx| {
 4379        editor.move_down(&Default::default(), window, cx);
 4380    });
 4381    cx.assert_editor_state(
 4382        &"
 4383            zero
 4384            «one
 4385            two
 4386            threeˇ»
 4387            four
 4388            five
 4389        "
 4390        .unindent(),
 4391    );
 4392
 4393    // Move down past the block.
 4394    cx.update_editor(|editor, window, cx| {
 4395        editor.move_down(&Default::default(), window, cx);
 4396    });
 4397    cx.assert_editor_state(
 4398        &"
 4399            zero
 4400            one
 4401            two
 4402            three
 4403            ˇfour
 4404            five
 4405        "
 4406        .unindent(),
 4407    );
 4408}
 4409
 4410#[gpui::test]
 4411fn test_transpose(cx: &mut TestAppContext) {
 4412    init_test(cx, |_| {});
 4413
 4414    _ = cx.add_window(|window, cx| {
 4415        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4416        editor.set_style(EditorStyle::default(), window, cx);
 4417        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4418        editor.transpose(&Default::default(), window, cx);
 4419        assert_eq!(editor.text(cx), "bac");
 4420        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4421
 4422        editor.transpose(&Default::default(), window, cx);
 4423        assert_eq!(editor.text(cx), "bca");
 4424        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4425
 4426        editor.transpose(&Default::default(), window, cx);
 4427        assert_eq!(editor.text(cx), "bac");
 4428        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4429
 4430        editor
 4431    });
 4432
 4433    _ = cx.add_window(|window, cx| {
 4434        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4435        editor.set_style(EditorStyle::default(), window, cx);
 4436        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4437        editor.transpose(&Default::default(), window, cx);
 4438        assert_eq!(editor.text(cx), "acb\nde");
 4439        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4440
 4441        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "acbd\ne");
 4444        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4445
 4446        editor.transpose(&Default::default(), window, cx);
 4447        assert_eq!(editor.text(cx), "acbde\n");
 4448        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4449
 4450        editor.transpose(&Default::default(), window, cx);
 4451        assert_eq!(editor.text(cx), "acbd\ne");
 4452        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4453
 4454        editor
 4455    });
 4456
 4457    _ = cx.add_window(|window, cx| {
 4458        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4459        editor.set_style(EditorStyle::default(), window, cx);
 4460        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4461        editor.transpose(&Default::default(), window, cx);
 4462        assert_eq!(editor.text(cx), "bacd\ne");
 4463        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4464
 4465        editor.transpose(&Default::default(), window, cx);
 4466        assert_eq!(editor.text(cx), "bcade\n");
 4467        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4468
 4469        editor.transpose(&Default::default(), window, cx);
 4470        assert_eq!(editor.text(cx), "bcda\ne");
 4471        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4472
 4473        editor.transpose(&Default::default(), window, cx);
 4474        assert_eq!(editor.text(cx), "bcade\n");
 4475        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4476
 4477        editor.transpose(&Default::default(), window, cx);
 4478        assert_eq!(editor.text(cx), "bcaed\n");
 4479        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4480
 4481        editor
 4482    });
 4483
 4484    _ = cx.add_window(|window, cx| {
 4485        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4486        editor.set_style(EditorStyle::default(), window, cx);
 4487        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4488        editor.transpose(&Default::default(), window, cx);
 4489        assert_eq!(editor.text(cx), "🏀🍐✋");
 4490        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4491
 4492        editor.transpose(&Default::default(), window, cx);
 4493        assert_eq!(editor.text(cx), "🏀✋🍐");
 4494        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4495
 4496        editor.transpose(&Default::default(), window, cx);
 4497        assert_eq!(editor.text(cx), "🏀🍐✋");
 4498        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4499
 4500        editor
 4501    });
 4502}
 4503
 4504#[gpui::test]
 4505async fn test_rewrap(cx: &mut TestAppContext) {
 4506    init_test(cx, |settings| {
 4507        settings.languages.extend([
 4508            (
 4509                "Markdown".into(),
 4510                LanguageSettingsContent {
 4511                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4512                    ..Default::default()
 4513                },
 4514            ),
 4515            (
 4516                "Plain Text".into(),
 4517                LanguageSettingsContent {
 4518                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4519                    ..Default::default()
 4520                },
 4521            ),
 4522        ])
 4523    });
 4524
 4525    let mut cx = EditorTestContext::new(cx).await;
 4526
 4527    let language_with_c_comments = Arc::new(Language::new(
 4528        LanguageConfig {
 4529            line_comments: vec!["// ".into()],
 4530            ..LanguageConfig::default()
 4531        },
 4532        None,
 4533    ));
 4534    let language_with_pound_comments = Arc::new(Language::new(
 4535        LanguageConfig {
 4536            line_comments: vec!["# ".into()],
 4537            ..LanguageConfig::default()
 4538        },
 4539        None,
 4540    ));
 4541    let markdown_language = Arc::new(Language::new(
 4542        LanguageConfig {
 4543            name: "Markdown".into(),
 4544            ..LanguageConfig::default()
 4545        },
 4546        None,
 4547    ));
 4548    let language_with_doc_comments = Arc::new(Language::new(
 4549        LanguageConfig {
 4550            line_comments: vec!["// ".into(), "/// ".into()],
 4551            ..LanguageConfig::default()
 4552        },
 4553        Some(tree_sitter_rust::LANGUAGE.into()),
 4554    ));
 4555
 4556    let plaintext_language = Arc::new(Language::new(
 4557        LanguageConfig {
 4558            name: "Plain Text".into(),
 4559            ..LanguageConfig::default()
 4560        },
 4561        None,
 4562    ));
 4563
 4564    assert_rewrap(
 4565        indoc! {"
 4566            // ˇ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.
 4567        "},
 4568        indoc! {"
 4569            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4570            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4571            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4572            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4573            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4574            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4575            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4576            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4577            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4578            // porttitor id. Aliquam id accumsan eros.
 4579        "},
 4580        language_with_c_comments.clone(),
 4581        &mut cx,
 4582    );
 4583
 4584    // Test that rewrapping works inside of a selection
 4585    assert_rewrap(
 4586        indoc! {"
 4587            «// 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.ˇ»
 4588        "},
 4589        indoc! {"
 4590            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4591            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4592            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4593            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4594            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4595            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4596            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4597            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4598            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4599            // porttitor id. Aliquam id accumsan eros.ˇ»
 4600        "},
 4601        language_with_c_comments.clone(),
 4602        &mut cx,
 4603    );
 4604
 4605    // Test that cursors that expand to the same region are collapsed.
 4606    assert_rewrap(
 4607        indoc! {"
 4608            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4609            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4610            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4611            // ˇ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.
 4612        "},
 4613        indoc! {"
 4614            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4615            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4616            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4617            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4618            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4619            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4620            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4621            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4622            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4623            // porttitor id. Aliquam id accumsan eros.
 4624        "},
 4625        language_with_c_comments.clone(),
 4626        &mut cx,
 4627    );
 4628
 4629    // Test that non-contiguous selections are treated separately.
 4630    assert_rewrap(
 4631        indoc! {"
 4632            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4633            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4634            //
 4635            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4636            // ˇ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.
 4637        "},
 4638        indoc! {"
 4639            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4640            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4641            // auctor, eu lacinia sapien scelerisque.
 4642            //
 4643            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4644            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4645            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4646            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4647            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4648            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4649            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4650        "},
 4651        language_with_c_comments.clone(),
 4652        &mut cx,
 4653    );
 4654
 4655    // Test that different comment prefixes are supported.
 4656    assert_rewrap(
 4657        indoc! {"
 4658            # ˇ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.
 4659        "},
 4660        indoc! {"
 4661            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4662            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4663            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4664            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4665            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4666            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4667            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4668            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4669            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4670            # accumsan eros.
 4671        "},
 4672        language_with_pound_comments.clone(),
 4673        &mut cx,
 4674    );
 4675
 4676    // Test that rewrapping is ignored outside of comments in most languages.
 4677    assert_rewrap(
 4678        indoc! {"
 4679            /// Adds two numbers.
 4680            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4681            fn add(a: u32, b: u32) -> u32 {
 4682                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ˇ
 4683            }
 4684        "},
 4685        indoc! {"
 4686            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4687            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4688            fn add(a: u32, b: u32) -> u32 {
 4689                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ˇ
 4690            }
 4691        "},
 4692        language_with_doc_comments.clone(),
 4693        &mut cx,
 4694    );
 4695
 4696    // Test that rewrapping works in Markdown and Plain Text languages.
 4697    assert_rewrap(
 4698        indoc! {"
 4699            # Hello
 4700
 4701            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.
 4702        "},
 4703        indoc! {"
 4704            # Hello
 4705
 4706            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4707            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4708            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4709            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4710            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4711            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4712            Integer sit amet scelerisque nisi.
 4713        "},
 4714        markdown_language,
 4715        &mut cx,
 4716    );
 4717
 4718    assert_rewrap(
 4719        indoc! {"
 4720            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.
 4721        "},
 4722        indoc! {"
 4723            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4724            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4725            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4726            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4727            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4728            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4729            Integer sit amet scelerisque nisi.
 4730        "},
 4731        plaintext_language,
 4732        &mut cx,
 4733    );
 4734
 4735    // Test rewrapping unaligned comments in a selection.
 4736    assert_rewrap(
 4737        indoc! {"
 4738            fn foo() {
 4739                if true {
 4740            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4741            // Praesent semper egestas tellus id dignissim.ˇ»
 4742                    do_something();
 4743                } else {
 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        language_with_doc_comments.clone(),
 4761        &mut cx,
 4762    );
 4763
 4764    assert_rewrap(
 4765        indoc! {"
 4766            fn foo() {
 4767                if true {
 4768            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4769            // Praesent semper egestas tellus id dignissim.»
 4770                    do_something();
 4771                } else {
 4772                    //
 4773                }
 4774
 4775            }
 4776        "},
 4777        indoc! {"
 4778            fn foo() {
 4779                if true {
 4780            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4781                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4782                    // egestas tellus id dignissim.»
 4783                    do_something();
 4784                } else {
 4785                    //
 4786                }
 4787
 4788            }
 4789        "},
 4790        language_with_doc_comments.clone(),
 4791        &mut cx,
 4792    );
 4793
 4794    #[track_caller]
 4795    fn assert_rewrap(
 4796        unwrapped_text: &str,
 4797        wrapped_text: &str,
 4798        language: Arc<Language>,
 4799        cx: &mut EditorTestContext,
 4800    ) {
 4801        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4802        cx.set_state(unwrapped_text);
 4803        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4804        cx.assert_editor_state(wrapped_text);
 4805    }
 4806}
 4807
 4808#[gpui::test]
 4809async fn test_hard_wrap(cx: &mut TestAppContext) {
 4810    init_test(cx, |_| {});
 4811    let mut cx = EditorTestContext::new(cx).await;
 4812
 4813    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4814    cx.update_editor(|editor, _, cx| {
 4815        editor.set_hard_wrap(Some(14), cx);
 4816    });
 4817
 4818    cx.set_state(indoc!(
 4819        "
 4820        one two three ˇ
 4821        "
 4822    ));
 4823    cx.simulate_input("four");
 4824    cx.run_until_parked();
 4825
 4826    cx.assert_editor_state(indoc!(
 4827        "
 4828        one two three
 4829        fourˇ
 4830        "
 4831    ));
 4832
 4833    cx.update_editor(|editor, window, cx| {
 4834        editor.newline(&Default::default(), window, cx);
 4835    });
 4836    cx.run_until_parked();
 4837    cx.assert_editor_state(indoc!(
 4838        "
 4839        one two three
 4840        four
 4841        ˇ
 4842        "
 4843    ));
 4844
 4845    cx.simulate_input("five");
 4846    cx.run_until_parked();
 4847    cx.assert_editor_state(indoc!(
 4848        "
 4849        one two three
 4850        four
 4851        fiveˇ
 4852        "
 4853    ));
 4854
 4855    cx.update_editor(|editor, window, cx| {
 4856        editor.newline(&Default::default(), window, cx);
 4857    });
 4858    cx.run_until_parked();
 4859    cx.simulate_input("# ");
 4860    cx.run_until_parked();
 4861    cx.assert_editor_state(indoc!(
 4862        "
 4863        one two three
 4864        four
 4865        five
 4866        # ˇ
 4867        "
 4868    ));
 4869
 4870    cx.update_editor(|editor, window, cx| {
 4871        editor.newline(&Default::default(), window, cx);
 4872    });
 4873    cx.run_until_parked();
 4874    cx.assert_editor_state(indoc!(
 4875        "
 4876        one two three
 4877        four
 4878        five
 4879        #\x20
 4880 4881        "
 4882    ));
 4883
 4884    cx.simulate_input(" 6");
 4885    cx.run_until_parked();
 4886    cx.assert_editor_state(indoc!(
 4887        "
 4888        one two three
 4889        four
 4890        five
 4891        #
 4892        # 6ˇ
 4893        "
 4894    ));
 4895}
 4896
 4897#[gpui::test]
 4898async fn test_clipboard(cx: &mut TestAppContext) {
 4899    init_test(cx, |_| {});
 4900
 4901    let mut cx = EditorTestContext::new(cx).await;
 4902
 4903    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4904    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4905    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4906
 4907    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4908    cx.set_state("two ˇfour ˇsix ˇ");
 4909    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4910    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4911
 4912    // Paste again but with only two cursors. Since the number of cursors doesn't
 4913    // match the number of slices in the clipboard, the entire clipboard text
 4914    // is pasted at each cursor.
 4915    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4916    cx.update_editor(|e, window, cx| {
 4917        e.handle_input("( ", window, cx);
 4918        e.paste(&Paste, window, cx);
 4919        e.handle_input(") ", window, cx);
 4920    });
 4921    cx.assert_editor_state(
 4922        &([
 4923            "( one✅ ",
 4924            "three ",
 4925            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4926            "three ",
 4927            "five ) ˇ",
 4928        ]
 4929        .join("\n")),
 4930    );
 4931
 4932    // Cut with three selections, one of which is full-line.
 4933    cx.set_state(indoc! {"
 4934        1«2ˇ»3
 4935        4ˇ567
 4936        «8ˇ»9"});
 4937    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4938    cx.assert_editor_state(indoc! {"
 4939        1ˇ3
 4940        ˇ9"});
 4941
 4942    // Paste with three selections, noticing how the copied selection that was full-line
 4943    // gets inserted before the second cursor.
 4944    cx.set_state(indoc! {"
 4945        1ˇ3
 4946 4947        «oˇ»ne"});
 4948    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4949    cx.assert_editor_state(indoc! {"
 4950        12ˇ3
 4951        4567
 4952 4953        8ˇne"});
 4954
 4955    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4956    cx.set_state(indoc! {"
 4957        The quick brown
 4958        fox juˇmps over
 4959        the lazy dog"});
 4960    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4961    assert_eq!(
 4962        cx.read_from_clipboard()
 4963            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4964        Some("fox jumps over\n".to_string())
 4965    );
 4966
 4967    // Paste with three selections, noticing how the copied full-line selection is inserted
 4968    // before the empty selections but replaces the selection that is non-empty.
 4969    cx.set_state(indoc! {"
 4970        Tˇhe quick brown
 4971        «foˇ»x jumps over
 4972        tˇhe lazy dog"});
 4973    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4974    cx.assert_editor_state(indoc! {"
 4975        fox jumps over
 4976        Tˇhe quick brown
 4977        fox jumps over
 4978        ˇx jumps over
 4979        fox jumps over
 4980        tˇhe lazy dog"});
 4981}
 4982
 4983#[gpui::test]
 4984async fn test_copy_trim(cx: &mut TestAppContext) {
 4985    init_test(cx, |_| {});
 4986
 4987    let mut cx = EditorTestContext::new(cx).await;
 4988    cx.set_state(
 4989        r#"            «for selection in selections.iter() {
 4990            let mut start = selection.start;
 4991            let mut end = selection.end;
 4992            let is_entire_line = selection.is_empty();
 4993            if is_entire_line {
 4994                start = Point::new(start.row, 0);ˇ»
 4995                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4996            }
 4997        "#,
 4998    );
 4999    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5000    assert_eq!(
 5001        cx.read_from_clipboard()
 5002            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5003        Some(
 5004            "for selection in selections.iter() {
 5005            let mut start = selection.start;
 5006            let mut end = selection.end;
 5007            let is_entire_line = selection.is_empty();
 5008            if is_entire_line {
 5009                start = Point::new(start.row, 0);"
 5010                .to_string()
 5011        ),
 5012        "Regular copying preserves all indentation selected",
 5013    );
 5014    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5015    assert_eq!(
 5016        cx.read_from_clipboard()
 5017            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5018        Some(
 5019            "for selection in selections.iter() {
 5020let mut start = selection.start;
 5021let mut end = selection.end;
 5022let is_entire_line = selection.is_empty();
 5023if is_entire_line {
 5024    start = Point::new(start.row, 0);"
 5025                .to_string()
 5026        ),
 5027        "Copying with stripping should strip all leading whitespaces"
 5028    );
 5029
 5030    cx.set_state(
 5031        r#"       «     for selection in selections.iter() {
 5032            let mut start = selection.start;
 5033            let mut end = selection.end;
 5034            let is_entire_line = selection.is_empty();
 5035            if is_entire_line {
 5036                start = Point::new(start.row, 0);ˇ»
 5037                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5038            }
 5039        "#,
 5040    );
 5041    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5042    assert_eq!(
 5043        cx.read_from_clipboard()
 5044            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5045        Some(
 5046            "     for selection in selections.iter() {
 5047            let mut start = selection.start;
 5048            let mut end = selection.end;
 5049            let is_entire_line = selection.is_empty();
 5050            if is_entire_line {
 5051                start = Point::new(start.row, 0);"
 5052                .to_string()
 5053        ),
 5054        "Regular copying preserves all indentation selected",
 5055    );
 5056    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5057    assert_eq!(
 5058        cx.read_from_clipboard()
 5059            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5060        Some(
 5061            "for selection in selections.iter() {
 5062let mut start = selection.start;
 5063let mut end = selection.end;
 5064let is_entire_line = selection.is_empty();
 5065if is_entire_line {
 5066    start = Point::new(start.row, 0);"
 5067                .to_string()
 5068        ),
 5069        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5070    );
 5071
 5072    cx.set_state(
 5073        r#"       «ˇ     for selection in selections.iter() {
 5074            let mut start = selection.start;
 5075            let mut end = selection.end;
 5076            let is_entire_line = selection.is_empty();
 5077            if is_entire_line {
 5078                start = Point::new(start.row, 0);»
 5079                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5080            }
 5081        "#,
 5082    );
 5083    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5084    assert_eq!(
 5085        cx.read_from_clipboard()
 5086            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5087        Some(
 5088            "     for selection in selections.iter() {
 5089            let mut start = selection.start;
 5090            let mut end = selection.end;
 5091            let is_entire_line = selection.is_empty();
 5092            if is_entire_line {
 5093                start = Point::new(start.row, 0);"
 5094                .to_string()
 5095        ),
 5096        "Regular copying for reverse selection works the same",
 5097    );
 5098    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5099    assert_eq!(
 5100        cx.read_from_clipboard()
 5101            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5102        Some(
 5103            "for selection in selections.iter() {
 5104let mut start = selection.start;
 5105let mut end = selection.end;
 5106let is_entire_line = selection.is_empty();
 5107if is_entire_line {
 5108    start = Point::new(start.row, 0);"
 5109                .to_string()
 5110        ),
 5111        "Copying with stripping for reverse selection works the same"
 5112    );
 5113
 5114    cx.set_state(
 5115        r#"            for selection «in selections.iter() {
 5116            let mut start = selection.start;
 5117            let mut end = selection.end;
 5118            let is_entire_line = selection.is_empty();
 5119            if is_entire_line {
 5120                start = Point::new(start.row, 0);ˇ»
 5121                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5122            }
 5123        "#,
 5124    );
 5125    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5126    assert_eq!(
 5127        cx.read_from_clipboard()
 5128            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5129        Some(
 5130            "in selections.iter() {
 5131            let mut start = selection.start;
 5132            let mut end = selection.end;
 5133            let is_entire_line = selection.is_empty();
 5134            if is_entire_line {
 5135                start = Point::new(start.row, 0);"
 5136                .to_string()
 5137        ),
 5138        "When selecting past the indent, the copying works as usual",
 5139    );
 5140    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5141    assert_eq!(
 5142        cx.read_from_clipboard()
 5143            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5144        Some(
 5145            "in selections.iter() {
 5146            let mut start = selection.start;
 5147            let mut end = selection.end;
 5148            let is_entire_line = selection.is_empty();
 5149            if is_entire_line {
 5150                start = Point::new(start.row, 0);"
 5151                .to_string()
 5152        ),
 5153        "When selecting past the indent, nothing is trimmed"
 5154    );
 5155
 5156    cx.set_state(
 5157        r#"            «for selection in selections.iter() {
 5158            let mut start = selection.start;
 5159
 5160            let mut end = selection.end;
 5161            let is_entire_line = selection.is_empty();
 5162            if is_entire_line {
 5163                start = Point::new(start.row, 0);
 5164ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5165            }
 5166        "#,
 5167    );
 5168    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5169    assert_eq!(
 5170        cx.read_from_clipboard()
 5171            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5172        Some(
 5173            "for selection in selections.iter() {
 5174let mut start = selection.start;
 5175
 5176let mut end = selection.end;
 5177let is_entire_line = selection.is_empty();
 5178if is_entire_line {
 5179    start = Point::new(start.row, 0);
 5180"
 5181            .to_string()
 5182        ),
 5183        "Copying with stripping should ignore empty lines"
 5184    );
 5185}
 5186
 5187#[gpui::test]
 5188async fn test_paste_multiline(cx: &mut TestAppContext) {
 5189    init_test(cx, |_| {});
 5190
 5191    let mut cx = EditorTestContext::new(cx).await;
 5192    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5193
 5194    // Cut an indented block, without the leading whitespace.
 5195    cx.set_state(indoc! {"
 5196        const a: B = (
 5197            c(),
 5198            «d(
 5199                e,
 5200                f
 5201            )ˇ»
 5202        );
 5203    "});
 5204    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5205    cx.assert_editor_state(indoc! {"
 5206        const a: B = (
 5207            c(),
 5208            ˇ
 5209        );
 5210    "});
 5211
 5212    // Paste it at the same position.
 5213    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5214    cx.assert_editor_state(indoc! {"
 5215        const a: B = (
 5216            c(),
 5217            d(
 5218                e,
 5219                f
 5220 5221        );
 5222    "});
 5223
 5224    // Paste it at a line with a lower indent level.
 5225    cx.set_state(indoc! {"
 5226        ˇ
 5227        const a: B = (
 5228            c(),
 5229        );
 5230    "});
 5231    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5232    cx.assert_editor_state(indoc! {"
 5233        d(
 5234            e,
 5235            f
 5236 5237        const a: B = (
 5238            c(),
 5239        );
 5240    "});
 5241
 5242    // Cut an indented block, with the leading whitespace.
 5243    cx.set_state(indoc! {"
 5244        const a: B = (
 5245            c(),
 5246        «    d(
 5247                e,
 5248                f
 5249            )
 5250        ˇ»);
 5251    "});
 5252    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5253    cx.assert_editor_state(indoc! {"
 5254        const a: B = (
 5255            c(),
 5256        ˇ);
 5257    "});
 5258
 5259    // Paste it at the same position.
 5260    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5261    cx.assert_editor_state(indoc! {"
 5262        const a: B = (
 5263            c(),
 5264            d(
 5265                e,
 5266                f
 5267            )
 5268        ˇ);
 5269    "});
 5270
 5271    // Paste it at a line with a higher indent level.
 5272    cx.set_state(indoc! {"
 5273        const a: B = (
 5274            c(),
 5275            d(
 5276                e,
 5277 5278            )
 5279        );
 5280    "});
 5281    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5282    cx.assert_editor_state(indoc! {"
 5283        const a: B = (
 5284            c(),
 5285            d(
 5286                e,
 5287                f    d(
 5288                    e,
 5289                    f
 5290                )
 5291        ˇ
 5292            )
 5293        );
 5294    "});
 5295
 5296    // Copy an indented block, starting mid-line
 5297    cx.set_state(indoc! {"
 5298        const a: B = (
 5299            c(),
 5300            somethin«g(
 5301                e,
 5302                f
 5303            )ˇ»
 5304        );
 5305    "});
 5306    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5307
 5308    // Paste it on a line with a lower indent level
 5309    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5310    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5311    cx.assert_editor_state(indoc! {"
 5312        const a: B = (
 5313            c(),
 5314            something(
 5315                e,
 5316                f
 5317            )
 5318        );
 5319        g(
 5320            e,
 5321            f
 5322"});
 5323}
 5324
 5325#[gpui::test]
 5326async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5327    init_test(cx, |_| {});
 5328
 5329    cx.write_to_clipboard(ClipboardItem::new_string(
 5330        "    d(\n        e\n    );\n".into(),
 5331    ));
 5332
 5333    let mut cx = EditorTestContext::new(cx).await;
 5334    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5335
 5336    cx.set_state(indoc! {"
 5337        fn a() {
 5338            b();
 5339            if c() {
 5340                ˇ
 5341            }
 5342        }
 5343    "});
 5344
 5345    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5346    cx.assert_editor_state(indoc! {"
 5347        fn a() {
 5348            b();
 5349            if c() {
 5350                d(
 5351                    e
 5352                );
 5353        ˇ
 5354            }
 5355        }
 5356    "});
 5357
 5358    cx.set_state(indoc! {"
 5359        fn a() {
 5360            b();
 5361            ˇ
 5362        }
 5363    "});
 5364
 5365    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5366    cx.assert_editor_state(indoc! {"
 5367        fn a() {
 5368            b();
 5369            d(
 5370                e
 5371            );
 5372        ˇ
 5373        }
 5374    "});
 5375}
 5376
 5377#[gpui::test]
 5378fn test_select_all(cx: &mut TestAppContext) {
 5379    init_test(cx, |_| {});
 5380
 5381    let editor = cx.add_window(|window, cx| {
 5382        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5383        build_editor(buffer, window, cx)
 5384    });
 5385    _ = editor.update(cx, |editor, window, cx| {
 5386        editor.select_all(&SelectAll, window, cx);
 5387        assert_eq!(
 5388            editor.selections.display_ranges(cx),
 5389            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5390        );
 5391    });
 5392}
 5393
 5394#[gpui::test]
 5395fn test_select_line(cx: &mut TestAppContext) {
 5396    init_test(cx, |_| {});
 5397
 5398    let editor = cx.add_window(|window, cx| {
 5399        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5400        build_editor(buffer, window, cx)
 5401    });
 5402    _ = editor.update(cx, |editor, window, cx| {
 5403        editor.change_selections(None, window, cx, |s| {
 5404            s.select_display_ranges([
 5405                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5406                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5407                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5408                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5409            ])
 5410        });
 5411        editor.select_line(&SelectLine, window, cx);
 5412        assert_eq!(
 5413            editor.selections.display_ranges(cx),
 5414            vec![
 5415                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5416                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5417            ]
 5418        );
 5419    });
 5420
 5421    _ = editor.update(cx, |editor, window, cx| {
 5422        editor.select_line(&SelectLine, window, cx);
 5423        assert_eq!(
 5424            editor.selections.display_ranges(cx),
 5425            vec![
 5426                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5427                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5428            ]
 5429        );
 5430    });
 5431
 5432    _ = editor.update(cx, |editor, window, cx| {
 5433        editor.select_line(&SelectLine, window, cx);
 5434        assert_eq!(
 5435            editor.selections.display_ranges(cx),
 5436            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5437        );
 5438    });
 5439}
 5440
 5441#[gpui::test]
 5442async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5443    init_test(cx, |_| {});
 5444    let mut cx = EditorTestContext::new(cx).await;
 5445
 5446    #[track_caller]
 5447    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5448        cx.set_state(initial_state);
 5449        cx.update_editor(|e, window, cx| {
 5450            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5451        });
 5452        cx.assert_editor_state(expected_state);
 5453    }
 5454
 5455    // Selection starts and ends at the middle of lines, left-to-right
 5456    test(
 5457        &mut cx,
 5458        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5459        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5460    );
 5461    // Same thing, right-to-left
 5462    test(
 5463        &mut cx,
 5464        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5465        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5466    );
 5467
 5468    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5469    test(
 5470        &mut cx,
 5471        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5472        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5473    );
 5474    // Same thing, right-to-left
 5475    test(
 5476        &mut cx,
 5477        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5478        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5479    );
 5480
 5481    // Whole buffer, left-to-right, last line ends with newline
 5482    test(
 5483        &mut cx,
 5484        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5485        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5486    );
 5487    // Same thing, right-to-left
 5488    test(
 5489        &mut cx,
 5490        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5491        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5492    );
 5493
 5494    // Starts at the end of a line, ends at the start of another
 5495    test(
 5496        &mut cx,
 5497        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5498        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5499    );
 5500}
 5501
 5502#[gpui::test]
 5503async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5504    init_test(cx, |_| {});
 5505
 5506    let editor = cx.add_window(|window, cx| {
 5507        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5508        build_editor(buffer, window, cx)
 5509    });
 5510
 5511    // setup
 5512    _ = editor.update(cx, |editor, window, cx| {
 5513        editor.fold_creases(
 5514            vec![
 5515                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5516                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5517                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5518            ],
 5519            true,
 5520            window,
 5521            cx,
 5522        );
 5523        assert_eq!(
 5524            editor.display_text(cx),
 5525            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5526        );
 5527    });
 5528
 5529    _ = editor.update(cx, |editor, window, cx| {
 5530        editor.change_selections(None, window, cx, |s| {
 5531            s.select_display_ranges([
 5532                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5533                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5534                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5535                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5536            ])
 5537        });
 5538        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5539        assert_eq!(
 5540            editor.display_text(cx),
 5541            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5542        );
 5543    });
 5544    EditorTestContext::for_editor(editor, cx)
 5545        .await
 5546        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5547
 5548    _ = editor.update(cx, |editor, window, cx| {
 5549        editor.change_selections(None, window, cx, |s| {
 5550            s.select_display_ranges([
 5551                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5552            ])
 5553        });
 5554        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5555        assert_eq!(
 5556            editor.display_text(cx),
 5557            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5558        );
 5559        assert_eq!(
 5560            editor.selections.display_ranges(cx),
 5561            [
 5562                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5563                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5564                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5565                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5566                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5567                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5568                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5569            ]
 5570        );
 5571    });
 5572    EditorTestContext::for_editor(editor, cx)
 5573        .await
 5574        .assert_editor_state(
 5575            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5576        );
 5577}
 5578
 5579#[gpui::test]
 5580async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5581    init_test(cx, |_| {});
 5582
 5583    let mut cx = EditorTestContext::new(cx).await;
 5584
 5585    cx.set_state(indoc!(
 5586        r#"abc
 5587           defˇghi
 5588
 5589           jk
 5590           nlmo
 5591           "#
 5592    ));
 5593
 5594    cx.update_editor(|editor, window, cx| {
 5595        editor.add_selection_above(&Default::default(), window, cx);
 5596    });
 5597
 5598    cx.assert_editor_state(indoc!(
 5599        r#"abcˇ
 5600           defˇghi
 5601
 5602           jk
 5603           nlmo
 5604           "#
 5605    ));
 5606
 5607    cx.update_editor(|editor, window, cx| {
 5608        editor.add_selection_above(&Default::default(), window, cx);
 5609    });
 5610
 5611    cx.assert_editor_state(indoc!(
 5612        r#"abcˇ
 5613            defˇghi
 5614
 5615            jk
 5616            nlmo
 5617            "#
 5618    ));
 5619
 5620    cx.update_editor(|editor, window, cx| {
 5621        editor.add_selection_below(&Default::default(), window, cx);
 5622    });
 5623
 5624    cx.assert_editor_state(indoc!(
 5625        r#"abc
 5626           defˇghi
 5627
 5628           jk
 5629           nlmo
 5630           "#
 5631    ));
 5632
 5633    cx.update_editor(|editor, window, cx| {
 5634        editor.undo_selection(&Default::default(), window, cx);
 5635    });
 5636
 5637    cx.assert_editor_state(indoc!(
 5638        r#"abcˇ
 5639           defˇghi
 5640
 5641           jk
 5642           nlmo
 5643           "#
 5644    ));
 5645
 5646    cx.update_editor(|editor, window, cx| {
 5647        editor.redo_selection(&Default::default(), window, cx);
 5648    });
 5649
 5650    cx.assert_editor_state(indoc!(
 5651        r#"abc
 5652           defˇghi
 5653
 5654           jk
 5655           nlmo
 5656           "#
 5657    ));
 5658
 5659    cx.update_editor(|editor, window, cx| {
 5660        editor.add_selection_below(&Default::default(), window, cx);
 5661    });
 5662
 5663    cx.assert_editor_state(indoc!(
 5664        r#"abc
 5665           defˇghi
 5666
 5667           jk
 5668           nlmˇo
 5669           "#
 5670    ));
 5671
 5672    cx.update_editor(|editor, window, cx| {
 5673        editor.add_selection_below(&Default::default(), window, cx);
 5674    });
 5675
 5676    cx.assert_editor_state(indoc!(
 5677        r#"abc
 5678           defˇghi
 5679
 5680           jk
 5681           nlmˇo
 5682           "#
 5683    ));
 5684
 5685    // change selections
 5686    cx.set_state(indoc!(
 5687        r#"abc
 5688           def«ˇg»hi
 5689
 5690           jk
 5691           nlmo
 5692           "#
 5693    ));
 5694
 5695    cx.update_editor(|editor, window, cx| {
 5696        editor.add_selection_below(&Default::default(), window, cx);
 5697    });
 5698
 5699    cx.assert_editor_state(indoc!(
 5700        r#"abc
 5701           def«ˇg»hi
 5702
 5703           jk
 5704           nlm«ˇo»
 5705           "#
 5706    ));
 5707
 5708    cx.update_editor(|editor, window, cx| {
 5709        editor.add_selection_below(&Default::default(), window, cx);
 5710    });
 5711
 5712    cx.assert_editor_state(indoc!(
 5713        r#"abc
 5714           def«ˇg»hi
 5715
 5716           jk
 5717           nlm«ˇo»
 5718           "#
 5719    ));
 5720
 5721    cx.update_editor(|editor, window, cx| {
 5722        editor.add_selection_above(&Default::default(), window, cx);
 5723    });
 5724
 5725    cx.assert_editor_state(indoc!(
 5726        r#"abc
 5727           def«ˇg»hi
 5728
 5729           jk
 5730           nlmo
 5731           "#
 5732    ));
 5733
 5734    cx.update_editor(|editor, window, cx| {
 5735        editor.add_selection_above(&Default::default(), window, cx);
 5736    });
 5737
 5738    cx.assert_editor_state(indoc!(
 5739        r#"abc
 5740           def«ˇg»hi
 5741
 5742           jk
 5743           nlmo
 5744           "#
 5745    ));
 5746
 5747    // Change selections again
 5748    cx.set_state(indoc!(
 5749        r#"a«bc
 5750           defgˇ»hi
 5751
 5752           jk
 5753           nlmo
 5754           "#
 5755    ));
 5756
 5757    cx.update_editor(|editor, window, cx| {
 5758        editor.add_selection_below(&Default::default(), window, cx);
 5759    });
 5760
 5761    cx.assert_editor_state(indoc!(
 5762        r#"a«bcˇ»
 5763           d«efgˇ»hi
 5764
 5765           j«kˇ»
 5766           nlmo
 5767           "#
 5768    ));
 5769
 5770    cx.update_editor(|editor, window, cx| {
 5771        editor.add_selection_below(&Default::default(), window, cx);
 5772    });
 5773    cx.assert_editor_state(indoc!(
 5774        r#"a«bcˇ»
 5775           d«efgˇ»hi
 5776
 5777           j«kˇ»
 5778           n«lmoˇ»
 5779           "#
 5780    ));
 5781    cx.update_editor(|editor, window, cx| {
 5782        editor.add_selection_above(&Default::default(), window, cx);
 5783    });
 5784
 5785    cx.assert_editor_state(indoc!(
 5786        r#"a«bcˇ»
 5787           d«efgˇ»hi
 5788
 5789           j«kˇ»
 5790           nlmo
 5791           "#
 5792    ));
 5793
 5794    // Change selections again
 5795    cx.set_state(indoc!(
 5796        r#"abc
 5797           d«ˇefghi
 5798
 5799           jk
 5800           nlm»o
 5801           "#
 5802    ));
 5803
 5804    cx.update_editor(|editor, window, cx| {
 5805        editor.add_selection_above(&Default::default(), window, cx);
 5806    });
 5807
 5808    cx.assert_editor_state(indoc!(
 5809        r#"a«ˇbc»
 5810           d«ˇef»ghi
 5811
 5812           j«ˇk»
 5813           n«ˇlm»o
 5814           "#
 5815    ));
 5816
 5817    cx.update_editor(|editor, window, cx| {
 5818        editor.add_selection_below(&Default::default(), window, cx);
 5819    });
 5820
 5821    cx.assert_editor_state(indoc!(
 5822        r#"abc
 5823           d«ˇef»ghi
 5824
 5825           j«ˇk»
 5826           n«ˇlm»o
 5827           "#
 5828    ));
 5829}
 5830
 5831#[gpui::test]
 5832async fn test_select_next(cx: &mut TestAppContext) {
 5833    init_test(cx, |_| {});
 5834
 5835    let mut cx = EditorTestContext::new(cx).await;
 5836    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5837
 5838    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5839        .unwrap();
 5840    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5841
 5842    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5843        .unwrap();
 5844    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5845
 5846    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5847    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5848
 5849    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5850    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5851
 5852    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5853        .unwrap();
 5854    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5855
 5856    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5857        .unwrap();
 5858    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5859
 5860    // Test selection direction should be preserved
 5861    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5862
 5863    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5866}
 5867
 5868#[gpui::test]
 5869async fn test_select_all_matches(cx: &mut TestAppContext) {
 5870    init_test(cx, |_| {});
 5871
 5872    let mut cx = EditorTestContext::new(cx).await;
 5873
 5874    // Test caret-only selections
 5875    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5876    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5877        .unwrap();
 5878    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5879
 5880    // Test left-to-right selections
 5881    cx.set_state("abc\n«abcˇ»\nabc");
 5882    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5883        .unwrap();
 5884    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5885
 5886    // Test right-to-left selections
 5887    cx.set_state("abc\n«ˇabc»\nabc");
 5888    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5889        .unwrap();
 5890    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5891
 5892    // Test selecting whitespace with caret selection
 5893    cx.set_state("abc\nˇ   abc\nabc");
 5894    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5895        .unwrap();
 5896    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5897
 5898    // Test selecting whitespace with left-to-right selection
 5899    cx.set_state("abc\n«ˇ  »abc\nabc");
 5900    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5901        .unwrap();
 5902    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5903
 5904    // Test no matches with right-to-left selection
 5905    cx.set_state("abc\n«  ˇ»abc\nabc");
 5906    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5907        .unwrap();
 5908    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5909}
 5910
 5911#[gpui::test]
 5912async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5913    init_test(cx, |_| {});
 5914
 5915    let mut cx = EditorTestContext::new(cx).await;
 5916
 5917    let large_body_1 = "\nd".repeat(200);
 5918    let large_body_2 = "\ne".repeat(200);
 5919
 5920    cx.set_state(&format!(
 5921        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5922    ));
 5923    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5924        let scroll_position = editor.scroll_position(cx);
 5925        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5926        scroll_position
 5927    });
 5928
 5929    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5930        .unwrap();
 5931    cx.assert_editor_state(&format!(
 5932        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5933    ));
 5934    let scroll_position_after_selection =
 5935        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5936    assert_eq!(
 5937        initial_scroll_position, scroll_position_after_selection,
 5938        "Scroll position should not change after selecting all matches"
 5939    );
 5940}
 5941
 5942#[gpui::test]
 5943async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5944    init_test(cx, |_| {});
 5945
 5946    let mut cx = EditorLspTestContext::new_rust(
 5947        lsp::ServerCapabilities {
 5948            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5949            ..Default::default()
 5950        },
 5951        cx,
 5952    )
 5953    .await;
 5954
 5955    cx.set_state(indoc! {"
 5956        line 1
 5957        line 2
 5958        linˇe 3
 5959        line 4
 5960        line 5
 5961    "});
 5962
 5963    // Make an edit
 5964    cx.update_editor(|editor, window, cx| {
 5965        editor.handle_input("X", window, cx);
 5966    });
 5967
 5968    // Move cursor to a different position
 5969    cx.update_editor(|editor, window, cx| {
 5970        editor.change_selections(None, window, cx, |s| {
 5971            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5972        });
 5973    });
 5974
 5975    cx.assert_editor_state(indoc! {"
 5976        line 1
 5977        line 2
 5978        linXe 3
 5979        line 4
 5980        liˇne 5
 5981    "});
 5982
 5983    cx.lsp
 5984        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5985            Ok(Some(vec![lsp::TextEdit::new(
 5986                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5987                "PREFIX ".to_string(),
 5988            )]))
 5989        });
 5990
 5991    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5992        .unwrap()
 5993        .await
 5994        .unwrap();
 5995
 5996    cx.assert_editor_state(indoc! {"
 5997        PREFIX line 1
 5998        line 2
 5999        linXe 3
 6000        line 4
 6001        liˇne 5
 6002    "});
 6003
 6004    // Undo formatting
 6005    cx.update_editor(|editor, window, cx| {
 6006        editor.undo(&Default::default(), window, cx);
 6007    });
 6008
 6009    // Verify cursor moved back to position after edit
 6010    cx.assert_editor_state(indoc! {"
 6011        line 1
 6012        line 2
 6013        linXˇe 3
 6014        line 4
 6015        line 5
 6016    "});
 6017}
 6018
 6019#[gpui::test]
 6020async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 6021    init_test(cx, |_| {});
 6022
 6023    let mut cx = EditorTestContext::new(cx).await;
 6024    cx.set_state(
 6025        r#"let foo = 2;
 6026lˇet foo = 2;
 6027let fooˇ = 2;
 6028let foo = 2;
 6029let foo = ˇ2;"#,
 6030    );
 6031
 6032    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6033        .unwrap();
 6034    cx.assert_editor_state(
 6035        r#"let foo = 2;
 6036«letˇ» foo = 2;
 6037let «fooˇ» = 2;
 6038let foo = 2;
 6039let foo = «2ˇ»;"#,
 6040    );
 6041
 6042    // noop for multiple selections with different contents
 6043    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6044        .unwrap();
 6045    cx.assert_editor_state(
 6046        r#"let foo = 2;
 6047«letˇ» foo = 2;
 6048let «fooˇ» = 2;
 6049let foo = 2;
 6050let foo = «2ˇ»;"#,
 6051    );
 6052
 6053    // Test last selection direction should be preserved
 6054    cx.set_state(
 6055        r#"let foo = 2;
 6056let foo = 2;
 6057let «fooˇ» = 2;
 6058let «ˇfoo» = 2;
 6059let foo = 2;"#,
 6060    );
 6061
 6062    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6063        .unwrap();
 6064    cx.assert_editor_state(
 6065        r#"let foo = 2;
 6066let foo = 2;
 6067let «fooˇ» = 2;
 6068let «ˇfoo» = 2;
 6069let «ˇfoo» = 2;"#,
 6070    );
 6071}
 6072
 6073#[gpui::test]
 6074async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6075    init_test(cx, |_| {});
 6076
 6077    let mut cx =
 6078        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6079
 6080    cx.assert_editor_state(indoc! {"
 6081        ˇbbb
 6082        ccc
 6083
 6084        bbb
 6085        ccc
 6086        "});
 6087    cx.dispatch_action(SelectPrevious::default());
 6088    cx.assert_editor_state(indoc! {"
 6089                «bbbˇ»
 6090                ccc
 6091
 6092                bbb
 6093                ccc
 6094                "});
 6095    cx.dispatch_action(SelectPrevious::default());
 6096    cx.assert_editor_state(indoc! {"
 6097                «bbbˇ»
 6098                ccc
 6099
 6100                «bbbˇ»
 6101                ccc
 6102                "});
 6103}
 6104
 6105#[gpui::test]
 6106async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6107    init_test(cx, |_| {});
 6108
 6109    let mut cx = EditorTestContext::new(cx).await;
 6110    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6111
 6112    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6113        .unwrap();
 6114    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6115
 6116    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6117        .unwrap();
 6118    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6119
 6120    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6121    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6122
 6123    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6124    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6125
 6126    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6127        .unwrap();
 6128    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6129
 6130    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6131        .unwrap();
 6132    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 6133
 6134    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6135        .unwrap();
 6136    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6137}
 6138
 6139#[gpui::test]
 6140async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6141    init_test(cx, |_| {});
 6142
 6143    let mut cx = EditorTestContext::new(cx).await;
 6144    cx.set_state("");
 6145
 6146    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6147        .unwrap();
 6148    cx.assert_editor_state("«aˇ»");
 6149    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6150        .unwrap();
 6151    cx.assert_editor_state("«aˇ»");
 6152}
 6153
 6154#[gpui::test]
 6155async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6156    init_test(cx, |_| {});
 6157
 6158    let mut cx = EditorTestContext::new(cx).await;
 6159    cx.set_state(
 6160        r#"let foo = 2;
 6161lˇet foo = 2;
 6162let fooˇ = 2;
 6163let foo = 2;
 6164let foo = ˇ2;"#,
 6165    );
 6166
 6167    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6168        .unwrap();
 6169    cx.assert_editor_state(
 6170        r#"let foo = 2;
 6171«letˇ» foo = 2;
 6172let «fooˇ» = 2;
 6173let foo = 2;
 6174let foo = «2ˇ»;"#,
 6175    );
 6176
 6177    // noop for multiple selections with different contents
 6178    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6179        .unwrap();
 6180    cx.assert_editor_state(
 6181        r#"let foo = 2;
 6182«letˇ» foo = 2;
 6183let «fooˇ» = 2;
 6184let foo = 2;
 6185let foo = «2ˇ»;"#,
 6186    );
 6187}
 6188
 6189#[gpui::test]
 6190async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6191    init_test(cx, |_| {});
 6192
 6193    let mut cx = EditorTestContext::new(cx).await;
 6194    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6195
 6196    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6197        .unwrap();
 6198    // selection direction is preserved
 6199    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6200
 6201    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6202        .unwrap();
 6203    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6204
 6205    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6206    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6207
 6208    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6209    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6210
 6211    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6212        .unwrap();
 6213    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6214
 6215    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6216        .unwrap();
 6217    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6218}
 6219
 6220#[gpui::test]
 6221async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6222    init_test(cx, |_| {});
 6223
 6224    let language = Arc::new(Language::new(
 6225        LanguageConfig::default(),
 6226        Some(tree_sitter_rust::LANGUAGE.into()),
 6227    ));
 6228
 6229    let text = r#"
 6230        use mod1::mod2::{mod3, mod4};
 6231
 6232        fn fn_1(param1: bool, param2: &str) {
 6233            let var1 = "text";
 6234        }
 6235    "#
 6236    .unindent();
 6237
 6238    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6239    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6240    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6241
 6242    editor
 6243        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6244        .await;
 6245
 6246    editor.update_in(cx, |editor, window, cx| {
 6247        editor.change_selections(None, window, cx, |s| {
 6248            s.select_display_ranges([
 6249                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6250                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6251                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6252            ]);
 6253        });
 6254        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6255    });
 6256    editor.update(cx, |editor, cx| {
 6257        assert_text_with_selections(
 6258            editor,
 6259            indoc! {r#"
 6260                use mod1::mod2::{mod3, «mod4ˇ»};
 6261
 6262                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6263                    let var1 = "«ˇtext»";
 6264                }
 6265            "#},
 6266            cx,
 6267        );
 6268    });
 6269
 6270    editor.update_in(cx, |editor, window, cx| {
 6271        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6272    });
 6273    editor.update(cx, |editor, cx| {
 6274        assert_text_with_selections(
 6275            editor,
 6276            indoc! {r#"
 6277                use mod1::mod2::«{mod3, mod4}ˇ»;
 6278
 6279                «ˇfn fn_1(param1: bool, param2: &str) {
 6280                    let var1 = "text";
 6281 6282            "#},
 6283            cx,
 6284        );
 6285    });
 6286
 6287    editor.update_in(cx, |editor, window, cx| {
 6288        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6289    });
 6290    assert_eq!(
 6291        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6292        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6293    );
 6294
 6295    // Trying to expand the selected syntax node one more time has no effect.
 6296    editor.update_in(cx, |editor, window, cx| {
 6297        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6298    });
 6299    assert_eq!(
 6300        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6301        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6302    );
 6303
 6304    editor.update_in(cx, |editor, window, cx| {
 6305        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6306    });
 6307    editor.update(cx, |editor, cx| {
 6308        assert_text_with_selections(
 6309            editor,
 6310            indoc! {r#"
 6311                use mod1::mod2::«{mod3, mod4}ˇ»;
 6312
 6313                «ˇfn fn_1(param1: bool, param2: &str) {
 6314                    let var1 = "text";
 6315 6316            "#},
 6317            cx,
 6318        );
 6319    });
 6320
 6321    editor.update_in(cx, |editor, window, cx| {
 6322        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6323    });
 6324    editor.update(cx, |editor, cx| {
 6325        assert_text_with_selections(
 6326            editor,
 6327            indoc! {r#"
 6328                use mod1::mod2::{mod3, «mod4ˇ»};
 6329
 6330                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6331                    let var1 = "«ˇtext»";
 6332                }
 6333            "#},
 6334            cx,
 6335        );
 6336    });
 6337
 6338    editor.update_in(cx, |editor, window, cx| {
 6339        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6340    });
 6341    editor.update(cx, |editor, cx| {
 6342        assert_text_with_selections(
 6343            editor,
 6344            indoc! {r#"
 6345                use mod1::mod2::{mod3, mo«ˇ»d4};
 6346
 6347                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6348                    let var1 = "te«ˇ»xt";
 6349                }
 6350            "#},
 6351            cx,
 6352        );
 6353    });
 6354
 6355    // Trying to shrink the selected syntax node one more time has no effect.
 6356    editor.update_in(cx, |editor, window, cx| {
 6357        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6358    });
 6359    editor.update_in(cx, |editor, _, cx| {
 6360        assert_text_with_selections(
 6361            editor,
 6362            indoc! {r#"
 6363                use mod1::mod2::{mod3, mo«ˇ»d4};
 6364
 6365                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6366                    let var1 = "te«ˇ»xt";
 6367                }
 6368            "#},
 6369            cx,
 6370        );
 6371    });
 6372
 6373    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6374    // a fold.
 6375    editor.update_in(cx, |editor, window, cx| {
 6376        editor.fold_creases(
 6377            vec![
 6378                Crease::simple(
 6379                    Point::new(0, 21)..Point::new(0, 24),
 6380                    FoldPlaceholder::test(),
 6381                ),
 6382                Crease::simple(
 6383                    Point::new(3, 20)..Point::new(3, 22),
 6384                    FoldPlaceholder::test(),
 6385                ),
 6386            ],
 6387            true,
 6388            window,
 6389            cx,
 6390        );
 6391        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6392    });
 6393    editor.update(cx, |editor, cx| {
 6394        assert_text_with_selections(
 6395            editor,
 6396            indoc! {r#"
 6397                use mod1::mod2::«{mod3, mod4}ˇ»;
 6398
 6399                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6400                    let var1 = "«ˇtext»";
 6401                }
 6402            "#},
 6403            cx,
 6404        );
 6405    });
 6406}
 6407
 6408#[gpui::test]
 6409async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6410    init_test(cx, |_| {});
 6411
 6412    let language = Arc::new(Language::new(
 6413        LanguageConfig::default(),
 6414        Some(tree_sitter_rust::LANGUAGE.into()),
 6415    ));
 6416
 6417    let text = r#"
 6418        use mod1::mod2::{mod3, mod4};
 6419
 6420        fn fn_1(param1: bool, param2: &str) {
 6421            let var1 = "hello world";
 6422        }
 6423    "#
 6424    .unindent();
 6425
 6426    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6427    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6428    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6429
 6430    editor
 6431        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6432        .await;
 6433
 6434    // Test 1: Cursor on a letter of a string word
 6435    editor.update_in(cx, |editor, window, cx| {
 6436        editor.change_selections(None, window, cx, |s| {
 6437            s.select_display_ranges([
 6438                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6439            ]);
 6440        });
 6441    });
 6442    editor.update_in(cx, |editor, window, cx| {
 6443        assert_text_with_selections(
 6444            editor,
 6445            indoc! {r#"
 6446                use mod1::mod2::{mod3, mod4};
 6447
 6448                fn fn_1(param1: bool, param2: &str) {
 6449                    let var1 = "hˇello world";
 6450                }
 6451            "#},
 6452            cx,
 6453        );
 6454        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6455        assert_text_with_selections(
 6456            editor,
 6457            indoc! {r#"
 6458                use mod1::mod2::{mod3, mod4};
 6459
 6460                fn fn_1(param1: bool, param2: &str) {
 6461                    let var1 = "«ˇhello» world";
 6462                }
 6463            "#},
 6464            cx,
 6465        );
 6466    });
 6467
 6468    // Test 2: Partial selection within a word
 6469    editor.update_in(cx, |editor, window, cx| {
 6470        editor.change_selections(None, window, cx, |s| {
 6471            s.select_display_ranges([
 6472                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6473            ]);
 6474        });
 6475    });
 6476    editor.update_in(cx, |editor, window, cx| {
 6477        assert_text_with_selections(
 6478            editor,
 6479            indoc! {r#"
 6480                use mod1::mod2::{mod3, mod4};
 6481
 6482                fn fn_1(param1: bool, param2: &str) {
 6483                    let var1 = "h«elˇ»lo world";
 6484                }
 6485            "#},
 6486            cx,
 6487        );
 6488        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6489        assert_text_with_selections(
 6490            editor,
 6491            indoc! {r#"
 6492                use mod1::mod2::{mod3, mod4};
 6493
 6494                fn fn_1(param1: bool, param2: &str) {
 6495                    let var1 = "«ˇhello» world";
 6496                }
 6497            "#},
 6498            cx,
 6499        );
 6500    });
 6501
 6502    // Test 3: Complete word already selected
 6503    editor.update_in(cx, |editor, window, cx| {
 6504        editor.change_selections(None, window, cx, |s| {
 6505            s.select_display_ranges([
 6506                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6507            ]);
 6508        });
 6509    });
 6510    editor.update_in(cx, |editor, window, cx| {
 6511        assert_text_with_selections(
 6512            editor,
 6513            indoc! {r#"
 6514                use mod1::mod2::{mod3, mod4};
 6515
 6516                fn fn_1(param1: bool, param2: &str) {
 6517                    let var1 = "«helloˇ» world";
 6518                }
 6519            "#},
 6520            cx,
 6521        );
 6522        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6523        assert_text_with_selections(
 6524            editor,
 6525            indoc! {r#"
 6526                use mod1::mod2::{mod3, mod4};
 6527
 6528                fn fn_1(param1: bool, param2: &str) {
 6529                    let var1 = "«hello worldˇ»";
 6530                }
 6531            "#},
 6532            cx,
 6533        );
 6534    });
 6535
 6536    // Test 4: Selection spanning across words
 6537    editor.update_in(cx, |editor, window, cx| {
 6538        editor.change_selections(None, window, cx, |s| {
 6539            s.select_display_ranges([
 6540                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6541            ]);
 6542        });
 6543    });
 6544    editor.update_in(cx, |editor, window, cx| {
 6545        assert_text_with_selections(
 6546            editor,
 6547            indoc! {r#"
 6548                use mod1::mod2::{mod3, mod4};
 6549
 6550                fn fn_1(param1: bool, param2: &str) {
 6551                    let var1 = "hel«lo woˇ»rld";
 6552                }
 6553            "#},
 6554            cx,
 6555        );
 6556        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6557        assert_text_with_selections(
 6558            editor,
 6559            indoc! {r#"
 6560                use mod1::mod2::{mod3, mod4};
 6561
 6562                fn fn_1(param1: bool, param2: &str) {
 6563                    let var1 = "«ˇhello world»";
 6564                }
 6565            "#},
 6566            cx,
 6567        );
 6568    });
 6569
 6570    // Test 5: Expansion beyond string
 6571    editor.update_in(cx, |editor, window, cx| {
 6572        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6573        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6574        assert_text_with_selections(
 6575            editor,
 6576            indoc! {r#"
 6577                use mod1::mod2::{mod3, mod4};
 6578
 6579                fn fn_1(param1: bool, param2: &str) {
 6580                    «ˇlet var1 = "hello world";»
 6581                }
 6582            "#},
 6583            cx,
 6584        );
 6585    });
 6586}
 6587
 6588#[gpui::test]
 6589async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6590    init_test(cx, |_| {});
 6591
 6592    let base_text = r#"
 6593        impl A {
 6594            // this is an uncommitted comment
 6595
 6596            fn b() {
 6597                c();
 6598            }
 6599
 6600            // this is another uncommitted comment
 6601
 6602            fn d() {
 6603                // e
 6604                // f
 6605            }
 6606        }
 6607
 6608        fn g() {
 6609            // h
 6610        }
 6611    "#
 6612    .unindent();
 6613
 6614    let text = r#"
 6615        ˇimpl A {
 6616
 6617            fn b() {
 6618                c();
 6619            }
 6620
 6621            fn d() {
 6622                // e
 6623                // f
 6624            }
 6625        }
 6626
 6627        fn g() {
 6628            // h
 6629        }
 6630    "#
 6631    .unindent();
 6632
 6633    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6634    cx.set_state(&text);
 6635    cx.set_head_text(&base_text);
 6636    cx.update_editor(|editor, window, cx| {
 6637        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6638    });
 6639
 6640    cx.assert_state_with_diff(
 6641        "
 6642        ˇimpl A {
 6643      -     // this is an uncommitted comment
 6644
 6645            fn b() {
 6646                c();
 6647            }
 6648
 6649      -     // this is another uncommitted comment
 6650      -
 6651            fn d() {
 6652                // e
 6653                // f
 6654            }
 6655        }
 6656
 6657        fn g() {
 6658            // h
 6659        }
 6660    "
 6661        .unindent(),
 6662    );
 6663
 6664    let expected_display_text = "
 6665        impl A {
 6666            // this is an uncommitted comment
 6667
 6668            fn b() {
 6669 6670            }
 6671
 6672            // this is another uncommitted comment
 6673
 6674            fn d() {
 6675 6676            }
 6677        }
 6678
 6679        fn g() {
 6680 6681        }
 6682        "
 6683    .unindent();
 6684
 6685    cx.update_editor(|editor, window, cx| {
 6686        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6687        assert_eq!(editor.display_text(cx), expected_display_text);
 6688    });
 6689}
 6690
 6691#[gpui::test]
 6692async fn test_autoindent(cx: &mut TestAppContext) {
 6693    init_test(cx, |_| {});
 6694
 6695    let language = Arc::new(
 6696        Language::new(
 6697            LanguageConfig {
 6698                brackets: BracketPairConfig {
 6699                    pairs: vec![
 6700                        BracketPair {
 6701                            start: "{".to_string(),
 6702                            end: "}".to_string(),
 6703                            close: false,
 6704                            surround: false,
 6705                            newline: true,
 6706                        },
 6707                        BracketPair {
 6708                            start: "(".to_string(),
 6709                            end: ")".to_string(),
 6710                            close: false,
 6711                            surround: false,
 6712                            newline: true,
 6713                        },
 6714                    ],
 6715                    ..Default::default()
 6716                },
 6717                ..Default::default()
 6718            },
 6719            Some(tree_sitter_rust::LANGUAGE.into()),
 6720        )
 6721        .with_indents_query(
 6722            r#"
 6723                (_ "(" ")" @end) @indent
 6724                (_ "{" "}" @end) @indent
 6725            "#,
 6726        )
 6727        .unwrap(),
 6728    );
 6729
 6730    let text = "fn a() {}";
 6731
 6732    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6733    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6734    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6735    editor
 6736        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6737        .await;
 6738
 6739    editor.update_in(cx, |editor, window, cx| {
 6740        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6741        editor.newline(&Newline, window, cx);
 6742        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6743        assert_eq!(
 6744            editor.selections.ranges(cx),
 6745            &[
 6746                Point::new(1, 4)..Point::new(1, 4),
 6747                Point::new(3, 4)..Point::new(3, 4),
 6748                Point::new(5, 0)..Point::new(5, 0)
 6749            ]
 6750        );
 6751    });
 6752}
 6753
 6754#[gpui::test]
 6755async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6756    init_test(cx, |_| {});
 6757
 6758    {
 6759        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6760        cx.set_state(indoc! {"
 6761            impl A {
 6762
 6763                fn b() {}
 6764
 6765            «fn c() {
 6766
 6767            }ˇ»
 6768            }
 6769        "});
 6770
 6771        cx.update_editor(|editor, window, cx| {
 6772            editor.autoindent(&Default::default(), window, cx);
 6773        });
 6774
 6775        cx.assert_editor_state(indoc! {"
 6776            impl A {
 6777
 6778                fn b() {}
 6779
 6780                «fn c() {
 6781
 6782                }ˇ»
 6783            }
 6784        "});
 6785    }
 6786
 6787    {
 6788        let mut cx = EditorTestContext::new_multibuffer(
 6789            cx,
 6790            [indoc! { "
 6791                impl A {
 6792                «
 6793                // a
 6794                fn b(){}
 6795                »
 6796                «
 6797                    }
 6798                    fn c(){}
 6799                »
 6800            "}],
 6801        );
 6802
 6803        let buffer = cx.update_editor(|editor, _, cx| {
 6804            let buffer = editor.buffer().update(cx, |buffer, _| {
 6805                buffer.all_buffers().iter().next().unwrap().clone()
 6806            });
 6807            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6808            buffer
 6809        });
 6810
 6811        cx.run_until_parked();
 6812        cx.update_editor(|editor, window, cx| {
 6813            editor.select_all(&Default::default(), window, cx);
 6814            editor.autoindent(&Default::default(), window, cx)
 6815        });
 6816        cx.run_until_parked();
 6817
 6818        cx.update(|_, cx| {
 6819            assert_eq!(
 6820                buffer.read(cx).text(),
 6821                indoc! { "
 6822                    impl A {
 6823
 6824                        // a
 6825                        fn b(){}
 6826
 6827
 6828                    }
 6829                    fn c(){}
 6830
 6831                " }
 6832            )
 6833        });
 6834    }
 6835}
 6836
 6837#[gpui::test]
 6838async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6839    init_test(cx, |_| {});
 6840
 6841    let mut cx = EditorTestContext::new(cx).await;
 6842
 6843    let language = Arc::new(Language::new(
 6844        LanguageConfig {
 6845            brackets: BracketPairConfig {
 6846                pairs: vec![
 6847                    BracketPair {
 6848                        start: "{".to_string(),
 6849                        end: "}".to_string(),
 6850                        close: true,
 6851                        surround: true,
 6852                        newline: true,
 6853                    },
 6854                    BracketPair {
 6855                        start: "(".to_string(),
 6856                        end: ")".to_string(),
 6857                        close: true,
 6858                        surround: true,
 6859                        newline: true,
 6860                    },
 6861                    BracketPair {
 6862                        start: "/*".to_string(),
 6863                        end: " */".to_string(),
 6864                        close: true,
 6865                        surround: true,
 6866                        newline: true,
 6867                    },
 6868                    BracketPair {
 6869                        start: "[".to_string(),
 6870                        end: "]".to_string(),
 6871                        close: false,
 6872                        surround: false,
 6873                        newline: true,
 6874                    },
 6875                    BracketPair {
 6876                        start: "\"".to_string(),
 6877                        end: "\"".to_string(),
 6878                        close: true,
 6879                        surround: true,
 6880                        newline: false,
 6881                    },
 6882                    BracketPair {
 6883                        start: "<".to_string(),
 6884                        end: ">".to_string(),
 6885                        close: false,
 6886                        surround: true,
 6887                        newline: true,
 6888                    },
 6889                ],
 6890                ..Default::default()
 6891            },
 6892            autoclose_before: "})]".to_string(),
 6893            ..Default::default()
 6894        },
 6895        Some(tree_sitter_rust::LANGUAGE.into()),
 6896    ));
 6897
 6898    cx.language_registry().add(language.clone());
 6899    cx.update_buffer(|buffer, cx| {
 6900        buffer.set_language(Some(language), cx);
 6901    });
 6902
 6903    cx.set_state(
 6904        &r#"
 6905            🏀ˇ
 6906            εˇ
 6907            ❤️ˇ
 6908        "#
 6909        .unindent(),
 6910    );
 6911
 6912    // autoclose multiple nested brackets at multiple cursors
 6913    cx.update_editor(|editor, window, cx| {
 6914        editor.handle_input("{", window, cx);
 6915        editor.handle_input("{", window, cx);
 6916        editor.handle_input("{", window, cx);
 6917    });
 6918    cx.assert_editor_state(
 6919        &"
 6920            🏀{{{ˇ}}}
 6921            ε{{{ˇ}}}
 6922            ❤️{{{ˇ}}}
 6923        "
 6924        .unindent(),
 6925    );
 6926
 6927    // insert a different closing bracket
 6928    cx.update_editor(|editor, window, cx| {
 6929        editor.handle_input(")", window, cx);
 6930    });
 6931    cx.assert_editor_state(
 6932        &"
 6933            🏀{{{)ˇ}}}
 6934            ε{{{)ˇ}}}
 6935            ❤️{{{)ˇ}}}
 6936        "
 6937        .unindent(),
 6938    );
 6939
 6940    // skip over the auto-closed brackets when typing a closing bracket
 6941    cx.update_editor(|editor, window, cx| {
 6942        editor.move_right(&MoveRight, window, cx);
 6943        editor.handle_input("}", window, cx);
 6944        editor.handle_input("}", window, cx);
 6945        editor.handle_input("}", window, cx);
 6946    });
 6947    cx.assert_editor_state(
 6948        &"
 6949            🏀{{{)}}}}ˇ
 6950            ε{{{)}}}}ˇ
 6951            ❤️{{{)}}}}ˇ
 6952        "
 6953        .unindent(),
 6954    );
 6955
 6956    // autoclose multi-character pairs
 6957    cx.set_state(
 6958        &"
 6959            ˇ
 6960            ˇ
 6961        "
 6962        .unindent(),
 6963    );
 6964    cx.update_editor(|editor, window, cx| {
 6965        editor.handle_input("/", window, cx);
 6966        editor.handle_input("*", window, cx);
 6967    });
 6968    cx.assert_editor_state(
 6969        &"
 6970            /*ˇ */
 6971            /*ˇ */
 6972        "
 6973        .unindent(),
 6974    );
 6975
 6976    // one cursor autocloses a multi-character pair, one cursor
 6977    // does not autoclose.
 6978    cx.set_state(
 6979        &"
 6980 6981            ˇ
 6982        "
 6983        .unindent(),
 6984    );
 6985    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6986    cx.assert_editor_state(
 6987        &"
 6988            /*ˇ */
 6989 6990        "
 6991        .unindent(),
 6992    );
 6993
 6994    // Don't autoclose if the next character isn't whitespace and isn't
 6995    // listed in the language's "autoclose_before" section.
 6996    cx.set_state("ˇa b");
 6997    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6998    cx.assert_editor_state("{ˇa b");
 6999
 7000    // Don't autoclose if `close` is false for the bracket pair
 7001    cx.set_state("ˇ");
 7002    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 7003    cx.assert_editor_state("");
 7004
 7005    // Surround with brackets if text is selected
 7006    cx.set_state("«aˇ» b");
 7007    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7008    cx.assert_editor_state("{«aˇ»} b");
 7009
 7010    // Autoclose when not immediately after a word character
 7011    cx.set_state("a ˇ");
 7012    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7013    cx.assert_editor_state("a \"ˇ\"");
 7014
 7015    // Autoclose pair where the start and end characters are the same
 7016    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7017    cx.assert_editor_state("a \"\"ˇ");
 7018
 7019    // Don't autoclose when immediately after a word character
 7020    cx.set_state("");
 7021    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7022    cx.assert_editor_state("a\"ˇ");
 7023
 7024    // Do autoclose when after a non-word character
 7025    cx.set_state("");
 7026    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 7027    cx.assert_editor_state("{\"ˇ\"");
 7028
 7029    // Non identical pairs autoclose regardless of preceding character
 7030    cx.set_state("");
 7031    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7032    cx.assert_editor_state("a{ˇ}");
 7033
 7034    // Don't autoclose pair if autoclose is disabled
 7035    cx.set_state("ˇ");
 7036    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7037    cx.assert_editor_state("");
 7038
 7039    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7040    cx.set_state("«aˇ» b");
 7041    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7042    cx.assert_editor_state("<«aˇ»> b");
 7043}
 7044
 7045#[gpui::test]
 7046async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7047    init_test(cx, |settings| {
 7048        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7049    });
 7050
 7051    let mut cx = EditorTestContext::new(cx).await;
 7052
 7053    let language = Arc::new(Language::new(
 7054        LanguageConfig {
 7055            brackets: BracketPairConfig {
 7056                pairs: vec![
 7057                    BracketPair {
 7058                        start: "{".to_string(),
 7059                        end: "}".to_string(),
 7060                        close: true,
 7061                        surround: true,
 7062                        newline: true,
 7063                    },
 7064                    BracketPair {
 7065                        start: "(".to_string(),
 7066                        end: ")".to_string(),
 7067                        close: true,
 7068                        surround: true,
 7069                        newline: true,
 7070                    },
 7071                    BracketPair {
 7072                        start: "[".to_string(),
 7073                        end: "]".to_string(),
 7074                        close: false,
 7075                        surround: false,
 7076                        newline: true,
 7077                    },
 7078                ],
 7079                ..Default::default()
 7080            },
 7081            autoclose_before: "})]".to_string(),
 7082            ..Default::default()
 7083        },
 7084        Some(tree_sitter_rust::LANGUAGE.into()),
 7085    ));
 7086
 7087    cx.language_registry().add(language.clone());
 7088    cx.update_buffer(|buffer, cx| {
 7089        buffer.set_language(Some(language), cx);
 7090    });
 7091
 7092    cx.set_state(
 7093        &"
 7094            ˇ
 7095            ˇ
 7096            ˇ
 7097        "
 7098        .unindent(),
 7099    );
 7100
 7101    // ensure only matching closing brackets are skipped over
 7102    cx.update_editor(|editor, window, cx| {
 7103        editor.handle_input("}", window, cx);
 7104        editor.move_left(&MoveLeft, window, cx);
 7105        editor.handle_input(")", window, cx);
 7106        editor.move_left(&MoveLeft, window, cx);
 7107    });
 7108    cx.assert_editor_state(
 7109        &"
 7110            ˇ)}
 7111            ˇ)}
 7112            ˇ)}
 7113        "
 7114        .unindent(),
 7115    );
 7116
 7117    // skip-over closing brackets at multiple cursors
 7118    cx.update_editor(|editor, window, cx| {
 7119        editor.handle_input(")", window, cx);
 7120        editor.handle_input("}", window, cx);
 7121    });
 7122    cx.assert_editor_state(
 7123        &"
 7124            )}ˇ
 7125            )}ˇ
 7126            )}ˇ
 7127        "
 7128        .unindent(),
 7129    );
 7130
 7131    // ignore non-close brackets
 7132    cx.update_editor(|editor, window, cx| {
 7133        editor.handle_input("]", window, cx);
 7134        editor.move_left(&MoveLeft, window, cx);
 7135        editor.handle_input("]", window, cx);
 7136    });
 7137    cx.assert_editor_state(
 7138        &"
 7139            )}]ˇ]
 7140            )}]ˇ]
 7141            )}]ˇ]
 7142        "
 7143        .unindent(),
 7144    );
 7145}
 7146
 7147#[gpui::test]
 7148async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7149    init_test(cx, |_| {});
 7150
 7151    let mut cx = EditorTestContext::new(cx).await;
 7152
 7153    let html_language = Arc::new(
 7154        Language::new(
 7155            LanguageConfig {
 7156                name: "HTML".into(),
 7157                brackets: BracketPairConfig {
 7158                    pairs: vec![
 7159                        BracketPair {
 7160                            start: "<".into(),
 7161                            end: ">".into(),
 7162                            close: true,
 7163                            ..Default::default()
 7164                        },
 7165                        BracketPair {
 7166                            start: "{".into(),
 7167                            end: "}".into(),
 7168                            close: true,
 7169                            ..Default::default()
 7170                        },
 7171                        BracketPair {
 7172                            start: "(".into(),
 7173                            end: ")".into(),
 7174                            close: true,
 7175                            ..Default::default()
 7176                        },
 7177                    ],
 7178                    ..Default::default()
 7179                },
 7180                autoclose_before: "})]>".into(),
 7181                ..Default::default()
 7182            },
 7183            Some(tree_sitter_html::LANGUAGE.into()),
 7184        )
 7185        .with_injection_query(
 7186            r#"
 7187            (script_element
 7188                (raw_text) @injection.content
 7189                (#set! injection.language "javascript"))
 7190            "#,
 7191        )
 7192        .unwrap(),
 7193    );
 7194
 7195    let javascript_language = Arc::new(Language::new(
 7196        LanguageConfig {
 7197            name: "JavaScript".into(),
 7198            brackets: BracketPairConfig {
 7199                pairs: vec![
 7200                    BracketPair {
 7201                        start: "/*".into(),
 7202                        end: " */".into(),
 7203                        close: true,
 7204                        ..Default::default()
 7205                    },
 7206                    BracketPair {
 7207                        start: "{".into(),
 7208                        end: "}".into(),
 7209                        close: true,
 7210                        ..Default::default()
 7211                    },
 7212                    BracketPair {
 7213                        start: "(".into(),
 7214                        end: ")".into(),
 7215                        close: true,
 7216                        ..Default::default()
 7217                    },
 7218                ],
 7219                ..Default::default()
 7220            },
 7221            autoclose_before: "})]>".into(),
 7222            ..Default::default()
 7223        },
 7224        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7225    ));
 7226
 7227    cx.language_registry().add(html_language.clone());
 7228    cx.language_registry().add(javascript_language.clone());
 7229
 7230    cx.update_buffer(|buffer, cx| {
 7231        buffer.set_language(Some(html_language), cx);
 7232    });
 7233
 7234    cx.set_state(
 7235        &r#"
 7236            <body>ˇ
 7237                <script>
 7238                    var x = 1;ˇ
 7239                </script>
 7240            </body>ˇ
 7241        "#
 7242        .unindent(),
 7243    );
 7244
 7245    // Precondition: different languages are active at different locations.
 7246    cx.update_editor(|editor, window, cx| {
 7247        let snapshot = editor.snapshot(window, cx);
 7248        let cursors = editor.selections.ranges::<usize>(cx);
 7249        let languages = cursors
 7250            .iter()
 7251            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7252            .collect::<Vec<_>>();
 7253        assert_eq!(
 7254            languages,
 7255            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7256        );
 7257    });
 7258
 7259    // Angle brackets autoclose in HTML, but not JavaScript.
 7260    cx.update_editor(|editor, window, cx| {
 7261        editor.handle_input("<", window, cx);
 7262        editor.handle_input("a", window, cx);
 7263    });
 7264    cx.assert_editor_state(
 7265        &r#"
 7266            <body><aˇ>
 7267                <script>
 7268                    var x = 1;<aˇ
 7269                </script>
 7270            </body><aˇ>
 7271        "#
 7272        .unindent(),
 7273    );
 7274
 7275    // Curly braces and parens autoclose in both HTML and JavaScript.
 7276    cx.update_editor(|editor, window, cx| {
 7277        editor.handle_input(" b=", window, cx);
 7278        editor.handle_input("{", window, cx);
 7279        editor.handle_input("c", window, cx);
 7280        editor.handle_input("(", window, cx);
 7281    });
 7282    cx.assert_editor_state(
 7283        &r#"
 7284            <body><a b={c(ˇ)}>
 7285                <script>
 7286                    var x = 1;<a b={c(ˇ)}
 7287                </script>
 7288            </body><a b={c(ˇ)}>
 7289        "#
 7290        .unindent(),
 7291    );
 7292
 7293    // Brackets that were already autoclosed are skipped.
 7294    cx.update_editor(|editor, window, cx| {
 7295        editor.handle_input(")", window, cx);
 7296        editor.handle_input("d", window, cx);
 7297        editor.handle_input("}", window, cx);
 7298    });
 7299    cx.assert_editor_state(
 7300        &r#"
 7301            <body><a b={c()d}ˇ>
 7302                <script>
 7303                    var x = 1;<a b={c()d}ˇ
 7304                </script>
 7305            </body><a b={c()d}ˇ>
 7306        "#
 7307        .unindent(),
 7308    );
 7309    cx.update_editor(|editor, window, cx| {
 7310        editor.handle_input(">", window, cx);
 7311    });
 7312    cx.assert_editor_state(
 7313        &r#"
 7314            <body><a b={c()d}>ˇ
 7315                <script>
 7316                    var x = 1;<a b={c()d}>ˇ
 7317                </script>
 7318            </body><a b={c()d}>ˇ
 7319        "#
 7320        .unindent(),
 7321    );
 7322
 7323    // Reset
 7324    cx.set_state(
 7325        &r#"
 7326            <body>ˇ
 7327                <script>
 7328                    var x = 1;ˇ
 7329                </script>
 7330            </body>ˇ
 7331        "#
 7332        .unindent(),
 7333    );
 7334
 7335    cx.update_editor(|editor, window, cx| {
 7336        editor.handle_input("<", window, cx);
 7337    });
 7338    cx.assert_editor_state(
 7339        &r#"
 7340            <body><ˇ>
 7341                <script>
 7342                    var x = 1;<ˇ
 7343                </script>
 7344            </body><ˇ>
 7345        "#
 7346        .unindent(),
 7347    );
 7348
 7349    // When backspacing, the closing angle brackets are removed.
 7350    cx.update_editor(|editor, window, cx| {
 7351        editor.backspace(&Backspace, window, cx);
 7352    });
 7353    cx.assert_editor_state(
 7354        &r#"
 7355            <body>ˇ
 7356                <script>
 7357                    var x = 1;ˇ
 7358                </script>
 7359            </body>ˇ
 7360        "#
 7361        .unindent(),
 7362    );
 7363
 7364    // Block comments autoclose in JavaScript, but not HTML.
 7365    cx.update_editor(|editor, window, cx| {
 7366        editor.handle_input("/", window, cx);
 7367        editor.handle_input("*", window, cx);
 7368    });
 7369    cx.assert_editor_state(
 7370        &r#"
 7371            <body>/*ˇ
 7372                <script>
 7373                    var x = 1;/*ˇ */
 7374                </script>
 7375            </body>/*ˇ
 7376        "#
 7377        .unindent(),
 7378    );
 7379}
 7380
 7381#[gpui::test]
 7382async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7383    init_test(cx, |_| {});
 7384
 7385    let mut cx = EditorTestContext::new(cx).await;
 7386
 7387    let rust_language = Arc::new(
 7388        Language::new(
 7389            LanguageConfig {
 7390                name: "Rust".into(),
 7391                brackets: serde_json::from_value(json!([
 7392                    { "start": "{", "end": "}", "close": true, "newline": true },
 7393                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7394                ]))
 7395                .unwrap(),
 7396                autoclose_before: "})]>".into(),
 7397                ..Default::default()
 7398            },
 7399            Some(tree_sitter_rust::LANGUAGE.into()),
 7400        )
 7401        .with_override_query("(string_literal) @string")
 7402        .unwrap(),
 7403    );
 7404
 7405    cx.language_registry().add(rust_language.clone());
 7406    cx.update_buffer(|buffer, cx| {
 7407        buffer.set_language(Some(rust_language), cx);
 7408    });
 7409
 7410    cx.set_state(
 7411        &r#"
 7412            let x = ˇ
 7413        "#
 7414        .unindent(),
 7415    );
 7416
 7417    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7418    cx.update_editor(|editor, window, cx| {
 7419        editor.handle_input("\"", window, cx);
 7420    });
 7421    cx.assert_editor_state(
 7422        &r#"
 7423            let x = "ˇ"
 7424        "#
 7425        .unindent(),
 7426    );
 7427
 7428    // Inserting another quotation mark. The cursor moves across the existing
 7429    // automatically-inserted quotation mark.
 7430    cx.update_editor(|editor, window, cx| {
 7431        editor.handle_input("\"", window, cx);
 7432    });
 7433    cx.assert_editor_state(
 7434        &r#"
 7435            let x = ""ˇ
 7436        "#
 7437        .unindent(),
 7438    );
 7439
 7440    // Reset
 7441    cx.set_state(
 7442        &r#"
 7443            let x = ˇ
 7444        "#
 7445        .unindent(),
 7446    );
 7447
 7448    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7449    cx.update_editor(|editor, window, cx| {
 7450        editor.handle_input("\"", window, cx);
 7451        editor.handle_input(" ", window, cx);
 7452        editor.move_left(&Default::default(), window, cx);
 7453        editor.handle_input("\\", window, cx);
 7454        editor.handle_input("\"", window, cx);
 7455    });
 7456    cx.assert_editor_state(
 7457        &r#"
 7458            let x = "\"ˇ "
 7459        "#
 7460        .unindent(),
 7461    );
 7462
 7463    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7464    // mark. Nothing is inserted.
 7465    cx.update_editor(|editor, window, cx| {
 7466        editor.move_right(&Default::default(), window, cx);
 7467        editor.handle_input("\"", window, cx);
 7468    });
 7469    cx.assert_editor_state(
 7470        &r#"
 7471            let x = "\" "ˇ
 7472        "#
 7473        .unindent(),
 7474    );
 7475}
 7476
 7477#[gpui::test]
 7478async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7479    init_test(cx, |_| {});
 7480
 7481    let language = Arc::new(Language::new(
 7482        LanguageConfig {
 7483            brackets: BracketPairConfig {
 7484                pairs: vec![
 7485                    BracketPair {
 7486                        start: "{".to_string(),
 7487                        end: "}".to_string(),
 7488                        close: true,
 7489                        surround: true,
 7490                        newline: true,
 7491                    },
 7492                    BracketPair {
 7493                        start: "/* ".to_string(),
 7494                        end: "*/".to_string(),
 7495                        close: true,
 7496                        surround: true,
 7497                        ..Default::default()
 7498                    },
 7499                ],
 7500                ..Default::default()
 7501            },
 7502            ..Default::default()
 7503        },
 7504        Some(tree_sitter_rust::LANGUAGE.into()),
 7505    ));
 7506
 7507    let text = r#"
 7508        a
 7509        b
 7510        c
 7511    "#
 7512    .unindent();
 7513
 7514    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7515    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7516    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7517    editor
 7518        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7519        .await;
 7520
 7521    editor.update_in(cx, |editor, window, cx| {
 7522        editor.change_selections(None, window, cx, |s| {
 7523            s.select_display_ranges([
 7524                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7525                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7526                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7527            ])
 7528        });
 7529
 7530        editor.handle_input("{", window, cx);
 7531        editor.handle_input("{", window, cx);
 7532        editor.handle_input("{", window, cx);
 7533        assert_eq!(
 7534            editor.text(cx),
 7535            "
 7536                {{{a}}}
 7537                {{{b}}}
 7538                {{{c}}}
 7539            "
 7540            .unindent()
 7541        );
 7542        assert_eq!(
 7543            editor.selections.display_ranges(cx),
 7544            [
 7545                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7546                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7547                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7548            ]
 7549        );
 7550
 7551        editor.undo(&Undo, window, cx);
 7552        editor.undo(&Undo, window, cx);
 7553        editor.undo(&Undo, window, cx);
 7554        assert_eq!(
 7555            editor.text(cx),
 7556            "
 7557                a
 7558                b
 7559                c
 7560            "
 7561            .unindent()
 7562        );
 7563        assert_eq!(
 7564            editor.selections.display_ranges(cx),
 7565            [
 7566                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7567                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7568                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7569            ]
 7570        );
 7571
 7572        // Ensure inserting the first character of a multi-byte bracket pair
 7573        // doesn't surround the selections with the bracket.
 7574        editor.handle_input("/", window, cx);
 7575        assert_eq!(
 7576            editor.text(cx),
 7577            "
 7578                /
 7579                /
 7580                /
 7581            "
 7582            .unindent()
 7583        );
 7584        assert_eq!(
 7585            editor.selections.display_ranges(cx),
 7586            [
 7587                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7588                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7589                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7590            ]
 7591        );
 7592
 7593        editor.undo(&Undo, window, cx);
 7594        assert_eq!(
 7595            editor.text(cx),
 7596            "
 7597                a
 7598                b
 7599                c
 7600            "
 7601            .unindent()
 7602        );
 7603        assert_eq!(
 7604            editor.selections.display_ranges(cx),
 7605            [
 7606                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7607                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7608                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7609            ]
 7610        );
 7611
 7612        // Ensure inserting the last character of a multi-byte bracket pair
 7613        // doesn't surround the selections with the bracket.
 7614        editor.handle_input("*", window, cx);
 7615        assert_eq!(
 7616            editor.text(cx),
 7617            "
 7618                *
 7619                *
 7620                *
 7621            "
 7622            .unindent()
 7623        );
 7624        assert_eq!(
 7625            editor.selections.display_ranges(cx),
 7626            [
 7627                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7628                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7629                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7630            ]
 7631        );
 7632    });
 7633}
 7634
 7635#[gpui::test]
 7636async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7637    init_test(cx, |_| {});
 7638
 7639    let language = Arc::new(Language::new(
 7640        LanguageConfig {
 7641            brackets: BracketPairConfig {
 7642                pairs: vec![BracketPair {
 7643                    start: "{".to_string(),
 7644                    end: "}".to_string(),
 7645                    close: true,
 7646                    surround: true,
 7647                    newline: true,
 7648                }],
 7649                ..Default::default()
 7650            },
 7651            autoclose_before: "}".to_string(),
 7652            ..Default::default()
 7653        },
 7654        Some(tree_sitter_rust::LANGUAGE.into()),
 7655    ));
 7656
 7657    let text = r#"
 7658        a
 7659        b
 7660        c
 7661    "#
 7662    .unindent();
 7663
 7664    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7665    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7666    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7667    editor
 7668        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7669        .await;
 7670
 7671    editor.update_in(cx, |editor, window, cx| {
 7672        editor.change_selections(None, window, cx, |s| {
 7673            s.select_ranges([
 7674                Point::new(0, 1)..Point::new(0, 1),
 7675                Point::new(1, 1)..Point::new(1, 1),
 7676                Point::new(2, 1)..Point::new(2, 1),
 7677            ])
 7678        });
 7679
 7680        editor.handle_input("{", window, cx);
 7681        editor.handle_input("{", window, cx);
 7682        editor.handle_input("_", window, cx);
 7683        assert_eq!(
 7684            editor.text(cx),
 7685            "
 7686                a{{_}}
 7687                b{{_}}
 7688                c{{_}}
 7689            "
 7690            .unindent()
 7691        );
 7692        assert_eq!(
 7693            editor.selections.ranges::<Point>(cx),
 7694            [
 7695                Point::new(0, 4)..Point::new(0, 4),
 7696                Point::new(1, 4)..Point::new(1, 4),
 7697                Point::new(2, 4)..Point::new(2, 4)
 7698            ]
 7699        );
 7700
 7701        editor.backspace(&Default::default(), window, cx);
 7702        editor.backspace(&Default::default(), window, cx);
 7703        assert_eq!(
 7704            editor.text(cx),
 7705            "
 7706                a{}
 7707                b{}
 7708                c{}
 7709            "
 7710            .unindent()
 7711        );
 7712        assert_eq!(
 7713            editor.selections.ranges::<Point>(cx),
 7714            [
 7715                Point::new(0, 2)..Point::new(0, 2),
 7716                Point::new(1, 2)..Point::new(1, 2),
 7717                Point::new(2, 2)..Point::new(2, 2)
 7718            ]
 7719        );
 7720
 7721        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7722        assert_eq!(
 7723            editor.text(cx),
 7724            "
 7725                a
 7726                b
 7727                c
 7728            "
 7729            .unindent()
 7730        );
 7731        assert_eq!(
 7732            editor.selections.ranges::<Point>(cx),
 7733            [
 7734                Point::new(0, 1)..Point::new(0, 1),
 7735                Point::new(1, 1)..Point::new(1, 1),
 7736                Point::new(2, 1)..Point::new(2, 1)
 7737            ]
 7738        );
 7739    });
 7740}
 7741
 7742#[gpui::test]
 7743async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7744    init_test(cx, |settings| {
 7745        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7746    });
 7747
 7748    let mut cx = EditorTestContext::new(cx).await;
 7749
 7750    let language = Arc::new(Language::new(
 7751        LanguageConfig {
 7752            brackets: BracketPairConfig {
 7753                pairs: vec![
 7754                    BracketPair {
 7755                        start: "{".to_string(),
 7756                        end: "}".to_string(),
 7757                        close: true,
 7758                        surround: true,
 7759                        newline: true,
 7760                    },
 7761                    BracketPair {
 7762                        start: "(".to_string(),
 7763                        end: ")".to_string(),
 7764                        close: true,
 7765                        surround: true,
 7766                        newline: true,
 7767                    },
 7768                    BracketPair {
 7769                        start: "[".to_string(),
 7770                        end: "]".to_string(),
 7771                        close: false,
 7772                        surround: true,
 7773                        newline: true,
 7774                    },
 7775                ],
 7776                ..Default::default()
 7777            },
 7778            autoclose_before: "})]".to_string(),
 7779            ..Default::default()
 7780        },
 7781        Some(tree_sitter_rust::LANGUAGE.into()),
 7782    ));
 7783
 7784    cx.language_registry().add(language.clone());
 7785    cx.update_buffer(|buffer, cx| {
 7786        buffer.set_language(Some(language), cx);
 7787    });
 7788
 7789    cx.set_state(
 7790        &"
 7791            {(ˇ)}
 7792            [[ˇ]]
 7793            {(ˇ)}
 7794        "
 7795        .unindent(),
 7796    );
 7797
 7798    cx.update_editor(|editor, window, cx| {
 7799        editor.backspace(&Default::default(), window, cx);
 7800        editor.backspace(&Default::default(), window, cx);
 7801    });
 7802
 7803    cx.assert_editor_state(
 7804        &"
 7805            ˇ
 7806            ˇ]]
 7807            ˇ
 7808        "
 7809        .unindent(),
 7810    );
 7811
 7812    cx.update_editor(|editor, window, cx| {
 7813        editor.handle_input("{", window, cx);
 7814        editor.handle_input("{", window, cx);
 7815        editor.move_right(&MoveRight, window, cx);
 7816        editor.move_right(&MoveRight, window, cx);
 7817        editor.move_left(&MoveLeft, window, cx);
 7818        editor.move_left(&MoveLeft, window, cx);
 7819        editor.backspace(&Default::default(), window, cx);
 7820    });
 7821
 7822    cx.assert_editor_state(
 7823        &"
 7824            {ˇ}
 7825            {ˇ}]]
 7826            {ˇ}
 7827        "
 7828        .unindent(),
 7829    );
 7830
 7831    cx.update_editor(|editor, window, cx| {
 7832        editor.backspace(&Default::default(), window, cx);
 7833    });
 7834
 7835    cx.assert_editor_state(
 7836        &"
 7837            ˇ
 7838            ˇ]]
 7839            ˇ
 7840        "
 7841        .unindent(),
 7842    );
 7843}
 7844
 7845#[gpui::test]
 7846async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7847    init_test(cx, |_| {});
 7848
 7849    let language = Arc::new(Language::new(
 7850        LanguageConfig::default(),
 7851        Some(tree_sitter_rust::LANGUAGE.into()),
 7852    ));
 7853
 7854    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7855    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7856    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7857    editor
 7858        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7859        .await;
 7860
 7861    editor.update_in(cx, |editor, window, cx| {
 7862        editor.set_auto_replace_emoji_shortcode(true);
 7863
 7864        editor.handle_input("Hello ", window, cx);
 7865        editor.handle_input(":wave", window, cx);
 7866        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7867
 7868        editor.handle_input(":", window, cx);
 7869        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7870
 7871        editor.handle_input(" :smile", window, cx);
 7872        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7873
 7874        editor.handle_input(":", window, cx);
 7875        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7876
 7877        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7878        editor.handle_input(":wave", window, cx);
 7879        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7880
 7881        editor.handle_input(":", window, cx);
 7882        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7883
 7884        editor.handle_input(":1", window, cx);
 7885        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7886
 7887        editor.handle_input(":", window, cx);
 7888        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7889
 7890        // Ensure shortcode does not get replaced when it is part of a word
 7891        editor.handle_input(" Test:wave", window, cx);
 7892        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7893
 7894        editor.handle_input(":", window, cx);
 7895        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7896
 7897        editor.set_auto_replace_emoji_shortcode(false);
 7898
 7899        // Ensure shortcode does not get replaced when auto replace is off
 7900        editor.handle_input(" :wave", window, cx);
 7901        assert_eq!(
 7902            editor.text(cx),
 7903            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7904        );
 7905
 7906        editor.handle_input(":", window, cx);
 7907        assert_eq!(
 7908            editor.text(cx),
 7909            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7910        );
 7911    });
 7912}
 7913
 7914#[gpui::test]
 7915async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7916    init_test(cx, |_| {});
 7917
 7918    let (text, insertion_ranges) = marked_text_ranges(
 7919        indoc! {"
 7920            ˇ
 7921        "},
 7922        false,
 7923    );
 7924
 7925    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7926    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7927
 7928    _ = editor.update_in(cx, |editor, window, cx| {
 7929        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7930
 7931        editor
 7932            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7933            .unwrap();
 7934
 7935        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7936            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7937            assert_eq!(editor.text(cx), expected_text);
 7938            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7939        }
 7940
 7941        assert(
 7942            editor,
 7943            cx,
 7944            indoc! {"
 7945            type «» =•
 7946            "},
 7947        );
 7948
 7949        assert!(editor.context_menu_visible(), "There should be a matches");
 7950    });
 7951}
 7952
 7953#[gpui::test]
 7954async fn test_snippets(cx: &mut TestAppContext) {
 7955    init_test(cx, |_| {});
 7956
 7957    let (text, insertion_ranges) = marked_text_ranges(
 7958        indoc! {"
 7959            a.ˇ b
 7960            a.ˇ b
 7961            a.ˇ b
 7962        "},
 7963        false,
 7964    );
 7965
 7966    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7967    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7968
 7969    editor.update_in(cx, |editor, window, cx| {
 7970        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7971
 7972        editor
 7973            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7974            .unwrap();
 7975
 7976        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7977            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7978            assert_eq!(editor.text(cx), expected_text);
 7979            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7980        }
 7981
 7982        assert(
 7983            editor,
 7984            cx,
 7985            indoc! {"
 7986                a.f(«one», two, «three») b
 7987                a.f(«one», two, «three») b
 7988                a.f(«one», two, «three») b
 7989            "},
 7990        );
 7991
 7992        // Can't move earlier than the first tab stop
 7993        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7994        assert(
 7995            editor,
 7996            cx,
 7997            indoc! {"
 7998                a.f(«one», two, «three») b
 7999                a.f(«one», two, «three») b
 8000                a.f(«one», two, «three») b
 8001            "},
 8002        );
 8003
 8004        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8005        assert(
 8006            editor,
 8007            cx,
 8008            indoc! {"
 8009                a.f(one, «two», three) b
 8010                a.f(one, «two», three) b
 8011                a.f(one, «two», three) b
 8012            "},
 8013        );
 8014
 8015        editor.move_to_prev_snippet_tabstop(window, cx);
 8016        assert(
 8017            editor,
 8018            cx,
 8019            indoc! {"
 8020                a.f(«one», two, «three») b
 8021                a.f(«one», two, «three») b
 8022                a.f(«one», two, «three») b
 8023            "},
 8024        );
 8025
 8026        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8027        assert(
 8028            editor,
 8029            cx,
 8030            indoc! {"
 8031                a.f(one, «two», three) b
 8032                a.f(one, «two», three) b
 8033                a.f(one, «two», three) b
 8034            "},
 8035        );
 8036        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8037        assert(
 8038            editor,
 8039            cx,
 8040            indoc! {"
 8041                a.f(one, two, three)ˇ b
 8042                a.f(one, two, three)ˇ b
 8043                a.f(one, two, three)ˇ b
 8044            "},
 8045        );
 8046
 8047        // As soon as the last tab stop is reached, snippet state is gone
 8048        editor.move_to_prev_snippet_tabstop(window, cx);
 8049        assert(
 8050            editor,
 8051            cx,
 8052            indoc! {"
 8053                a.f(one, two, three)ˇ b
 8054                a.f(one, two, three)ˇ b
 8055                a.f(one, two, three)ˇ b
 8056            "},
 8057        );
 8058    });
 8059}
 8060
 8061#[gpui::test]
 8062async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8063    init_test(cx, |_| {});
 8064
 8065    let fs = FakeFs::new(cx.executor());
 8066    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8067
 8068    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8069
 8070    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8071    language_registry.add(rust_lang());
 8072    let mut fake_servers = language_registry.register_fake_lsp(
 8073        "Rust",
 8074        FakeLspAdapter {
 8075            capabilities: lsp::ServerCapabilities {
 8076                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8077                ..Default::default()
 8078            },
 8079            ..Default::default()
 8080        },
 8081    );
 8082
 8083    let buffer = project
 8084        .update(cx, |project, cx| {
 8085            project.open_local_buffer(path!("/file.rs"), cx)
 8086        })
 8087        .await
 8088        .unwrap();
 8089
 8090    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8091    let (editor, cx) = cx.add_window_view(|window, cx| {
 8092        build_editor_with_project(project.clone(), buffer, window, cx)
 8093    });
 8094    editor.update_in(cx, |editor, window, cx| {
 8095        editor.set_text("one\ntwo\nthree\n", window, cx)
 8096    });
 8097    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8098
 8099    cx.executor().start_waiting();
 8100    let fake_server = fake_servers.next().await.unwrap();
 8101
 8102    {
 8103        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8104            move |params, _| async move {
 8105                assert_eq!(
 8106                    params.text_document.uri,
 8107                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8108                );
 8109                assert_eq!(params.options.tab_size, 4);
 8110                Ok(Some(vec![lsp::TextEdit::new(
 8111                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8112                    ", ".to_string(),
 8113                )]))
 8114            },
 8115        );
 8116        let save = editor
 8117            .update_in(cx, |editor, window, cx| {
 8118                editor.save(true, project.clone(), window, cx)
 8119            })
 8120            .unwrap();
 8121        cx.executor().start_waiting();
 8122        save.await;
 8123
 8124        assert_eq!(
 8125            editor.update(cx, |editor, cx| editor.text(cx)),
 8126            "one, two\nthree\n"
 8127        );
 8128        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8129    }
 8130
 8131    {
 8132        editor.update_in(cx, |editor, window, cx| {
 8133            editor.set_text("one\ntwo\nthree\n", window, cx)
 8134        });
 8135        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8136
 8137        // Ensure we can still save even if formatting hangs.
 8138        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8139            move |params, _| async move {
 8140                assert_eq!(
 8141                    params.text_document.uri,
 8142                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8143                );
 8144                futures::future::pending::<()>().await;
 8145                unreachable!()
 8146            },
 8147        );
 8148        let save = editor
 8149            .update_in(cx, |editor, window, cx| {
 8150                editor.save(true, project.clone(), window, cx)
 8151            })
 8152            .unwrap();
 8153        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8154        cx.executor().start_waiting();
 8155        save.await;
 8156        assert_eq!(
 8157            editor.update(cx, |editor, cx| editor.text(cx)),
 8158            "one\ntwo\nthree\n"
 8159        );
 8160    }
 8161
 8162    // For non-dirty buffer, no formatting request should be sent
 8163    {
 8164        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8165
 8166        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8167            panic!("Should not be invoked on non-dirty buffer");
 8168        });
 8169        let save = editor
 8170            .update_in(cx, |editor, window, cx| {
 8171                editor.save(true, project.clone(), window, cx)
 8172            })
 8173            .unwrap();
 8174        cx.executor().start_waiting();
 8175        save.await;
 8176    }
 8177
 8178    // Set rust language override and assert overridden tabsize is sent to language server
 8179    update_test_language_settings(cx, |settings| {
 8180        settings.languages.insert(
 8181            "Rust".into(),
 8182            LanguageSettingsContent {
 8183                tab_size: NonZeroU32::new(8),
 8184                ..Default::default()
 8185            },
 8186        );
 8187    });
 8188
 8189    {
 8190        editor.update_in(cx, |editor, window, cx| {
 8191            editor.set_text("somehting_new\n", window, cx)
 8192        });
 8193        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8194        let _formatting_request_signal = fake_server
 8195            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8196                assert_eq!(
 8197                    params.text_document.uri,
 8198                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8199                );
 8200                assert_eq!(params.options.tab_size, 8);
 8201                Ok(Some(vec![]))
 8202            });
 8203        let save = editor
 8204            .update_in(cx, |editor, window, cx| {
 8205                editor.save(true, project.clone(), window, cx)
 8206            })
 8207            .unwrap();
 8208        cx.executor().start_waiting();
 8209        save.await;
 8210    }
 8211}
 8212
 8213#[gpui::test]
 8214async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8215    init_test(cx, |_| {});
 8216
 8217    let cols = 4;
 8218    let rows = 10;
 8219    let sample_text_1 = sample_text(rows, cols, 'a');
 8220    assert_eq!(
 8221        sample_text_1,
 8222        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8223    );
 8224    let sample_text_2 = sample_text(rows, cols, 'l');
 8225    assert_eq!(
 8226        sample_text_2,
 8227        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8228    );
 8229    let sample_text_3 = sample_text(rows, cols, 'v');
 8230    assert_eq!(
 8231        sample_text_3,
 8232        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8233    );
 8234
 8235    let fs = FakeFs::new(cx.executor());
 8236    fs.insert_tree(
 8237        path!("/a"),
 8238        json!({
 8239            "main.rs": sample_text_1,
 8240            "other.rs": sample_text_2,
 8241            "lib.rs": sample_text_3,
 8242        }),
 8243    )
 8244    .await;
 8245
 8246    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8247    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8248    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8249
 8250    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8251    language_registry.add(rust_lang());
 8252    let mut fake_servers = language_registry.register_fake_lsp(
 8253        "Rust",
 8254        FakeLspAdapter {
 8255            capabilities: lsp::ServerCapabilities {
 8256                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8257                ..Default::default()
 8258            },
 8259            ..Default::default()
 8260        },
 8261    );
 8262
 8263    let worktree = project.update(cx, |project, cx| {
 8264        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8265        assert_eq!(worktrees.len(), 1);
 8266        worktrees.pop().unwrap()
 8267    });
 8268    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8269
 8270    let buffer_1 = project
 8271        .update(cx, |project, cx| {
 8272            project.open_buffer((worktree_id, "main.rs"), cx)
 8273        })
 8274        .await
 8275        .unwrap();
 8276    let buffer_2 = project
 8277        .update(cx, |project, cx| {
 8278            project.open_buffer((worktree_id, "other.rs"), cx)
 8279        })
 8280        .await
 8281        .unwrap();
 8282    let buffer_3 = project
 8283        .update(cx, |project, cx| {
 8284            project.open_buffer((worktree_id, "lib.rs"), cx)
 8285        })
 8286        .await
 8287        .unwrap();
 8288
 8289    let multi_buffer = cx.new(|cx| {
 8290        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8291        multi_buffer.push_excerpts(
 8292            buffer_1.clone(),
 8293            [
 8294                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8295                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8296                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8297            ],
 8298            cx,
 8299        );
 8300        multi_buffer.push_excerpts(
 8301            buffer_2.clone(),
 8302            [
 8303                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8304                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8305                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8306            ],
 8307            cx,
 8308        );
 8309        multi_buffer.push_excerpts(
 8310            buffer_3.clone(),
 8311            [
 8312                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8313                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8314                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8315            ],
 8316            cx,
 8317        );
 8318        multi_buffer
 8319    });
 8320    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8321        Editor::new(
 8322            EditorMode::full(),
 8323            multi_buffer,
 8324            Some(project.clone()),
 8325            window,
 8326            cx,
 8327        )
 8328    });
 8329
 8330    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8331        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8332            s.select_ranges(Some(1..2))
 8333        });
 8334        editor.insert("|one|two|three|", window, cx);
 8335    });
 8336    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8337    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8338        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8339            s.select_ranges(Some(60..70))
 8340        });
 8341        editor.insert("|four|five|six|", window, cx);
 8342    });
 8343    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8344
 8345    // First two buffers should be edited, but not the third one.
 8346    assert_eq!(
 8347        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8348        "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}",
 8349    );
 8350    buffer_1.update(cx, |buffer, _| {
 8351        assert!(buffer.is_dirty());
 8352        assert_eq!(
 8353            buffer.text(),
 8354            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8355        )
 8356    });
 8357    buffer_2.update(cx, |buffer, _| {
 8358        assert!(buffer.is_dirty());
 8359        assert_eq!(
 8360            buffer.text(),
 8361            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8362        )
 8363    });
 8364    buffer_3.update(cx, |buffer, _| {
 8365        assert!(!buffer.is_dirty());
 8366        assert_eq!(buffer.text(), sample_text_3,)
 8367    });
 8368    cx.executor().run_until_parked();
 8369
 8370    cx.executor().start_waiting();
 8371    let save = multi_buffer_editor
 8372        .update_in(cx, |editor, window, cx| {
 8373            editor.save(true, project.clone(), window, cx)
 8374        })
 8375        .unwrap();
 8376
 8377    let fake_server = fake_servers.next().await.unwrap();
 8378    fake_server
 8379        .server
 8380        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8381            Ok(Some(vec![lsp::TextEdit::new(
 8382                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8383                format!("[{} formatted]", params.text_document.uri),
 8384            )]))
 8385        })
 8386        .detach();
 8387    save.await;
 8388
 8389    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8390    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8391    assert_eq!(
 8392        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8393        uri!(
 8394            "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}"
 8395        ),
 8396    );
 8397    buffer_1.update(cx, |buffer, _| {
 8398        assert!(!buffer.is_dirty());
 8399        assert_eq!(
 8400            buffer.text(),
 8401            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8402        )
 8403    });
 8404    buffer_2.update(cx, |buffer, _| {
 8405        assert!(!buffer.is_dirty());
 8406        assert_eq!(
 8407            buffer.text(),
 8408            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8409        )
 8410    });
 8411    buffer_3.update(cx, |buffer, _| {
 8412        assert!(!buffer.is_dirty());
 8413        assert_eq!(buffer.text(), sample_text_3,)
 8414    });
 8415}
 8416
 8417#[gpui::test]
 8418async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8419    init_test(cx, |_| {});
 8420
 8421    let fs = FakeFs::new(cx.executor());
 8422    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8423
 8424    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8425
 8426    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8427    language_registry.add(rust_lang());
 8428    let mut fake_servers = language_registry.register_fake_lsp(
 8429        "Rust",
 8430        FakeLspAdapter {
 8431            capabilities: lsp::ServerCapabilities {
 8432                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8433                ..Default::default()
 8434            },
 8435            ..Default::default()
 8436        },
 8437    );
 8438
 8439    let buffer = project
 8440        .update(cx, |project, cx| {
 8441            project.open_local_buffer(path!("/file.rs"), cx)
 8442        })
 8443        .await
 8444        .unwrap();
 8445
 8446    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8447    let (editor, cx) = cx.add_window_view(|window, cx| {
 8448        build_editor_with_project(project.clone(), buffer, window, cx)
 8449    });
 8450    editor.update_in(cx, |editor, window, cx| {
 8451        editor.set_text("one\ntwo\nthree\n", window, cx)
 8452    });
 8453    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8454
 8455    cx.executor().start_waiting();
 8456    let fake_server = fake_servers.next().await.unwrap();
 8457
 8458    let save = editor
 8459        .update_in(cx, |editor, window, cx| {
 8460            editor.save(true, project.clone(), window, cx)
 8461        })
 8462        .unwrap();
 8463    fake_server
 8464        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8465            assert_eq!(
 8466                params.text_document.uri,
 8467                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8468            );
 8469            assert_eq!(params.options.tab_size, 4);
 8470            Ok(Some(vec![lsp::TextEdit::new(
 8471                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8472                ", ".to_string(),
 8473            )]))
 8474        })
 8475        .next()
 8476        .await;
 8477    cx.executor().start_waiting();
 8478    save.await;
 8479    assert_eq!(
 8480        editor.update(cx, |editor, cx| editor.text(cx)),
 8481        "one, two\nthree\n"
 8482    );
 8483    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8484
 8485    editor.update_in(cx, |editor, window, cx| {
 8486        editor.set_text("one\ntwo\nthree\n", window, cx)
 8487    });
 8488    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8489
 8490    // Ensure we can still save even if formatting hangs.
 8491    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8492        move |params, _| async move {
 8493            assert_eq!(
 8494                params.text_document.uri,
 8495                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8496            );
 8497            futures::future::pending::<()>().await;
 8498            unreachable!()
 8499        },
 8500    );
 8501    let save = editor
 8502        .update_in(cx, |editor, window, cx| {
 8503            editor.save(true, project.clone(), window, cx)
 8504        })
 8505        .unwrap();
 8506    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8507    cx.executor().start_waiting();
 8508    save.await;
 8509    assert_eq!(
 8510        editor.update(cx, |editor, cx| editor.text(cx)),
 8511        "one\ntwo\nthree\n"
 8512    );
 8513    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8514
 8515    // For non-dirty buffer, no formatting request should be sent
 8516    let save = editor
 8517        .update_in(cx, |editor, window, cx| {
 8518            editor.save(true, project.clone(), window, cx)
 8519        })
 8520        .unwrap();
 8521    let _pending_format_request = fake_server
 8522        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8523            panic!("Should not be invoked on non-dirty buffer");
 8524        })
 8525        .next();
 8526    cx.executor().start_waiting();
 8527    save.await;
 8528
 8529    // Set Rust language override and assert overridden tabsize is sent to language server
 8530    update_test_language_settings(cx, |settings| {
 8531        settings.languages.insert(
 8532            "Rust".into(),
 8533            LanguageSettingsContent {
 8534                tab_size: NonZeroU32::new(8),
 8535                ..Default::default()
 8536            },
 8537        );
 8538    });
 8539
 8540    editor.update_in(cx, |editor, window, cx| {
 8541        editor.set_text("somehting_new\n", window, cx)
 8542    });
 8543    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8544    let save = editor
 8545        .update_in(cx, |editor, window, cx| {
 8546            editor.save(true, project.clone(), window, cx)
 8547        })
 8548        .unwrap();
 8549    fake_server
 8550        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8551            assert_eq!(
 8552                params.text_document.uri,
 8553                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8554            );
 8555            assert_eq!(params.options.tab_size, 8);
 8556            Ok(Some(vec![]))
 8557        })
 8558        .next()
 8559        .await;
 8560    cx.executor().start_waiting();
 8561    save.await;
 8562}
 8563
 8564#[gpui::test]
 8565async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8566    init_test(cx, |settings| {
 8567        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8568            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8569        ))
 8570    });
 8571
 8572    let fs = FakeFs::new(cx.executor());
 8573    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8574
 8575    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8576
 8577    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8578    language_registry.add(Arc::new(Language::new(
 8579        LanguageConfig {
 8580            name: "Rust".into(),
 8581            matcher: LanguageMatcher {
 8582                path_suffixes: vec!["rs".to_string()],
 8583                ..Default::default()
 8584            },
 8585            ..LanguageConfig::default()
 8586        },
 8587        Some(tree_sitter_rust::LANGUAGE.into()),
 8588    )));
 8589    update_test_language_settings(cx, |settings| {
 8590        // Enable Prettier formatting for the same buffer, and ensure
 8591        // LSP is called instead of Prettier.
 8592        settings.defaults.prettier = Some(PrettierSettings {
 8593            allowed: true,
 8594            ..PrettierSettings::default()
 8595        });
 8596    });
 8597    let mut fake_servers = language_registry.register_fake_lsp(
 8598        "Rust",
 8599        FakeLspAdapter {
 8600            capabilities: lsp::ServerCapabilities {
 8601                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8602                ..Default::default()
 8603            },
 8604            ..Default::default()
 8605        },
 8606    );
 8607
 8608    let buffer = project
 8609        .update(cx, |project, cx| {
 8610            project.open_local_buffer(path!("/file.rs"), cx)
 8611        })
 8612        .await
 8613        .unwrap();
 8614
 8615    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8616    let (editor, cx) = cx.add_window_view(|window, cx| {
 8617        build_editor_with_project(project.clone(), buffer, window, cx)
 8618    });
 8619    editor.update_in(cx, |editor, window, cx| {
 8620        editor.set_text("one\ntwo\nthree\n", window, cx)
 8621    });
 8622
 8623    cx.executor().start_waiting();
 8624    let fake_server = fake_servers.next().await.unwrap();
 8625
 8626    let format = editor
 8627        .update_in(cx, |editor, window, cx| {
 8628            editor.perform_format(
 8629                project.clone(),
 8630                FormatTrigger::Manual,
 8631                FormatTarget::Buffers,
 8632                window,
 8633                cx,
 8634            )
 8635        })
 8636        .unwrap();
 8637    fake_server
 8638        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8639            assert_eq!(
 8640                params.text_document.uri,
 8641                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8642            );
 8643            assert_eq!(params.options.tab_size, 4);
 8644            Ok(Some(vec![lsp::TextEdit::new(
 8645                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8646                ", ".to_string(),
 8647            )]))
 8648        })
 8649        .next()
 8650        .await;
 8651    cx.executor().start_waiting();
 8652    format.await;
 8653    assert_eq!(
 8654        editor.update(cx, |editor, cx| editor.text(cx)),
 8655        "one, two\nthree\n"
 8656    );
 8657
 8658    editor.update_in(cx, |editor, window, cx| {
 8659        editor.set_text("one\ntwo\nthree\n", window, cx)
 8660    });
 8661    // Ensure we don't lock if formatting hangs.
 8662    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8663        move |params, _| async move {
 8664            assert_eq!(
 8665                params.text_document.uri,
 8666                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8667            );
 8668            futures::future::pending::<()>().await;
 8669            unreachable!()
 8670        },
 8671    );
 8672    let format = editor
 8673        .update_in(cx, |editor, window, cx| {
 8674            editor.perform_format(
 8675                project,
 8676                FormatTrigger::Manual,
 8677                FormatTarget::Buffers,
 8678                window,
 8679                cx,
 8680            )
 8681        })
 8682        .unwrap();
 8683    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8684    cx.executor().start_waiting();
 8685    format.await;
 8686    assert_eq!(
 8687        editor.update(cx, |editor, cx| editor.text(cx)),
 8688        "one\ntwo\nthree\n"
 8689    );
 8690}
 8691
 8692#[gpui::test]
 8693async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8694    init_test(cx, |settings| {
 8695        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8696        settings.defaults.formatter =
 8697            Some(language_settings::SelectedFormatter::List(FormatterList(
 8698                vec![
 8699                    Formatter::LanguageServer { name: None },
 8700                    Formatter::CodeActions(
 8701                        [
 8702                            ("code-action-1".into(), true),
 8703                            ("code-action-2".into(), true),
 8704                        ]
 8705                        .into_iter()
 8706                        .collect(),
 8707                    ),
 8708                ]
 8709                .into(),
 8710            )))
 8711    });
 8712
 8713    let fs = FakeFs::new(cx.executor());
 8714    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8715        .await;
 8716
 8717    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8718    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8719    language_registry.add(rust_lang());
 8720
 8721    let mut fake_servers = language_registry.register_fake_lsp(
 8722        "Rust",
 8723        FakeLspAdapter {
 8724            capabilities: lsp::ServerCapabilities {
 8725                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8726                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8727                    commands: vec!["the-command-for-code-action-1".into()],
 8728                    ..Default::default()
 8729                }),
 8730                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8731                ..Default::default()
 8732            },
 8733            ..Default::default()
 8734        },
 8735    );
 8736
 8737    let buffer = project
 8738        .update(cx, |project, cx| {
 8739            project.open_local_buffer(path!("/file.rs"), cx)
 8740        })
 8741        .await
 8742        .unwrap();
 8743
 8744    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8745    let (editor, cx) = cx.add_window_view(|window, cx| {
 8746        build_editor_with_project(project.clone(), buffer, window, cx)
 8747    });
 8748
 8749    cx.executor().start_waiting();
 8750
 8751    let fake_server = fake_servers.next().await.unwrap();
 8752    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8753        move |_params, _| async move {
 8754            Ok(Some(vec![lsp::TextEdit::new(
 8755                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8756                "applied-formatting\n".to_string(),
 8757            )]))
 8758        },
 8759    );
 8760    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8761        move |params, _| async move {
 8762            assert_eq!(
 8763                params.context.only,
 8764                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8765            );
 8766            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8767            Ok(Some(vec![
 8768                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8769                    kind: Some("code-action-1".into()),
 8770                    edit: Some(lsp::WorkspaceEdit::new(
 8771                        [(
 8772                            uri.clone(),
 8773                            vec![lsp::TextEdit::new(
 8774                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8775                                "applied-code-action-1-edit\n".to_string(),
 8776                            )],
 8777                        )]
 8778                        .into_iter()
 8779                        .collect(),
 8780                    )),
 8781                    command: Some(lsp::Command {
 8782                        command: "the-command-for-code-action-1".into(),
 8783                        ..Default::default()
 8784                    }),
 8785                    ..Default::default()
 8786                }),
 8787                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8788                    kind: Some("code-action-2".into()),
 8789                    edit: Some(lsp::WorkspaceEdit::new(
 8790                        [(
 8791                            uri.clone(),
 8792                            vec![lsp::TextEdit::new(
 8793                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8794                                "applied-code-action-2-edit\n".to_string(),
 8795                            )],
 8796                        )]
 8797                        .into_iter()
 8798                        .collect(),
 8799                    )),
 8800                    ..Default::default()
 8801                }),
 8802            ]))
 8803        },
 8804    );
 8805
 8806    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8807        move |params, _| async move { Ok(params) }
 8808    });
 8809
 8810    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8811    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8812        let fake = fake_server.clone();
 8813        let lock = command_lock.clone();
 8814        move |params, _| {
 8815            assert_eq!(params.command, "the-command-for-code-action-1");
 8816            let fake = fake.clone();
 8817            let lock = lock.clone();
 8818            async move {
 8819                lock.lock().await;
 8820                fake.server
 8821                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8822                        label: None,
 8823                        edit: lsp::WorkspaceEdit {
 8824                            changes: Some(
 8825                                [(
 8826                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8827                                    vec![lsp::TextEdit {
 8828                                        range: lsp::Range::new(
 8829                                            lsp::Position::new(0, 0),
 8830                                            lsp::Position::new(0, 0),
 8831                                        ),
 8832                                        new_text: "applied-code-action-1-command\n".into(),
 8833                                    }],
 8834                                )]
 8835                                .into_iter()
 8836                                .collect(),
 8837                            ),
 8838                            ..Default::default()
 8839                        },
 8840                    })
 8841                    .await
 8842                    .unwrap();
 8843                Ok(Some(json!(null)))
 8844            }
 8845        }
 8846    });
 8847
 8848    cx.executor().start_waiting();
 8849    editor
 8850        .update_in(cx, |editor, window, cx| {
 8851            editor.perform_format(
 8852                project.clone(),
 8853                FormatTrigger::Manual,
 8854                FormatTarget::Buffers,
 8855                window,
 8856                cx,
 8857            )
 8858        })
 8859        .unwrap()
 8860        .await;
 8861    editor.update(cx, |editor, cx| {
 8862        assert_eq!(
 8863            editor.text(cx),
 8864            r#"
 8865                applied-code-action-2-edit
 8866                applied-code-action-1-command
 8867                applied-code-action-1-edit
 8868                applied-formatting
 8869                one
 8870                two
 8871                three
 8872            "#
 8873            .unindent()
 8874        );
 8875    });
 8876
 8877    editor.update_in(cx, |editor, window, cx| {
 8878        editor.undo(&Default::default(), window, cx);
 8879        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8880    });
 8881
 8882    // Perform a manual edit while waiting for an LSP command
 8883    // that's being run as part of a formatting code action.
 8884    let lock_guard = command_lock.lock().await;
 8885    let format = editor
 8886        .update_in(cx, |editor, window, cx| {
 8887            editor.perform_format(
 8888                project.clone(),
 8889                FormatTrigger::Manual,
 8890                FormatTarget::Buffers,
 8891                window,
 8892                cx,
 8893            )
 8894        })
 8895        .unwrap();
 8896    cx.run_until_parked();
 8897    editor.update(cx, |editor, cx| {
 8898        assert_eq!(
 8899            editor.text(cx),
 8900            r#"
 8901                applied-code-action-1-edit
 8902                applied-formatting
 8903                one
 8904                two
 8905                three
 8906            "#
 8907            .unindent()
 8908        );
 8909
 8910        editor.buffer.update(cx, |buffer, cx| {
 8911            let ix = buffer.len(cx);
 8912            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8913        });
 8914    });
 8915
 8916    // Allow the LSP command to proceed. Because the buffer was edited,
 8917    // the second code action will not be run.
 8918    drop(lock_guard);
 8919    format.await;
 8920    editor.update_in(cx, |editor, window, cx| {
 8921        assert_eq!(
 8922            editor.text(cx),
 8923            r#"
 8924                applied-code-action-1-command
 8925                applied-code-action-1-edit
 8926                applied-formatting
 8927                one
 8928                two
 8929                three
 8930                edited
 8931            "#
 8932            .unindent()
 8933        );
 8934
 8935        // The manual edit is undone first, because it is the last thing the user did
 8936        // (even though the command completed afterwards).
 8937        editor.undo(&Default::default(), window, cx);
 8938        assert_eq!(
 8939            editor.text(cx),
 8940            r#"
 8941                applied-code-action-1-command
 8942                applied-code-action-1-edit
 8943                applied-formatting
 8944                one
 8945                two
 8946                three
 8947            "#
 8948            .unindent()
 8949        );
 8950
 8951        // All the formatting (including the command, which completed after the manual edit)
 8952        // is undone together.
 8953        editor.undo(&Default::default(), window, cx);
 8954        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8955    });
 8956}
 8957
 8958#[gpui::test]
 8959async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8960    init_test(cx, |settings| {
 8961        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8962            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8963        ))
 8964    });
 8965
 8966    let fs = FakeFs::new(cx.executor());
 8967    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8968
 8969    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8970
 8971    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8972    language_registry.add(Arc::new(Language::new(
 8973        LanguageConfig {
 8974            name: "TypeScript".into(),
 8975            matcher: LanguageMatcher {
 8976                path_suffixes: vec!["ts".to_string()],
 8977                ..Default::default()
 8978            },
 8979            ..LanguageConfig::default()
 8980        },
 8981        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8982    )));
 8983    update_test_language_settings(cx, |settings| {
 8984        settings.defaults.prettier = Some(PrettierSettings {
 8985            allowed: true,
 8986            ..PrettierSettings::default()
 8987        });
 8988    });
 8989    let mut fake_servers = language_registry.register_fake_lsp(
 8990        "TypeScript",
 8991        FakeLspAdapter {
 8992            capabilities: lsp::ServerCapabilities {
 8993                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8994                ..Default::default()
 8995            },
 8996            ..Default::default()
 8997        },
 8998    );
 8999
 9000    let buffer = project
 9001        .update(cx, |project, cx| {
 9002            project.open_local_buffer(path!("/file.ts"), cx)
 9003        })
 9004        .await
 9005        .unwrap();
 9006
 9007    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 9008    let (editor, cx) = cx.add_window_view(|window, cx| {
 9009        build_editor_with_project(project.clone(), buffer, window, cx)
 9010    });
 9011    editor.update_in(cx, |editor, window, cx| {
 9012        editor.set_text(
 9013            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9014            window,
 9015            cx,
 9016        )
 9017    });
 9018
 9019    cx.executor().start_waiting();
 9020    let fake_server = fake_servers.next().await.unwrap();
 9021
 9022    let format = editor
 9023        .update_in(cx, |editor, window, cx| {
 9024            editor.perform_code_action_kind(
 9025                project.clone(),
 9026                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9027                window,
 9028                cx,
 9029            )
 9030        })
 9031        .unwrap();
 9032    fake_server
 9033        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9034            assert_eq!(
 9035                params.text_document.uri,
 9036                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9037            );
 9038            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9039                lsp::CodeAction {
 9040                    title: "Organize Imports".to_string(),
 9041                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9042                    edit: Some(lsp::WorkspaceEdit {
 9043                        changes: Some(
 9044                            [(
 9045                                params.text_document.uri.clone(),
 9046                                vec![lsp::TextEdit::new(
 9047                                    lsp::Range::new(
 9048                                        lsp::Position::new(1, 0),
 9049                                        lsp::Position::new(2, 0),
 9050                                    ),
 9051                                    "".to_string(),
 9052                                )],
 9053                            )]
 9054                            .into_iter()
 9055                            .collect(),
 9056                        ),
 9057                        ..Default::default()
 9058                    }),
 9059                    ..Default::default()
 9060                },
 9061            )]))
 9062        })
 9063        .next()
 9064        .await;
 9065    cx.executor().start_waiting();
 9066    format.await;
 9067    assert_eq!(
 9068        editor.update(cx, |editor, cx| editor.text(cx)),
 9069        "import { a } from 'module';\n\nconst x = a;\n"
 9070    );
 9071
 9072    editor.update_in(cx, |editor, window, cx| {
 9073        editor.set_text(
 9074            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9075            window,
 9076            cx,
 9077        )
 9078    });
 9079    // Ensure we don't lock if code action hangs.
 9080    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9081        move |params, _| async move {
 9082            assert_eq!(
 9083                params.text_document.uri,
 9084                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9085            );
 9086            futures::future::pending::<()>().await;
 9087            unreachable!()
 9088        },
 9089    );
 9090    let format = editor
 9091        .update_in(cx, |editor, window, cx| {
 9092            editor.perform_code_action_kind(
 9093                project,
 9094                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9095                window,
 9096                cx,
 9097            )
 9098        })
 9099        .unwrap();
 9100    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9101    cx.executor().start_waiting();
 9102    format.await;
 9103    assert_eq!(
 9104        editor.update(cx, |editor, cx| editor.text(cx)),
 9105        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9106    );
 9107}
 9108
 9109#[gpui::test]
 9110async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9111    init_test(cx, |_| {});
 9112
 9113    let mut cx = EditorLspTestContext::new_rust(
 9114        lsp::ServerCapabilities {
 9115            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9116            ..Default::default()
 9117        },
 9118        cx,
 9119    )
 9120    .await;
 9121
 9122    cx.set_state(indoc! {"
 9123        one.twoˇ
 9124    "});
 9125
 9126    // The format request takes a long time. When it completes, it inserts
 9127    // a newline and an indent before the `.`
 9128    cx.lsp
 9129        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9130            let executor = cx.background_executor().clone();
 9131            async move {
 9132                executor.timer(Duration::from_millis(100)).await;
 9133                Ok(Some(vec![lsp::TextEdit {
 9134                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9135                    new_text: "\n    ".into(),
 9136                }]))
 9137            }
 9138        });
 9139
 9140    // Submit a format request.
 9141    let format_1 = cx
 9142        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9143        .unwrap();
 9144    cx.executor().run_until_parked();
 9145
 9146    // Submit a second format request.
 9147    let format_2 = cx
 9148        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9149        .unwrap();
 9150    cx.executor().run_until_parked();
 9151
 9152    // Wait for both format requests to complete
 9153    cx.executor().advance_clock(Duration::from_millis(200));
 9154    cx.executor().start_waiting();
 9155    format_1.await.unwrap();
 9156    cx.executor().start_waiting();
 9157    format_2.await.unwrap();
 9158
 9159    // The formatting edits only happens once.
 9160    cx.assert_editor_state(indoc! {"
 9161        one
 9162            .twoˇ
 9163    "});
 9164}
 9165
 9166#[gpui::test]
 9167async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9168    init_test(cx, |settings| {
 9169        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9170    });
 9171
 9172    let mut cx = EditorLspTestContext::new_rust(
 9173        lsp::ServerCapabilities {
 9174            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9175            ..Default::default()
 9176        },
 9177        cx,
 9178    )
 9179    .await;
 9180
 9181    // Set up a buffer white some trailing whitespace and no trailing newline.
 9182    cx.set_state(
 9183        &[
 9184            "one ",   //
 9185            "twoˇ",   //
 9186            "three ", //
 9187            "four",   //
 9188        ]
 9189        .join("\n"),
 9190    );
 9191
 9192    // Submit a format request.
 9193    let format = cx
 9194        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9195        .unwrap();
 9196
 9197    // Record which buffer changes have been sent to the language server
 9198    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9199    cx.lsp
 9200        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9201            let buffer_changes = buffer_changes.clone();
 9202            move |params, _| {
 9203                buffer_changes.lock().extend(
 9204                    params
 9205                        .content_changes
 9206                        .into_iter()
 9207                        .map(|e| (e.range.unwrap(), e.text)),
 9208                );
 9209            }
 9210        });
 9211
 9212    // Handle formatting requests to the language server.
 9213    cx.lsp
 9214        .set_request_handler::<lsp::request::Formatting, _, _>({
 9215            let buffer_changes = buffer_changes.clone();
 9216            move |_, _| {
 9217                // When formatting is requested, trailing whitespace has already been stripped,
 9218                // and the trailing newline has already been added.
 9219                assert_eq!(
 9220                    &buffer_changes.lock()[1..],
 9221                    &[
 9222                        (
 9223                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9224                            "".into()
 9225                        ),
 9226                        (
 9227                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9228                            "".into()
 9229                        ),
 9230                        (
 9231                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9232                            "\n".into()
 9233                        ),
 9234                    ]
 9235                );
 9236
 9237                // Insert blank lines between each line of the buffer.
 9238                async move {
 9239                    Ok(Some(vec![
 9240                        lsp::TextEdit {
 9241                            range: lsp::Range::new(
 9242                                lsp::Position::new(1, 0),
 9243                                lsp::Position::new(1, 0),
 9244                            ),
 9245                            new_text: "\n".into(),
 9246                        },
 9247                        lsp::TextEdit {
 9248                            range: lsp::Range::new(
 9249                                lsp::Position::new(2, 0),
 9250                                lsp::Position::new(2, 0),
 9251                            ),
 9252                            new_text: "\n".into(),
 9253                        },
 9254                    ]))
 9255                }
 9256            }
 9257        });
 9258
 9259    // After formatting the buffer, the trailing whitespace is stripped,
 9260    // a newline is appended, and the edits provided by the language server
 9261    // have been applied.
 9262    format.await.unwrap();
 9263    cx.assert_editor_state(
 9264        &[
 9265            "one",   //
 9266            "",      //
 9267            "twoˇ",  //
 9268            "",      //
 9269            "three", //
 9270            "four",  //
 9271            "",      //
 9272        ]
 9273        .join("\n"),
 9274    );
 9275
 9276    // Undoing the formatting undoes the trailing whitespace removal, the
 9277    // trailing newline, and the LSP edits.
 9278    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9279    cx.assert_editor_state(
 9280        &[
 9281            "one ",   //
 9282            "twoˇ",   //
 9283            "three ", //
 9284            "four",   //
 9285        ]
 9286        .join("\n"),
 9287    );
 9288}
 9289
 9290#[gpui::test]
 9291async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9292    cx: &mut TestAppContext,
 9293) {
 9294    init_test(cx, |_| {});
 9295
 9296    cx.update(|cx| {
 9297        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9298            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9299                settings.auto_signature_help = Some(true);
 9300            });
 9301        });
 9302    });
 9303
 9304    let mut cx = EditorLspTestContext::new_rust(
 9305        lsp::ServerCapabilities {
 9306            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9307                ..Default::default()
 9308            }),
 9309            ..Default::default()
 9310        },
 9311        cx,
 9312    )
 9313    .await;
 9314
 9315    let language = Language::new(
 9316        LanguageConfig {
 9317            name: "Rust".into(),
 9318            brackets: BracketPairConfig {
 9319                pairs: vec![
 9320                    BracketPair {
 9321                        start: "{".to_string(),
 9322                        end: "}".to_string(),
 9323                        close: true,
 9324                        surround: true,
 9325                        newline: true,
 9326                    },
 9327                    BracketPair {
 9328                        start: "(".to_string(),
 9329                        end: ")".to_string(),
 9330                        close: true,
 9331                        surround: true,
 9332                        newline: true,
 9333                    },
 9334                    BracketPair {
 9335                        start: "/*".to_string(),
 9336                        end: " */".to_string(),
 9337                        close: true,
 9338                        surround: true,
 9339                        newline: true,
 9340                    },
 9341                    BracketPair {
 9342                        start: "[".to_string(),
 9343                        end: "]".to_string(),
 9344                        close: false,
 9345                        surround: false,
 9346                        newline: true,
 9347                    },
 9348                    BracketPair {
 9349                        start: "\"".to_string(),
 9350                        end: "\"".to_string(),
 9351                        close: true,
 9352                        surround: true,
 9353                        newline: false,
 9354                    },
 9355                    BracketPair {
 9356                        start: "<".to_string(),
 9357                        end: ">".to_string(),
 9358                        close: false,
 9359                        surround: true,
 9360                        newline: true,
 9361                    },
 9362                ],
 9363                ..Default::default()
 9364            },
 9365            autoclose_before: "})]".to_string(),
 9366            ..Default::default()
 9367        },
 9368        Some(tree_sitter_rust::LANGUAGE.into()),
 9369    );
 9370    let language = Arc::new(language);
 9371
 9372    cx.language_registry().add(language.clone());
 9373    cx.update_buffer(|buffer, cx| {
 9374        buffer.set_language(Some(language), cx);
 9375    });
 9376
 9377    cx.set_state(
 9378        &r#"
 9379            fn main() {
 9380                sampleˇ
 9381            }
 9382        "#
 9383        .unindent(),
 9384    );
 9385
 9386    cx.update_editor(|editor, window, cx| {
 9387        editor.handle_input("(", window, cx);
 9388    });
 9389    cx.assert_editor_state(
 9390        &"
 9391            fn main() {
 9392                sample(ˇ)
 9393            }
 9394        "
 9395        .unindent(),
 9396    );
 9397
 9398    let mocked_response = lsp::SignatureHelp {
 9399        signatures: vec![lsp::SignatureInformation {
 9400            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9401            documentation: None,
 9402            parameters: Some(vec![
 9403                lsp::ParameterInformation {
 9404                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9405                    documentation: None,
 9406                },
 9407                lsp::ParameterInformation {
 9408                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9409                    documentation: None,
 9410                },
 9411            ]),
 9412            active_parameter: None,
 9413        }],
 9414        active_signature: Some(0),
 9415        active_parameter: Some(0),
 9416    };
 9417    handle_signature_help_request(&mut cx, mocked_response).await;
 9418
 9419    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9420        .await;
 9421
 9422    cx.editor(|editor, _, _| {
 9423        let signature_help_state = editor.signature_help_state.popover().cloned();
 9424        assert_eq!(
 9425            signature_help_state.unwrap().label,
 9426            "param1: u8, param2: u8"
 9427        );
 9428    });
 9429}
 9430
 9431#[gpui::test]
 9432async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9433    init_test(cx, |_| {});
 9434
 9435    cx.update(|cx| {
 9436        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9437            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9438                settings.auto_signature_help = Some(false);
 9439                settings.show_signature_help_after_edits = Some(false);
 9440            });
 9441        });
 9442    });
 9443
 9444    let mut cx = EditorLspTestContext::new_rust(
 9445        lsp::ServerCapabilities {
 9446            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9447                ..Default::default()
 9448            }),
 9449            ..Default::default()
 9450        },
 9451        cx,
 9452    )
 9453    .await;
 9454
 9455    let language = Language::new(
 9456        LanguageConfig {
 9457            name: "Rust".into(),
 9458            brackets: BracketPairConfig {
 9459                pairs: vec![
 9460                    BracketPair {
 9461                        start: "{".to_string(),
 9462                        end: "}".to_string(),
 9463                        close: true,
 9464                        surround: true,
 9465                        newline: true,
 9466                    },
 9467                    BracketPair {
 9468                        start: "(".to_string(),
 9469                        end: ")".to_string(),
 9470                        close: true,
 9471                        surround: true,
 9472                        newline: true,
 9473                    },
 9474                    BracketPair {
 9475                        start: "/*".to_string(),
 9476                        end: " */".to_string(),
 9477                        close: true,
 9478                        surround: true,
 9479                        newline: true,
 9480                    },
 9481                    BracketPair {
 9482                        start: "[".to_string(),
 9483                        end: "]".to_string(),
 9484                        close: false,
 9485                        surround: false,
 9486                        newline: true,
 9487                    },
 9488                    BracketPair {
 9489                        start: "\"".to_string(),
 9490                        end: "\"".to_string(),
 9491                        close: true,
 9492                        surround: true,
 9493                        newline: false,
 9494                    },
 9495                    BracketPair {
 9496                        start: "<".to_string(),
 9497                        end: ">".to_string(),
 9498                        close: false,
 9499                        surround: true,
 9500                        newline: true,
 9501                    },
 9502                ],
 9503                ..Default::default()
 9504            },
 9505            autoclose_before: "})]".to_string(),
 9506            ..Default::default()
 9507        },
 9508        Some(tree_sitter_rust::LANGUAGE.into()),
 9509    );
 9510    let language = Arc::new(language);
 9511
 9512    cx.language_registry().add(language.clone());
 9513    cx.update_buffer(|buffer, cx| {
 9514        buffer.set_language(Some(language), cx);
 9515    });
 9516
 9517    // Ensure that signature_help is not called when no signature help is enabled.
 9518    cx.set_state(
 9519        &r#"
 9520            fn main() {
 9521                sampleˇ
 9522            }
 9523        "#
 9524        .unindent(),
 9525    );
 9526    cx.update_editor(|editor, window, cx| {
 9527        editor.handle_input("(", window, cx);
 9528    });
 9529    cx.assert_editor_state(
 9530        &"
 9531            fn main() {
 9532                sample(ˇ)
 9533            }
 9534        "
 9535        .unindent(),
 9536    );
 9537    cx.editor(|editor, _, _| {
 9538        assert!(editor.signature_help_state.task().is_none());
 9539    });
 9540
 9541    let mocked_response = lsp::SignatureHelp {
 9542        signatures: vec![lsp::SignatureInformation {
 9543            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9544            documentation: None,
 9545            parameters: Some(vec![
 9546                lsp::ParameterInformation {
 9547                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9548                    documentation: None,
 9549                },
 9550                lsp::ParameterInformation {
 9551                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9552                    documentation: None,
 9553                },
 9554            ]),
 9555            active_parameter: None,
 9556        }],
 9557        active_signature: Some(0),
 9558        active_parameter: Some(0),
 9559    };
 9560
 9561    // Ensure that signature_help is called when enabled afte edits
 9562    cx.update(|_, cx| {
 9563        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9564            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9565                settings.auto_signature_help = Some(false);
 9566                settings.show_signature_help_after_edits = Some(true);
 9567            });
 9568        });
 9569    });
 9570    cx.set_state(
 9571        &r#"
 9572            fn main() {
 9573                sampleˇ
 9574            }
 9575        "#
 9576        .unindent(),
 9577    );
 9578    cx.update_editor(|editor, window, cx| {
 9579        editor.handle_input("(", window, cx);
 9580    });
 9581    cx.assert_editor_state(
 9582        &"
 9583            fn main() {
 9584                sample(ˇ)
 9585            }
 9586        "
 9587        .unindent(),
 9588    );
 9589    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9590    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9591        .await;
 9592    cx.update_editor(|editor, _, _| {
 9593        let signature_help_state = editor.signature_help_state.popover().cloned();
 9594        assert!(signature_help_state.is_some());
 9595        assert_eq!(
 9596            signature_help_state.unwrap().label,
 9597            "param1: u8, param2: u8"
 9598        );
 9599        editor.signature_help_state = SignatureHelpState::default();
 9600    });
 9601
 9602    // Ensure that signature_help is called when auto signature help override is enabled
 9603    cx.update(|_, cx| {
 9604        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9605            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9606                settings.auto_signature_help = Some(true);
 9607                settings.show_signature_help_after_edits = Some(false);
 9608            });
 9609        });
 9610    });
 9611    cx.set_state(
 9612        &r#"
 9613            fn main() {
 9614                sampleˇ
 9615            }
 9616        "#
 9617        .unindent(),
 9618    );
 9619    cx.update_editor(|editor, window, cx| {
 9620        editor.handle_input("(", window, cx);
 9621    });
 9622    cx.assert_editor_state(
 9623        &"
 9624            fn main() {
 9625                sample(ˇ)
 9626            }
 9627        "
 9628        .unindent(),
 9629    );
 9630    handle_signature_help_request(&mut cx, mocked_response).await;
 9631    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9632        .await;
 9633    cx.editor(|editor, _, _| {
 9634        let signature_help_state = editor.signature_help_state.popover().cloned();
 9635        assert!(signature_help_state.is_some());
 9636        assert_eq!(
 9637            signature_help_state.unwrap().label,
 9638            "param1: u8, param2: u8"
 9639        );
 9640    });
 9641}
 9642
 9643#[gpui::test]
 9644async fn test_signature_help(cx: &mut TestAppContext) {
 9645    init_test(cx, |_| {});
 9646    cx.update(|cx| {
 9647        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9648            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9649                settings.auto_signature_help = Some(true);
 9650            });
 9651        });
 9652    });
 9653
 9654    let mut cx = EditorLspTestContext::new_rust(
 9655        lsp::ServerCapabilities {
 9656            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9657                ..Default::default()
 9658            }),
 9659            ..Default::default()
 9660        },
 9661        cx,
 9662    )
 9663    .await;
 9664
 9665    // A test that directly calls `show_signature_help`
 9666    cx.update_editor(|editor, window, cx| {
 9667        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9668    });
 9669
 9670    let mocked_response = lsp::SignatureHelp {
 9671        signatures: vec![lsp::SignatureInformation {
 9672            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9673            documentation: None,
 9674            parameters: Some(vec![
 9675                lsp::ParameterInformation {
 9676                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9677                    documentation: None,
 9678                },
 9679                lsp::ParameterInformation {
 9680                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9681                    documentation: None,
 9682                },
 9683            ]),
 9684            active_parameter: None,
 9685        }],
 9686        active_signature: Some(0),
 9687        active_parameter: Some(0),
 9688    };
 9689    handle_signature_help_request(&mut cx, mocked_response).await;
 9690
 9691    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9692        .await;
 9693
 9694    cx.editor(|editor, _, _| {
 9695        let signature_help_state = editor.signature_help_state.popover().cloned();
 9696        assert!(signature_help_state.is_some());
 9697        assert_eq!(
 9698            signature_help_state.unwrap().label,
 9699            "param1: u8, param2: u8"
 9700        );
 9701    });
 9702
 9703    // When exiting outside from inside the brackets, `signature_help` is closed.
 9704    cx.set_state(indoc! {"
 9705        fn main() {
 9706            sample(ˇ);
 9707        }
 9708
 9709        fn sample(param1: u8, param2: u8) {}
 9710    "});
 9711
 9712    cx.update_editor(|editor, window, cx| {
 9713        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9714    });
 9715
 9716    let mocked_response = lsp::SignatureHelp {
 9717        signatures: Vec::new(),
 9718        active_signature: None,
 9719        active_parameter: None,
 9720    };
 9721    handle_signature_help_request(&mut cx, mocked_response).await;
 9722
 9723    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9724        .await;
 9725
 9726    cx.editor(|editor, _, _| {
 9727        assert!(!editor.signature_help_state.is_shown());
 9728    });
 9729
 9730    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9731    cx.set_state(indoc! {"
 9732        fn main() {
 9733            sample(ˇ);
 9734        }
 9735
 9736        fn sample(param1: u8, param2: u8) {}
 9737    "});
 9738
 9739    let mocked_response = lsp::SignatureHelp {
 9740        signatures: vec![lsp::SignatureInformation {
 9741            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9742            documentation: None,
 9743            parameters: Some(vec![
 9744                lsp::ParameterInformation {
 9745                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9746                    documentation: None,
 9747                },
 9748                lsp::ParameterInformation {
 9749                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9750                    documentation: None,
 9751                },
 9752            ]),
 9753            active_parameter: None,
 9754        }],
 9755        active_signature: Some(0),
 9756        active_parameter: Some(0),
 9757    };
 9758    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9759    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9760        .await;
 9761    cx.editor(|editor, _, _| {
 9762        assert!(editor.signature_help_state.is_shown());
 9763    });
 9764
 9765    // Restore the popover with more parameter input
 9766    cx.set_state(indoc! {"
 9767        fn main() {
 9768            sample(param1, param2ˇ);
 9769        }
 9770
 9771        fn sample(param1: u8, param2: u8) {}
 9772    "});
 9773
 9774    let mocked_response = lsp::SignatureHelp {
 9775        signatures: vec![lsp::SignatureInformation {
 9776            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9777            documentation: None,
 9778            parameters: Some(vec![
 9779                lsp::ParameterInformation {
 9780                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9781                    documentation: None,
 9782                },
 9783                lsp::ParameterInformation {
 9784                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9785                    documentation: None,
 9786                },
 9787            ]),
 9788            active_parameter: None,
 9789        }],
 9790        active_signature: Some(0),
 9791        active_parameter: Some(1),
 9792    };
 9793    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9794    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9795        .await;
 9796
 9797    // When selecting a range, the popover is gone.
 9798    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9799    cx.update_editor(|editor, window, cx| {
 9800        editor.change_selections(None, window, cx, |s| {
 9801            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9802        })
 9803    });
 9804    cx.assert_editor_state(indoc! {"
 9805        fn main() {
 9806            sample(param1, «ˇparam2»);
 9807        }
 9808
 9809        fn sample(param1: u8, param2: u8) {}
 9810    "});
 9811    cx.editor(|editor, _, _| {
 9812        assert!(!editor.signature_help_state.is_shown());
 9813    });
 9814
 9815    // When unselecting again, the popover is back if within the brackets.
 9816    cx.update_editor(|editor, window, cx| {
 9817        editor.change_selections(None, window, cx, |s| {
 9818            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9819        })
 9820    });
 9821    cx.assert_editor_state(indoc! {"
 9822        fn main() {
 9823            sample(param1, ˇparam2);
 9824        }
 9825
 9826        fn sample(param1: u8, param2: u8) {}
 9827    "});
 9828    handle_signature_help_request(&mut cx, mocked_response).await;
 9829    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9830        .await;
 9831    cx.editor(|editor, _, _| {
 9832        assert!(editor.signature_help_state.is_shown());
 9833    });
 9834
 9835    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9836    cx.update_editor(|editor, window, cx| {
 9837        editor.change_selections(None, window, cx, |s| {
 9838            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9839            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9840        })
 9841    });
 9842    cx.assert_editor_state(indoc! {"
 9843        fn main() {
 9844            sample(param1, ˇparam2);
 9845        }
 9846
 9847        fn sample(param1: u8, param2: u8) {}
 9848    "});
 9849
 9850    let mocked_response = lsp::SignatureHelp {
 9851        signatures: vec![lsp::SignatureInformation {
 9852            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9853            documentation: None,
 9854            parameters: Some(vec![
 9855                lsp::ParameterInformation {
 9856                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9857                    documentation: None,
 9858                },
 9859                lsp::ParameterInformation {
 9860                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9861                    documentation: None,
 9862                },
 9863            ]),
 9864            active_parameter: None,
 9865        }],
 9866        active_signature: Some(0),
 9867        active_parameter: Some(1),
 9868    };
 9869    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9870    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9871        .await;
 9872    cx.update_editor(|editor, _, cx| {
 9873        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9874    });
 9875    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9876        .await;
 9877    cx.update_editor(|editor, window, cx| {
 9878        editor.change_selections(None, window, cx, |s| {
 9879            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9880        })
 9881    });
 9882    cx.assert_editor_state(indoc! {"
 9883        fn main() {
 9884            sample(param1, «ˇparam2»);
 9885        }
 9886
 9887        fn sample(param1: u8, param2: u8) {}
 9888    "});
 9889    cx.update_editor(|editor, window, cx| {
 9890        editor.change_selections(None, window, cx, |s| {
 9891            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9892        })
 9893    });
 9894    cx.assert_editor_state(indoc! {"
 9895        fn main() {
 9896            sample(param1, ˇparam2);
 9897        }
 9898
 9899        fn sample(param1: u8, param2: u8) {}
 9900    "});
 9901    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9902        .await;
 9903}
 9904
 9905#[gpui::test]
 9906async fn test_completion_mode(cx: &mut TestAppContext) {
 9907    init_test(cx, |_| {});
 9908    let mut cx = EditorLspTestContext::new_rust(
 9909        lsp::ServerCapabilities {
 9910            completion_provider: Some(lsp::CompletionOptions {
 9911                resolve_provider: Some(true),
 9912                ..Default::default()
 9913            }),
 9914            ..Default::default()
 9915        },
 9916        cx,
 9917    )
 9918    .await;
 9919
 9920    struct Run {
 9921        run_description: &'static str,
 9922        initial_state: String,
 9923        buffer_marked_text: String,
 9924        completion_text: &'static str,
 9925        expected_with_insert_mode: String,
 9926        expected_with_replace_mode: String,
 9927        expected_with_replace_subsequence_mode: String,
 9928        expected_with_replace_suffix_mode: String,
 9929    }
 9930
 9931    let runs = [
 9932        Run {
 9933            run_description: "Start of word matches completion text",
 9934            initial_state: "before ediˇ after".into(),
 9935            buffer_marked_text: "before <edi|> after".into(),
 9936            completion_text: "editor",
 9937            expected_with_insert_mode: "before editorˇ after".into(),
 9938            expected_with_replace_mode: "before editorˇ after".into(),
 9939            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9940            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9941        },
 9942        Run {
 9943            run_description: "Accept same text at the middle of the word",
 9944            initial_state: "before ediˇtor after".into(),
 9945            buffer_marked_text: "before <edi|tor> after".into(),
 9946            completion_text: "editor",
 9947            expected_with_insert_mode: "before editorˇtor after".into(),
 9948            expected_with_replace_mode: "before editorˇ after".into(),
 9949            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9950            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9951        },
 9952        Run {
 9953            run_description: "End of word matches completion text -- cursor at end",
 9954            initial_state: "before torˇ after".into(),
 9955            buffer_marked_text: "before <tor|> after".into(),
 9956            completion_text: "editor",
 9957            expected_with_insert_mode: "before editorˇ after".into(),
 9958            expected_with_replace_mode: "before editorˇ after".into(),
 9959            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9960            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9961        },
 9962        Run {
 9963            run_description: "End of word matches completion text -- cursor at start",
 9964            initial_state: "before ˇtor after".into(),
 9965            buffer_marked_text: "before <|tor> after".into(),
 9966            completion_text: "editor",
 9967            expected_with_insert_mode: "before editorˇtor after".into(),
 9968            expected_with_replace_mode: "before editorˇ after".into(),
 9969            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9970            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9971        },
 9972        Run {
 9973            run_description: "Prepend text containing whitespace",
 9974            initial_state: "pˇfield: bool".into(),
 9975            buffer_marked_text: "<p|field>: bool".into(),
 9976            completion_text: "pub ",
 9977            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9978            expected_with_replace_mode: "pub ˇ: bool".into(),
 9979            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9980            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9981        },
 9982        Run {
 9983            run_description: "Add element to start of list",
 9984            initial_state: "[element_ˇelement_2]".into(),
 9985            buffer_marked_text: "[<element_|element_2>]".into(),
 9986            completion_text: "element_1",
 9987            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9988            expected_with_replace_mode: "[element_1ˇ]".into(),
 9989            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9990            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9991        },
 9992        Run {
 9993            run_description: "Add element to start of list -- first and second elements are equal",
 9994            initial_state: "[elˇelement]".into(),
 9995            buffer_marked_text: "[<el|element>]".into(),
 9996            completion_text: "element",
 9997            expected_with_insert_mode: "[elementˇelement]".into(),
 9998            expected_with_replace_mode: "[elementˇ]".into(),
 9999            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
10000            expected_with_replace_suffix_mode: "[elementˇ]".into(),
10001        },
10002        Run {
10003            run_description: "Ends with matching suffix",
10004            initial_state: "SubˇError".into(),
10005            buffer_marked_text: "<Sub|Error>".into(),
10006            completion_text: "SubscriptionError",
10007            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
10008            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10009            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10010            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
10011        },
10012        Run {
10013            run_description: "Suffix is a subsequence -- contiguous",
10014            initial_state: "SubˇErr".into(),
10015            buffer_marked_text: "<Sub|Err>".into(),
10016            completion_text: "SubscriptionError",
10017            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
10018            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10019            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10020            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
10021        },
10022        Run {
10023            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
10024            initial_state: "Suˇscrirr".into(),
10025            buffer_marked_text: "<Su|scrirr>".into(),
10026            completion_text: "SubscriptionError",
10027            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
10028            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
10029            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
10030            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10031        },
10032        Run {
10033            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10034            initial_state: "foo(indˇix)".into(),
10035            buffer_marked_text: "foo(<ind|ix>)".into(),
10036            completion_text: "node_index",
10037            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10038            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10039            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10040            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10041        },
10042    ];
10043
10044    for run in runs {
10045        let run_variations = [
10046            (LspInsertMode::Insert, run.expected_with_insert_mode),
10047            (LspInsertMode::Replace, run.expected_with_replace_mode),
10048            (
10049                LspInsertMode::ReplaceSubsequence,
10050                run.expected_with_replace_subsequence_mode,
10051            ),
10052            (
10053                LspInsertMode::ReplaceSuffix,
10054                run.expected_with_replace_suffix_mode,
10055            ),
10056        ];
10057
10058        for (lsp_insert_mode, expected_text) in run_variations {
10059            eprintln!(
10060                "run = {:?}, mode = {lsp_insert_mode:.?}",
10061                run.run_description,
10062            );
10063
10064            update_test_language_settings(&mut cx, |settings| {
10065                settings.defaults.completions = Some(CompletionSettings {
10066                    lsp_insert_mode,
10067                    words: WordsCompletionMode::Disabled,
10068                    lsp: true,
10069                    lsp_fetch_timeout_ms: 0,
10070                });
10071            });
10072
10073            cx.set_state(&run.initial_state);
10074            cx.update_editor(|editor, window, cx| {
10075                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10076            });
10077
10078            let counter = Arc::new(AtomicUsize::new(0));
10079            handle_completion_request_with_insert_and_replace(
10080                &mut cx,
10081                &run.buffer_marked_text,
10082                vec![run.completion_text],
10083                counter.clone(),
10084            )
10085            .await;
10086            cx.condition(|editor, _| editor.context_menu_visible())
10087                .await;
10088            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10089
10090            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10091                editor
10092                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10093                    .unwrap()
10094            });
10095            cx.assert_editor_state(&expected_text);
10096            handle_resolve_completion_request(&mut cx, None).await;
10097            apply_additional_edits.await.unwrap();
10098        }
10099    }
10100}
10101
10102#[gpui::test]
10103async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10104    init_test(cx, |_| {});
10105    let mut cx = EditorLspTestContext::new_rust(
10106        lsp::ServerCapabilities {
10107            completion_provider: Some(lsp::CompletionOptions {
10108                resolve_provider: Some(true),
10109                ..Default::default()
10110            }),
10111            ..Default::default()
10112        },
10113        cx,
10114    )
10115    .await;
10116
10117    let initial_state = "SubˇError";
10118    let buffer_marked_text = "<Sub|Error>";
10119    let completion_text = "SubscriptionError";
10120    let expected_with_insert_mode = "SubscriptionErrorˇError";
10121    let expected_with_replace_mode = "SubscriptionErrorˇ";
10122
10123    update_test_language_settings(&mut cx, |settings| {
10124        settings.defaults.completions = Some(CompletionSettings {
10125            words: WordsCompletionMode::Disabled,
10126            // set the opposite here to ensure that the action is overriding the default behavior
10127            lsp_insert_mode: LspInsertMode::Insert,
10128            lsp: true,
10129            lsp_fetch_timeout_ms: 0,
10130        });
10131    });
10132
10133    cx.set_state(initial_state);
10134    cx.update_editor(|editor, window, cx| {
10135        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10136    });
10137
10138    let counter = Arc::new(AtomicUsize::new(0));
10139    handle_completion_request_with_insert_and_replace(
10140        &mut cx,
10141        &buffer_marked_text,
10142        vec![completion_text],
10143        counter.clone(),
10144    )
10145    .await;
10146    cx.condition(|editor, _| editor.context_menu_visible())
10147        .await;
10148    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10149
10150    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10151        editor
10152            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10153            .unwrap()
10154    });
10155    cx.assert_editor_state(&expected_with_replace_mode);
10156    handle_resolve_completion_request(&mut cx, None).await;
10157    apply_additional_edits.await.unwrap();
10158
10159    update_test_language_settings(&mut cx, |settings| {
10160        settings.defaults.completions = Some(CompletionSettings {
10161            words: WordsCompletionMode::Disabled,
10162            // set the opposite here to ensure that the action is overriding the default behavior
10163            lsp_insert_mode: LspInsertMode::Replace,
10164            lsp: true,
10165            lsp_fetch_timeout_ms: 0,
10166        });
10167    });
10168
10169    cx.set_state(initial_state);
10170    cx.update_editor(|editor, window, cx| {
10171        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10172    });
10173    handle_completion_request_with_insert_and_replace(
10174        &mut cx,
10175        &buffer_marked_text,
10176        vec![completion_text],
10177        counter.clone(),
10178    )
10179    .await;
10180    cx.condition(|editor, _| editor.context_menu_visible())
10181        .await;
10182    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10183
10184    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10185        editor
10186            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10187            .unwrap()
10188    });
10189    cx.assert_editor_state(&expected_with_insert_mode);
10190    handle_resolve_completion_request(&mut cx, None).await;
10191    apply_additional_edits.await.unwrap();
10192}
10193
10194#[gpui::test]
10195async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10196    init_test(cx, |_| {});
10197    let mut cx = EditorLspTestContext::new_rust(
10198        lsp::ServerCapabilities {
10199            completion_provider: Some(lsp::CompletionOptions {
10200                resolve_provider: Some(true),
10201                ..Default::default()
10202            }),
10203            ..Default::default()
10204        },
10205        cx,
10206    )
10207    .await;
10208
10209    // scenario: surrounding text matches completion text
10210    let completion_text = "to_offset";
10211    let initial_state = indoc! {"
10212        1. buf.to_offˇsuffix
10213        2. buf.to_offˇsuf
10214        3. buf.to_offˇfix
10215        4. buf.to_offˇ
10216        5. into_offˇensive
10217        6. ˇsuffix
10218        7. let ˇ //
10219        8. aaˇzz
10220        9. buf.to_off«zzzzzˇ»suffix
10221        10. buf.«ˇzzzzz»suffix
10222        11. to_off«ˇzzzzz»
10223
10224        buf.to_offˇsuffix  // newest cursor
10225    "};
10226    let completion_marked_buffer = indoc! {"
10227        1. buf.to_offsuffix
10228        2. buf.to_offsuf
10229        3. buf.to_offfix
10230        4. buf.to_off
10231        5. into_offensive
10232        6. suffix
10233        7. let  //
10234        8. aazz
10235        9. buf.to_offzzzzzsuffix
10236        10. buf.zzzzzsuffix
10237        11. to_offzzzzz
10238
10239        buf.<to_off|suffix>  // newest cursor
10240    "};
10241    let expected = indoc! {"
10242        1. buf.to_offsetˇ
10243        2. buf.to_offsetˇsuf
10244        3. buf.to_offsetˇfix
10245        4. buf.to_offsetˇ
10246        5. into_offsetˇensive
10247        6. to_offsetˇsuffix
10248        7. let to_offsetˇ //
10249        8. aato_offsetˇzz
10250        9. buf.to_offsetˇ
10251        10. buf.to_offsetˇsuffix
10252        11. to_offsetˇ
10253
10254        buf.to_offsetˇ  // newest cursor
10255    "};
10256    cx.set_state(initial_state);
10257    cx.update_editor(|editor, window, cx| {
10258        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10259    });
10260    handle_completion_request_with_insert_and_replace(
10261        &mut cx,
10262        completion_marked_buffer,
10263        vec![completion_text],
10264        Arc::new(AtomicUsize::new(0)),
10265    )
10266    .await;
10267    cx.condition(|editor, _| editor.context_menu_visible())
10268        .await;
10269    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10270        editor
10271            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10272            .unwrap()
10273    });
10274    cx.assert_editor_state(expected);
10275    handle_resolve_completion_request(&mut cx, None).await;
10276    apply_additional_edits.await.unwrap();
10277
10278    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10279    let completion_text = "foo_and_bar";
10280    let initial_state = indoc! {"
10281        1. ooanbˇ
10282        2. zooanbˇ
10283        3. ooanbˇz
10284        4. zooanbˇz
10285        5. ooanˇ
10286        6. oanbˇ
10287
10288        ooanbˇ
10289    "};
10290    let completion_marked_buffer = indoc! {"
10291        1. ooanb
10292        2. zooanb
10293        3. ooanbz
10294        4. zooanbz
10295        5. ooan
10296        6. oanb
10297
10298        <ooanb|>
10299    "};
10300    let expected = indoc! {"
10301        1. foo_and_barˇ
10302        2. zfoo_and_barˇ
10303        3. foo_and_barˇz
10304        4. zfoo_and_barˇz
10305        5. ooanfoo_and_barˇ
10306        6. oanbfoo_and_barˇ
10307
10308        foo_and_barˇ
10309    "};
10310    cx.set_state(initial_state);
10311    cx.update_editor(|editor, window, cx| {
10312        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10313    });
10314    handle_completion_request_with_insert_and_replace(
10315        &mut cx,
10316        completion_marked_buffer,
10317        vec![completion_text],
10318        Arc::new(AtomicUsize::new(0)),
10319    )
10320    .await;
10321    cx.condition(|editor, _| editor.context_menu_visible())
10322        .await;
10323    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10324        editor
10325            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10326            .unwrap()
10327    });
10328    cx.assert_editor_state(expected);
10329    handle_resolve_completion_request(&mut cx, None).await;
10330    apply_additional_edits.await.unwrap();
10331
10332    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10333    // (expects the same as if it was inserted at the end)
10334    let completion_text = "foo_and_bar";
10335    let initial_state = indoc! {"
10336        1. ooˇanb
10337        2. zooˇanb
10338        3. ooˇanbz
10339        4. zooˇanbz
10340
10341        ooˇanb
10342    "};
10343    let completion_marked_buffer = indoc! {"
10344        1. ooanb
10345        2. zooanb
10346        3. ooanbz
10347        4. zooanbz
10348
10349        <oo|anb>
10350    "};
10351    let expected = indoc! {"
10352        1. foo_and_barˇ
10353        2. zfoo_and_barˇ
10354        3. foo_and_barˇz
10355        4. zfoo_and_barˇz
10356
10357        foo_and_barˇ
10358    "};
10359    cx.set_state(initial_state);
10360    cx.update_editor(|editor, window, cx| {
10361        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10362    });
10363    handle_completion_request_with_insert_and_replace(
10364        &mut cx,
10365        completion_marked_buffer,
10366        vec![completion_text],
10367        Arc::new(AtomicUsize::new(0)),
10368    )
10369    .await;
10370    cx.condition(|editor, _| editor.context_menu_visible())
10371        .await;
10372    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10373        editor
10374            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10375            .unwrap()
10376    });
10377    cx.assert_editor_state(expected);
10378    handle_resolve_completion_request(&mut cx, None).await;
10379    apply_additional_edits.await.unwrap();
10380}
10381
10382// This used to crash
10383#[gpui::test]
10384async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10385    init_test(cx, |_| {});
10386
10387    let buffer_text = indoc! {"
10388        fn main() {
10389            10.satu;
10390
10391            //
10392            // separate cursors so they open in different excerpts (manually reproducible)
10393            //
10394
10395            10.satu20;
10396        }
10397    "};
10398    let multibuffer_text_with_selections = indoc! {"
10399        fn main() {
10400            10.satuˇ;
10401
10402            //
10403
10404            //
10405
10406            10.satuˇ20;
10407        }
10408    "};
10409    let expected_multibuffer = indoc! {"
10410        fn main() {
10411            10.saturating_sub()ˇ;
10412
10413            //
10414
10415            //
10416
10417            10.saturating_sub()ˇ;
10418        }
10419    "};
10420
10421    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10422    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10423
10424    let fs = FakeFs::new(cx.executor());
10425    fs.insert_tree(
10426        path!("/a"),
10427        json!({
10428            "main.rs": buffer_text,
10429        }),
10430    )
10431    .await;
10432
10433    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10434    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10435    language_registry.add(rust_lang());
10436    let mut fake_servers = language_registry.register_fake_lsp(
10437        "Rust",
10438        FakeLspAdapter {
10439            capabilities: lsp::ServerCapabilities {
10440                completion_provider: Some(lsp::CompletionOptions {
10441                    resolve_provider: None,
10442                    ..lsp::CompletionOptions::default()
10443                }),
10444                ..lsp::ServerCapabilities::default()
10445            },
10446            ..FakeLspAdapter::default()
10447        },
10448    );
10449    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10450    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10451    let buffer = project
10452        .update(cx, |project, cx| {
10453            project.open_local_buffer(path!("/a/main.rs"), cx)
10454        })
10455        .await
10456        .unwrap();
10457
10458    let multi_buffer = cx.new(|cx| {
10459        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10460        multi_buffer.push_excerpts(
10461            buffer.clone(),
10462            [ExcerptRange::new(0..first_excerpt_end)],
10463            cx,
10464        );
10465        multi_buffer.push_excerpts(
10466            buffer.clone(),
10467            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10468            cx,
10469        );
10470        multi_buffer
10471    });
10472
10473    let editor = workspace
10474        .update(cx, |_, window, cx| {
10475            cx.new(|cx| {
10476                Editor::new(
10477                    EditorMode::Full {
10478                        scale_ui_elements_with_buffer_font_size: false,
10479                        show_active_line_background: false,
10480                        sized_by_content: false,
10481                    },
10482                    multi_buffer.clone(),
10483                    Some(project.clone()),
10484                    window,
10485                    cx,
10486                )
10487            })
10488        })
10489        .unwrap();
10490
10491    let pane = workspace
10492        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10493        .unwrap();
10494    pane.update_in(cx, |pane, window, cx| {
10495        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10496    });
10497
10498    let fake_server = fake_servers.next().await.unwrap();
10499
10500    editor.update_in(cx, |editor, window, cx| {
10501        editor.change_selections(None, window, cx, |s| {
10502            s.select_ranges([
10503                Point::new(1, 11)..Point::new(1, 11),
10504                Point::new(7, 11)..Point::new(7, 11),
10505            ])
10506        });
10507
10508        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10509    });
10510
10511    editor.update_in(cx, |editor, window, cx| {
10512        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10513    });
10514
10515    fake_server
10516        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10517            let completion_item = lsp::CompletionItem {
10518                label: "saturating_sub()".into(),
10519                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10520                    lsp::InsertReplaceEdit {
10521                        new_text: "saturating_sub()".to_owned(),
10522                        insert: lsp::Range::new(
10523                            lsp::Position::new(7, 7),
10524                            lsp::Position::new(7, 11),
10525                        ),
10526                        replace: lsp::Range::new(
10527                            lsp::Position::new(7, 7),
10528                            lsp::Position::new(7, 13),
10529                        ),
10530                    },
10531                )),
10532                ..lsp::CompletionItem::default()
10533            };
10534
10535            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10536        })
10537        .next()
10538        .await
10539        .unwrap();
10540
10541    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10542        .await;
10543
10544    editor
10545        .update_in(cx, |editor, window, cx| {
10546            editor
10547                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10548                .unwrap()
10549        })
10550        .await
10551        .unwrap();
10552
10553    editor.update(cx, |editor, cx| {
10554        assert_text_with_selections(editor, expected_multibuffer, cx);
10555    })
10556}
10557
10558#[gpui::test]
10559async fn test_completion(cx: &mut TestAppContext) {
10560    init_test(cx, |_| {});
10561
10562    let mut cx = EditorLspTestContext::new_rust(
10563        lsp::ServerCapabilities {
10564            completion_provider: Some(lsp::CompletionOptions {
10565                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10566                resolve_provider: Some(true),
10567                ..Default::default()
10568            }),
10569            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10570            ..Default::default()
10571        },
10572        cx,
10573    )
10574    .await;
10575    let counter = Arc::new(AtomicUsize::new(0));
10576
10577    cx.set_state(indoc! {"
10578        oneˇ
10579        two
10580        three
10581    "});
10582    cx.simulate_keystroke(".");
10583    handle_completion_request(
10584        &mut cx,
10585        indoc! {"
10586            one.|<>
10587            two
10588            three
10589        "},
10590        vec!["first_completion", "second_completion"],
10591        counter.clone(),
10592    )
10593    .await;
10594    cx.condition(|editor, _| editor.context_menu_visible())
10595        .await;
10596    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10597
10598    let _handler = handle_signature_help_request(
10599        &mut cx,
10600        lsp::SignatureHelp {
10601            signatures: vec![lsp::SignatureInformation {
10602                label: "test signature".to_string(),
10603                documentation: None,
10604                parameters: Some(vec![lsp::ParameterInformation {
10605                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10606                    documentation: None,
10607                }]),
10608                active_parameter: None,
10609            }],
10610            active_signature: None,
10611            active_parameter: None,
10612        },
10613    );
10614    cx.update_editor(|editor, window, cx| {
10615        assert!(
10616            !editor.signature_help_state.is_shown(),
10617            "No signature help was called for"
10618        );
10619        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10620    });
10621    cx.run_until_parked();
10622    cx.update_editor(|editor, _, _| {
10623        assert!(
10624            !editor.signature_help_state.is_shown(),
10625            "No signature help should be shown when completions menu is open"
10626        );
10627    });
10628
10629    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10630        editor.context_menu_next(&Default::default(), window, cx);
10631        editor
10632            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10633            .unwrap()
10634    });
10635    cx.assert_editor_state(indoc! {"
10636        one.second_completionˇ
10637        two
10638        three
10639    "});
10640
10641    handle_resolve_completion_request(
10642        &mut cx,
10643        Some(vec![
10644            (
10645                //This overlaps with the primary completion edit which is
10646                //misbehavior from the LSP spec, test that we filter it out
10647                indoc! {"
10648                    one.second_ˇcompletion
10649                    two
10650                    threeˇ
10651                "},
10652                "overlapping additional edit",
10653            ),
10654            (
10655                indoc! {"
10656                    one.second_completion
10657                    two
10658                    threeˇ
10659                "},
10660                "\nadditional edit",
10661            ),
10662        ]),
10663    )
10664    .await;
10665    apply_additional_edits.await.unwrap();
10666    cx.assert_editor_state(indoc! {"
10667        one.second_completionˇ
10668        two
10669        three
10670        additional edit
10671    "});
10672
10673    cx.set_state(indoc! {"
10674        one.second_completion
10675        twoˇ
10676        threeˇ
10677        additional edit
10678    "});
10679    cx.simulate_keystroke(" ");
10680    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10681    cx.simulate_keystroke("s");
10682    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10683
10684    cx.assert_editor_state(indoc! {"
10685        one.second_completion
10686        two sˇ
10687        three sˇ
10688        additional edit
10689    "});
10690    handle_completion_request(
10691        &mut cx,
10692        indoc! {"
10693            one.second_completion
10694            two s
10695            three <s|>
10696            additional edit
10697        "},
10698        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10699        counter.clone(),
10700    )
10701    .await;
10702    cx.condition(|editor, _| editor.context_menu_visible())
10703        .await;
10704    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10705
10706    cx.simulate_keystroke("i");
10707
10708    handle_completion_request(
10709        &mut cx,
10710        indoc! {"
10711            one.second_completion
10712            two si
10713            three <si|>
10714            additional edit
10715        "},
10716        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10717        counter.clone(),
10718    )
10719    .await;
10720    cx.condition(|editor, _| editor.context_menu_visible())
10721        .await;
10722    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10723
10724    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10725        editor
10726            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10727            .unwrap()
10728    });
10729    cx.assert_editor_state(indoc! {"
10730        one.second_completion
10731        two sixth_completionˇ
10732        three sixth_completionˇ
10733        additional edit
10734    "});
10735
10736    apply_additional_edits.await.unwrap();
10737
10738    update_test_language_settings(&mut cx, |settings| {
10739        settings.defaults.show_completions_on_input = Some(false);
10740    });
10741    cx.set_state("editorˇ");
10742    cx.simulate_keystroke(".");
10743    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10744    cx.simulate_keystrokes("c l o");
10745    cx.assert_editor_state("editor.cloˇ");
10746    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10747    cx.update_editor(|editor, window, cx| {
10748        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10749    });
10750    handle_completion_request(
10751        &mut cx,
10752        "editor.<clo|>",
10753        vec!["close", "clobber"],
10754        counter.clone(),
10755    )
10756    .await;
10757    cx.condition(|editor, _| editor.context_menu_visible())
10758        .await;
10759    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10760
10761    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10762        editor
10763            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10764            .unwrap()
10765    });
10766    cx.assert_editor_state("editor.closeˇ");
10767    handle_resolve_completion_request(&mut cx, None).await;
10768    apply_additional_edits.await.unwrap();
10769}
10770
10771#[gpui::test]
10772async fn test_word_completion(cx: &mut TestAppContext) {
10773    let lsp_fetch_timeout_ms = 10;
10774    init_test(cx, |language_settings| {
10775        language_settings.defaults.completions = Some(CompletionSettings {
10776            words: WordsCompletionMode::Fallback,
10777            lsp: true,
10778            lsp_fetch_timeout_ms: 10,
10779            lsp_insert_mode: LspInsertMode::Insert,
10780        });
10781    });
10782
10783    let mut cx = EditorLspTestContext::new_rust(
10784        lsp::ServerCapabilities {
10785            completion_provider: Some(lsp::CompletionOptions {
10786                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10787                ..lsp::CompletionOptions::default()
10788            }),
10789            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10790            ..lsp::ServerCapabilities::default()
10791        },
10792        cx,
10793    )
10794    .await;
10795
10796    let throttle_completions = Arc::new(AtomicBool::new(false));
10797
10798    let lsp_throttle_completions = throttle_completions.clone();
10799    let _completion_requests_handler =
10800        cx.lsp
10801            .server
10802            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10803                let lsp_throttle_completions = lsp_throttle_completions.clone();
10804                let cx = cx.clone();
10805                async move {
10806                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10807                        cx.background_executor()
10808                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10809                            .await;
10810                    }
10811                    Ok(Some(lsp::CompletionResponse::Array(vec![
10812                        lsp::CompletionItem {
10813                            label: "first".into(),
10814                            ..lsp::CompletionItem::default()
10815                        },
10816                        lsp::CompletionItem {
10817                            label: "last".into(),
10818                            ..lsp::CompletionItem::default()
10819                        },
10820                    ])))
10821                }
10822            });
10823
10824    cx.set_state(indoc! {"
10825        oneˇ
10826        two
10827        three
10828    "});
10829    cx.simulate_keystroke(".");
10830    cx.executor().run_until_parked();
10831    cx.condition(|editor, _| editor.context_menu_visible())
10832        .await;
10833    cx.update_editor(|editor, window, cx| {
10834        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10835        {
10836            assert_eq!(
10837                completion_menu_entries(&menu),
10838                &["first", "last"],
10839                "When LSP server is fast to reply, no fallback word completions are used"
10840            );
10841        } else {
10842            panic!("expected completion menu to be open");
10843        }
10844        editor.cancel(&Cancel, window, cx);
10845    });
10846    cx.executor().run_until_parked();
10847    cx.condition(|editor, _| !editor.context_menu_visible())
10848        .await;
10849
10850    throttle_completions.store(true, atomic::Ordering::Release);
10851    cx.simulate_keystroke(".");
10852    cx.executor()
10853        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10854    cx.executor().run_until_parked();
10855    cx.condition(|editor, _| editor.context_menu_visible())
10856        .await;
10857    cx.update_editor(|editor, _, _| {
10858        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10859        {
10860            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10861                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10862        } else {
10863            panic!("expected completion menu to be open");
10864        }
10865    });
10866}
10867
10868#[gpui::test]
10869async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10870    init_test(cx, |language_settings| {
10871        language_settings.defaults.completions = Some(CompletionSettings {
10872            words: WordsCompletionMode::Enabled,
10873            lsp: true,
10874            lsp_fetch_timeout_ms: 0,
10875            lsp_insert_mode: LspInsertMode::Insert,
10876        });
10877    });
10878
10879    let mut cx = EditorLspTestContext::new_rust(
10880        lsp::ServerCapabilities {
10881            completion_provider: Some(lsp::CompletionOptions {
10882                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10883                ..lsp::CompletionOptions::default()
10884            }),
10885            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10886            ..lsp::ServerCapabilities::default()
10887        },
10888        cx,
10889    )
10890    .await;
10891
10892    let _completion_requests_handler =
10893        cx.lsp
10894            .server
10895            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10896                Ok(Some(lsp::CompletionResponse::Array(vec![
10897                    lsp::CompletionItem {
10898                        label: "first".into(),
10899                        ..lsp::CompletionItem::default()
10900                    },
10901                    lsp::CompletionItem {
10902                        label: "last".into(),
10903                        ..lsp::CompletionItem::default()
10904                    },
10905                ])))
10906            });
10907
10908    cx.set_state(indoc! {"ˇ
10909        first
10910        last
10911        second
10912    "});
10913    cx.simulate_keystroke(".");
10914    cx.executor().run_until_parked();
10915    cx.condition(|editor, _| editor.context_menu_visible())
10916        .await;
10917    cx.update_editor(|editor, _, _| {
10918        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10919        {
10920            assert_eq!(
10921                completion_menu_entries(&menu),
10922                &["first", "last", "second"],
10923                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10924            );
10925        } else {
10926            panic!("expected completion menu to be open");
10927        }
10928    });
10929}
10930
10931#[gpui::test]
10932async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10933    init_test(cx, |language_settings| {
10934        language_settings.defaults.completions = Some(CompletionSettings {
10935            words: WordsCompletionMode::Disabled,
10936            lsp: true,
10937            lsp_fetch_timeout_ms: 0,
10938            lsp_insert_mode: LspInsertMode::Insert,
10939        });
10940    });
10941
10942    let mut cx = EditorLspTestContext::new_rust(
10943        lsp::ServerCapabilities {
10944            completion_provider: Some(lsp::CompletionOptions {
10945                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10946                ..lsp::CompletionOptions::default()
10947            }),
10948            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10949            ..lsp::ServerCapabilities::default()
10950        },
10951        cx,
10952    )
10953    .await;
10954
10955    let _completion_requests_handler =
10956        cx.lsp
10957            .server
10958            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10959                panic!("LSP completions should not be queried when dealing with word completions")
10960            });
10961
10962    cx.set_state(indoc! {"ˇ
10963        first
10964        last
10965        second
10966    "});
10967    cx.update_editor(|editor, window, cx| {
10968        editor.show_word_completions(&ShowWordCompletions, window, cx);
10969    });
10970    cx.executor().run_until_parked();
10971    cx.condition(|editor, _| editor.context_menu_visible())
10972        .await;
10973    cx.update_editor(|editor, _, _| {
10974        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10975        {
10976            assert_eq!(
10977                completion_menu_entries(&menu),
10978                &["first", "last", "second"],
10979                "`ShowWordCompletions` action should show word completions"
10980            );
10981        } else {
10982            panic!("expected completion menu to be open");
10983        }
10984    });
10985
10986    cx.simulate_keystroke("l");
10987    cx.executor().run_until_parked();
10988    cx.condition(|editor, _| editor.context_menu_visible())
10989        .await;
10990    cx.update_editor(|editor, _, _| {
10991        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10992        {
10993            assert_eq!(
10994                completion_menu_entries(&menu),
10995                &["last"],
10996                "After showing word completions, further editing should filter them and not query the LSP"
10997            );
10998        } else {
10999            panic!("expected completion menu to be open");
11000        }
11001    });
11002}
11003
11004#[gpui::test]
11005async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
11006    init_test(cx, |language_settings| {
11007        language_settings.defaults.completions = Some(CompletionSettings {
11008            words: WordsCompletionMode::Fallback,
11009            lsp: false,
11010            lsp_fetch_timeout_ms: 0,
11011            lsp_insert_mode: LspInsertMode::Insert,
11012        });
11013    });
11014
11015    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
11016
11017    cx.set_state(indoc! {"ˇ
11018        0_usize
11019        let
11020        33
11021        4.5f32
11022    "});
11023    cx.update_editor(|editor, window, cx| {
11024        editor.show_completions(&ShowCompletions::default(), window, cx);
11025    });
11026    cx.executor().run_until_parked();
11027    cx.condition(|editor, _| editor.context_menu_visible())
11028        .await;
11029    cx.update_editor(|editor, window, cx| {
11030        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11031        {
11032            assert_eq!(
11033                completion_menu_entries(&menu),
11034                &["let"],
11035                "With no digits in the completion query, no digits should be in the word completions"
11036            );
11037        } else {
11038            panic!("expected completion menu to be open");
11039        }
11040        editor.cancel(&Cancel, window, cx);
11041    });
11042
11043    cx.set_state(indoc! {"11044        0_usize
11045        let
11046        3
11047        33.35f32
11048    "});
11049    cx.update_editor(|editor, window, cx| {
11050        editor.show_completions(&ShowCompletions::default(), window, cx);
11051    });
11052    cx.executor().run_until_parked();
11053    cx.condition(|editor, _| editor.context_menu_visible())
11054        .await;
11055    cx.update_editor(|editor, _, _| {
11056        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11057        {
11058            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11059                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11060        } else {
11061            panic!("expected completion menu to be open");
11062        }
11063    });
11064}
11065
11066fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11067    let position = || lsp::Position {
11068        line: params.text_document_position.position.line,
11069        character: params.text_document_position.position.character,
11070    };
11071    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11072        range: lsp::Range {
11073            start: position(),
11074            end: position(),
11075        },
11076        new_text: text.to_string(),
11077    }))
11078}
11079
11080#[gpui::test]
11081async fn test_multiline_completion(cx: &mut TestAppContext) {
11082    init_test(cx, |_| {});
11083
11084    let fs = FakeFs::new(cx.executor());
11085    fs.insert_tree(
11086        path!("/a"),
11087        json!({
11088            "main.ts": "a",
11089        }),
11090    )
11091    .await;
11092
11093    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11094    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11095    let typescript_language = Arc::new(Language::new(
11096        LanguageConfig {
11097            name: "TypeScript".into(),
11098            matcher: LanguageMatcher {
11099                path_suffixes: vec!["ts".to_string()],
11100                ..LanguageMatcher::default()
11101            },
11102            line_comments: vec!["// ".into()],
11103            ..LanguageConfig::default()
11104        },
11105        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11106    ));
11107    language_registry.add(typescript_language.clone());
11108    let mut fake_servers = language_registry.register_fake_lsp(
11109        "TypeScript",
11110        FakeLspAdapter {
11111            capabilities: lsp::ServerCapabilities {
11112                completion_provider: Some(lsp::CompletionOptions {
11113                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11114                    ..lsp::CompletionOptions::default()
11115                }),
11116                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11117                ..lsp::ServerCapabilities::default()
11118            },
11119            // Emulate vtsls label generation
11120            label_for_completion: Some(Box::new(|item, _| {
11121                let text = if let Some(description) = item
11122                    .label_details
11123                    .as_ref()
11124                    .and_then(|label_details| label_details.description.as_ref())
11125                {
11126                    format!("{} {}", item.label, description)
11127                } else if let Some(detail) = &item.detail {
11128                    format!("{} {}", item.label, detail)
11129                } else {
11130                    item.label.clone()
11131                };
11132                let len = text.len();
11133                Some(language::CodeLabel {
11134                    text,
11135                    runs: Vec::new(),
11136                    filter_range: 0..len,
11137                })
11138            })),
11139            ..FakeLspAdapter::default()
11140        },
11141    );
11142    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11143    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11144    let worktree_id = workspace
11145        .update(cx, |workspace, _window, cx| {
11146            workspace.project().update(cx, |project, cx| {
11147                project.worktrees(cx).next().unwrap().read(cx).id()
11148            })
11149        })
11150        .unwrap();
11151    let _buffer = project
11152        .update(cx, |project, cx| {
11153            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11154        })
11155        .await
11156        .unwrap();
11157    let editor = workspace
11158        .update(cx, |workspace, window, cx| {
11159            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11160        })
11161        .unwrap()
11162        .await
11163        .unwrap()
11164        .downcast::<Editor>()
11165        .unwrap();
11166    let fake_server = fake_servers.next().await.unwrap();
11167
11168    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11169    let multiline_label_2 = "a\nb\nc\n";
11170    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11171    let multiline_description = "d\ne\nf\n";
11172    let multiline_detail_2 = "g\nh\ni\n";
11173
11174    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11175        move |params, _| async move {
11176            Ok(Some(lsp::CompletionResponse::Array(vec![
11177                lsp::CompletionItem {
11178                    label: multiline_label.to_string(),
11179                    text_edit: gen_text_edit(&params, "new_text_1"),
11180                    ..lsp::CompletionItem::default()
11181                },
11182                lsp::CompletionItem {
11183                    label: "single line label 1".to_string(),
11184                    detail: Some(multiline_detail.to_string()),
11185                    text_edit: gen_text_edit(&params, "new_text_2"),
11186                    ..lsp::CompletionItem::default()
11187                },
11188                lsp::CompletionItem {
11189                    label: "single line label 2".to_string(),
11190                    label_details: Some(lsp::CompletionItemLabelDetails {
11191                        description: Some(multiline_description.to_string()),
11192                        detail: None,
11193                    }),
11194                    text_edit: gen_text_edit(&params, "new_text_2"),
11195                    ..lsp::CompletionItem::default()
11196                },
11197                lsp::CompletionItem {
11198                    label: multiline_label_2.to_string(),
11199                    detail: Some(multiline_detail_2.to_string()),
11200                    text_edit: gen_text_edit(&params, "new_text_3"),
11201                    ..lsp::CompletionItem::default()
11202                },
11203                lsp::CompletionItem {
11204                    label: "Label with many     spaces and \t but without newlines".to_string(),
11205                    detail: Some(
11206                        "Details with many     spaces and \t but without newlines".to_string(),
11207                    ),
11208                    text_edit: gen_text_edit(&params, "new_text_4"),
11209                    ..lsp::CompletionItem::default()
11210                },
11211            ])))
11212        },
11213    );
11214
11215    editor.update_in(cx, |editor, window, cx| {
11216        cx.focus_self(window);
11217        editor.move_to_end(&MoveToEnd, window, cx);
11218        editor.handle_input(".", window, cx);
11219    });
11220    cx.run_until_parked();
11221    completion_handle.next().await.unwrap();
11222
11223    editor.update(cx, |editor, _| {
11224        assert!(editor.context_menu_visible());
11225        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11226        {
11227            let completion_labels = menu
11228                .completions
11229                .borrow()
11230                .iter()
11231                .map(|c| c.label.text.clone())
11232                .collect::<Vec<_>>();
11233            assert_eq!(
11234                completion_labels,
11235                &[
11236                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11237                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11238                    "single line label 2 d e f ",
11239                    "a b c g h i ",
11240                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11241                ],
11242                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11243            );
11244
11245            for completion in menu
11246                .completions
11247                .borrow()
11248                .iter() {
11249                    assert_eq!(
11250                        completion.label.filter_range,
11251                        0..completion.label.text.len(),
11252                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11253                    );
11254                }
11255        } else {
11256            panic!("expected completion menu to be open");
11257        }
11258    });
11259}
11260
11261#[gpui::test]
11262async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11263    init_test(cx, |_| {});
11264    let mut cx = EditorLspTestContext::new_rust(
11265        lsp::ServerCapabilities {
11266            completion_provider: Some(lsp::CompletionOptions {
11267                trigger_characters: Some(vec![".".to_string()]),
11268                ..Default::default()
11269            }),
11270            ..Default::default()
11271        },
11272        cx,
11273    )
11274    .await;
11275    cx.lsp
11276        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11277            Ok(Some(lsp::CompletionResponse::Array(vec![
11278                lsp::CompletionItem {
11279                    label: "first".into(),
11280                    ..Default::default()
11281                },
11282                lsp::CompletionItem {
11283                    label: "last".into(),
11284                    ..Default::default()
11285                },
11286            ])))
11287        });
11288    cx.set_state("variableˇ");
11289    cx.simulate_keystroke(".");
11290    cx.executor().run_until_parked();
11291
11292    cx.update_editor(|editor, _, _| {
11293        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11294        {
11295            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11296        } else {
11297            panic!("expected completion menu to be open");
11298        }
11299    });
11300
11301    cx.update_editor(|editor, window, cx| {
11302        editor.move_page_down(&MovePageDown::default(), window, cx);
11303        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11304        {
11305            assert!(
11306                menu.selected_item == 1,
11307                "expected PageDown to select the last item from the context menu"
11308            );
11309        } else {
11310            panic!("expected completion menu to stay open after PageDown");
11311        }
11312    });
11313
11314    cx.update_editor(|editor, window, cx| {
11315        editor.move_page_up(&MovePageUp::default(), window, cx);
11316        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11317        {
11318            assert!(
11319                menu.selected_item == 0,
11320                "expected PageUp to select the first item from the context menu"
11321            );
11322        } else {
11323            panic!("expected completion menu to stay open after PageUp");
11324        }
11325    });
11326}
11327
11328#[gpui::test]
11329async fn test_as_is_completions(cx: &mut TestAppContext) {
11330    init_test(cx, |_| {});
11331    let mut cx = EditorLspTestContext::new_rust(
11332        lsp::ServerCapabilities {
11333            completion_provider: Some(lsp::CompletionOptions {
11334                ..Default::default()
11335            }),
11336            ..Default::default()
11337        },
11338        cx,
11339    )
11340    .await;
11341    cx.lsp
11342        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11343            Ok(Some(lsp::CompletionResponse::Array(vec![
11344                lsp::CompletionItem {
11345                    label: "unsafe".into(),
11346                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11347                        range: lsp::Range {
11348                            start: lsp::Position {
11349                                line: 1,
11350                                character: 2,
11351                            },
11352                            end: lsp::Position {
11353                                line: 1,
11354                                character: 3,
11355                            },
11356                        },
11357                        new_text: "unsafe".to_string(),
11358                    })),
11359                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11360                    ..Default::default()
11361                },
11362            ])))
11363        });
11364    cx.set_state("fn a() {}\n");
11365    cx.executor().run_until_parked();
11366    cx.update_editor(|editor, window, cx| {
11367        editor.show_completions(
11368            &ShowCompletions {
11369                trigger: Some("\n".into()),
11370            },
11371            window,
11372            cx,
11373        );
11374    });
11375    cx.executor().run_until_parked();
11376
11377    cx.update_editor(|editor, window, cx| {
11378        editor.confirm_completion(&Default::default(), window, cx)
11379    });
11380    cx.executor().run_until_parked();
11381    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11382}
11383
11384#[gpui::test]
11385async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11386    init_test(cx, |_| {});
11387
11388    let mut cx = EditorLspTestContext::new_rust(
11389        lsp::ServerCapabilities {
11390            completion_provider: Some(lsp::CompletionOptions {
11391                trigger_characters: Some(vec![".".to_string()]),
11392                resolve_provider: Some(true),
11393                ..Default::default()
11394            }),
11395            ..Default::default()
11396        },
11397        cx,
11398    )
11399    .await;
11400
11401    cx.set_state("fn main() { let a = 2ˇ; }");
11402    cx.simulate_keystroke(".");
11403    let completion_item = lsp::CompletionItem {
11404        label: "Some".into(),
11405        kind: Some(lsp::CompletionItemKind::SNIPPET),
11406        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11407        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11408            kind: lsp::MarkupKind::Markdown,
11409            value: "```rust\nSome(2)\n```".to_string(),
11410        })),
11411        deprecated: Some(false),
11412        sort_text: Some("Some".to_string()),
11413        filter_text: Some("Some".to_string()),
11414        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11415        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11416            range: lsp::Range {
11417                start: lsp::Position {
11418                    line: 0,
11419                    character: 22,
11420                },
11421                end: lsp::Position {
11422                    line: 0,
11423                    character: 22,
11424                },
11425            },
11426            new_text: "Some(2)".to_string(),
11427        })),
11428        additional_text_edits: Some(vec![lsp::TextEdit {
11429            range: lsp::Range {
11430                start: lsp::Position {
11431                    line: 0,
11432                    character: 20,
11433                },
11434                end: lsp::Position {
11435                    line: 0,
11436                    character: 22,
11437                },
11438            },
11439            new_text: "".to_string(),
11440        }]),
11441        ..Default::default()
11442    };
11443
11444    let closure_completion_item = completion_item.clone();
11445    let counter = Arc::new(AtomicUsize::new(0));
11446    let counter_clone = counter.clone();
11447    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11448        let task_completion_item = closure_completion_item.clone();
11449        counter_clone.fetch_add(1, atomic::Ordering::Release);
11450        async move {
11451            Ok(Some(lsp::CompletionResponse::Array(vec![
11452                task_completion_item,
11453            ])))
11454        }
11455    });
11456
11457    cx.condition(|editor, _| editor.context_menu_visible())
11458        .await;
11459    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11460    assert!(request.next().await.is_some());
11461    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11462
11463    cx.simulate_keystrokes("S o m");
11464    cx.condition(|editor, _| editor.context_menu_visible())
11465        .await;
11466    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11467    assert!(request.next().await.is_some());
11468    assert!(request.next().await.is_some());
11469    assert!(request.next().await.is_some());
11470    request.close();
11471    assert!(request.next().await.is_none());
11472    assert_eq!(
11473        counter.load(atomic::Ordering::Acquire),
11474        4,
11475        "With the completions menu open, only one LSP request should happen per input"
11476    );
11477}
11478
11479#[gpui::test]
11480async fn test_toggle_comment(cx: &mut TestAppContext) {
11481    init_test(cx, |_| {});
11482    let mut cx = EditorTestContext::new(cx).await;
11483    let language = Arc::new(Language::new(
11484        LanguageConfig {
11485            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11486            ..Default::default()
11487        },
11488        Some(tree_sitter_rust::LANGUAGE.into()),
11489    ));
11490    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11491
11492    // If multiple selections intersect a line, the line is only toggled once.
11493    cx.set_state(indoc! {"
11494        fn a() {
11495            «//b();
11496            ˇ»// «c();
11497            //ˇ»  d();
11498        }
11499    "});
11500
11501    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11502
11503    cx.assert_editor_state(indoc! {"
11504        fn a() {
11505            «b();
11506            c();
11507            ˇ» d();
11508        }
11509    "});
11510
11511    // The comment prefix is inserted at the same column for every line in a
11512    // selection.
11513    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11514
11515    cx.assert_editor_state(indoc! {"
11516        fn a() {
11517            // «b();
11518            // c();
11519            ˇ»//  d();
11520        }
11521    "});
11522
11523    // If a selection ends at the beginning of a line, that line is not toggled.
11524    cx.set_selections_state(indoc! {"
11525        fn a() {
11526            // b();
11527            «// c();
11528        ˇ»    //  d();
11529        }
11530    "});
11531
11532    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11533
11534    cx.assert_editor_state(indoc! {"
11535        fn a() {
11536            // b();
11537            «c();
11538        ˇ»    //  d();
11539        }
11540    "});
11541
11542    // If a selection span a single line and is empty, the line is toggled.
11543    cx.set_state(indoc! {"
11544        fn a() {
11545            a();
11546            b();
11547        ˇ
11548        }
11549    "});
11550
11551    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11552
11553    cx.assert_editor_state(indoc! {"
11554        fn a() {
11555            a();
11556            b();
11557        //•ˇ
11558        }
11559    "});
11560
11561    // If a selection span multiple lines, empty lines are not toggled.
11562    cx.set_state(indoc! {"
11563        fn a() {
11564            «a();
11565
11566            c();ˇ»
11567        }
11568    "});
11569
11570    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11571
11572    cx.assert_editor_state(indoc! {"
11573        fn a() {
11574            // «a();
11575
11576            // c();ˇ»
11577        }
11578    "});
11579
11580    // If a selection includes multiple comment prefixes, all lines are uncommented.
11581    cx.set_state(indoc! {"
11582        fn a() {
11583            «// a();
11584            /// b();
11585            //! c();ˇ»
11586        }
11587    "});
11588
11589    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11590
11591    cx.assert_editor_state(indoc! {"
11592        fn a() {
11593            «a();
11594            b();
11595            c();ˇ»
11596        }
11597    "});
11598}
11599
11600#[gpui::test]
11601async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11602    init_test(cx, |_| {});
11603    let mut cx = EditorTestContext::new(cx).await;
11604    let language = Arc::new(Language::new(
11605        LanguageConfig {
11606            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11607            ..Default::default()
11608        },
11609        Some(tree_sitter_rust::LANGUAGE.into()),
11610    ));
11611    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11612
11613    let toggle_comments = &ToggleComments {
11614        advance_downwards: false,
11615        ignore_indent: true,
11616    };
11617
11618    // If multiple selections intersect a line, the line is only toggled once.
11619    cx.set_state(indoc! {"
11620        fn a() {
11621        //    «b();
11622        //    c();
11623        //    ˇ» d();
11624        }
11625    "});
11626
11627    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11628
11629    cx.assert_editor_state(indoc! {"
11630        fn a() {
11631            «b();
11632            c();
11633            ˇ» d();
11634        }
11635    "});
11636
11637    // The comment prefix is inserted at the beginning of each line
11638    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11639
11640    cx.assert_editor_state(indoc! {"
11641        fn a() {
11642        //    «b();
11643        //    c();
11644        //    ˇ» d();
11645        }
11646    "});
11647
11648    // If a selection ends at the beginning of a line, that line is not toggled.
11649    cx.set_selections_state(indoc! {"
11650        fn a() {
11651        //    b();
11652        //    «c();
11653        ˇ»//     d();
11654        }
11655    "});
11656
11657    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11658
11659    cx.assert_editor_state(indoc! {"
11660        fn a() {
11661        //    b();
11662            «c();
11663        ˇ»//     d();
11664        }
11665    "});
11666
11667    // If a selection span a single line and is empty, the line is toggled.
11668    cx.set_state(indoc! {"
11669        fn a() {
11670            a();
11671            b();
11672        ˇ
11673        }
11674    "});
11675
11676    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11677
11678    cx.assert_editor_state(indoc! {"
11679        fn a() {
11680            a();
11681            b();
11682        //ˇ
11683        }
11684    "});
11685
11686    // If a selection span multiple lines, empty lines are not toggled.
11687    cx.set_state(indoc! {"
11688        fn a() {
11689            «a();
11690
11691            c();ˇ»
11692        }
11693    "});
11694
11695    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11696
11697    cx.assert_editor_state(indoc! {"
11698        fn a() {
11699        //    «a();
11700
11701        //    c();ˇ»
11702        }
11703    "});
11704
11705    // If a selection includes multiple comment prefixes, all lines are uncommented.
11706    cx.set_state(indoc! {"
11707        fn a() {
11708        //    «a();
11709        ///    b();
11710        //!    c();ˇ»
11711        }
11712    "});
11713
11714    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11715
11716    cx.assert_editor_state(indoc! {"
11717        fn a() {
11718            «a();
11719            b();
11720            c();ˇ»
11721        }
11722    "});
11723}
11724
11725#[gpui::test]
11726async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11727    init_test(cx, |_| {});
11728
11729    let language = Arc::new(Language::new(
11730        LanguageConfig {
11731            line_comments: vec!["// ".into()],
11732            ..Default::default()
11733        },
11734        Some(tree_sitter_rust::LANGUAGE.into()),
11735    ));
11736
11737    let mut cx = EditorTestContext::new(cx).await;
11738
11739    cx.language_registry().add(language.clone());
11740    cx.update_buffer(|buffer, cx| {
11741        buffer.set_language(Some(language), cx);
11742    });
11743
11744    let toggle_comments = &ToggleComments {
11745        advance_downwards: true,
11746        ignore_indent: false,
11747    };
11748
11749    // Single cursor on one line -> advance
11750    // Cursor moves horizontally 3 characters as well on non-blank line
11751    cx.set_state(indoc!(
11752        "fn a() {
11753             ˇdog();
11754             cat();
11755        }"
11756    ));
11757    cx.update_editor(|editor, window, cx| {
11758        editor.toggle_comments(toggle_comments, window, cx);
11759    });
11760    cx.assert_editor_state(indoc!(
11761        "fn a() {
11762             // dog();
11763             catˇ();
11764        }"
11765    ));
11766
11767    // Single selection on one line -> don't advance
11768    cx.set_state(indoc!(
11769        "fn a() {
11770             «dog()ˇ»;
11771             cat();
11772        }"
11773    ));
11774    cx.update_editor(|editor, window, cx| {
11775        editor.toggle_comments(toggle_comments, window, cx);
11776    });
11777    cx.assert_editor_state(indoc!(
11778        "fn a() {
11779             // «dog()ˇ»;
11780             cat();
11781        }"
11782    ));
11783
11784    // Multiple cursors on one line -> advance
11785    cx.set_state(indoc!(
11786        "fn a() {
11787             ˇdˇog();
11788             cat();
11789        }"
11790    ));
11791    cx.update_editor(|editor, window, cx| {
11792        editor.toggle_comments(toggle_comments, window, cx);
11793    });
11794    cx.assert_editor_state(indoc!(
11795        "fn a() {
11796             // dog();
11797             catˇ(ˇ);
11798        }"
11799    ));
11800
11801    // Multiple cursors on one line, with selection -> don't advance
11802    cx.set_state(indoc!(
11803        "fn a() {
11804             ˇdˇog«()ˇ»;
11805             cat();
11806        }"
11807    ));
11808    cx.update_editor(|editor, window, cx| {
11809        editor.toggle_comments(toggle_comments, window, cx);
11810    });
11811    cx.assert_editor_state(indoc!(
11812        "fn a() {
11813             // ˇdˇog«()ˇ»;
11814             cat();
11815        }"
11816    ));
11817
11818    // Single cursor on one line -> advance
11819    // Cursor moves to column 0 on blank line
11820    cx.set_state(indoc!(
11821        "fn a() {
11822             ˇdog();
11823
11824             cat();
11825        }"
11826    ));
11827    cx.update_editor(|editor, window, cx| {
11828        editor.toggle_comments(toggle_comments, window, cx);
11829    });
11830    cx.assert_editor_state(indoc!(
11831        "fn a() {
11832             // dog();
11833        ˇ
11834             cat();
11835        }"
11836    ));
11837
11838    // Single cursor on one line -> advance
11839    // Cursor starts and ends at column 0
11840    cx.set_state(indoc!(
11841        "fn a() {
11842         ˇ    dog();
11843             cat();
11844        }"
11845    ));
11846    cx.update_editor(|editor, window, cx| {
11847        editor.toggle_comments(toggle_comments, window, cx);
11848    });
11849    cx.assert_editor_state(indoc!(
11850        "fn a() {
11851             // dog();
11852         ˇ    cat();
11853        }"
11854    ));
11855}
11856
11857#[gpui::test]
11858async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11859    init_test(cx, |_| {});
11860
11861    let mut cx = EditorTestContext::new(cx).await;
11862
11863    let html_language = Arc::new(
11864        Language::new(
11865            LanguageConfig {
11866                name: "HTML".into(),
11867                block_comment: Some(("<!-- ".into(), " -->".into())),
11868                ..Default::default()
11869            },
11870            Some(tree_sitter_html::LANGUAGE.into()),
11871        )
11872        .with_injection_query(
11873            r#"
11874            (script_element
11875                (raw_text) @injection.content
11876                (#set! injection.language "javascript"))
11877            "#,
11878        )
11879        .unwrap(),
11880    );
11881
11882    let javascript_language = Arc::new(Language::new(
11883        LanguageConfig {
11884            name: "JavaScript".into(),
11885            line_comments: vec!["// ".into()],
11886            ..Default::default()
11887        },
11888        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11889    ));
11890
11891    cx.language_registry().add(html_language.clone());
11892    cx.language_registry().add(javascript_language.clone());
11893    cx.update_buffer(|buffer, cx| {
11894        buffer.set_language(Some(html_language), cx);
11895    });
11896
11897    // Toggle comments for empty selections
11898    cx.set_state(
11899        &r#"
11900            <p>A</p>ˇ
11901            <p>B</p>ˇ
11902            <p>C</p>ˇ
11903        "#
11904        .unindent(),
11905    );
11906    cx.update_editor(|editor, window, cx| {
11907        editor.toggle_comments(&ToggleComments::default(), window, cx)
11908    });
11909    cx.assert_editor_state(
11910        &r#"
11911            <!-- <p>A</p>ˇ -->
11912            <!-- <p>B</p>ˇ -->
11913            <!-- <p>C</p>ˇ -->
11914        "#
11915        .unindent(),
11916    );
11917    cx.update_editor(|editor, window, cx| {
11918        editor.toggle_comments(&ToggleComments::default(), window, cx)
11919    });
11920    cx.assert_editor_state(
11921        &r#"
11922            <p>A</p>ˇ
11923            <p>B</p>ˇ
11924            <p>C</p>ˇ
11925        "#
11926        .unindent(),
11927    );
11928
11929    // Toggle comments for mixture of empty and non-empty selections, where
11930    // multiple selections occupy a given line.
11931    cx.set_state(
11932        &r#"
11933            <p>A«</p>
11934            <p>ˇ»B</p>ˇ
11935            <p>C«</p>
11936            <p>ˇ»D</p>ˇ
11937        "#
11938        .unindent(),
11939    );
11940
11941    cx.update_editor(|editor, window, cx| {
11942        editor.toggle_comments(&ToggleComments::default(), window, cx)
11943    });
11944    cx.assert_editor_state(
11945        &r#"
11946            <!-- <p>A«</p>
11947            <p>ˇ»B</p>ˇ -->
11948            <!-- <p>C«</p>
11949            <p>ˇ»D</p>ˇ -->
11950        "#
11951        .unindent(),
11952    );
11953    cx.update_editor(|editor, window, cx| {
11954        editor.toggle_comments(&ToggleComments::default(), window, cx)
11955    });
11956    cx.assert_editor_state(
11957        &r#"
11958            <p>A«</p>
11959            <p>ˇ»B</p>ˇ
11960            <p>C«</p>
11961            <p>ˇ»D</p>ˇ
11962        "#
11963        .unindent(),
11964    );
11965
11966    // Toggle comments when different languages are active for different
11967    // selections.
11968    cx.set_state(
11969        &r#"
11970            ˇ<script>
11971                ˇvar x = new Y();
11972            ˇ</script>
11973        "#
11974        .unindent(),
11975    );
11976    cx.executor().run_until_parked();
11977    cx.update_editor(|editor, window, cx| {
11978        editor.toggle_comments(&ToggleComments::default(), window, cx)
11979    });
11980    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11981    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11982    cx.assert_editor_state(
11983        &r#"
11984            <!-- ˇ<script> -->
11985                // ˇvar x = new Y();
11986            <!-- ˇ</script> -->
11987        "#
11988        .unindent(),
11989    );
11990}
11991
11992#[gpui::test]
11993fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11994    init_test(cx, |_| {});
11995
11996    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11997    let multibuffer = cx.new(|cx| {
11998        let mut multibuffer = MultiBuffer::new(ReadWrite);
11999        multibuffer.push_excerpts(
12000            buffer.clone(),
12001            [
12002                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
12003                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
12004            ],
12005            cx,
12006        );
12007        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
12008        multibuffer
12009    });
12010
12011    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12012    editor.update_in(cx, |editor, window, cx| {
12013        assert_eq!(editor.text(cx), "aaaa\nbbbb");
12014        editor.change_selections(None, window, cx, |s| {
12015            s.select_ranges([
12016                Point::new(0, 0)..Point::new(0, 0),
12017                Point::new(1, 0)..Point::new(1, 0),
12018            ])
12019        });
12020
12021        editor.handle_input("X", window, cx);
12022        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
12023        assert_eq!(
12024            editor.selections.ranges(cx),
12025            [
12026                Point::new(0, 1)..Point::new(0, 1),
12027                Point::new(1, 1)..Point::new(1, 1),
12028            ]
12029        );
12030
12031        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12032        editor.change_selections(None, window, cx, |s| {
12033            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12034        });
12035        editor.backspace(&Default::default(), window, cx);
12036        assert_eq!(editor.text(cx), "Xa\nbbb");
12037        assert_eq!(
12038            editor.selections.ranges(cx),
12039            [Point::new(1, 0)..Point::new(1, 0)]
12040        );
12041
12042        editor.change_selections(None, window, cx, |s| {
12043            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12044        });
12045        editor.backspace(&Default::default(), window, cx);
12046        assert_eq!(editor.text(cx), "X\nbb");
12047        assert_eq!(
12048            editor.selections.ranges(cx),
12049            [Point::new(0, 1)..Point::new(0, 1)]
12050        );
12051    });
12052}
12053
12054#[gpui::test]
12055fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12056    init_test(cx, |_| {});
12057
12058    let markers = vec![('[', ']').into(), ('(', ')').into()];
12059    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12060        indoc! {"
12061            [aaaa
12062            (bbbb]
12063            cccc)",
12064        },
12065        markers.clone(),
12066    );
12067    let excerpt_ranges = markers.into_iter().map(|marker| {
12068        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12069        ExcerptRange::new(context.clone())
12070    });
12071    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12072    let multibuffer = cx.new(|cx| {
12073        let mut multibuffer = MultiBuffer::new(ReadWrite);
12074        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12075        multibuffer
12076    });
12077
12078    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12079    editor.update_in(cx, |editor, window, cx| {
12080        let (expected_text, selection_ranges) = marked_text_ranges(
12081            indoc! {"
12082                aaaa
12083                bˇbbb
12084                bˇbbˇb
12085                cccc"
12086            },
12087            true,
12088        );
12089        assert_eq!(editor.text(cx), expected_text);
12090        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12091
12092        editor.handle_input("X", window, cx);
12093
12094        let (expected_text, expected_selections) = marked_text_ranges(
12095            indoc! {"
12096                aaaa
12097                bXˇbbXb
12098                bXˇbbXˇb
12099                cccc"
12100            },
12101            false,
12102        );
12103        assert_eq!(editor.text(cx), expected_text);
12104        assert_eq!(editor.selections.ranges(cx), expected_selections);
12105
12106        editor.newline(&Newline, window, cx);
12107        let (expected_text, expected_selections) = marked_text_ranges(
12108            indoc! {"
12109                aaaa
12110                bX
12111                ˇbbX
12112                b
12113                bX
12114                ˇbbX
12115                ˇb
12116                cccc"
12117            },
12118            false,
12119        );
12120        assert_eq!(editor.text(cx), expected_text);
12121        assert_eq!(editor.selections.ranges(cx), expected_selections);
12122    });
12123}
12124
12125#[gpui::test]
12126fn test_refresh_selections(cx: &mut TestAppContext) {
12127    init_test(cx, |_| {});
12128
12129    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12130    let mut excerpt1_id = None;
12131    let multibuffer = cx.new(|cx| {
12132        let mut multibuffer = MultiBuffer::new(ReadWrite);
12133        excerpt1_id = multibuffer
12134            .push_excerpts(
12135                buffer.clone(),
12136                [
12137                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12138                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12139                ],
12140                cx,
12141            )
12142            .into_iter()
12143            .next();
12144        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12145        multibuffer
12146    });
12147
12148    let editor = cx.add_window(|window, cx| {
12149        let mut editor = build_editor(multibuffer.clone(), window, cx);
12150        let snapshot = editor.snapshot(window, cx);
12151        editor.change_selections(None, window, cx, |s| {
12152            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12153        });
12154        editor.begin_selection(
12155            Point::new(2, 1).to_display_point(&snapshot),
12156            true,
12157            1,
12158            window,
12159            cx,
12160        );
12161        assert_eq!(
12162            editor.selections.ranges(cx),
12163            [
12164                Point::new(1, 3)..Point::new(1, 3),
12165                Point::new(2, 1)..Point::new(2, 1),
12166            ]
12167        );
12168        editor
12169    });
12170
12171    // Refreshing selections is a no-op when excerpts haven't changed.
12172    _ = editor.update(cx, |editor, window, cx| {
12173        editor.change_selections(None, window, cx, |s| s.refresh());
12174        assert_eq!(
12175            editor.selections.ranges(cx),
12176            [
12177                Point::new(1, 3)..Point::new(1, 3),
12178                Point::new(2, 1)..Point::new(2, 1),
12179            ]
12180        );
12181    });
12182
12183    multibuffer.update(cx, |multibuffer, cx| {
12184        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12185    });
12186    _ = editor.update(cx, |editor, window, cx| {
12187        // Removing an excerpt causes the first selection to become degenerate.
12188        assert_eq!(
12189            editor.selections.ranges(cx),
12190            [
12191                Point::new(0, 0)..Point::new(0, 0),
12192                Point::new(0, 1)..Point::new(0, 1)
12193            ]
12194        );
12195
12196        // Refreshing selections will relocate the first selection to the original buffer
12197        // location.
12198        editor.change_selections(None, window, cx, |s| s.refresh());
12199        assert_eq!(
12200            editor.selections.ranges(cx),
12201            [
12202                Point::new(0, 1)..Point::new(0, 1),
12203                Point::new(0, 3)..Point::new(0, 3)
12204            ]
12205        );
12206        assert!(editor.selections.pending_anchor().is_some());
12207    });
12208}
12209
12210#[gpui::test]
12211fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12212    init_test(cx, |_| {});
12213
12214    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12215    let mut excerpt1_id = None;
12216    let multibuffer = cx.new(|cx| {
12217        let mut multibuffer = MultiBuffer::new(ReadWrite);
12218        excerpt1_id = multibuffer
12219            .push_excerpts(
12220                buffer.clone(),
12221                [
12222                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12223                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12224                ],
12225                cx,
12226            )
12227            .into_iter()
12228            .next();
12229        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12230        multibuffer
12231    });
12232
12233    let editor = cx.add_window(|window, cx| {
12234        let mut editor = build_editor(multibuffer.clone(), window, cx);
12235        let snapshot = editor.snapshot(window, cx);
12236        editor.begin_selection(
12237            Point::new(1, 3).to_display_point(&snapshot),
12238            false,
12239            1,
12240            window,
12241            cx,
12242        );
12243        assert_eq!(
12244            editor.selections.ranges(cx),
12245            [Point::new(1, 3)..Point::new(1, 3)]
12246        );
12247        editor
12248    });
12249
12250    multibuffer.update(cx, |multibuffer, cx| {
12251        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12252    });
12253    _ = editor.update(cx, |editor, window, cx| {
12254        assert_eq!(
12255            editor.selections.ranges(cx),
12256            [Point::new(0, 0)..Point::new(0, 0)]
12257        );
12258
12259        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12260        editor.change_selections(None, window, cx, |s| s.refresh());
12261        assert_eq!(
12262            editor.selections.ranges(cx),
12263            [Point::new(0, 3)..Point::new(0, 3)]
12264        );
12265        assert!(editor.selections.pending_anchor().is_some());
12266    });
12267}
12268
12269#[gpui::test]
12270async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12271    init_test(cx, |_| {});
12272
12273    let language = Arc::new(
12274        Language::new(
12275            LanguageConfig {
12276                brackets: BracketPairConfig {
12277                    pairs: vec![
12278                        BracketPair {
12279                            start: "{".to_string(),
12280                            end: "}".to_string(),
12281                            close: true,
12282                            surround: true,
12283                            newline: true,
12284                        },
12285                        BracketPair {
12286                            start: "/* ".to_string(),
12287                            end: " */".to_string(),
12288                            close: true,
12289                            surround: true,
12290                            newline: true,
12291                        },
12292                    ],
12293                    ..Default::default()
12294                },
12295                ..Default::default()
12296            },
12297            Some(tree_sitter_rust::LANGUAGE.into()),
12298        )
12299        .with_indents_query("")
12300        .unwrap(),
12301    );
12302
12303    let text = concat!(
12304        "{   }\n",     //
12305        "  x\n",       //
12306        "  /*   */\n", //
12307        "x\n",         //
12308        "{{} }\n",     //
12309    );
12310
12311    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12312    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12313    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12314    editor
12315        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12316        .await;
12317
12318    editor.update_in(cx, |editor, window, cx| {
12319        editor.change_selections(None, window, cx, |s| {
12320            s.select_display_ranges([
12321                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12322                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12323                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12324            ])
12325        });
12326        editor.newline(&Newline, window, cx);
12327
12328        assert_eq!(
12329            editor.buffer().read(cx).read(cx).text(),
12330            concat!(
12331                "{ \n",    // Suppress rustfmt
12332                "\n",      //
12333                "}\n",     //
12334                "  x\n",   //
12335                "  /* \n", //
12336                "  \n",    //
12337                "  */\n",  //
12338                "x\n",     //
12339                "{{} \n",  //
12340                "}\n",     //
12341            )
12342        );
12343    });
12344}
12345
12346#[gpui::test]
12347fn test_highlighted_ranges(cx: &mut TestAppContext) {
12348    init_test(cx, |_| {});
12349
12350    let editor = cx.add_window(|window, cx| {
12351        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12352        build_editor(buffer.clone(), window, cx)
12353    });
12354
12355    _ = editor.update(cx, |editor, window, cx| {
12356        struct Type1;
12357        struct Type2;
12358
12359        let buffer = editor.buffer.read(cx).snapshot(cx);
12360
12361        let anchor_range =
12362            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12363
12364        editor.highlight_background::<Type1>(
12365            &[
12366                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12367                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12368                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12369                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12370            ],
12371            |_| Hsla::red(),
12372            cx,
12373        );
12374        editor.highlight_background::<Type2>(
12375            &[
12376                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12377                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12378                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12379                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12380            ],
12381            |_| Hsla::green(),
12382            cx,
12383        );
12384
12385        let snapshot = editor.snapshot(window, cx);
12386        let mut highlighted_ranges = editor.background_highlights_in_range(
12387            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12388            &snapshot,
12389            cx.theme().colors(),
12390        );
12391        // Enforce a consistent ordering based on color without relying on the ordering of the
12392        // highlight's `TypeId` which is non-executor.
12393        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12394        assert_eq!(
12395            highlighted_ranges,
12396            &[
12397                (
12398                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12399                    Hsla::red(),
12400                ),
12401                (
12402                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12403                    Hsla::red(),
12404                ),
12405                (
12406                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12407                    Hsla::green(),
12408                ),
12409                (
12410                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12411                    Hsla::green(),
12412                ),
12413            ]
12414        );
12415        assert_eq!(
12416            editor.background_highlights_in_range(
12417                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12418                &snapshot,
12419                cx.theme().colors(),
12420            ),
12421            &[(
12422                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12423                Hsla::red(),
12424            )]
12425        );
12426    });
12427}
12428
12429#[gpui::test]
12430async fn test_following(cx: &mut TestAppContext) {
12431    init_test(cx, |_| {});
12432
12433    let fs = FakeFs::new(cx.executor());
12434    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12435
12436    let buffer = project.update(cx, |project, cx| {
12437        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12438        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12439    });
12440    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12441    let follower = cx.update(|cx| {
12442        cx.open_window(
12443            WindowOptions {
12444                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12445                    gpui::Point::new(px(0.), px(0.)),
12446                    gpui::Point::new(px(10.), px(80.)),
12447                ))),
12448                ..Default::default()
12449            },
12450            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12451        )
12452        .unwrap()
12453    });
12454
12455    let is_still_following = Rc::new(RefCell::new(true));
12456    let follower_edit_event_count = Rc::new(RefCell::new(0));
12457    let pending_update = Rc::new(RefCell::new(None));
12458    let leader_entity = leader.root(cx).unwrap();
12459    let follower_entity = follower.root(cx).unwrap();
12460    _ = follower.update(cx, {
12461        let update = pending_update.clone();
12462        let is_still_following = is_still_following.clone();
12463        let follower_edit_event_count = follower_edit_event_count.clone();
12464        |_, window, cx| {
12465            cx.subscribe_in(
12466                &leader_entity,
12467                window,
12468                move |_, leader, event, window, cx| {
12469                    leader.read(cx).add_event_to_update_proto(
12470                        event,
12471                        &mut update.borrow_mut(),
12472                        window,
12473                        cx,
12474                    );
12475                },
12476            )
12477            .detach();
12478
12479            cx.subscribe_in(
12480                &follower_entity,
12481                window,
12482                move |_, _, event: &EditorEvent, _window, _cx| {
12483                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12484                        *is_still_following.borrow_mut() = false;
12485                    }
12486
12487                    if let EditorEvent::BufferEdited = event {
12488                        *follower_edit_event_count.borrow_mut() += 1;
12489                    }
12490                },
12491            )
12492            .detach();
12493        }
12494    });
12495
12496    // Update the selections only
12497    _ = leader.update(cx, |leader, window, cx| {
12498        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12499    });
12500    follower
12501        .update(cx, |follower, window, cx| {
12502            follower.apply_update_proto(
12503                &project,
12504                pending_update.borrow_mut().take().unwrap(),
12505                window,
12506                cx,
12507            )
12508        })
12509        .unwrap()
12510        .await
12511        .unwrap();
12512    _ = follower.update(cx, |follower, _, cx| {
12513        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12514    });
12515    assert!(*is_still_following.borrow());
12516    assert_eq!(*follower_edit_event_count.borrow(), 0);
12517
12518    // Update the scroll position only
12519    _ = leader.update(cx, |leader, window, cx| {
12520        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12521    });
12522    follower
12523        .update(cx, |follower, window, cx| {
12524            follower.apply_update_proto(
12525                &project,
12526                pending_update.borrow_mut().take().unwrap(),
12527                window,
12528                cx,
12529            )
12530        })
12531        .unwrap()
12532        .await
12533        .unwrap();
12534    assert_eq!(
12535        follower
12536            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12537            .unwrap(),
12538        gpui::Point::new(1.5, 3.5)
12539    );
12540    assert!(*is_still_following.borrow());
12541    assert_eq!(*follower_edit_event_count.borrow(), 0);
12542
12543    // Update the selections and scroll position. The follower's scroll position is updated
12544    // via autoscroll, not via the leader's exact scroll position.
12545    _ = leader.update(cx, |leader, window, cx| {
12546        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12547        leader.request_autoscroll(Autoscroll::newest(), cx);
12548        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12549    });
12550    follower
12551        .update(cx, |follower, window, cx| {
12552            follower.apply_update_proto(
12553                &project,
12554                pending_update.borrow_mut().take().unwrap(),
12555                window,
12556                cx,
12557            )
12558        })
12559        .unwrap()
12560        .await
12561        .unwrap();
12562    _ = follower.update(cx, |follower, _, cx| {
12563        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12564        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12565    });
12566    assert!(*is_still_following.borrow());
12567
12568    // Creating a pending selection that precedes another selection
12569    _ = leader.update(cx, |leader, window, cx| {
12570        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12571        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12572    });
12573    follower
12574        .update(cx, |follower, window, cx| {
12575            follower.apply_update_proto(
12576                &project,
12577                pending_update.borrow_mut().take().unwrap(),
12578                window,
12579                cx,
12580            )
12581        })
12582        .unwrap()
12583        .await
12584        .unwrap();
12585    _ = follower.update(cx, |follower, _, cx| {
12586        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12587    });
12588    assert!(*is_still_following.borrow());
12589
12590    // Extend the pending selection so that it surrounds another selection
12591    _ = leader.update(cx, |leader, window, cx| {
12592        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12593    });
12594    follower
12595        .update(cx, |follower, window, cx| {
12596            follower.apply_update_proto(
12597                &project,
12598                pending_update.borrow_mut().take().unwrap(),
12599                window,
12600                cx,
12601            )
12602        })
12603        .unwrap()
12604        .await
12605        .unwrap();
12606    _ = follower.update(cx, |follower, _, cx| {
12607        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12608    });
12609
12610    // Scrolling locally breaks the follow
12611    _ = follower.update(cx, |follower, window, cx| {
12612        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12613        follower.set_scroll_anchor(
12614            ScrollAnchor {
12615                anchor: top_anchor,
12616                offset: gpui::Point::new(0.0, 0.5),
12617            },
12618            window,
12619            cx,
12620        );
12621    });
12622    assert!(!(*is_still_following.borrow()));
12623}
12624
12625#[gpui::test]
12626async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12627    init_test(cx, |_| {});
12628
12629    let fs = FakeFs::new(cx.executor());
12630    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12631    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12632    let pane = workspace
12633        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12634        .unwrap();
12635
12636    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12637
12638    let leader = pane.update_in(cx, |_, window, cx| {
12639        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12640        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12641    });
12642
12643    // Start following the editor when it has no excerpts.
12644    let mut state_message =
12645        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12646    let workspace_entity = workspace.root(cx).unwrap();
12647    let follower_1 = cx
12648        .update_window(*workspace.deref(), |_, window, cx| {
12649            Editor::from_state_proto(
12650                workspace_entity,
12651                ViewId {
12652                    creator: Default::default(),
12653                    id: 0,
12654                },
12655                &mut state_message,
12656                window,
12657                cx,
12658            )
12659        })
12660        .unwrap()
12661        .unwrap()
12662        .await
12663        .unwrap();
12664
12665    let update_message = Rc::new(RefCell::new(None));
12666    follower_1.update_in(cx, {
12667        let update = update_message.clone();
12668        |_, window, cx| {
12669            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12670                leader.read(cx).add_event_to_update_proto(
12671                    event,
12672                    &mut update.borrow_mut(),
12673                    window,
12674                    cx,
12675                );
12676            })
12677            .detach();
12678        }
12679    });
12680
12681    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12682        (
12683            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12684            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12685        )
12686    });
12687
12688    // Insert some excerpts.
12689    leader.update(cx, |leader, cx| {
12690        leader.buffer.update(cx, |multibuffer, cx| {
12691            multibuffer.set_excerpts_for_path(
12692                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12693                buffer_1.clone(),
12694                vec![
12695                    Point::row_range(0..3),
12696                    Point::row_range(1..6),
12697                    Point::row_range(12..15),
12698                ],
12699                0,
12700                cx,
12701            );
12702            multibuffer.set_excerpts_for_path(
12703                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12704                buffer_2.clone(),
12705                vec![Point::row_range(0..6), Point::row_range(8..12)],
12706                0,
12707                cx,
12708            );
12709        });
12710    });
12711
12712    // Apply the update of adding the excerpts.
12713    follower_1
12714        .update_in(cx, |follower, window, cx| {
12715            follower.apply_update_proto(
12716                &project,
12717                update_message.borrow().clone().unwrap(),
12718                window,
12719                cx,
12720            )
12721        })
12722        .await
12723        .unwrap();
12724    assert_eq!(
12725        follower_1.update(cx, |editor, cx| editor.text(cx)),
12726        leader.update(cx, |editor, cx| editor.text(cx))
12727    );
12728    update_message.borrow_mut().take();
12729
12730    // Start following separately after it already has excerpts.
12731    let mut state_message =
12732        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12733    let workspace_entity = workspace.root(cx).unwrap();
12734    let follower_2 = cx
12735        .update_window(*workspace.deref(), |_, window, cx| {
12736            Editor::from_state_proto(
12737                workspace_entity,
12738                ViewId {
12739                    creator: Default::default(),
12740                    id: 0,
12741                },
12742                &mut state_message,
12743                window,
12744                cx,
12745            )
12746        })
12747        .unwrap()
12748        .unwrap()
12749        .await
12750        .unwrap();
12751    assert_eq!(
12752        follower_2.update(cx, |editor, cx| editor.text(cx)),
12753        leader.update(cx, |editor, cx| editor.text(cx))
12754    );
12755
12756    // Remove some excerpts.
12757    leader.update(cx, |leader, cx| {
12758        leader.buffer.update(cx, |multibuffer, cx| {
12759            let excerpt_ids = multibuffer.excerpt_ids();
12760            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12761            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12762        });
12763    });
12764
12765    // Apply the update of removing the excerpts.
12766    follower_1
12767        .update_in(cx, |follower, window, cx| {
12768            follower.apply_update_proto(
12769                &project,
12770                update_message.borrow().clone().unwrap(),
12771                window,
12772                cx,
12773            )
12774        })
12775        .await
12776        .unwrap();
12777    follower_2
12778        .update_in(cx, |follower, window, cx| {
12779            follower.apply_update_proto(
12780                &project,
12781                update_message.borrow().clone().unwrap(),
12782                window,
12783                cx,
12784            )
12785        })
12786        .await
12787        .unwrap();
12788    update_message.borrow_mut().take();
12789    assert_eq!(
12790        follower_1.update(cx, |editor, cx| editor.text(cx)),
12791        leader.update(cx, |editor, cx| editor.text(cx))
12792    );
12793}
12794
12795#[gpui::test]
12796async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12797    init_test(cx, |_| {});
12798
12799    let mut cx = EditorTestContext::new(cx).await;
12800    let lsp_store =
12801        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12802
12803    cx.set_state(indoc! {"
12804        ˇfn func(abc def: i32) -> u32 {
12805        }
12806    "});
12807
12808    cx.update(|_, cx| {
12809        lsp_store.update(cx, |lsp_store, cx| {
12810            lsp_store
12811                .update_diagnostics(
12812                    LanguageServerId(0),
12813                    lsp::PublishDiagnosticsParams {
12814                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12815                        version: None,
12816                        diagnostics: vec![
12817                            lsp::Diagnostic {
12818                                range: lsp::Range::new(
12819                                    lsp::Position::new(0, 11),
12820                                    lsp::Position::new(0, 12),
12821                                ),
12822                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12823                                ..Default::default()
12824                            },
12825                            lsp::Diagnostic {
12826                                range: lsp::Range::new(
12827                                    lsp::Position::new(0, 12),
12828                                    lsp::Position::new(0, 15),
12829                                ),
12830                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12831                                ..Default::default()
12832                            },
12833                            lsp::Diagnostic {
12834                                range: lsp::Range::new(
12835                                    lsp::Position::new(0, 25),
12836                                    lsp::Position::new(0, 28),
12837                                ),
12838                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12839                                ..Default::default()
12840                            },
12841                        ],
12842                    },
12843                    &[],
12844                    cx,
12845                )
12846                .unwrap()
12847        });
12848    });
12849
12850    executor.run_until_parked();
12851
12852    cx.update_editor(|editor, window, cx| {
12853        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12854    });
12855
12856    cx.assert_editor_state(indoc! {"
12857        fn func(abc def: i32) -> ˇu32 {
12858        }
12859    "});
12860
12861    cx.update_editor(|editor, window, cx| {
12862        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12863    });
12864
12865    cx.assert_editor_state(indoc! {"
12866        fn func(abc ˇdef: i32) -> u32 {
12867        }
12868    "});
12869
12870    cx.update_editor(|editor, window, cx| {
12871        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12872    });
12873
12874    cx.assert_editor_state(indoc! {"
12875        fn func(abcˇ def: i32) -> u32 {
12876        }
12877    "});
12878
12879    cx.update_editor(|editor, window, cx| {
12880        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12881    });
12882
12883    cx.assert_editor_state(indoc! {"
12884        fn func(abc def: i32) -> ˇu32 {
12885        }
12886    "});
12887}
12888
12889#[gpui::test]
12890async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12891    init_test(cx, |_| {});
12892
12893    let mut cx = EditorTestContext::new(cx).await;
12894
12895    let diff_base = r#"
12896        use some::mod;
12897
12898        const A: u32 = 42;
12899
12900        fn main() {
12901            println!("hello");
12902
12903            println!("world");
12904        }
12905        "#
12906    .unindent();
12907
12908    // Edits are modified, removed, modified, added
12909    cx.set_state(
12910        &r#"
12911        use some::modified;
12912
12913        ˇ
12914        fn main() {
12915            println!("hello there");
12916
12917            println!("around the");
12918            println!("world");
12919        }
12920        "#
12921        .unindent(),
12922    );
12923
12924    cx.set_head_text(&diff_base);
12925    executor.run_until_parked();
12926
12927    cx.update_editor(|editor, window, cx| {
12928        //Wrap around the bottom of the buffer
12929        for _ in 0..3 {
12930            editor.go_to_next_hunk(&GoToHunk, window, cx);
12931        }
12932    });
12933
12934    cx.assert_editor_state(
12935        &r#"
12936        ˇuse some::modified;
12937
12938
12939        fn main() {
12940            println!("hello there");
12941
12942            println!("around the");
12943            println!("world");
12944        }
12945        "#
12946        .unindent(),
12947    );
12948
12949    cx.update_editor(|editor, window, cx| {
12950        //Wrap around the top of the buffer
12951        for _ in 0..2 {
12952            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12953        }
12954    });
12955
12956    cx.assert_editor_state(
12957        &r#"
12958        use some::modified;
12959
12960
12961        fn main() {
12962        ˇ    println!("hello there");
12963
12964            println!("around the");
12965            println!("world");
12966        }
12967        "#
12968        .unindent(),
12969    );
12970
12971    cx.update_editor(|editor, window, cx| {
12972        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12973    });
12974
12975    cx.assert_editor_state(
12976        &r#"
12977        use some::modified;
12978
12979        ˇ
12980        fn main() {
12981            println!("hello there");
12982
12983            println!("around the");
12984            println!("world");
12985        }
12986        "#
12987        .unindent(),
12988    );
12989
12990    cx.update_editor(|editor, window, cx| {
12991        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12992    });
12993
12994    cx.assert_editor_state(
12995        &r#"
12996        ˇuse some::modified;
12997
12998
12999        fn main() {
13000            println!("hello there");
13001
13002            println!("around the");
13003            println!("world");
13004        }
13005        "#
13006        .unindent(),
13007    );
13008
13009    cx.update_editor(|editor, window, cx| {
13010        for _ in 0..2 {
13011            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
13012        }
13013    });
13014
13015    cx.assert_editor_state(
13016        &r#"
13017        use some::modified;
13018
13019
13020        fn main() {
13021        ˇ    println!("hello there");
13022
13023            println!("around the");
13024            println!("world");
13025        }
13026        "#
13027        .unindent(),
13028    );
13029
13030    cx.update_editor(|editor, window, cx| {
13031        editor.fold(&Fold, window, cx);
13032    });
13033
13034    cx.update_editor(|editor, window, cx| {
13035        editor.go_to_next_hunk(&GoToHunk, window, cx);
13036    });
13037
13038    cx.assert_editor_state(
13039        &r#"
13040        ˇuse some::modified;
13041
13042
13043        fn main() {
13044            println!("hello there");
13045
13046            println!("around the");
13047            println!("world");
13048        }
13049        "#
13050        .unindent(),
13051    );
13052}
13053
13054#[test]
13055fn test_split_words() {
13056    fn split(text: &str) -> Vec<&str> {
13057        split_words(text).collect()
13058    }
13059
13060    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13061    assert_eq!(split("hello_world"), &["hello_", "world"]);
13062    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13063    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13064    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13065    assert_eq!(split("helloworld"), &["helloworld"]);
13066
13067    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13068}
13069
13070#[gpui::test]
13071async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13072    init_test(cx, |_| {});
13073
13074    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13075    let mut assert = |before, after| {
13076        let _state_context = cx.set_state(before);
13077        cx.run_until_parked();
13078        cx.update_editor(|editor, window, cx| {
13079            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13080        });
13081        cx.run_until_parked();
13082        cx.assert_editor_state(after);
13083    };
13084
13085    // Outside bracket jumps to outside of matching bracket
13086    assert("console.logˇ(var);", "console.log(var)ˇ;");
13087    assert("console.log(var)ˇ;", "console.logˇ(var);");
13088
13089    // Inside bracket jumps to inside of matching bracket
13090    assert("console.log(ˇvar);", "console.log(varˇ);");
13091    assert("console.log(varˇ);", "console.log(ˇvar);");
13092
13093    // When outside a bracket and inside, favor jumping to the inside bracket
13094    assert(
13095        "console.log('foo', [1, 2, 3]ˇ);",
13096        "console.log(ˇ'foo', [1, 2, 3]);",
13097    );
13098    assert(
13099        "console.log(ˇ'foo', [1, 2, 3]);",
13100        "console.log('foo', [1, 2, 3]ˇ);",
13101    );
13102
13103    // Bias forward if two options are equally likely
13104    assert(
13105        "let result = curried_fun()ˇ();",
13106        "let result = curried_fun()()ˇ;",
13107    );
13108
13109    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13110    assert(
13111        indoc! {"
13112            function test() {
13113                console.log('test')ˇ
13114            }"},
13115        indoc! {"
13116            function test() {
13117                console.logˇ('test')
13118            }"},
13119    );
13120}
13121
13122#[gpui::test]
13123async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13124    init_test(cx, |_| {});
13125
13126    let fs = FakeFs::new(cx.executor());
13127    fs.insert_tree(
13128        path!("/a"),
13129        json!({
13130            "main.rs": "fn main() { let a = 5; }",
13131            "other.rs": "// Test file",
13132        }),
13133    )
13134    .await;
13135    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13136
13137    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13138    language_registry.add(Arc::new(Language::new(
13139        LanguageConfig {
13140            name: "Rust".into(),
13141            matcher: LanguageMatcher {
13142                path_suffixes: vec!["rs".to_string()],
13143                ..Default::default()
13144            },
13145            brackets: BracketPairConfig {
13146                pairs: vec![BracketPair {
13147                    start: "{".to_string(),
13148                    end: "}".to_string(),
13149                    close: true,
13150                    surround: true,
13151                    newline: true,
13152                }],
13153                disabled_scopes_by_bracket_ix: Vec::new(),
13154            },
13155            ..Default::default()
13156        },
13157        Some(tree_sitter_rust::LANGUAGE.into()),
13158    )));
13159    let mut fake_servers = language_registry.register_fake_lsp(
13160        "Rust",
13161        FakeLspAdapter {
13162            capabilities: lsp::ServerCapabilities {
13163                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13164                    first_trigger_character: "{".to_string(),
13165                    more_trigger_character: None,
13166                }),
13167                ..Default::default()
13168            },
13169            ..Default::default()
13170        },
13171    );
13172
13173    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13174
13175    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13176
13177    let worktree_id = workspace
13178        .update(cx, |workspace, _, cx| {
13179            workspace.project().update(cx, |project, cx| {
13180                project.worktrees(cx).next().unwrap().read(cx).id()
13181            })
13182        })
13183        .unwrap();
13184
13185    let buffer = project
13186        .update(cx, |project, cx| {
13187            project.open_local_buffer(path!("/a/main.rs"), cx)
13188        })
13189        .await
13190        .unwrap();
13191    let editor_handle = workspace
13192        .update(cx, |workspace, window, cx| {
13193            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13194        })
13195        .unwrap()
13196        .await
13197        .unwrap()
13198        .downcast::<Editor>()
13199        .unwrap();
13200
13201    cx.executor().start_waiting();
13202    let fake_server = fake_servers.next().await.unwrap();
13203
13204    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13205        |params, _| async move {
13206            assert_eq!(
13207                params.text_document_position.text_document.uri,
13208                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13209            );
13210            assert_eq!(
13211                params.text_document_position.position,
13212                lsp::Position::new(0, 21),
13213            );
13214
13215            Ok(Some(vec![lsp::TextEdit {
13216                new_text: "]".to_string(),
13217                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13218            }]))
13219        },
13220    );
13221
13222    editor_handle.update_in(cx, |editor, window, cx| {
13223        window.focus(&editor.focus_handle(cx));
13224        editor.change_selections(None, window, cx, |s| {
13225            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13226        });
13227        editor.handle_input("{", window, cx);
13228    });
13229
13230    cx.executor().run_until_parked();
13231
13232    buffer.update(cx, |buffer, _| {
13233        assert_eq!(
13234            buffer.text(),
13235            "fn main() { let a = {5}; }",
13236            "No extra braces from on type formatting should appear in the buffer"
13237        )
13238    });
13239}
13240
13241#[gpui::test]
13242async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13243    init_test(cx, |_| {});
13244
13245    let fs = FakeFs::new(cx.executor());
13246    fs.insert_tree(
13247        path!("/a"),
13248        json!({
13249            "main.rs": "fn main() { let a = 5; }",
13250            "other.rs": "// Test file",
13251        }),
13252    )
13253    .await;
13254
13255    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13256
13257    let server_restarts = Arc::new(AtomicUsize::new(0));
13258    let closure_restarts = Arc::clone(&server_restarts);
13259    let language_server_name = "test language server";
13260    let language_name: LanguageName = "Rust".into();
13261
13262    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13263    language_registry.add(Arc::new(Language::new(
13264        LanguageConfig {
13265            name: language_name.clone(),
13266            matcher: LanguageMatcher {
13267                path_suffixes: vec!["rs".to_string()],
13268                ..Default::default()
13269            },
13270            ..Default::default()
13271        },
13272        Some(tree_sitter_rust::LANGUAGE.into()),
13273    )));
13274    let mut fake_servers = language_registry.register_fake_lsp(
13275        "Rust",
13276        FakeLspAdapter {
13277            name: language_server_name,
13278            initialization_options: Some(json!({
13279                "testOptionValue": true
13280            })),
13281            initializer: Some(Box::new(move |fake_server| {
13282                let task_restarts = Arc::clone(&closure_restarts);
13283                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13284                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13285                    futures::future::ready(Ok(()))
13286                });
13287            })),
13288            ..Default::default()
13289        },
13290    );
13291
13292    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13293    let _buffer = project
13294        .update(cx, |project, cx| {
13295            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13296        })
13297        .await
13298        .unwrap();
13299    let _fake_server = fake_servers.next().await.unwrap();
13300    update_test_language_settings(cx, |language_settings| {
13301        language_settings.languages.insert(
13302            language_name.clone(),
13303            LanguageSettingsContent {
13304                tab_size: NonZeroU32::new(8),
13305                ..Default::default()
13306            },
13307        );
13308    });
13309    cx.executor().run_until_parked();
13310    assert_eq!(
13311        server_restarts.load(atomic::Ordering::Acquire),
13312        0,
13313        "Should not restart LSP server on an unrelated change"
13314    );
13315
13316    update_test_project_settings(cx, |project_settings| {
13317        project_settings.lsp.insert(
13318            "Some other server name".into(),
13319            LspSettings {
13320                binary: None,
13321                settings: None,
13322                initialization_options: Some(json!({
13323                    "some other init value": false
13324                })),
13325                enable_lsp_tasks: false,
13326            },
13327        );
13328    });
13329    cx.executor().run_until_parked();
13330    assert_eq!(
13331        server_restarts.load(atomic::Ordering::Acquire),
13332        0,
13333        "Should not restart LSP server on an unrelated LSP settings change"
13334    );
13335
13336    update_test_project_settings(cx, |project_settings| {
13337        project_settings.lsp.insert(
13338            language_server_name.into(),
13339            LspSettings {
13340                binary: None,
13341                settings: None,
13342                initialization_options: Some(json!({
13343                    "anotherInitValue": false
13344                })),
13345                enable_lsp_tasks: false,
13346            },
13347        );
13348    });
13349    cx.executor().run_until_parked();
13350    assert_eq!(
13351        server_restarts.load(atomic::Ordering::Acquire),
13352        1,
13353        "Should restart LSP server on a related LSP settings change"
13354    );
13355
13356    update_test_project_settings(cx, |project_settings| {
13357        project_settings.lsp.insert(
13358            language_server_name.into(),
13359            LspSettings {
13360                binary: None,
13361                settings: None,
13362                initialization_options: Some(json!({
13363                    "anotherInitValue": false
13364                })),
13365                enable_lsp_tasks: false,
13366            },
13367        );
13368    });
13369    cx.executor().run_until_parked();
13370    assert_eq!(
13371        server_restarts.load(atomic::Ordering::Acquire),
13372        1,
13373        "Should not restart LSP server on a related LSP settings change that is the same"
13374    );
13375
13376    update_test_project_settings(cx, |project_settings| {
13377        project_settings.lsp.insert(
13378            language_server_name.into(),
13379            LspSettings {
13380                binary: None,
13381                settings: None,
13382                initialization_options: None,
13383                enable_lsp_tasks: false,
13384            },
13385        );
13386    });
13387    cx.executor().run_until_parked();
13388    assert_eq!(
13389        server_restarts.load(atomic::Ordering::Acquire),
13390        2,
13391        "Should restart LSP server on another related LSP settings change"
13392    );
13393}
13394
13395#[gpui::test]
13396async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13397    init_test(cx, |_| {});
13398
13399    let mut cx = EditorLspTestContext::new_rust(
13400        lsp::ServerCapabilities {
13401            completion_provider: Some(lsp::CompletionOptions {
13402                trigger_characters: Some(vec![".".to_string()]),
13403                resolve_provider: Some(true),
13404                ..Default::default()
13405            }),
13406            ..Default::default()
13407        },
13408        cx,
13409    )
13410    .await;
13411
13412    cx.set_state("fn main() { let a = 2ˇ; }");
13413    cx.simulate_keystroke(".");
13414    let completion_item = lsp::CompletionItem {
13415        label: "some".into(),
13416        kind: Some(lsp::CompletionItemKind::SNIPPET),
13417        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13418        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13419            kind: lsp::MarkupKind::Markdown,
13420            value: "```rust\nSome(2)\n```".to_string(),
13421        })),
13422        deprecated: Some(false),
13423        sort_text: Some("fffffff2".to_string()),
13424        filter_text: Some("some".to_string()),
13425        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13426        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13427            range: lsp::Range {
13428                start: lsp::Position {
13429                    line: 0,
13430                    character: 22,
13431                },
13432                end: lsp::Position {
13433                    line: 0,
13434                    character: 22,
13435                },
13436            },
13437            new_text: "Some(2)".to_string(),
13438        })),
13439        additional_text_edits: Some(vec![lsp::TextEdit {
13440            range: lsp::Range {
13441                start: lsp::Position {
13442                    line: 0,
13443                    character: 20,
13444                },
13445                end: lsp::Position {
13446                    line: 0,
13447                    character: 22,
13448                },
13449            },
13450            new_text: "".to_string(),
13451        }]),
13452        ..Default::default()
13453    };
13454
13455    let closure_completion_item = completion_item.clone();
13456    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13457        let task_completion_item = closure_completion_item.clone();
13458        async move {
13459            Ok(Some(lsp::CompletionResponse::Array(vec![
13460                task_completion_item,
13461            ])))
13462        }
13463    });
13464
13465    request.next().await;
13466
13467    cx.condition(|editor, _| editor.context_menu_visible())
13468        .await;
13469    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13470        editor
13471            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13472            .unwrap()
13473    });
13474    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13475
13476    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13477        let task_completion_item = completion_item.clone();
13478        async move { Ok(task_completion_item) }
13479    })
13480    .next()
13481    .await
13482    .unwrap();
13483    apply_additional_edits.await.unwrap();
13484    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13485}
13486
13487#[gpui::test]
13488async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13489    init_test(cx, |_| {});
13490
13491    let mut cx = EditorLspTestContext::new_rust(
13492        lsp::ServerCapabilities {
13493            completion_provider: Some(lsp::CompletionOptions {
13494                trigger_characters: Some(vec![".".to_string()]),
13495                resolve_provider: Some(true),
13496                ..Default::default()
13497            }),
13498            ..Default::default()
13499        },
13500        cx,
13501    )
13502    .await;
13503
13504    cx.set_state("fn main() { let a = 2ˇ; }");
13505    cx.simulate_keystroke(".");
13506
13507    let item1 = lsp::CompletionItem {
13508        label: "method id()".to_string(),
13509        filter_text: Some("id".to_string()),
13510        detail: None,
13511        documentation: None,
13512        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13513            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13514            new_text: ".id".to_string(),
13515        })),
13516        ..lsp::CompletionItem::default()
13517    };
13518
13519    let item2 = lsp::CompletionItem {
13520        label: "other".to_string(),
13521        filter_text: Some("other".to_string()),
13522        detail: None,
13523        documentation: None,
13524        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13525            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13526            new_text: ".other".to_string(),
13527        })),
13528        ..lsp::CompletionItem::default()
13529    };
13530
13531    let item1 = item1.clone();
13532    cx.set_request_handler::<lsp::request::Completion, _, _>({
13533        let item1 = item1.clone();
13534        move |_, _, _| {
13535            let item1 = item1.clone();
13536            let item2 = item2.clone();
13537            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13538        }
13539    })
13540    .next()
13541    .await;
13542
13543    cx.condition(|editor, _| editor.context_menu_visible())
13544        .await;
13545    cx.update_editor(|editor, _, _| {
13546        let context_menu = editor.context_menu.borrow_mut();
13547        let context_menu = context_menu
13548            .as_ref()
13549            .expect("Should have the context menu deployed");
13550        match context_menu {
13551            CodeContextMenu::Completions(completions_menu) => {
13552                let completions = completions_menu.completions.borrow_mut();
13553                assert_eq!(
13554                    completions
13555                        .iter()
13556                        .map(|completion| &completion.label.text)
13557                        .collect::<Vec<_>>(),
13558                    vec!["method id()", "other"]
13559                )
13560            }
13561            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13562        }
13563    });
13564
13565    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13566        let item1 = item1.clone();
13567        move |_, item_to_resolve, _| {
13568            let item1 = item1.clone();
13569            async move {
13570                if item1 == item_to_resolve {
13571                    Ok(lsp::CompletionItem {
13572                        label: "method id()".to_string(),
13573                        filter_text: Some("id".to_string()),
13574                        detail: Some("Now resolved!".to_string()),
13575                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13576                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13577                            range: lsp::Range::new(
13578                                lsp::Position::new(0, 22),
13579                                lsp::Position::new(0, 22),
13580                            ),
13581                            new_text: ".id".to_string(),
13582                        })),
13583                        ..lsp::CompletionItem::default()
13584                    })
13585                } else {
13586                    Ok(item_to_resolve)
13587                }
13588            }
13589        }
13590    })
13591    .next()
13592    .await
13593    .unwrap();
13594    cx.run_until_parked();
13595
13596    cx.update_editor(|editor, window, cx| {
13597        editor.context_menu_next(&Default::default(), window, cx);
13598    });
13599
13600    cx.update_editor(|editor, _, _| {
13601        let context_menu = editor.context_menu.borrow_mut();
13602        let context_menu = context_menu
13603            .as_ref()
13604            .expect("Should have the context menu deployed");
13605        match context_menu {
13606            CodeContextMenu::Completions(completions_menu) => {
13607                let completions = completions_menu.completions.borrow_mut();
13608                assert_eq!(
13609                    completions
13610                        .iter()
13611                        .map(|completion| &completion.label.text)
13612                        .collect::<Vec<_>>(),
13613                    vec!["method id() Now resolved!", "other"],
13614                    "Should update first completion label, but not second as the filter text did not match."
13615                );
13616            }
13617            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13618        }
13619    });
13620}
13621
13622#[gpui::test]
13623async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13624    init_test(cx, |_| {});
13625
13626    let mut cx = EditorLspTestContext::new_rust(
13627        lsp::ServerCapabilities {
13628            completion_provider: Some(lsp::CompletionOptions {
13629                trigger_characters: Some(vec![".".to_string()]),
13630                resolve_provider: Some(true),
13631                ..Default::default()
13632            }),
13633            ..Default::default()
13634        },
13635        cx,
13636    )
13637    .await;
13638
13639    cx.set_state("fn main() { let a = 2ˇ; }");
13640    cx.simulate_keystroke(".");
13641
13642    let unresolved_item_1 = lsp::CompletionItem {
13643        label: "id".to_string(),
13644        filter_text: Some("id".to_string()),
13645        detail: None,
13646        documentation: None,
13647        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13648            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13649            new_text: ".id".to_string(),
13650        })),
13651        ..lsp::CompletionItem::default()
13652    };
13653    let resolved_item_1 = lsp::CompletionItem {
13654        additional_text_edits: Some(vec![lsp::TextEdit {
13655            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13656            new_text: "!!".to_string(),
13657        }]),
13658        ..unresolved_item_1.clone()
13659    };
13660    let unresolved_item_2 = lsp::CompletionItem {
13661        label: "other".to_string(),
13662        filter_text: Some("other".to_string()),
13663        detail: None,
13664        documentation: None,
13665        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13666            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13667            new_text: ".other".to_string(),
13668        })),
13669        ..lsp::CompletionItem::default()
13670    };
13671    let resolved_item_2 = lsp::CompletionItem {
13672        additional_text_edits: Some(vec![lsp::TextEdit {
13673            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13674            new_text: "??".to_string(),
13675        }]),
13676        ..unresolved_item_2.clone()
13677    };
13678
13679    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13680    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13681    cx.lsp
13682        .server
13683        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13684            let unresolved_item_1 = unresolved_item_1.clone();
13685            let resolved_item_1 = resolved_item_1.clone();
13686            let unresolved_item_2 = unresolved_item_2.clone();
13687            let resolved_item_2 = resolved_item_2.clone();
13688            let resolve_requests_1 = resolve_requests_1.clone();
13689            let resolve_requests_2 = resolve_requests_2.clone();
13690            move |unresolved_request, _| {
13691                let unresolved_item_1 = unresolved_item_1.clone();
13692                let resolved_item_1 = resolved_item_1.clone();
13693                let unresolved_item_2 = unresolved_item_2.clone();
13694                let resolved_item_2 = resolved_item_2.clone();
13695                let resolve_requests_1 = resolve_requests_1.clone();
13696                let resolve_requests_2 = resolve_requests_2.clone();
13697                async move {
13698                    if unresolved_request == unresolved_item_1 {
13699                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13700                        Ok(resolved_item_1.clone())
13701                    } else if unresolved_request == unresolved_item_2 {
13702                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13703                        Ok(resolved_item_2.clone())
13704                    } else {
13705                        panic!("Unexpected completion item {unresolved_request:?}")
13706                    }
13707                }
13708            }
13709        })
13710        .detach();
13711
13712    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13713        let unresolved_item_1 = unresolved_item_1.clone();
13714        let unresolved_item_2 = unresolved_item_2.clone();
13715        async move {
13716            Ok(Some(lsp::CompletionResponse::Array(vec![
13717                unresolved_item_1,
13718                unresolved_item_2,
13719            ])))
13720        }
13721    })
13722    .next()
13723    .await;
13724
13725    cx.condition(|editor, _| editor.context_menu_visible())
13726        .await;
13727    cx.update_editor(|editor, _, _| {
13728        let context_menu = editor.context_menu.borrow_mut();
13729        let context_menu = context_menu
13730            .as_ref()
13731            .expect("Should have the context menu deployed");
13732        match context_menu {
13733            CodeContextMenu::Completions(completions_menu) => {
13734                let completions = completions_menu.completions.borrow_mut();
13735                assert_eq!(
13736                    completions
13737                        .iter()
13738                        .map(|completion| &completion.label.text)
13739                        .collect::<Vec<_>>(),
13740                    vec!["id", "other"]
13741                )
13742            }
13743            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13744        }
13745    });
13746    cx.run_until_parked();
13747
13748    cx.update_editor(|editor, window, cx| {
13749        editor.context_menu_next(&ContextMenuNext, window, cx);
13750    });
13751    cx.run_until_parked();
13752    cx.update_editor(|editor, window, cx| {
13753        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13754    });
13755    cx.run_until_parked();
13756    cx.update_editor(|editor, window, cx| {
13757        editor.context_menu_next(&ContextMenuNext, window, cx);
13758    });
13759    cx.run_until_parked();
13760    cx.update_editor(|editor, window, cx| {
13761        editor
13762            .compose_completion(&ComposeCompletion::default(), window, cx)
13763            .expect("No task returned")
13764    })
13765    .await
13766    .expect("Completion failed");
13767    cx.run_until_parked();
13768
13769    cx.update_editor(|editor, _, cx| {
13770        assert_eq!(
13771            resolve_requests_1.load(atomic::Ordering::Acquire),
13772            1,
13773            "Should always resolve once despite multiple selections"
13774        );
13775        assert_eq!(
13776            resolve_requests_2.load(atomic::Ordering::Acquire),
13777            1,
13778            "Should always resolve once after multiple selections and applying the completion"
13779        );
13780        assert_eq!(
13781            editor.text(cx),
13782            "fn main() { let a = ??.other; }",
13783            "Should use resolved data when applying the completion"
13784        );
13785    });
13786}
13787
13788#[gpui::test]
13789async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13790    init_test(cx, |_| {});
13791
13792    let item_0 = lsp::CompletionItem {
13793        label: "abs".into(),
13794        insert_text: Some("abs".into()),
13795        data: Some(json!({ "very": "special"})),
13796        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13797        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13798            lsp::InsertReplaceEdit {
13799                new_text: "abs".to_string(),
13800                insert: lsp::Range::default(),
13801                replace: lsp::Range::default(),
13802            },
13803        )),
13804        ..lsp::CompletionItem::default()
13805    };
13806    let items = iter::once(item_0.clone())
13807        .chain((11..51).map(|i| lsp::CompletionItem {
13808            label: format!("item_{}", i),
13809            insert_text: Some(format!("item_{}", i)),
13810            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13811            ..lsp::CompletionItem::default()
13812        }))
13813        .collect::<Vec<_>>();
13814
13815    let default_commit_characters = vec!["?".to_string()];
13816    let default_data = json!({ "default": "data"});
13817    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13818    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13819    let default_edit_range = lsp::Range {
13820        start: lsp::Position {
13821            line: 0,
13822            character: 5,
13823        },
13824        end: lsp::Position {
13825            line: 0,
13826            character: 5,
13827        },
13828    };
13829
13830    let mut cx = EditorLspTestContext::new_rust(
13831        lsp::ServerCapabilities {
13832            completion_provider: Some(lsp::CompletionOptions {
13833                trigger_characters: Some(vec![".".to_string()]),
13834                resolve_provider: Some(true),
13835                ..Default::default()
13836            }),
13837            ..Default::default()
13838        },
13839        cx,
13840    )
13841    .await;
13842
13843    cx.set_state("fn main() { let a = 2ˇ; }");
13844    cx.simulate_keystroke(".");
13845
13846    let completion_data = default_data.clone();
13847    let completion_characters = default_commit_characters.clone();
13848    let completion_items = items.clone();
13849    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13850        let default_data = completion_data.clone();
13851        let default_commit_characters = completion_characters.clone();
13852        let items = completion_items.clone();
13853        async move {
13854            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13855                items,
13856                item_defaults: Some(lsp::CompletionListItemDefaults {
13857                    data: Some(default_data.clone()),
13858                    commit_characters: Some(default_commit_characters.clone()),
13859                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13860                        default_edit_range,
13861                    )),
13862                    insert_text_format: Some(default_insert_text_format),
13863                    insert_text_mode: Some(default_insert_text_mode),
13864                }),
13865                ..lsp::CompletionList::default()
13866            })))
13867        }
13868    })
13869    .next()
13870    .await;
13871
13872    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13873    cx.lsp
13874        .server
13875        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13876            let closure_resolved_items = resolved_items.clone();
13877            move |item_to_resolve, _| {
13878                let closure_resolved_items = closure_resolved_items.clone();
13879                async move {
13880                    closure_resolved_items.lock().push(item_to_resolve.clone());
13881                    Ok(item_to_resolve)
13882                }
13883            }
13884        })
13885        .detach();
13886
13887    cx.condition(|editor, _| editor.context_menu_visible())
13888        .await;
13889    cx.run_until_parked();
13890    cx.update_editor(|editor, _, _| {
13891        let menu = editor.context_menu.borrow_mut();
13892        match menu.as_ref().expect("should have the completions menu") {
13893            CodeContextMenu::Completions(completions_menu) => {
13894                assert_eq!(
13895                    completions_menu
13896                        .entries
13897                        .borrow()
13898                        .iter()
13899                        .map(|mat| mat.string.clone())
13900                        .collect::<Vec<String>>(),
13901                    items
13902                        .iter()
13903                        .map(|completion| completion.label.clone())
13904                        .collect::<Vec<String>>()
13905                );
13906            }
13907            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13908        }
13909    });
13910    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13911    // with 4 from the end.
13912    assert_eq!(
13913        *resolved_items.lock(),
13914        [&items[0..16], &items[items.len() - 4..items.len()]]
13915            .concat()
13916            .iter()
13917            .cloned()
13918            .map(|mut item| {
13919                if item.data.is_none() {
13920                    item.data = Some(default_data.clone());
13921                }
13922                item
13923            })
13924            .collect::<Vec<lsp::CompletionItem>>(),
13925        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13926    );
13927    resolved_items.lock().clear();
13928
13929    cx.update_editor(|editor, window, cx| {
13930        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13931    });
13932    cx.run_until_parked();
13933    // Completions that have already been resolved are skipped.
13934    assert_eq!(
13935        *resolved_items.lock(),
13936        items[items.len() - 16..items.len() - 4]
13937            .iter()
13938            .cloned()
13939            .map(|mut item| {
13940                if item.data.is_none() {
13941                    item.data = Some(default_data.clone());
13942                }
13943                item
13944            })
13945            .collect::<Vec<lsp::CompletionItem>>()
13946    );
13947    resolved_items.lock().clear();
13948}
13949
13950#[gpui::test]
13951async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13952    init_test(cx, |_| {});
13953
13954    let mut cx = EditorLspTestContext::new(
13955        Language::new(
13956            LanguageConfig {
13957                matcher: LanguageMatcher {
13958                    path_suffixes: vec!["jsx".into()],
13959                    ..Default::default()
13960                },
13961                overrides: [(
13962                    "element".into(),
13963                    LanguageConfigOverride {
13964                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13965                        ..Default::default()
13966                    },
13967                )]
13968                .into_iter()
13969                .collect(),
13970                ..Default::default()
13971            },
13972            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13973        )
13974        .with_override_query("(jsx_self_closing_element) @element")
13975        .unwrap(),
13976        lsp::ServerCapabilities {
13977            completion_provider: Some(lsp::CompletionOptions {
13978                trigger_characters: Some(vec![":".to_string()]),
13979                ..Default::default()
13980            }),
13981            ..Default::default()
13982        },
13983        cx,
13984    )
13985    .await;
13986
13987    cx.lsp
13988        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13989            Ok(Some(lsp::CompletionResponse::Array(vec![
13990                lsp::CompletionItem {
13991                    label: "bg-blue".into(),
13992                    ..Default::default()
13993                },
13994                lsp::CompletionItem {
13995                    label: "bg-red".into(),
13996                    ..Default::default()
13997                },
13998                lsp::CompletionItem {
13999                    label: "bg-yellow".into(),
14000                    ..Default::default()
14001                },
14002            ])))
14003        });
14004
14005    cx.set_state(r#"<p class="bgˇ" />"#);
14006
14007    // Trigger completion when typing a dash, because the dash is an extra
14008    // word character in the 'element' scope, which contains the cursor.
14009    cx.simulate_keystroke("-");
14010    cx.executor().run_until_parked();
14011    cx.update_editor(|editor, _, _| {
14012        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14013        {
14014            assert_eq!(
14015                completion_menu_entries(&menu),
14016                &["bg-red", "bg-blue", "bg-yellow"]
14017            );
14018        } else {
14019            panic!("expected completion menu to be open");
14020        }
14021    });
14022
14023    cx.simulate_keystroke("l");
14024    cx.executor().run_until_parked();
14025    cx.update_editor(|editor, _, _| {
14026        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14027        {
14028            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
14029        } else {
14030            panic!("expected completion menu to be open");
14031        }
14032    });
14033
14034    // When filtering completions, consider the character after the '-' to
14035    // be the start of a subword.
14036    cx.set_state(r#"<p class="yelˇ" />"#);
14037    cx.simulate_keystroke("l");
14038    cx.executor().run_until_parked();
14039    cx.update_editor(|editor, _, _| {
14040        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14041        {
14042            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14043        } else {
14044            panic!("expected completion menu to be open");
14045        }
14046    });
14047}
14048
14049fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14050    let entries = menu.entries.borrow();
14051    entries.iter().map(|mat| mat.string.clone()).collect()
14052}
14053
14054#[gpui::test]
14055async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14056    init_test(cx, |settings| {
14057        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14058            FormatterList(vec![Formatter::Prettier].into()),
14059        ))
14060    });
14061
14062    let fs = FakeFs::new(cx.executor());
14063    fs.insert_file(path!("/file.ts"), Default::default()).await;
14064
14065    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14066    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14067
14068    language_registry.add(Arc::new(Language::new(
14069        LanguageConfig {
14070            name: "TypeScript".into(),
14071            matcher: LanguageMatcher {
14072                path_suffixes: vec!["ts".to_string()],
14073                ..Default::default()
14074            },
14075            ..Default::default()
14076        },
14077        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14078    )));
14079    update_test_language_settings(cx, |settings| {
14080        settings.defaults.prettier = Some(PrettierSettings {
14081            allowed: true,
14082            ..PrettierSettings::default()
14083        });
14084    });
14085
14086    let test_plugin = "test_plugin";
14087    let _ = language_registry.register_fake_lsp(
14088        "TypeScript",
14089        FakeLspAdapter {
14090            prettier_plugins: vec![test_plugin],
14091            ..Default::default()
14092        },
14093    );
14094
14095    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14096    let buffer = project
14097        .update(cx, |project, cx| {
14098            project.open_local_buffer(path!("/file.ts"), cx)
14099        })
14100        .await
14101        .unwrap();
14102
14103    let buffer_text = "one\ntwo\nthree\n";
14104    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14105    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14106    editor.update_in(cx, |editor, window, cx| {
14107        editor.set_text(buffer_text, window, cx)
14108    });
14109
14110    editor
14111        .update_in(cx, |editor, window, cx| {
14112            editor.perform_format(
14113                project.clone(),
14114                FormatTrigger::Manual,
14115                FormatTarget::Buffers,
14116                window,
14117                cx,
14118            )
14119        })
14120        .unwrap()
14121        .await;
14122    assert_eq!(
14123        editor.update(cx, |editor, cx| editor.text(cx)),
14124        buffer_text.to_string() + prettier_format_suffix,
14125        "Test prettier formatting was not applied to the original buffer text",
14126    );
14127
14128    update_test_language_settings(cx, |settings| {
14129        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14130    });
14131    let format = editor.update_in(cx, |editor, window, cx| {
14132        editor.perform_format(
14133            project.clone(),
14134            FormatTrigger::Manual,
14135            FormatTarget::Buffers,
14136            window,
14137            cx,
14138        )
14139    });
14140    format.await.unwrap();
14141    assert_eq!(
14142        editor.update(cx, |editor, cx| editor.text(cx)),
14143        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14144        "Autoformatting (via test prettier) was not applied to the original buffer text",
14145    );
14146}
14147
14148#[gpui::test]
14149async fn test_addition_reverts(cx: &mut TestAppContext) {
14150    init_test(cx, |_| {});
14151    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14152    let base_text = indoc! {r#"
14153        struct Row;
14154        struct Row1;
14155        struct Row2;
14156
14157        struct Row4;
14158        struct Row5;
14159        struct Row6;
14160
14161        struct Row8;
14162        struct Row9;
14163        struct Row10;"#};
14164
14165    // When addition hunks are not adjacent to carets, no hunk revert is performed
14166    assert_hunk_revert(
14167        indoc! {r#"struct Row;
14168                   struct Row1;
14169                   struct Row1.1;
14170                   struct Row1.2;
14171                   struct Row2;ˇ
14172
14173                   struct Row4;
14174                   struct Row5;
14175                   struct Row6;
14176
14177                   struct Row8;
14178                   ˇstruct Row9;
14179                   struct Row9.1;
14180                   struct Row9.2;
14181                   struct Row9.3;
14182                   struct Row10;"#},
14183        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14184        indoc! {r#"struct Row;
14185                   struct Row1;
14186                   struct Row1.1;
14187                   struct Row1.2;
14188                   struct Row2;ˇ
14189
14190                   struct Row4;
14191                   struct Row5;
14192                   struct Row6;
14193
14194                   struct Row8;
14195                   ˇstruct Row9;
14196                   struct Row9.1;
14197                   struct Row9.2;
14198                   struct Row9.3;
14199                   struct Row10;"#},
14200        base_text,
14201        &mut cx,
14202    );
14203    // Same for selections
14204    assert_hunk_revert(
14205        indoc! {r#"struct Row;
14206                   struct Row1;
14207                   struct Row2;
14208                   struct Row2.1;
14209                   struct Row2.2;
14210                   «ˇ
14211                   struct Row4;
14212                   struct» Row5;
14213                   «struct Row6;
14214                   ˇ»
14215                   struct Row9.1;
14216                   struct Row9.2;
14217                   struct Row9.3;
14218                   struct Row8;
14219                   struct Row9;
14220                   struct Row10;"#},
14221        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14222        indoc! {r#"struct Row;
14223                   struct Row1;
14224                   struct Row2;
14225                   struct Row2.1;
14226                   struct Row2.2;
14227                   «ˇ
14228                   struct Row4;
14229                   struct» Row5;
14230                   «struct Row6;
14231                   ˇ»
14232                   struct Row9.1;
14233                   struct Row9.2;
14234                   struct Row9.3;
14235                   struct Row8;
14236                   struct Row9;
14237                   struct Row10;"#},
14238        base_text,
14239        &mut cx,
14240    );
14241
14242    // When carets and selections intersect the addition hunks, those are reverted.
14243    // Adjacent carets got merged.
14244    assert_hunk_revert(
14245        indoc! {r#"struct Row;
14246                   ˇ// something on the top
14247                   struct Row1;
14248                   struct Row2;
14249                   struct Roˇw3.1;
14250                   struct Row2.2;
14251                   struct Row2.3;ˇ
14252
14253                   struct Row4;
14254                   struct ˇRow5.1;
14255                   struct Row5.2;
14256                   struct «Rowˇ»5.3;
14257                   struct Row5;
14258                   struct Row6;
14259                   ˇ
14260                   struct Row9.1;
14261                   struct «Rowˇ»9.2;
14262                   struct «ˇRow»9.3;
14263                   struct Row8;
14264                   struct Row9;
14265                   «ˇ// something on bottom»
14266                   struct Row10;"#},
14267        vec![
14268            DiffHunkStatusKind::Added,
14269            DiffHunkStatusKind::Added,
14270            DiffHunkStatusKind::Added,
14271            DiffHunkStatusKind::Added,
14272            DiffHunkStatusKind::Added,
14273        ],
14274        indoc! {r#"struct Row;
14275                   ˇstruct Row1;
14276                   struct Row2;
14277                   ˇ
14278                   struct Row4;
14279                   ˇstruct Row5;
14280                   struct Row6;
14281                   ˇ
14282                   ˇstruct Row8;
14283                   struct Row9;
14284                   ˇstruct Row10;"#},
14285        base_text,
14286        &mut cx,
14287    );
14288}
14289
14290#[gpui::test]
14291async fn test_modification_reverts(cx: &mut TestAppContext) {
14292    init_test(cx, |_| {});
14293    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14294    let base_text = indoc! {r#"
14295        struct Row;
14296        struct Row1;
14297        struct Row2;
14298
14299        struct Row4;
14300        struct Row5;
14301        struct Row6;
14302
14303        struct Row8;
14304        struct Row9;
14305        struct Row10;"#};
14306
14307    // Modification hunks behave the same as the addition ones.
14308    assert_hunk_revert(
14309        indoc! {r#"struct Row;
14310                   struct Row1;
14311                   struct Row33;
14312                   ˇ
14313                   struct Row4;
14314                   struct Row5;
14315                   struct Row6;
14316                   ˇ
14317                   struct Row99;
14318                   struct Row9;
14319                   struct Row10;"#},
14320        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14321        indoc! {r#"struct Row;
14322                   struct Row1;
14323                   struct Row33;
14324                   ˇ
14325                   struct Row4;
14326                   struct Row5;
14327                   struct Row6;
14328                   ˇ
14329                   struct Row99;
14330                   struct Row9;
14331                   struct Row10;"#},
14332        base_text,
14333        &mut cx,
14334    );
14335    assert_hunk_revert(
14336        indoc! {r#"struct Row;
14337                   struct Row1;
14338                   struct Row33;
14339                   «ˇ
14340                   struct Row4;
14341                   struct» Row5;
14342                   «struct Row6;
14343                   ˇ»
14344                   struct Row99;
14345                   struct Row9;
14346                   struct Row10;"#},
14347        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14348        indoc! {r#"struct Row;
14349                   struct Row1;
14350                   struct Row33;
14351                   «ˇ
14352                   struct Row4;
14353                   struct» Row5;
14354                   «struct Row6;
14355                   ˇ»
14356                   struct Row99;
14357                   struct Row9;
14358                   struct Row10;"#},
14359        base_text,
14360        &mut cx,
14361    );
14362
14363    assert_hunk_revert(
14364        indoc! {r#"ˇstruct Row1.1;
14365                   struct Row1;
14366                   «ˇstr»uct Row22;
14367
14368                   struct ˇRow44;
14369                   struct Row5;
14370                   struct «Rˇ»ow66;ˇ
14371
14372                   «struˇ»ct Row88;
14373                   struct Row9;
14374                   struct Row1011;ˇ"#},
14375        vec![
14376            DiffHunkStatusKind::Modified,
14377            DiffHunkStatusKind::Modified,
14378            DiffHunkStatusKind::Modified,
14379            DiffHunkStatusKind::Modified,
14380            DiffHunkStatusKind::Modified,
14381            DiffHunkStatusKind::Modified,
14382        ],
14383        indoc! {r#"struct Row;
14384                   ˇstruct Row1;
14385                   struct Row2;
14386                   ˇ
14387                   struct Row4;
14388                   ˇstruct Row5;
14389                   struct Row6;
14390                   ˇ
14391                   struct Row8;
14392                   ˇstruct Row9;
14393                   struct Row10;ˇ"#},
14394        base_text,
14395        &mut cx,
14396    );
14397}
14398
14399#[gpui::test]
14400async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14401    init_test(cx, |_| {});
14402    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14403    let base_text = indoc! {r#"
14404        one
14405
14406        two
14407        three
14408        "#};
14409
14410    cx.set_head_text(base_text);
14411    cx.set_state("\nˇ\n");
14412    cx.executor().run_until_parked();
14413    cx.update_editor(|editor, _window, cx| {
14414        editor.expand_selected_diff_hunks(cx);
14415    });
14416    cx.executor().run_until_parked();
14417    cx.update_editor(|editor, window, cx| {
14418        editor.backspace(&Default::default(), window, cx);
14419    });
14420    cx.run_until_parked();
14421    cx.assert_state_with_diff(
14422        indoc! {r#"
14423
14424        - two
14425        - threeˇ
14426        +
14427        "#}
14428        .to_string(),
14429    );
14430}
14431
14432#[gpui::test]
14433async fn test_deletion_reverts(cx: &mut TestAppContext) {
14434    init_test(cx, |_| {});
14435    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14436    let base_text = indoc! {r#"struct Row;
14437struct Row1;
14438struct Row2;
14439
14440struct Row4;
14441struct Row5;
14442struct Row6;
14443
14444struct Row8;
14445struct Row9;
14446struct Row10;"#};
14447
14448    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14449    assert_hunk_revert(
14450        indoc! {r#"struct Row;
14451                   struct Row2;
14452
14453                   ˇstruct Row4;
14454                   struct Row5;
14455                   struct Row6;
14456                   ˇ
14457                   struct Row8;
14458                   struct Row10;"#},
14459        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14460        indoc! {r#"struct Row;
14461                   struct Row2;
14462
14463                   ˇstruct Row4;
14464                   struct Row5;
14465                   struct Row6;
14466                   ˇ
14467                   struct Row8;
14468                   struct Row10;"#},
14469        base_text,
14470        &mut cx,
14471    );
14472    assert_hunk_revert(
14473        indoc! {r#"struct Row;
14474                   struct Row2;
14475
14476                   «ˇstruct Row4;
14477                   struct» Row5;
14478                   «struct Row6;
14479                   ˇ»
14480                   struct Row8;
14481                   struct Row10;"#},
14482        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14483        indoc! {r#"struct Row;
14484                   struct Row2;
14485
14486                   «ˇstruct Row4;
14487                   struct» Row5;
14488                   «struct Row6;
14489                   ˇ»
14490                   struct Row8;
14491                   struct Row10;"#},
14492        base_text,
14493        &mut cx,
14494    );
14495
14496    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14497    assert_hunk_revert(
14498        indoc! {r#"struct Row;
14499                   ˇstruct Row2;
14500
14501                   struct Row4;
14502                   struct Row5;
14503                   struct Row6;
14504
14505                   struct Row8;ˇ
14506                   struct Row10;"#},
14507        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14508        indoc! {r#"struct Row;
14509                   struct Row1;
14510                   ˇstruct Row2;
14511
14512                   struct Row4;
14513                   struct Row5;
14514                   struct Row6;
14515
14516                   struct Row8;ˇ
14517                   struct Row9;
14518                   struct Row10;"#},
14519        base_text,
14520        &mut cx,
14521    );
14522    assert_hunk_revert(
14523        indoc! {r#"struct Row;
14524                   struct Row2«ˇ;
14525                   struct Row4;
14526                   struct» Row5;
14527                   «struct Row6;
14528
14529                   struct Row8;ˇ»
14530                   struct Row10;"#},
14531        vec![
14532            DiffHunkStatusKind::Deleted,
14533            DiffHunkStatusKind::Deleted,
14534            DiffHunkStatusKind::Deleted,
14535        ],
14536        indoc! {r#"struct Row;
14537                   struct Row1;
14538                   struct Row2«ˇ;
14539
14540                   struct Row4;
14541                   struct» Row5;
14542                   «struct Row6;
14543
14544                   struct Row8;ˇ»
14545                   struct Row9;
14546                   struct Row10;"#},
14547        base_text,
14548        &mut cx,
14549    );
14550}
14551
14552#[gpui::test]
14553async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14554    init_test(cx, |_| {});
14555
14556    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14557    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14558    let base_text_3 =
14559        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14560
14561    let text_1 = edit_first_char_of_every_line(base_text_1);
14562    let text_2 = edit_first_char_of_every_line(base_text_2);
14563    let text_3 = edit_first_char_of_every_line(base_text_3);
14564
14565    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14566    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14567    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14568
14569    let multibuffer = cx.new(|cx| {
14570        let mut multibuffer = MultiBuffer::new(ReadWrite);
14571        multibuffer.push_excerpts(
14572            buffer_1.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, 4)),
14577            ],
14578            cx,
14579        );
14580        multibuffer.push_excerpts(
14581            buffer_2.clone(),
14582            [
14583                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14584                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14585                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14586            ],
14587            cx,
14588        );
14589        multibuffer.push_excerpts(
14590            buffer_3.clone(),
14591            [
14592                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14593                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14594                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14595            ],
14596            cx,
14597        );
14598        multibuffer
14599    });
14600
14601    let fs = FakeFs::new(cx.executor());
14602    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14603    let (editor, cx) = cx
14604        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14605    editor.update_in(cx, |editor, _window, cx| {
14606        for (buffer, diff_base) in [
14607            (buffer_1.clone(), base_text_1),
14608            (buffer_2.clone(), base_text_2),
14609            (buffer_3.clone(), base_text_3),
14610        ] {
14611            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14612            editor
14613                .buffer
14614                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14615        }
14616    });
14617    cx.executor().run_until_parked();
14618
14619    editor.update_in(cx, |editor, window, cx| {
14620        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}");
14621        editor.select_all(&SelectAll, window, cx);
14622        editor.git_restore(&Default::default(), window, cx);
14623    });
14624    cx.executor().run_until_parked();
14625
14626    // When all ranges are selected, all buffer hunks are reverted.
14627    editor.update(cx, |editor, cx| {
14628        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");
14629    });
14630    buffer_1.update(cx, |buffer, _| {
14631        assert_eq!(buffer.text(), base_text_1);
14632    });
14633    buffer_2.update(cx, |buffer, _| {
14634        assert_eq!(buffer.text(), base_text_2);
14635    });
14636    buffer_3.update(cx, |buffer, _| {
14637        assert_eq!(buffer.text(), base_text_3);
14638    });
14639
14640    editor.update_in(cx, |editor, window, cx| {
14641        editor.undo(&Default::default(), window, cx);
14642    });
14643
14644    editor.update_in(cx, |editor, window, cx| {
14645        editor.change_selections(None, window, cx, |s| {
14646            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14647        });
14648        editor.git_restore(&Default::default(), window, cx);
14649    });
14650
14651    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14652    // but not affect buffer_2 and its related excerpts.
14653    editor.update(cx, |editor, cx| {
14654        assert_eq!(
14655            editor.text(cx),
14656            "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}"
14657        );
14658    });
14659    buffer_1.update(cx, |buffer, _| {
14660        assert_eq!(buffer.text(), base_text_1);
14661    });
14662    buffer_2.update(cx, |buffer, _| {
14663        assert_eq!(
14664            buffer.text(),
14665            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14666        );
14667    });
14668    buffer_3.update(cx, |buffer, _| {
14669        assert_eq!(
14670            buffer.text(),
14671            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14672        );
14673    });
14674
14675    fn edit_first_char_of_every_line(text: &str) -> String {
14676        text.split('\n')
14677            .map(|line| format!("X{}", &line[1..]))
14678            .collect::<Vec<_>>()
14679            .join("\n")
14680    }
14681}
14682
14683#[gpui::test]
14684async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14685    init_test(cx, |_| {});
14686
14687    let cols = 4;
14688    let rows = 10;
14689    let sample_text_1 = sample_text(rows, cols, 'a');
14690    assert_eq!(
14691        sample_text_1,
14692        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14693    );
14694    let sample_text_2 = sample_text(rows, cols, 'l');
14695    assert_eq!(
14696        sample_text_2,
14697        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14698    );
14699    let sample_text_3 = sample_text(rows, cols, 'v');
14700    assert_eq!(
14701        sample_text_3,
14702        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14703    );
14704
14705    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14706    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14707    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14708
14709    let multi_buffer = cx.new(|cx| {
14710        let mut multibuffer = MultiBuffer::new(ReadWrite);
14711        multibuffer.push_excerpts(
14712            buffer_1.clone(),
14713            [
14714                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14715                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14716                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14717            ],
14718            cx,
14719        );
14720        multibuffer.push_excerpts(
14721            buffer_2.clone(),
14722            [
14723                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14724                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14725                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14726            ],
14727            cx,
14728        );
14729        multibuffer.push_excerpts(
14730            buffer_3.clone(),
14731            [
14732                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14733                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14734                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14735            ],
14736            cx,
14737        );
14738        multibuffer
14739    });
14740
14741    let fs = FakeFs::new(cx.executor());
14742    fs.insert_tree(
14743        "/a",
14744        json!({
14745            "main.rs": sample_text_1,
14746            "other.rs": sample_text_2,
14747            "lib.rs": sample_text_3,
14748        }),
14749    )
14750    .await;
14751    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14752    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14753    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14754    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14755        Editor::new(
14756            EditorMode::full(),
14757            multi_buffer,
14758            Some(project.clone()),
14759            window,
14760            cx,
14761        )
14762    });
14763    let multibuffer_item_id = workspace
14764        .update(cx, |workspace, window, cx| {
14765            assert!(
14766                workspace.active_item(cx).is_none(),
14767                "active item should be None before the first item is added"
14768            );
14769            workspace.add_item_to_active_pane(
14770                Box::new(multi_buffer_editor.clone()),
14771                None,
14772                true,
14773                window,
14774                cx,
14775            );
14776            let active_item = workspace
14777                .active_item(cx)
14778                .expect("should have an active item after adding the multi buffer");
14779            assert!(
14780                !active_item.is_singleton(cx),
14781                "A multi buffer was expected to active after adding"
14782            );
14783            active_item.item_id()
14784        })
14785        .unwrap();
14786    cx.executor().run_until_parked();
14787
14788    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14789        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14790            s.select_ranges(Some(1..2))
14791        });
14792        editor.open_excerpts(&OpenExcerpts, window, cx);
14793    });
14794    cx.executor().run_until_parked();
14795    let first_item_id = workspace
14796        .update(cx, |workspace, window, cx| {
14797            let active_item = workspace
14798                .active_item(cx)
14799                .expect("should have an active item after navigating into the 1st buffer");
14800            let first_item_id = active_item.item_id();
14801            assert_ne!(
14802                first_item_id, multibuffer_item_id,
14803                "Should navigate into the 1st buffer and activate it"
14804            );
14805            assert!(
14806                active_item.is_singleton(cx),
14807                "New active item should be a singleton buffer"
14808            );
14809            assert_eq!(
14810                active_item
14811                    .act_as::<Editor>(cx)
14812                    .expect("should have navigated into an editor for the 1st buffer")
14813                    .read(cx)
14814                    .text(cx),
14815                sample_text_1
14816            );
14817
14818            workspace
14819                .go_back(workspace.active_pane().downgrade(), window, cx)
14820                .detach_and_log_err(cx);
14821
14822            first_item_id
14823        })
14824        .unwrap();
14825    cx.executor().run_until_parked();
14826    workspace
14827        .update(cx, |workspace, _, cx| {
14828            let active_item = workspace
14829                .active_item(cx)
14830                .expect("should have an active item after navigating back");
14831            assert_eq!(
14832                active_item.item_id(),
14833                multibuffer_item_id,
14834                "Should navigate back to the multi buffer"
14835            );
14836            assert!(!active_item.is_singleton(cx));
14837        })
14838        .unwrap();
14839
14840    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14841        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14842            s.select_ranges(Some(39..40))
14843        });
14844        editor.open_excerpts(&OpenExcerpts, window, cx);
14845    });
14846    cx.executor().run_until_parked();
14847    let second_item_id = workspace
14848        .update(cx, |workspace, window, cx| {
14849            let active_item = workspace
14850                .active_item(cx)
14851                .expect("should have an active item after navigating into the 2nd buffer");
14852            let second_item_id = active_item.item_id();
14853            assert_ne!(
14854                second_item_id, multibuffer_item_id,
14855                "Should navigate away from the multibuffer"
14856            );
14857            assert_ne!(
14858                second_item_id, first_item_id,
14859                "Should navigate into the 2nd buffer and activate it"
14860            );
14861            assert!(
14862                active_item.is_singleton(cx),
14863                "New active item should be a singleton buffer"
14864            );
14865            assert_eq!(
14866                active_item
14867                    .act_as::<Editor>(cx)
14868                    .expect("should have navigated into an editor")
14869                    .read(cx)
14870                    .text(cx),
14871                sample_text_2
14872            );
14873
14874            workspace
14875                .go_back(workspace.active_pane().downgrade(), window, cx)
14876                .detach_and_log_err(cx);
14877
14878            second_item_id
14879        })
14880        .unwrap();
14881    cx.executor().run_until_parked();
14882    workspace
14883        .update(cx, |workspace, _, cx| {
14884            let active_item = workspace
14885                .active_item(cx)
14886                .expect("should have an active item after navigating back from the 2nd buffer");
14887            assert_eq!(
14888                active_item.item_id(),
14889                multibuffer_item_id,
14890                "Should navigate back from the 2nd buffer to the multi buffer"
14891            );
14892            assert!(!active_item.is_singleton(cx));
14893        })
14894        .unwrap();
14895
14896    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14897        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14898            s.select_ranges(Some(70..70))
14899        });
14900        editor.open_excerpts(&OpenExcerpts, window, cx);
14901    });
14902    cx.executor().run_until_parked();
14903    workspace
14904        .update(cx, |workspace, window, cx| {
14905            let active_item = workspace
14906                .active_item(cx)
14907                .expect("should have an active item after navigating into the 3rd buffer");
14908            let third_item_id = active_item.item_id();
14909            assert_ne!(
14910                third_item_id, multibuffer_item_id,
14911                "Should navigate into the 3rd buffer and activate it"
14912            );
14913            assert_ne!(third_item_id, first_item_id);
14914            assert_ne!(third_item_id, second_item_id);
14915            assert!(
14916                active_item.is_singleton(cx),
14917                "New active item should be a singleton buffer"
14918            );
14919            assert_eq!(
14920                active_item
14921                    .act_as::<Editor>(cx)
14922                    .expect("should have navigated into an editor")
14923                    .read(cx)
14924                    .text(cx),
14925                sample_text_3
14926            );
14927
14928            workspace
14929                .go_back(workspace.active_pane().downgrade(), window, cx)
14930                .detach_and_log_err(cx);
14931        })
14932        .unwrap();
14933    cx.executor().run_until_parked();
14934    workspace
14935        .update(cx, |workspace, _, cx| {
14936            let active_item = workspace
14937                .active_item(cx)
14938                .expect("should have an active item after navigating back from the 3rd buffer");
14939            assert_eq!(
14940                active_item.item_id(),
14941                multibuffer_item_id,
14942                "Should navigate back from the 3rd buffer to the multi buffer"
14943            );
14944            assert!(!active_item.is_singleton(cx));
14945        })
14946        .unwrap();
14947}
14948
14949#[gpui::test]
14950async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14951    init_test(cx, |_| {});
14952
14953    let mut cx = EditorTestContext::new(cx).await;
14954
14955    let diff_base = r#"
14956        use some::mod;
14957
14958        const A: u32 = 42;
14959
14960        fn main() {
14961            println!("hello");
14962
14963            println!("world");
14964        }
14965        "#
14966    .unindent();
14967
14968    cx.set_state(
14969        &r#"
14970        use some::modified;
14971
14972        ˇ
14973        fn main() {
14974            println!("hello there");
14975
14976            println!("around the");
14977            println!("world");
14978        }
14979        "#
14980        .unindent(),
14981    );
14982
14983    cx.set_head_text(&diff_base);
14984    executor.run_until_parked();
14985
14986    cx.update_editor(|editor, window, cx| {
14987        editor.go_to_next_hunk(&GoToHunk, window, cx);
14988        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14989    });
14990    executor.run_until_parked();
14991    cx.assert_state_with_diff(
14992        r#"
14993          use some::modified;
14994
14995
14996          fn main() {
14997        -     println!("hello");
14998        + ˇ    println!("hello there");
14999
15000              println!("around the");
15001              println!("world");
15002          }
15003        "#
15004        .unindent(),
15005    );
15006
15007    cx.update_editor(|editor, window, cx| {
15008        for _ in 0..2 {
15009            editor.go_to_next_hunk(&GoToHunk, window, cx);
15010            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15011        }
15012    });
15013    executor.run_until_parked();
15014    cx.assert_state_with_diff(
15015        r#"
15016        - use some::mod;
15017        + ˇuse some::modified;
15018
15019
15020          fn main() {
15021        -     println!("hello");
15022        +     println!("hello there");
15023
15024        +     println!("around the");
15025              println!("world");
15026          }
15027        "#
15028        .unindent(),
15029    );
15030
15031    cx.update_editor(|editor, window, cx| {
15032        editor.go_to_next_hunk(&GoToHunk, window, cx);
15033        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15034    });
15035    executor.run_until_parked();
15036    cx.assert_state_with_diff(
15037        r#"
15038        - use some::mod;
15039        + use some::modified;
15040
15041        - const A: u32 = 42;
15042          ˇ
15043          fn main() {
15044        -     println!("hello");
15045        +     println!("hello there");
15046
15047        +     println!("around the");
15048              println!("world");
15049          }
15050        "#
15051        .unindent(),
15052    );
15053
15054    cx.update_editor(|editor, window, cx| {
15055        editor.cancel(&Cancel, window, cx);
15056    });
15057
15058    cx.assert_state_with_diff(
15059        r#"
15060          use some::modified;
15061
15062          ˇ
15063          fn main() {
15064              println!("hello there");
15065
15066              println!("around the");
15067              println!("world");
15068          }
15069        "#
15070        .unindent(),
15071    );
15072}
15073
15074#[gpui::test]
15075async fn test_diff_base_change_with_expanded_diff_hunks(
15076    executor: BackgroundExecutor,
15077    cx: &mut TestAppContext,
15078) {
15079    init_test(cx, |_| {});
15080
15081    let mut cx = EditorTestContext::new(cx).await;
15082
15083    let diff_base = r#"
15084        use some::mod1;
15085        use some::mod2;
15086
15087        const A: u32 = 42;
15088        const B: u32 = 42;
15089        const C: u32 = 42;
15090
15091        fn main() {
15092            println!("hello");
15093
15094            println!("world");
15095        }
15096        "#
15097    .unindent();
15098
15099    cx.set_state(
15100        &r#"
15101        use some::mod2;
15102
15103        const A: u32 = 42;
15104        const C: u32 = 42;
15105
15106        fn main(ˇ) {
15107            //println!("hello");
15108
15109            println!("world");
15110            //
15111            //
15112        }
15113        "#
15114        .unindent(),
15115    );
15116
15117    cx.set_head_text(&diff_base);
15118    executor.run_until_parked();
15119
15120    cx.update_editor(|editor, window, cx| {
15121        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, 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          fn main(ˇ) {
15134        -     println!("hello");
15135        +     //println!("hello");
15136
15137              println!("world");
15138        +     //
15139        +     //
15140          }
15141        "#
15142        .unindent(),
15143    );
15144
15145    cx.set_head_text("new diff base!");
15146    executor.run_until_parked();
15147    cx.assert_state_with_diff(
15148        r#"
15149        - new diff base!
15150        + use some::mod2;
15151        +
15152        + const A: u32 = 42;
15153        + const C: u32 = 42;
15154        +
15155        + fn main(ˇ) {
15156        +     //println!("hello");
15157        +
15158        +     println!("world");
15159        +     //
15160        +     //
15161        + }
15162        "#
15163        .unindent(),
15164    );
15165}
15166
15167#[gpui::test]
15168async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15169    init_test(cx, |_| {});
15170
15171    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15172    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15173    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15174    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15175    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15176    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15177
15178    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15179    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15180    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15181
15182    let multi_buffer = cx.new(|cx| {
15183        let mut multibuffer = MultiBuffer::new(ReadWrite);
15184        multibuffer.push_excerpts(
15185            buffer_1.clone(),
15186            [
15187                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15188                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15189                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15190            ],
15191            cx,
15192        );
15193        multibuffer.push_excerpts(
15194            buffer_2.clone(),
15195            [
15196                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15197                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15198                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15199            ],
15200            cx,
15201        );
15202        multibuffer.push_excerpts(
15203            buffer_3.clone(),
15204            [
15205                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15206                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15207                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15208            ],
15209            cx,
15210        );
15211        multibuffer
15212    });
15213
15214    let editor =
15215        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15216    editor
15217        .update(cx, |editor, _window, cx| {
15218            for (buffer, diff_base) in [
15219                (buffer_1.clone(), file_1_old),
15220                (buffer_2.clone(), file_2_old),
15221                (buffer_3.clone(), file_3_old),
15222            ] {
15223                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15224                editor
15225                    .buffer
15226                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15227            }
15228        })
15229        .unwrap();
15230
15231    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15232    cx.run_until_parked();
15233
15234    cx.assert_editor_state(
15235        &"
15236            ˇaaa
15237            ccc
15238            ddd
15239
15240            ggg
15241            hhh
15242
15243
15244            lll
15245            mmm
15246            NNN
15247
15248            qqq
15249            rrr
15250
15251            uuu
15252            111
15253            222
15254            333
15255
15256            666
15257            777
15258
15259            000
15260            !!!"
15261        .unindent(),
15262    );
15263
15264    cx.update_editor(|editor, window, cx| {
15265        editor.select_all(&SelectAll, window, cx);
15266        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15267    });
15268    cx.executor().run_until_parked();
15269
15270    cx.assert_state_with_diff(
15271        "
15272            «aaa
15273          - bbb
15274            ccc
15275            ddd
15276
15277            ggg
15278            hhh
15279
15280
15281            lll
15282            mmm
15283          - nnn
15284          + NNN
15285
15286            qqq
15287            rrr
15288
15289            uuu
15290            111
15291            222
15292            333
15293
15294          + 666
15295            777
15296
15297            000
15298            !!!ˇ»"
15299            .unindent(),
15300    );
15301}
15302
15303#[gpui::test]
15304async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15305    init_test(cx, |_| {});
15306
15307    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15308    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15309
15310    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15311    let multi_buffer = cx.new(|cx| {
15312        let mut multibuffer = MultiBuffer::new(ReadWrite);
15313        multibuffer.push_excerpts(
15314            buffer.clone(),
15315            [
15316                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15317                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15318                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15319            ],
15320            cx,
15321        );
15322        multibuffer
15323    });
15324
15325    let editor =
15326        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15327    editor
15328        .update(cx, |editor, _window, cx| {
15329            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15330            editor
15331                .buffer
15332                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15333        })
15334        .unwrap();
15335
15336    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15337    cx.run_until_parked();
15338
15339    cx.update_editor(|editor, window, cx| {
15340        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15341    });
15342    cx.executor().run_until_parked();
15343
15344    // When the start of a hunk coincides with the start of its excerpt,
15345    // the hunk is expanded. When the start of a a hunk is earlier than
15346    // the start of its excerpt, the hunk is not expanded.
15347    cx.assert_state_with_diff(
15348        "
15349            ˇaaa
15350          - bbb
15351          + BBB
15352
15353          - ddd
15354          - eee
15355          + DDD
15356          + EEE
15357            fff
15358
15359            iii
15360        "
15361        .unindent(),
15362    );
15363}
15364
15365#[gpui::test]
15366async fn test_edits_around_expanded_insertion_hunks(
15367    executor: BackgroundExecutor,
15368    cx: &mut TestAppContext,
15369) {
15370    init_test(cx, |_| {});
15371
15372    let mut cx = EditorTestContext::new(cx).await;
15373
15374    let diff_base = r#"
15375        use some::mod1;
15376        use some::mod2;
15377
15378        const A: u32 = 42;
15379
15380        fn main() {
15381            println!("hello");
15382
15383            println!("world");
15384        }
15385        "#
15386    .unindent();
15387    executor.run_until_parked();
15388    cx.set_state(
15389        &r#"
15390        use some::mod1;
15391        use some::mod2;
15392
15393        const A: u32 = 42;
15394        const B: u32 = 42;
15395        const C: u32 = 42;
15396        ˇ
15397
15398        fn main() {
15399            println!("hello");
15400
15401            println!("world");
15402        }
15403        "#
15404        .unindent(),
15405    );
15406
15407    cx.set_head_text(&diff_base);
15408    executor.run_until_parked();
15409
15410    cx.update_editor(|editor, window, cx| {
15411        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15412    });
15413    executor.run_until_parked();
15414
15415    cx.assert_state_with_diff(
15416        r#"
15417        use some::mod1;
15418        use some::mod2;
15419
15420        const A: u32 = 42;
15421      + const B: u32 = 42;
15422      + const C: u32 = 42;
15423      + ˇ
15424
15425        fn main() {
15426            println!("hello");
15427
15428            println!("world");
15429        }
15430      "#
15431        .unindent(),
15432    );
15433
15434    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15435    executor.run_until_parked();
15436
15437    cx.assert_state_with_diff(
15438        r#"
15439        use some::mod1;
15440        use some::mod2;
15441
15442        const A: u32 = 42;
15443      + const B: u32 = 42;
15444      + const C: u32 = 42;
15445      + const D: u32 = 42;
15446      + ˇ
15447
15448        fn main() {
15449            println!("hello");
15450
15451            println!("world");
15452        }
15453      "#
15454        .unindent(),
15455    );
15456
15457    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15458    executor.run_until_parked();
15459
15460    cx.assert_state_with_diff(
15461        r#"
15462        use some::mod1;
15463        use some::mod2;
15464
15465        const A: u32 = 42;
15466      + const B: u32 = 42;
15467      + const C: u32 = 42;
15468      + const D: u32 = 42;
15469      + const E: u32 = 42;
15470      + ˇ
15471
15472        fn main() {
15473            println!("hello");
15474
15475            println!("world");
15476        }
15477      "#
15478        .unindent(),
15479    );
15480
15481    cx.update_editor(|editor, window, cx| {
15482        editor.delete_line(&DeleteLine, window, cx);
15483    });
15484    executor.run_until_parked();
15485
15486    cx.assert_state_with_diff(
15487        r#"
15488        use some::mod1;
15489        use some::mod2;
15490
15491        const A: u32 = 42;
15492      + const B: u32 = 42;
15493      + const C: u32 = 42;
15494      + const D: u32 = 42;
15495      + const E: u32 = 42;
15496        ˇ
15497        fn main() {
15498            println!("hello");
15499
15500            println!("world");
15501        }
15502      "#
15503        .unindent(),
15504    );
15505
15506    cx.update_editor(|editor, window, cx| {
15507        editor.move_up(&MoveUp, window, cx);
15508        editor.delete_line(&DeleteLine, window, cx);
15509        editor.move_up(&MoveUp, window, cx);
15510        editor.delete_line(&DeleteLine, window, cx);
15511        editor.move_up(&MoveUp, window, cx);
15512        editor.delete_line(&DeleteLine, window, cx);
15513    });
15514    executor.run_until_parked();
15515    cx.assert_state_with_diff(
15516        r#"
15517        use some::mod1;
15518        use some::mod2;
15519
15520        const A: u32 = 42;
15521      + const B: u32 = 42;
15522        ˇ
15523        fn main() {
15524            println!("hello");
15525
15526            println!("world");
15527        }
15528      "#
15529        .unindent(),
15530    );
15531
15532    cx.update_editor(|editor, window, cx| {
15533        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15534        editor.delete_line(&DeleteLine, window, cx);
15535    });
15536    executor.run_until_parked();
15537    cx.assert_state_with_diff(
15538        r#"
15539        ˇ
15540        fn main() {
15541            println!("hello");
15542
15543            println!("world");
15544        }
15545      "#
15546        .unindent(),
15547    );
15548}
15549
15550#[gpui::test]
15551async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15552    init_test(cx, |_| {});
15553
15554    let mut cx = EditorTestContext::new(cx).await;
15555    cx.set_head_text(indoc! { "
15556        one
15557        two
15558        three
15559        four
15560        five
15561        "
15562    });
15563    cx.set_state(indoc! { "
15564        one
15565        ˇthree
15566        five
15567    "});
15568    cx.run_until_parked();
15569    cx.update_editor(|editor, window, cx| {
15570        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15571    });
15572    cx.assert_state_with_diff(
15573        indoc! { "
15574        one
15575      - two
15576        ˇthree
15577      - four
15578        five
15579    "}
15580        .to_string(),
15581    );
15582    cx.update_editor(|editor, window, cx| {
15583        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15584    });
15585
15586    cx.assert_state_with_diff(
15587        indoc! { "
15588        one
15589        ˇthree
15590        five
15591    "}
15592        .to_string(),
15593    );
15594
15595    cx.set_state(indoc! { "
15596        one
15597        ˇTWO
15598        three
15599        four
15600        five
15601    "});
15602    cx.run_until_parked();
15603    cx.update_editor(|editor, window, cx| {
15604        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15605    });
15606
15607    cx.assert_state_with_diff(
15608        indoc! { "
15609            one
15610          - two
15611          + ˇTWO
15612            three
15613            four
15614            five
15615        "}
15616        .to_string(),
15617    );
15618    cx.update_editor(|editor, window, cx| {
15619        editor.move_up(&Default::default(), window, cx);
15620        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15621    });
15622    cx.assert_state_with_diff(
15623        indoc! { "
15624            one
15625            ˇTWO
15626            three
15627            four
15628            five
15629        "}
15630        .to_string(),
15631    );
15632}
15633
15634#[gpui::test]
15635async fn test_edits_around_expanded_deletion_hunks(
15636    executor: BackgroundExecutor,
15637    cx: &mut TestAppContext,
15638) {
15639    init_test(cx, |_| {});
15640
15641    let mut cx = EditorTestContext::new(cx).await;
15642
15643    let diff_base = r#"
15644        use some::mod1;
15645        use some::mod2;
15646
15647        const A: u32 = 42;
15648        const B: u32 = 42;
15649        const C: u32 = 42;
15650
15651
15652        fn main() {
15653            println!("hello");
15654
15655            println!("world");
15656        }
15657    "#
15658    .unindent();
15659    executor.run_until_parked();
15660    cx.set_state(
15661        &r#"
15662        use some::mod1;
15663        use some::mod2;
15664
15665        ˇconst B: u32 = 42;
15666        const C: u32 = 42;
15667
15668
15669        fn main() {
15670            println!("hello");
15671
15672            println!("world");
15673        }
15674        "#
15675        .unindent(),
15676    );
15677
15678    cx.set_head_text(&diff_base);
15679    executor.run_until_parked();
15680
15681    cx.update_editor(|editor, window, cx| {
15682        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15683    });
15684    executor.run_until_parked();
15685
15686    cx.assert_state_with_diff(
15687        r#"
15688        use some::mod1;
15689        use some::mod2;
15690
15691      - const A: u32 = 42;
15692        ˇconst B: u32 = 42;
15693        const C: u32 = 42;
15694
15695
15696        fn main() {
15697            println!("hello");
15698
15699            println!("world");
15700        }
15701      "#
15702        .unindent(),
15703    );
15704
15705    cx.update_editor(|editor, window, cx| {
15706        editor.delete_line(&DeleteLine, window, cx);
15707    });
15708    executor.run_until_parked();
15709    cx.assert_state_with_diff(
15710        r#"
15711        use some::mod1;
15712        use some::mod2;
15713
15714      - const A: u32 = 42;
15715      - const B: u32 = 42;
15716        ˇconst C: u32 = 42;
15717
15718
15719        fn main() {
15720            println!("hello");
15721
15722            println!("world");
15723        }
15724      "#
15725        .unindent(),
15726    );
15727
15728    cx.update_editor(|editor, window, cx| {
15729        editor.delete_line(&DeleteLine, window, cx);
15730    });
15731    executor.run_until_parked();
15732    cx.assert_state_with_diff(
15733        r#"
15734        use some::mod1;
15735        use some::mod2;
15736
15737      - const A: u32 = 42;
15738      - const B: u32 = 42;
15739      - const C: u32 = 42;
15740        ˇ
15741
15742        fn main() {
15743            println!("hello");
15744
15745            println!("world");
15746        }
15747      "#
15748        .unindent(),
15749    );
15750
15751    cx.update_editor(|editor, window, cx| {
15752        editor.handle_input("replacement", window, cx);
15753    });
15754    executor.run_until_parked();
15755    cx.assert_state_with_diff(
15756        r#"
15757        use some::mod1;
15758        use some::mod2;
15759
15760      - const A: u32 = 42;
15761      - const B: u32 = 42;
15762      - const C: u32 = 42;
15763      -
15764      + replacementˇ
15765
15766        fn main() {
15767            println!("hello");
15768
15769            println!("world");
15770        }
15771      "#
15772        .unindent(),
15773    );
15774}
15775
15776#[gpui::test]
15777async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15778    init_test(cx, |_| {});
15779
15780    let mut cx = EditorTestContext::new(cx).await;
15781
15782    let base_text = r#"
15783        one
15784        two
15785        three
15786        four
15787        five
15788    "#
15789    .unindent();
15790    executor.run_until_parked();
15791    cx.set_state(
15792        &r#"
15793        one
15794        two
15795        fˇour
15796        five
15797        "#
15798        .unindent(),
15799    );
15800
15801    cx.set_head_text(&base_text);
15802    executor.run_until_parked();
15803
15804    cx.update_editor(|editor, window, cx| {
15805        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15806    });
15807    executor.run_until_parked();
15808
15809    cx.assert_state_with_diff(
15810        r#"
15811          one
15812          two
15813        - three
15814          fˇour
15815          five
15816        "#
15817        .unindent(),
15818    );
15819
15820    cx.update_editor(|editor, window, cx| {
15821        editor.backspace(&Backspace, window, cx);
15822        editor.backspace(&Backspace, window, cx);
15823    });
15824    executor.run_until_parked();
15825    cx.assert_state_with_diff(
15826        r#"
15827          one
15828          two
15829        - threeˇ
15830        - four
15831        + our
15832          five
15833        "#
15834        .unindent(),
15835    );
15836}
15837
15838#[gpui::test]
15839async fn test_edit_after_expanded_modification_hunk(
15840    executor: BackgroundExecutor,
15841    cx: &mut TestAppContext,
15842) {
15843    init_test(cx, |_| {});
15844
15845    let mut cx = EditorTestContext::new(cx).await;
15846
15847    let diff_base = r#"
15848        use some::mod1;
15849        use some::mod2;
15850
15851        const A: u32 = 42;
15852        const B: u32 = 42;
15853        const C: u32 = 42;
15854        const D: u32 = 42;
15855
15856
15857        fn main() {
15858            println!("hello");
15859
15860            println!("world");
15861        }"#
15862    .unindent();
15863
15864    cx.set_state(
15865        &r#"
15866        use some::mod1;
15867        use some::mod2;
15868
15869        const A: u32 = 42;
15870        const B: u32 = 42;
15871        const C: u32 = 43ˇ
15872        const D: u32 = 42;
15873
15874
15875        fn main() {
15876            println!("hello");
15877
15878            println!("world");
15879        }"#
15880        .unindent(),
15881    );
15882
15883    cx.set_head_text(&diff_base);
15884    executor.run_until_parked();
15885    cx.update_editor(|editor, window, cx| {
15886        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15887    });
15888    executor.run_until_parked();
15889
15890    cx.assert_state_with_diff(
15891        r#"
15892        use some::mod1;
15893        use some::mod2;
15894
15895        const A: u32 = 42;
15896        const B: u32 = 42;
15897      - const C: u32 = 42;
15898      + const C: u32 = 43ˇ
15899        const D: u32 = 42;
15900
15901
15902        fn main() {
15903            println!("hello");
15904
15905            println!("world");
15906        }"#
15907        .unindent(),
15908    );
15909
15910    cx.update_editor(|editor, window, cx| {
15911        editor.handle_input("\nnew_line\n", window, cx);
15912    });
15913    executor.run_until_parked();
15914
15915    cx.assert_state_with_diff(
15916        r#"
15917        use some::mod1;
15918        use some::mod2;
15919
15920        const A: u32 = 42;
15921        const B: u32 = 42;
15922      - const C: u32 = 42;
15923      + const C: u32 = 43
15924      + new_line
15925      + ˇ
15926        const D: u32 = 42;
15927
15928
15929        fn main() {
15930            println!("hello");
15931
15932            println!("world");
15933        }"#
15934        .unindent(),
15935    );
15936}
15937
15938#[gpui::test]
15939async fn test_stage_and_unstage_added_file_hunk(
15940    executor: BackgroundExecutor,
15941    cx: &mut TestAppContext,
15942) {
15943    init_test(cx, |_| {});
15944
15945    let mut cx = EditorTestContext::new(cx).await;
15946    cx.update_editor(|editor, _, cx| {
15947        editor.set_expand_all_diff_hunks(cx);
15948    });
15949
15950    let working_copy = r#"
15951            ˇfn main() {
15952                println!("hello, world!");
15953            }
15954        "#
15955    .unindent();
15956
15957    cx.set_state(&working_copy);
15958    executor.run_until_parked();
15959
15960    cx.assert_state_with_diff(
15961        r#"
15962            + ˇfn main() {
15963            +     println!("hello, world!");
15964            + }
15965        "#
15966        .unindent(),
15967    );
15968    cx.assert_index_text(None);
15969
15970    cx.update_editor(|editor, window, cx| {
15971        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15972    });
15973    executor.run_until_parked();
15974    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15975    cx.assert_state_with_diff(
15976        r#"
15977            + ˇfn main() {
15978            +     println!("hello, world!");
15979            + }
15980        "#
15981        .unindent(),
15982    );
15983
15984    cx.update_editor(|editor, window, cx| {
15985        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15986    });
15987    executor.run_until_parked();
15988    cx.assert_index_text(None);
15989}
15990
15991async fn setup_indent_guides_editor(
15992    text: &str,
15993    cx: &mut TestAppContext,
15994) -> (BufferId, EditorTestContext) {
15995    init_test(cx, |_| {});
15996
15997    let mut cx = EditorTestContext::new(cx).await;
15998
15999    let buffer_id = cx.update_editor(|editor, window, cx| {
16000        editor.set_text(text, window, cx);
16001        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
16002
16003        buffer_ids[0]
16004    });
16005
16006    (buffer_id, cx)
16007}
16008
16009fn assert_indent_guides(
16010    range: Range<u32>,
16011    expected: Vec<IndentGuide>,
16012    active_indices: Option<Vec<usize>>,
16013    cx: &mut EditorTestContext,
16014) {
16015    let indent_guides = cx.update_editor(|editor, window, cx| {
16016        let snapshot = editor.snapshot(window, cx).display_snapshot;
16017        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
16018            editor,
16019            MultiBufferRow(range.start)..MultiBufferRow(range.end),
16020            true,
16021            &snapshot,
16022            cx,
16023        );
16024
16025        indent_guides.sort_by(|a, b| {
16026            a.depth.cmp(&b.depth).then(
16027                a.start_row
16028                    .cmp(&b.start_row)
16029                    .then(a.end_row.cmp(&b.end_row)),
16030            )
16031        });
16032        indent_guides
16033    });
16034
16035    if let Some(expected) = active_indices {
16036        let active_indices = cx.update_editor(|editor, window, cx| {
16037            let snapshot = editor.snapshot(window, cx).display_snapshot;
16038            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16039        });
16040
16041        assert_eq!(
16042            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16043            expected,
16044            "Active indent guide indices do not match"
16045        );
16046    }
16047
16048    assert_eq!(indent_guides, expected, "Indent guides do not match");
16049}
16050
16051fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16052    IndentGuide {
16053        buffer_id,
16054        start_row: MultiBufferRow(start_row),
16055        end_row: MultiBufferRow(end_row),
16056        depth,
16057        tab_size: 4,
16058        settings: IndentGuideSettings {
16059            enabled: true,
16060            line_width: 1,
16061            active_line_width: 1,
16062            ..Default::default()
16063        },
16064    }
16065}
16066
16067#[gpui::test]
16068async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16069    let (buffer_id, mut cx) = setup_indent_guides_editor(
16070        &"
16071    fn main() {
16072        let a = 1;
16073    }"
16074        .unindent(),
16075        cx,
16076    )
16077    .await;
16078
16079    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16080}
16081
16082#[gpui::test]
16083async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16084    let (buffer_id, mut cx) = setup_indent_guides_editor(
16085        &"
16086    fn main() {
16087        let a = 1;
16088        let b = 2;
16089    }"
16090        .unindent(),
16091        cx,
16092    )
16093    .await;
16094
16095    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16096}
16097
16098#[gpui::test]
16099async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16100    let (buffer_id, mut cx) = setup_indent_guides_editor(
16101        &"
16102    fn main() {
16103        let a = 1;
16104        if a == 3 {
16105            let b = 2;
16106        } else {
16107            let c = 3;
16108        }
16109    }"
16110        .unindent(),
16111        cx,
16112    )
16113    .await;
16114
16115    assert_indent_guides(
16116        0..8,
16117        vec![
16118            indent_guide(buffer_id, 1, 6, 0),
16119            indent_guide(buffer_id, 3, 3, 1),
16120            indent_guide(buffer_id, 5, 5, 1),
16121        ],
16122        None,
16123        &mut cx,
16124    );
16125}
16126
16127#[gpui::test]
16128async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16129    let (buffer_id, mut cx) = setup_indent_guides_editor(
16130        &"
16131    fn main() {
16132        let a = 1;
16133            let b = 2;
16134        let c = 3;
16135    }"
16136        .unindent(),
16137        cx,
16138    )
16139    .await;
16140
16141    assert_indent_guides(
16142        0..5,
16143        vec![
16144            indent_guide(buffer_id, 1, 3, 0),
16145            indent_guide(buffer_id, 2, 2, 1),
16146        ],
16147        None,
16148        &mut cx,
16149    );
16150}
16151
16152#[gpui::test]
16153async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16154    let (buffer_id, mut cx) = setup_indent_guides_editor(
16155        &"
16156        fn main() {
16157            let a = 1;
16158
16159            let c = 3;
16160        }"
16161        .unindent(),
16162        cx,
16163    )
16164    .await;
16165
16166    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16167}
16168
16169#[gpui::test]
16170async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16171    let (buffer_id, mut cx) = setup_indent_guides_editor(
16172        &"
16173        fn main() {
16174            let a = 1;
16175
16176            let c = 3;
16177
16178            if a == 3 {
16179                let b = 2;
16180            } else {
16181                let c = 3;
16182            }
16183        }"
16184        .unindent(),
16185        cx,
16186    )
16187    .await;
16188
16189    assert_indent_guides(
16190        0..11,
16191        vec![
16192            indent_guide(buffer_id, 1, 9, 0),
16193            indent_guide(buffer_id, 6, 6, 1),
16194            indent_guide(buffer_id, 8, 8, 1),
16195        ],
16196        None,
16197        &mut cx,
16198    );
16199}
16200
16201#[gpui::test]
16202async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16203    let (buffer_id, mut cx) = setup_indent_guides_editor(
16204        &"
16205        fn main() {
16206            let a = 1;
16207
16208            let c = 3;
16209
16210            if a == 3 {
16211                let b = 2;
16212            } else {
16213                let c = 3;
16214            }
16215        }"
16216        .unindent(),
16217        cx,
16218    )
16219    .await;
16220
16221    assert_indent_guides(
16222        1..11,
16223        vec![
16224            indent_guide(buffer_id, 1, 9, 0),
16225            indent_guide(buffer_id, 6, 6, 1),
16226            indent_guide(buffer_id, 8, 8, 1),
16227        ],
16228        None,
16229        &mut cx,
16230    );
16231}
16232
16233#[gpui::test]
16234async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16235    let (buffer_id, mut cx) = setup_indent_guides_editor(
16236        &"
16237        fn main() {
16238            let a = 1;
16239
16240            let c = 3;
16241
16242            if a == 3 {
16243                let b = 2;
16244            } else {
16245                let c = 3;
16246            }
16247        }"
16248        .unindent(),
16249        cx,
16250    )
16251    .await;
16252
16253    assert_indent_guides(
16254        1..10,
16255        vec![
16256            indent_guide(buffer_id, 1, 9, 0),
16257            indent_guide(buffer_id, 6, 6, 1),
16258            indent_guide(buffer_id, 8, 8, 1),
16259        ],
16260        None,
16261        &mut cx,
16262    );
16263}
16264
16265#[gpui::test]
16266async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16267    let (buffer_id, mut cx) = setup_indent_guides_editor(
16268        &"
16269        block1
16270            block2
16271                block3
16272                    block4
16273            block2
16274        block1
16275        block1"
16276            .unindent(),
16277        cx,
16278    )
16279    .await;
16280
16281    assert_indent_guides(
16282        1..10,
16283        vec![
16284            indent_guide(buffer_id, 1, 4, 0),
16285            indent_guide(buffer_id, 2, 3, 1),
16286            indent_guide(buffer_id, 3, 3, 2),
16287        ],
16288        None,
16289        &mut cx,
16290    );
16291}
16292
16293#[gpui::test]
16294async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16295    let (buffer_id, mut cx) = setup_indent_guides_editor(
16296        &"
16297        block1
16298            block2
16299                block3
16300
16301        block1
16302        block1"
16303            .unindent(),
16304        cx,
16305    )
16306    .await;
16307
16308    assert_indent_guides(
16309        0..6,
16310        vec![
16311            indent_guide(buffer_id, 1, 2, 0),
16312            indent_guide(buffer_id, 2, 2, 1),
16313        ],
16314        None,
16315        &mut cx,
16316    );
16317}
16318
16319#[gpui::test]
16320async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16321    let (buffer_id, mut cx) = setup_indent_guides_editor(
16322        &"
16323        block1
16324
16325
16326
16327            block2
16328        "
16329        .unindent(),
16330        cx,
16331    )
16332    .await;
16333
16334    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16335}
16336
16337#[gpui::test]
16338async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16339    let (buffer_id, mut cx) = setup_indent_guides_editor(
16340        &"
16341        def a:
16342        \tb = 3
16343        \tif True:
16344        \t\tc = 4
16345        \t\td = 5
16346        \tprint(b)
16347        "
16348        .unindent(),
16349        cx,
16350    )
16351    .await;
16352
16353    assert_indent_guides(
16354        0..6,
16355        vec![
16356            indent_guide(buffer_id, 1, 6, 0),
16357            indent_guide(buffer_id, 3, 4, 1),
16358        ],
16359        None,
16360        &mut cx,
16361    );
16362}
16363
16364#[gpui::test]
16365async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16366    let (buffer_id, mut cx) = setup_indent_guides_editor(
16367        &"
16368    fn main() {
16369        let a = 1;
16370    }"
16371        .unindent(),
16372        cx,
16373    )
16374    .await;
16375
16376    cx.update_editor(|editor, window, cx| {
16377        editor.change_selections(None, window, cx, |s| {
16378            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16379        });
16380    });
16381
16382    assert_indent_guides(
16383        0..3,
16384        vec![indent_guide(buffer_id, 1, 1, 0)],
16385        Some(vec![0]),
16386        &mut cx,
16387    );
16388}
16389
16390#[gpui::test]
16391async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16392    let (buffer_id, mut cx) = setup_indent_guides_editor(
16393        &"
16394    fn main() {
16395        if 1 == 2 {
16396            let a = 1;
16397        }
16398    }"
16399        .unindent(),
16400        cx,
16401    )
16402    .await;
16403
16404    cx.update_editor(|editor, window, cx| {
16405        editor.change_selections(None, window, cx, |s| {
16406            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16407        });
16408    });
16409
16410    assert_indent_guides(
16411        0..4,
16412        vec![
16413            indent_guide(buffer_id, 1, 3, 0),
16414            indent_guide(buffer_id, 2, 2, 1),
16415        ],
16416        Some(vec![1]),
16417        &mut cx,
16418    );
16419
16420    cx.update_editor(|editor, window, cx| {
16421        editor.change_selections(None, window, cx, |s| {
16422            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16423        });
16424    });
16425
16426    assert_indent_guides(
16427        0..4,
16428        vec![
16429            indent_guide(buffer_id, 1, 3, 0),
16430            indent_guide(buffer_id, 2, 2, 1),
16431        ],
16432        Some(vec![1]),
16433        &mut cx,
16434    );
16435
16436    cx.update_editor(|editor, window, cx| {
16437        editor.change_selections(None, window, cx, |s| {
16438            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16439        });
16440    });
16441
16442    assert_indent_guides(
16443        0..4,
16444        vec![
16445            indent_guide(buffer_id, 1, 3, 0),
16446            indent_guide(buffer_id, 2, 2, 1),
16447        ],
16448        Some(vec![0]),
16449        &mut cx,
16450    );
16451}
16452
16453#[gpui::test]
16454async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16455    let (buffer_id, mut cx) = setup_indent_guides_editor(
16456        &"
16457    fn main() {
16458        let a = 1;
16459
16460        let b = 2;
16461    }"
16462        .unindent(),
16463        cx,
16464    )
16465    .await;
16466
16467    cx.update_editor(|editor, window, cx| {
16468        editor.change_selections(None, window, cx, |s| {
16469            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16470        });
16471    });
16472
16473    assert_indent_guides(
16474        0..5,
16475        vec![indent_guide(buffer_id, 1, 3, 0)],
16476        Some(vec![0]),
16477        &mut cx,
16478    );
16479}
16480
16481#[gpui::test]
16482async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16483    let (buffer_id, mut cx) = setup_indent_guides_editor(
16484        &"
16485    def m:
16486        a = 1
16487        pass"
16488            .unindent(),
16489        cx,
16490    )
16491    .await;
16492
16493    cx.update_editor(|editor, window, cx| {
16494        editor.change_selections(None, window, cx, |s| {
16495            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16496        });
16497    });
16498
16499    assert_indent_guides(
16500        0..3,
16501        vec![indent_guide(buffer_id, 1, 2, 0)],
16502        Some(vec![0]),
16503        &mut cx,
16504    );
16505}
16506
16507#[gpui::test]
16508async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16509    init_test(cx, |_| {});
16510    let mut cx = EditorTestContext::new(cx).await;
16511    let text = indoc! {
16512        "
16513        impl A {
16514            fn b() {
16515                0;
16516                3;
16517                5;
16518                6;
16519                7;
16520            }
16521        }
16522        "
16523    };
16524    let base_text = indoc! {
16525        "
16526        impl A {
16527            fn b() {
16528                0;
16529                1;
16530                2;
16531                3;
16532                4;
16533            }
16534            fn c() {
16535                5;
16536                6;
16537                7;
16538            }
16539        }
16540        "
16541    };
16542
16543    cx.update_editor(|editor, window, cx| {
16544        editor.set_text(text, window, cx);
16545
16546        editor.buffer().update(cx, |multibuffer, cx| {
16547            let buffer = multibuffer.as_singleton().unwrap();
16548            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16549
16550            multibuffer.set_all_diff_hunks_expanded(cx);
16551            multibuffer.add_diff(diff, cx);
16552
16553            buffer.read(cx).remote_id()
16554        })
16555    });
16556    cx.run_until_parked();
16557
16558    cx.assert_state_with_diff(
16559        indoc! { "
16560          impl A {
16561              fn b() {
16562                  0;
16563        -         1;
16564        -         2;
16565                  3;
16566        -         4;
16567        -     }
16568        -     fn c() {
16569                  5;
16570                  6;
16571                  7;
16572              }
16573          }
16574          ˇ"
16575        }
16576        .to_string(),
16577    );
16578
16579    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16580        editor
16581            .snapshot(window, cx)
16582            .buffer_snapshot
16583            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16584            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16585            .collect::<Vec<_>>()
16586    });
16587    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16588    assert_eq!(
16589        actual_guides,
16590        vec![
16591            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16592            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16593            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16594        ]
16595    );
16596}
16597
16598#[gpui::test]
16599async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16600    init_test(cx, |_| {});
16601    let mut cx = EditorTestContext::new(cx).await;
16602
16603    let diff_base = r#"
16604        a
16605        b
16606        c
16607        "#
16608    .unindent();
16609
16610    cx.set_state(
16611        &r#"
16612        ˇA
16613        b
16614        C
16615        "#
16616        .unindent(),
16617    );
16618    cx.set_head_text(&diff_base);
16619    cx.update_editor(|editor, window, cx| {
16620        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16621    });
16622    executor.run_until_parked();
16623
16624    let both_hunks_expanded = r#"
16625        - a
16626        + ˇA
16627          b
16628        - c
16629        + C
16630        "#
16631    .unindent();
16632
16633    cx.assert_state_with_diff(both_hunks_expanded.clone());
16634
16635    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16636        let snapshot = editor.snapshot(window, cx);
16637        let hunks = editor
16638            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16639            .collect::<Vec<_>>();
16640        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16641        let buffer_id = hunks[0].buffer_id;
16642        hunks
16643            .into_iter()
16644            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16645            .collect::<Vec<_>>()
16646    });
16647    assert_eq!(hunk_ranges.len(), 2);
16648
16649    cx.update_editor(|editor, _, cx| {
16650        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16651    });
16652    executor.run_until_parked();
16653
16654    let second_hunk_expanded = r#"
16655          ˇA
16656          b
16657        - c
16658        + C
16659        "#
16660    .unindent();
16661
16662    cx.assert_state_with_diff(second_hunk_expanded);
16663
16664    cx.update_editor(|editor, _, cx| {
16665        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16666    });
16667    executor.run_until_parked();
16668
16669    cx.assert_state_with_diff(both_hunks_expanded.clone());
16670
16671    cx.update_editor(|editor, _, cx| {
16672        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16673    });
16674    executor.run_until_parked();
16675
16676    let first_hunk_expanded = r#"
16677        - a
16678        + ˇA
16679          b
16680          C
16681        "#
16682    .unindent();
16683
16684    cx.assert_state_with_diff(first_hunk_expanded);
16685
16686    cx.update_editor(|editor, _, cx| {
16687        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16688    });
16689    executor.run_until_parked();
16690
16691    cx.assert_state_with_diff(both_hunks_expanded);
16692
16693    cx.set_state(
16694        &r#"
16695        ˇA
16696        b
16697        "#
16698        .unindent(),
16699    );
16700    cx.run_until_parked();
16701
16702    // TODO this cursor position seems bad
16703    cx.assert_state_with_diff(
16704        r#"
16705        - ˇa
16706        + A
16707          b
16708        "#
16709        .unindent(),
16710    );
16711
16712    cx.update_editor(|editor, window, cx| {
16713        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16714    });
16715
16716    cx.assert_state_with_diff(
16717        r#"
16718            - ˇa
16719            + A
16720              b
16721            - c
16722            "#
16723        .unindent(),
16724    );
16725
16726    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16727        let snapshot = editor.snapshot(window, cx);
16728        let hunks = editor
16729            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16730            .collect::<Vec<_>>();
16731        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16732        let buffer_id = hunks[0].buffer_id;
16733        hunks
16734            .into_iter()
16735            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16736            .collect::<Vec<_>>()
16737    });
16738    assert_eq!(hunk_ranges.len(), 2);
16739
16740    cx.update_editor(|editor, _, cx| {
16741        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16742    });
16743    executor.run_until_parked();
16744
16745    cx.assert_state_with_diff(
16746        r#"
16747        - ˇa
16748        + A
16749          b
16750        "#
16751        .unindent(),
16752    );
16753}
16754
16755#[gpui::test]
16756async fn test_toggle_deletion_hunk_at_start_of_file(
16757    executor: BackgroundExecutor,
16758    cx: &mut TestAppContext,
16759) {
16760    init_test(cx, |_| {});
16761    let mut cx = EditorTestContext::new(cx).await;
16762
16763    let diff_base = r#"
16764        a
16765        b
16766        c
16767        "#
16768    .unindent();
16769
16770    cx.set_state(
16771        &r#"
16772        ˇb
16773        c
16774        "#
16775        .unindent(),
16776    );
16777    cx.set_head_text(&diff_base);
16778    cx.update_editor(|editor, window, cx| {
16779        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16780    });
16781    executor.run_until_parked();
16782
16783    let hunk_expanded = r#"
16784        - a
16785          ˇb
16786          c
16787        "#
16788    .unindent();
16789
16790    cx.assert_state_with_diff(hunk_expanded.clone());
16791
16792    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16793        let snapshot = editor.snapshot(window, cx);
16794        let hunks = editor
16795            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16796            .collect::<Vec<_>>();
16797        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16798        let buffer_id = hunks[0].buffer_id;
16799        hunks
16800            .into_iter()
16801            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16802            .collect::<Vec<_>>()
16803    });
16804    assert_eq!(hunk_ranges.len(), 1);
16805
16806    cx.update_editor(|editor, _, cx| {
16807        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16808    });
16809    executor.run_until_parked();
16810
16811    let hunk_collapsed = r#"
16812          ˇb
16813          c
16814        "#
16815    .unindent();
16816
16817    cx.assert_state_with_diff(hunk_collapsed);
16818
16819    cx.update_editor(|editor, _, cx| {
16820        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16821    });
16822    executor.run_until_parked();
16823
16824    cx.assert_state_with_diff(hunk_expanded.clone());
16825}
16826
16827#[gpui::test]
16828async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16829    init_test(cx, |_| {});
16830
16831    let fs = FakeFs::new(cx.executor());
16832    fs.insert_tree(
16833        path!("/test"),
16834        json!({
16835            ".git": {},
16836            "file-1": "ONE\n",
16837            "file-2": "TWO\n",
16838            "file-3": "THREE\n",
16839        }),
16840    )
16841    .await;
16842
16843    fs.set_head_for_repo(
16844        path!("/test/.git").as_ref(),
16845        &[
16846            ("file-1".into(), "one\n".into()),
16847            ("file-2".into(), "two\n".into()),
16848            ("file-3".into(), "three\n".into()),
16849        ],
16850    );
16851
16852    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16853    let mut buffers = vec![];
16854    for i in 1..=3 {
16855        let buffer = project
16856            .update(cx, |project, cx| {
16857                let path = format!(path!("/test/file-{}"), i);
16858                project.open_local_buffer(path, cx)
16859            })
16860            .await
16861            .unwrap();
16862        buffers.push(buffer);
16863    }
16864
16865    let multibuffer = cx.new(|cx| {
16866        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16867        multibuffer.set_all_diff_hunks_expanded(cx);
16868        for buffer in &buffers {
16869            let snapshot = buffer.read(cx).snapshot();
16870            multibuffer.set_excerpts_for_path(
16871                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16872                buffer.clone(),
16873                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16874                DEFAULT_MULTIBUFFER_CONTEXT,
16875                cx,
16876            );
16877        }
16878        multibuffer
16879    });
16880
16881    let editor = cx.add_window(|window, cx| {
16882        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16883    });
16884    cx.run_until_parked();
16885
16886    let snapshot = editor
16887        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16888        .unwrap();
16889    let hunks = snapshot
16890        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16891        .map(|hunk| match hunk {
16892            DisplayDiffHunk::Unfolded {
16893                display_row_range, ..
16894            } => display_row_range,
16895            DisplayDiffHunk::Folded { .. } => unreachable!(),
16896        })
16897        .collect::<Vec<_>>();
16898    assert_eq!(
16899        hunks,
16900        [
16901            DisplayRow(2)..DisplayRow(4),
16902            DisplayRow(7)..DisplayRow(9),
16903            DisplayRow(12)..DisplayRow(14),
16904        ]
16905    );
16906}
16907
16908#[gpui::test]
16909async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16910    init_test(cx, |_| {});
16911
16912    let mut cx = EditorTestContext::new(cx).await;
16913    cx.set_head_text(indoc! { "
16914        one
16915        two
16916        three
16917        four
16918        five
16919        "
16920    });
16921    cx.set_index_text(indoc! { "
16922        one
16923        two
16924        three
16925        four
16926        five
16927        "
16928    });
16929    cx.set_state(indoc! {"
16930        one
16931        TWO
16932        ˇTHREE
16933        FOUR
16934        five
16935    "});
16936    cx.run_until_parked();
16937    cx.update_editor(|editor, window, cx| {
16938        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16939    });
16940    cx.run_until_parked();
16941    cx.assert_index_text(Some(indoc! {"
16942        one
16943        TWO
16944        THREE
16945        FOUR
16946        five
16947    "}));
16948    cx.set_state(indoc! { "
16949        one
16950        TWO
16951        ˇTHREE-HUNDRED
16952        FOUR
16953        five
16954    "});
16955    cx.run_until_parked();
16956    cx.update_editor(|editor, window, cx| {
16957        let snapshot = editor.snapshot(window, cx);
16958        let hunks = editor
16959            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16960            .collect::<Vec<_>>();
16961        assert_eq!(hunks.len(), 1);
16962        assert_eq!(
16963            hunks[0].status(),
16964            DiffHunkStatus {
16965                kind: DiffHunkStatusKind::Modified,
16966                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16967            }
16968        );
16969
16970        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16971    });
16972    cx.run_until_parked();
16973    cx.assert_index_text(Some(indoc! {"
16974        one
16975        TWO
16976        THREE-HUNDRED
16977        FOUR
16978        five
16979    "}));
16980}
16981
16982#[gpui::test]
16983fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16984    init_test(cx, |_| {});
16985
16986    let editor = cx.add_window(|window, cx| {
16987        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16988        build_editor(buffer, window, cx)
16989    });
16990
16991    let render_args = Arc::new(Mutex::new(None));
16992    let snapshot = editor
16993        .update(cx, |editor, window, cx| {
16994            let snapshot = editor.buffer().read(cx).snapshot(cx);
16995            let range =
16996                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16997
16998            struct RenderArgs {
16999                row: MultiBufferRow,
17000                folded: bool,
17001                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
17002            }
17003
17004            let crease = Crease::inline(
17005                range,
17006                FoldPlaceholder::test(),
17007                {
17008                    let toggle_callback = render_args.clone();
17009                    move |row, folded, callback, _window, _cx| {
17010                        *toggle_callback.lock() = Some(RenderArgs {
17011                            row,
17012                            folded,
17013                            callback,
17014                        });
17015                        div()
17016                    }
17017                },
17018                |_row, _folded, _window, _cx| div(),
17019            );
17020
17021            editor.insert_creases(Some(crease), cx);
17022            let snapshot = editor.snapshot(window, cx);
17023            let _div = snapshot.render_crease_toggle(
17024                MultiBufferRow(1),
17025                false,
17026                cx.entity().clone(),
17027                window,
17028                cx,
17029            );
17030            snapshot
17031        })
17032        .unwrap();
17033
17034    let render_args = render_args.lock().take().unwrap();
17035    assert_eq!(render_args.row, MultiBufferRow(1));
17036    assert!(!render_args.folded);
17037    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17038
17039    cx.update_window(*editor, |_, window, cx| {
17040        (render_args.callback)(true, window, cx)
17041    })
17042    .unwrap();
17043    let snapshot = editor
17044        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17045        .unwrap();
17046    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17047
17048    cx.update_window(*editor, |_, window, cx| {
17049        (render_args.callback)(false, window, cx)
17050    })
17051    .unwrap();
17052    let snapshot = editor
17053        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17054        .unwrap();
17055    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17056}
17057
17058#[gpui::test]
17059async fn test_input_text(cx: &mut TestAppContext) {
17060    init_test(cx, |_| {});
17061    let mut cx = EditorTestContext::new(cx).await;
17062
17063    cx.set_state(
17064        &r#"ˇone
17065        two
17066
17067        three
17068        fourˇ
17069        five
17070
17071        siˇx"#
17072            .unindent(),
17073    );
17074
17075    cx.dispatch_action(HandleInput(String::new()));
17076    cx.assert_editor_state(
17077        &r#"ˇone
17078        two
17079
17080        three
17081        fourˇ
17082        five
17083
17084        siˇx"#
17085            .unindent(),
17086    );
17087
17088    cx.dispatch_action(HandleInput("AAAA".to_string()));
17089    cx.assert_editor_state(
17090        &r#"AAAAˇone
17091        two
17092
17093        three
17094        fourAAAAˇ
17095        five
17096
17097        siAAAAˇx"#
17098            .unindent(),
17099    );
17100}
17101
17102#[gpui::test]
17103async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17104    init_test(cx, |_| {});
17105
17106    let mut cx = EditorTestContext::new(cx).await;
17107    cx.set_state(
17108        r#"let foo = 1;
17109let foo = 2;
17110let foo = 3;
17111let fooˇ = 4;
17112let foo = 5;
17113let foo = 6;
17114let foo = 7;
17115let foo = 8;
17116let foo = 9;
17117let foo = 10;
17118let foo = 11;
17119let foo = 12;
17120let foo = 13;
17121let foo = 14;
17122let foo = 15;"#,
17123    );
17124
17125    cx.update_editor(|e, window, cx| {
17126        assert_eq!(
17127            e.next_scroll_position,
17128            NextScrollCursorCenterTopBottom::Center,
17129            "Default next scroll direction is center",
17130        );
17131
17132        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17133        assert_eq!(
17134            e.next_scroll_position,
17135            NextScrollCursorCenterTopBottom::Top,
17136            "After center, next scroll direction should be top",
17137        );
17138
17139        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17140        assert_eq!(
17141            e.next_scroll_position,
17142            NextScrollCursorCenterTopBottom::Bottom,
17143            "After top, next scroll direction should be bottom",
17144        );
17145
17146        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17147        assert_eq!(
17148            e.next_scroll_position,
17149            NextScrollCursorCenterTopBottom::Center,
17150            "After bottom, scrolling should start over",
17151        );
17152
17153        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17154        assert_eq!(
17155            e.next_scroll_position,
17156            NextScrollCursorCenterTopBottom::Top,
17157            "Scrolling continues if retriggered fast enough"
17158        );
17159    });
17160
17161    cx.executor()
17162        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17163    cx.executor().run_until_parked();
17164    cx.update_editor(|e, _, _| {
17165        assert_eq!(
17166            e.next_scroll_position,
17167            NextScrollCursorCenterTopBottom::Center,
17168            "If scrolling is not triggered fast enough, it should reset"
17169        );
17170    });
17171}
17172
17173#[gpui::test]
17174async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17175    init_test(cx, |_| {});
17176    let mut cx = EditorLspTestContext::new_rust(
17177        lsp::ServerCapabilities {
17178            definition_provider: Some(lsp::OneOf::Left(true)),
17179            references_provider: Some(lsp::OneOf::Left(true)),
17180            ..lsp::ServerCapabilities::default()
17181        },
17182        cx,
17183    )
17184    .await;
17185
17186    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17187        let go_to_definition = cx
17188            .lsp
17189            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17190                move |params, _| async move {
17191                    if empty_go_to_definition {
17192                        Ok(None)
17193                    } else {
17194                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17195                            uri: params.text_document_position_params.text_document.uri,
17196                            range: lsp::Range::new(
17197                                lsp::Position::new(4, 3),
17198                                lsp::Position::new(4, 6),
17199                            ),
17200                        })))
17201                    }
17202                },
17203            );
17204        let references = cx
17205            .lsp
17206            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17207                Ok(Some(vec![lsp::Location {
17208                    uri: params.text_document_position.text_document.uri,
17209                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17210                }]))
17211            });
17212        (go_to_definition, references)
17213    };
17214
17215    cx.set_state(
17216        &r#"fn one() {
17217            let mut a = ˇtwo();
17218        }
17219
17220        fn two() {}"#
17221            .unindent(),
17222    );
17223    set_up_lsp_handlers(false, &mut cx);
17224    let navigated = cx
17225        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17226        .await
17227        .expect("Failed to navigate to definition");
17228    assert_eq!(
17229        navigated,
17230        Navigated::Yes,
17231        "Should have navigated to definition from the GetDefinition response"
17232    );
17233    cx.assert_editor_state(
17234        &r#"fn one() {
17235            let mut a = two();
17236        }
17237
17238        fn «twoˇ»() {}"#
17239            .unindent(),
17240    );
17241
17242    let editors = cx.update_workspace(|workspace, _, cx| {
17243        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17244    });
17245    cx.update_editor(|_, _, test_editor_cx| {
17246        assert_eq!(
17247            editors.len(),
17248            1,
17249            "Initially, only one, test, editor should be open in the workspace"
17250        );
17251        assert_eq!(
17252            test_editor_cx.entity(),
17253            editors.last().expect("Asserted len is 1").clone()
17254        );
17255    });
17256
17257    set_up_lsp_handlers(true, &mut cx);
17258    let navigated = cx
17259        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17260        .await
17261        .expect("Failed to navigate to lookup references");
17262    assert_eq!(
17263        navigated,
17264        Navigated::Yes,
17265        "Should have navigated to references as a fallback after empty GoToDefinition response"
17266    );
17267    // We should not change the selections in the existing file,
17268    // if opening another milti buffer with the references
17269    cx.assert_editor_state(
17270        &r#"fn one() {
17271            let mut a = two();
17272        }
17273
17274        fn «twoˇ»() {}"#
17275            .unindent(),
17276    );
17277    let editors = cx.update_workspace(|workspace, _, cx| {
17278        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17279    });
17280    cx.update_editor(|_, _, test_editor_cx| {
17281        assert_eq!(
17282            editors.len(),
17283            2,
17284            "After falling back to references search, we open a new editor with the results"
17285        );
17286        let references_fallback_text = editors
17287            .into_iter()
17288            .find(|new_editor| *new_editor != test_editor_cx.entity())
17289            .expect("Should have one non-test editor now")
17290            .read(test_editor_cx)
17291            .text(test_editor_cx);
17292        assert_eq!(
17293            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17294            "Should use the range from the references response and not the GoToDefinition one"
17295        );
17296    });
17297}
17298
17299#[gpui::test]
17300async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17301    init_test(cx, |_| {});
17302    cx.update(|cx| {
17303        let mut editor_settings = EditorSettings::get_global(cx).clone();
17304        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17305        EditorSettings::override_global(editor_settings, cx);
17306    });
17307    let mut cx = EditorLspTestContext::new_rust(
17308        lsp::ServerCapabilities {
17309            definition_provider: Some(lsp::OneOf::Left(true)),
17310            references_provider: Some(lsp::OneOf::Left(true)),
17311            ..lsp::ServerCapabilities::default()
17312        },
17313        cx,
17314    )
17315    .await;
17316    let original_state = r#"fn one() {
17317        let mut a = ˇtwo();
17318    }
17319
17320    fn two() {}"#
17321        .unindent();
17322    cx.set_state(&original_state);
17323
17324    let mut go_to_definition = cx
17325        .lsp
17326        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17327            move |_, _| async move { Ok(None) },
17328        );
17329    let _references = cx
17330        .lsp
17331        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17332            panic!("Should not call for references with no go to definition fallback")
17333        });
17334
17335    let navigated = cx
17336        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17337        .await
17338        .expect("Failed to navigate to lookup references");
17339    go_to_definition
17340        .next()
17341        .await
17342        .expect("Should have called the go_to_definition handler");
17343
17344    assert_eq!(
17345        navigated,
17346        Navigated::No,
17347        "Should have navigated to references as a fallback after empty GoToDefinition response"
17348    );
17349    cx.assert_editor_state(&original_state);
17350    let editors = cx.update_workspace(|workspace, _, cx| {
17351        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17352    });
17353    cx.update_editor(|_, _, _| {
17354        assert_eq!(
17355            editors.len(),
17356            1,
17357            "After unsuccessful fallback, no other editor should have been opened"
17358        );
17359    });
17360}
17361
17362#[gpui::test]
17363async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17364    init_test(cx, |_| {});
17365
17366    let language = Arc::new(Language::new(
17367        LanguageConfig::default(),
17368        Some(tree_sitter_rust::LANGUAGE.into()),
17369    ));
17370
17371    let text = r#"
17372        #[cfg(test)]
17373        mod tests() {
17374            #[test]
17375            fn runnable_1() {
17376                let a = 1;
17377            }
17378
17379            #[test]
17380            fn runnable_2() {
17381                let a = 1;
17382                let b = 2;
17383            }
17384        }
17385    "#
17386    .unindent();
17387
17388    let fs = FakeFs::new(cx.executor());
17389    fs.insert_file("/file.rs", Default::default()).await;
17390
17391    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17392    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17393    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17394    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17395    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17396
17397    let editor = cx.new_window_entity(|window, cx| {
17398        Editor::new(
17399            EditorMode::full(),
17400            multi_buffer,
17401            Some(project.clone()),
17402            window,
17403            cx,
17404        )
17405    });
17406
17407    editor.update_in(cx, |editor, window, cx| {
17408        let snapshot = editor.buffer().read(cx).snapshot(cx);
17409        editor.tasks.insert(
17410            (buffer.read(cx).remote_id(), 3),
17411            RunnableTasks {
17412                templates: vec![],
17413                offset: snapshot.anchor_before(43),
17414                column: 0,
17415                extra_variables: HashMap::default(),
17416                context_range: BufferOffset(43)..BufferOffset(85),
17417            },
17418        );
17419        editor.tasks.insert(
17420            (buffer.read(cx).remote_id(), 8),
17421            RunnableTasks {
17422                templates: vec![],
17423                offset: snapshot.anchor_before(86),
17424                column: 0,
17425                extra_variables: HashMap::default(),
17426                context_range: BufferOffset(86)..BufferOffset(191),
17427            },
17428        );
17429
17430        // Test finding task when cursor is inside function body
17431        editor.change_selections(None, window, cx, |s| {
17432            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17433        });
17434        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17435        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17436
17437        // Test finding task when cursor is on function name
17438        editor.change_selections(None, window, cx, |s| {
17439            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17440        });
17441        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17442        assert_eq!(row, 8, "Should find task when cursor is on function name");
17443    });
17444}
17445
17446#[gpui::test]
17447async fn test_folding_buffers(cx: &mut TestAppContext) {
17448    init_test(cx, |_| {});
17449
17450    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17451    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17452    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17453
17454    let fs = FakeFs::new(cx.executor());
17455    fs.insert_tree(
17456        path!("/a"),
17457        json!({
17458            "first.rs": sample_text_1,
17459            "second.rs": sample_text_2,
17460            "third.rs": sample_text_3,
17461        }),
17462    )
17463    .await;
17464    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17465    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17466    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17467    let worktree = project.update(cx, |project, cx| {
17468        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17469        assert_eq!(worktrees.len(), 1);
17470        worktrees.pop().unwrap()
17471    });
17472    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17473
17474    let buffer_1 = project
17475        .update(cx, |project, cx| {
17476            project.open_buffer((worktree_id, "first.rs"), cx)
17477        })
17478        .await
17479        .unwrap();
17480    let buffer_2 = project
17481        .update(cx, |project, cx| {
17482            project.open_buffer((worktree_id, "second.rs"), cx)
17483        })
17484        .await
17485        .unwrap();
17486    let buffer_3 = project
17487        .update(cx, |project, cx| {
17488            project.open_buffer((worktree_id, "third.rs"), cx)
17489        })
17490        .await
17491        .unwrap();
17492
17493    let multi_buffer = cx.new(|cx| {
17494        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17495        multi_buffer.push_excerpts(
17496            buffer_1.clone(),
17497            [
17498                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17499                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17500                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17501            ],
17502            cx,
17503        );
17504        multi_buffer.push_excerpts(
17505            buffer_2.clone(),
17506            [
17507                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17508                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17509                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17510            ],
17511            cx,
17512        );
17513        multi_buffer.push_excerpts(
17514            buffer_3.clone(),
17515            [
17516                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17517                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17518                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17519            ],
17520            cx,
17521        );
17522        multi_buffer
17523    });
17524    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17525        Editor::new(
17526            EditorMode::full(),
17527            multi_buffer.clone(),
17528            Some(project.clone()),
17529            window,
17530            cx,
17531        )
17532    });
17533
17534    assert_eq!(
17535        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17536        "\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",
17537    );
17538
17539    multi_buffer_editor.update(cx, |editor, cx| {
17540        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17541    });
17542    assert_eq!(
17543        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17544        "\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",
17545        "After folding the first buffer, its text should not be displayed"
17546    );
17547
17548    multi_buffer_editor.update(cx, |editor, cx| {
17549        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17550    });
17551    assert_eq!(
17552        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17553        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17554        "After folding the second buffer, its text should not be displayed"
17555    );
17556
17557    multi_buffer_editor.update(cx, |editor, cx| {
17558        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17559    });
17560    assert_eq!(
17561        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17562        "\n\n\n\n\n",
17563        "After folding the third buffer, its text should not be displayed"
17564    );
17565
17566    // Emulate selection inside the fold logic, that should work
17567    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17568        editor
17569            .snapshot(window, cx)
17570            .next_line_boundary(Point::new(0, 4));
17571    });
17572
17573    multi_buffer_editor.update(cx, |editor, cx| {
17574        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17575    });
17576    assert_eq!(
17577        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17578        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17579        "After unfolding the second buffer, its text should be displayed"
17580    );
17581
17582    // Typing inside of buffer 1 causes that buffer to be unfolded.
17583    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17584        assert_eq!(
17585            multi_buffer
17586                .read(cx)
17587                .snapshot(cx)
17588                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17589                .collect::<String>(),
17590            "bbbb"
17591        );
17592        editor.change_selections(None, window, cx, |selections| {
17593            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17594        });
17595        editor.handle_input("B", window, cx);
17596    });
17597
17598    assert_eq!(
17599        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17600        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17601        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17602    );
17603
17604    multi_buffer_editor.update(cx, |editor, cx| {
17605        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17606    });
17607    assert_eq!(
17608        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17609        "\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",
17610        "After unfolding the all buffers, all original text should be displayed"
17611    );
17612}
17613
17614#[gpui::test]
17615async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17616    init_test(cx, |_| {});
17617
17618    let sample_text_1 = "1111\n2222\n3333".to_string();
17619    let sample_text_2 = "4444\n5555\n6666".to_string();
17620    let sample_text_3 = "7777\n8888\n9999".to_string();
17621
17622    let fs = FakeFs::new(cx.executor());
17623    fs.insert_tree(
17624        path!("/a"),
17625        json!({
17626            "first.rs": sample_text_1,
17627            "second.rs": sample_text_2,
17628            "third.rs": sample_text_3,
17629        }),
17630    )
17631    .await;
17632    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17633    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17634    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17635    let worktree = project.update(cx, |project, cx| {
17636        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17637        assert_eq!(worktrees.len(), 1);
17638        worktrees.pop().unwrap()
17639    });
17640    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17641
17642    let buffer_1 = project
17643        .update(cx, |project, cx| {
17644            project.open_buffer((worktree_id, "first.rs"), cx)
17645        })
17646        .await
17647        .unwrap();
17648    let buffer_2 = project
17649        .update(cx, |project, cx| {
17650            project.open_buffer((worktree_id, "second.rs"), cx)
17651        })
17652        .await
17653        .unwrap();
17654    let buffer_3 = project
17655        .update(cx, |project, cx| {
17656            project.open_buffer((worktree_id, "third.rs"), cx)
17657        })
17658        .await
17659        .unwrap();
17660
17661    let multi_buffer = cx.new(|cx| {
17662        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17663        multi_buffer.push_excerpts(
17664            buffer_1.clone(),
17665            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17666            cx,
17667        );
17668        multi_buffer.push_excerpts(
17669            buffer_2.clone(),
17670            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17671            cx,
17672        );
17673        multi_buffer.push_excerpts(
17674            buffer_3.clone(),
17675            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17676            cx,
17677        );
17678        multi_buffer
17679    });
17680
17681    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17682        Editor::new(
17683            EditorMode::full(),
17684            multi_buffer,
17685            Some(project.clone()),
17686            window,
17687            cx,
17688        )
17689    });
17690
17691    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17692    assert_eq!(
17693        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17694        full_text,
17695    );
17696
17697    multi_buffer_editor.update(cx, |editor, cx| {
17698        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17699    });
17700    assert_eq!(
17701        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17702        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17703        "After folding the first buffer, its text should not be displayed"
17704    );
17705
17706    multi_buffer_editor.update(cx, |editor, cx| {
17707        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17708    });
17709
17710    assert_eq!(
17711        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17712        "\n\n\n\n\n\n7777\n8888\n9999",
17713        "After folding the second buffer, its text should not be displayed"
17714    );
17715
17716    multi_buffer_editor.update(cx, |editor, cx| {
17717        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17718    });
17719    assert_eq!(
17720        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17721        "\n\n\n\n\n",
17722        "After folding the third buffer, its text should not be displayed"
17723    );
17724
17725    multi_buffer_editor.update(cx, |editor, cx| {
17726        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17727    });
17728    assert_eq!(
17729        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17730        "\n\n\n\n4444\n5555\n6666\n\n",
17731        "After unfolding the second buffer, its text should be displayed"
17732    );
17733
17734    multi_buffer_editor.update(cx, |editor, cx| {
17735        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17736    });
17737    assert_eq!(
17738        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17739        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17740        "After unfolding the first buffer, its text should be displayed"
17741    );
17742
17743    multi_buffer_editor.update(cx, |editor, cx| {
17744        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17745    });
17746    assert_eq!(
17747        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17748        full_text,
17749        "After unfolding all buffers, all original text should be displayed"
17750    );
17751}
17752
17753#[gpui::test]
17754async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17755    init_test(cx, |_| {});
17756
17757    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17758
17759    let fs = FakeFs::new(cx.executor());
17760    fs.insert_tree(
17761        path!("/a"),
17762        json!({
17763            "main.rs": sample_text,
17764        }),
17765    )
17766    .await;
17767    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17768    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17769    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17770    let worktree = project.update(cx, |project, cx| {
17771        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17772        assert_eq!(worktrees.len(), 1);
17773        worktrees.pop().unwrap()
17774    });
17775    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17776
17777    let buffer_1 = project
17778        .update(cx, |project, cx| {
17779            project.open_buffer((worktree_id, "main.rs"), cx)
17780        })
17781        .await
17782        .unwrap();
17783
17784    let multi_buffer = cx.new(|cx| {
17785        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17786        multi_buffer.push_excerpts(
17787            buffer_1.clone(),
17788            [ExcerptRange::new(
17789                Point::new(0, 0)
17790                    ..Point::new(
17791                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17792                        0,
17793                    ),
17794            )],
17795            cx,
17796        );
17797        multi_buffer
17798    });
17799    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17800        Editor::new(
17801            EditorMode::full(),
17802            multi_buffer,
17803            Some(project.clone()),
17804            window,
17805            cx,
17806        )
17807    });
17808
17809    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17810    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17811        enum TestHighlight {}
17812        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17813        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17814        editor.highlight_text::<TestHighlight>(
17815            vec![highlight_range.clone()],
17816            HighlightStyle::color(Hsla::green()),
17817            cx,
17818        );
17819        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17820    });
17821
17822    let full_text = format!("\n\n{sample_text}");
17823    assert_eq!(
17824        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17825        full_text,
17826    );
17827}
17828
17829#[gpui::test]
17830async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17831    init_test(cx, |_| {});
17832    cx.update(|cx| {
17833        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17834            "keymaps/default-linux.json",
17835            cx,
17836        )
17837        .unwrap();
17838        cx.bind_keys(default_key_bindings);
17839    });
17840
17841    let (editor, cx) = cx.add_window_view(|window, cx| {
17842        let multi_buffer = MultiBuffer::build_multi(
17843            [
17844                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17845                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17846                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17847                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17848            ],
17849            cx,
17850        );
17851        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17852
17853        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17854        // fold all but the second buffer, so that we test navigating between two
17855        // adjacent folded buffers, as well as folded buffers at the start and
17856        // end the multibuffer
17857        editor.fold_buffer(buffer_ids[0], cx);
17858        editor.fold_buffer(buffer_ids[2], cx);
17859        editor.fold_buffer(buffer_ids[3], cx);
17860
17861        editor
17862    });
17863    cx.simulate_resize(size(px(1000.), px(1000.)));
17864
17865    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17866    cx.assert_excerpts_with_selections(indoc! {"
17867        [EXCERPT]
17868        ˇ[FOLDED]
17869        [EXCERPT]
17870        a1
17871        b1
17872        [EXCERPT]
17873        [FOLDED]
17874        [EXCERPT]
17875        [FOLDED]
17876        "
17877    });
17878    cx.simulate_keystroke("down");
17879    cx.assert_excerpts_with_selections(indoc! {"
17880        [EXCERPT]
17881        [FOLDED]
17882        [EXCERPT]
17883        ˇa1
17884        b1
17885        [EXCERPT]
17886        [FOLDED]
17887        [EXCERPT]
17888        [FOLDED]
17889        "
17890    });
17891    cx.simulate_keystroke("down");
17892    cx.assert_excerpts_with_selections(indoc! {"
17893        [EXCERPT]
17894        [FOLDED]
17895        [EXCERPT]
17896        a1
17897        ˇb1
17898        [EXCERPT]
17899        [FOLDED]
17900        [EXCERPT]
17901        [FOLDED]
17902        "
17903    });
17904    cx.simulate_keystroke("down");
17905    cx.assert_excerpts_with_selections(indoc! {"
17906        [EXCERPT]
17907        [FOLDED]
17908        [EXCERPT]
17909        a1
17910        b1
17911        ˇ[EXCERPT]
17912        [FOLDED]
17913        [EXCERPT]
17914        [FOLDED]
17915        "
17916    });
17917    cx.simulate_keystroke("down");
17918    cx.assert_excerpts_with_selections(indoc! {"
17919        [EXCERPT]
17920        [FOLDED]
17921        [EXCERPT]
17922        a1
17923        b1
17924        [EXCERPT]
17925        ˇ[FOLDED]
17926        [EXCERPT]
17927        [FOLDED]
17928        "
17929    });
17930    for _ in 0..5 {
17931        cx.simulate_keystroke("down");
17932        cx.assert_excerpts_with_selections(indoc! {"
17933            [EXCERPT]
17934            [FOLDED]
17935            [EXCERPT]
17936            a1
17937            b1
17938            [EXCERPT]
17939            [FOLDED]
17940            [EXCERPT]
17941            ˇ[FOLDED]
17942            "
17943        });
17944    }
17945
17946    cx.simulate_keystroke("up");
17947    cx.assert_excerpts_with_selections(indoc! {"
17948        [EXCERPT]
17949        [FOLDED]
17950        [EXCERPT]
17951        a1
17952        b1
17953        [EXCERPT]
17954        ˇ[FOLDED]
17955        [EXCERPT]
17956        [FOLDED]
17957        "
17958    });
17959    cx.simulate_keystroke("up");
17960    cx.assert_excerpts_with_selections(indoc! {"
17961        [EXCERPT]
17962        [FOLDED]
17963        [EXCERPT]
17964        a1
17965        b1
17966        ˇ[EXCERPT]
17967        [FOLDED]
17968        [EXCERPT]
17969        [FOLDED]
17970        "
17971    });
17972    cx.simulate_keystroke("up");
17973    cx.assert_excerpts_with_selections(indoc! {"
17974        [EXCERPT]
17975        [FOLDED]
17976        [EXCERPT]
17977        a1
17978        ˇb1
17979        [EXCERPT]
17980        [FOLDED]
17981        [EXCERPT]
17982        [FOLDED]
17983        "
17984    });
17985    cx.simulate_keystroke("up");
17986    cx.assert_excerpts_with_selections(indoc! {"
17987        [EXCERPT]
17988        [FOLDED]
17989        [EXCERPT]
17990        ˇa1
17991        b1
17992        [EXCERPT]
17993        [FOLDED]
17994        [EXCERPT]
17995        [FOLDED]
17996        "
17997    });
17998    for _ in 0..5 {
17999        cx.simulate_keystroke("up");
18000        cx.assert_excerpts_with_selections(indoc! {"
18001            [EXCERPT]
18002            ˇ[FOLDED]
18003            [EXCERPT]
18004            a1
18005            b1
18006            [EXCERPT]
18007            [FOLDED]
18008            [EXCERPT]
18009            [FOLDED]
18010            "
18011        });
18012    }
18013}
18014
18015#[gpui::test]
18016async fn test_inline_completion_text(cx: &mut TestAppContext) {
18017    init_test(cx, |_| {});
18018
18019    // Simple insertion
18020    assert_highlighted_edits(
18021        "Hello, world!",
18022        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
18023        true,
18024        cx,
18025        |highlighted_edits, cx| {
18026            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
18027            assert_eq!(highlighted_edits.highlights.len(), 1);
18028            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
18029            assert_eq!(
18030                highlighted_edits.highlights[0].1.background_color,
18031                Some(cx.theme().status().created_background)
18032            );
18033        },
18034    )
18035    .await;
18036
18037    // Replacement
18038    assert_highlighted_edits(
18039        "This is a test.",
18040        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18041        false,
18042        cx,
18043        |highlighted_edits, cx| {
18044            assert_eq!(highlighted_edits.text, "That is a test.");
18045            assert_eq!(highlighted_edits.highlights.len(), 1);
18046            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18047            assert_eq!(
18048                highlighted_edits.highlights[0].1.background_color,
18049                Some(cx.theme().status().created_background)
18050            );
18051        },
18052    )
18053    .await;
18054
18055    // Multiple edits
18056    assert_highlighted_edits(
18057        "Hello, world!",
18058        vec![
18059            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18060            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18061        ],
18062        false,
18063        cx,
18064        |highlighted_edits, cx| {
18065            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18066            assert_eq!(highlighted_edits.highlights.len(), 2);
18067            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18068            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18069            assert_eq!(
18070                highlighted_edits.highlights[0].1.background_color,
18071                Some(cx.theme().status().created_background)
18072            );
18073            assert_eq!(
18074                highlighted_edits.highlights[1].1.background_color,
18075                Some(cx.theme().status().created_background)
18076            );
18077        },
18078    )
18079    .await;
18080
18081    // Multiple lines with edits
18082    assert_highlighted_edits(
18083        "First line\nSecond line\nThird line\nFourth line",
18084        vec![
18085            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18086            (
18087                Point::new(2, 0)..Point::new(2, 10),
18088                "New third line".to_string(),
18089            ),
18090            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18091        ],
18092        false,
18093        cx,
18094        |highlighted_edits, cx| {
18095            assert_eq!(
18096                highlighted_edits.text,
18097                "Second modified\nNew third line\nFourth updated line"
18098            );
18099            assert_eq!(highlighted_edits.highlights.len(), 3);
18100            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18101            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18102            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18103            for highlight in &highlighted_edits.highlights {
18104                assert_eq!(
18105                    highlight.1.background_color,
18106                    Some(cx.theme().status().created_background)
18107                );
18108            }
18109        },
18110    )
18111    .await;
18112}
18113
18114#[gpui::test]
18115async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18116    init_test(cx, |_| {});
18117
18118    // Deletion
18119    assert_highlighted_edits(
18120        "Hello, world!",
18121        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18122        true,
18123        cx,
18124        |highlighted_edits, cx| {
18125            assert_eq!(highlighted_edits.text, "Hello, world!");
18126            assert_eq!(highlighted_edits.highlights.len(), 1);
18127            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18128            assert_eq!(
18129                highlighted_edits.highlights[0].1.background_color,
18130                Some(cx.theme().status().deleted_background)
18131            );
18132        },
18133    )
18134    .await;
18135
18136    // Insertion
18137    assert_highlighted_edits(
18138        "Hello, world!",
18139        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18140        true,
18141        cx,
18142        |highlighted_edits, cx| {
18143            assert_eq!(highlighted_edits.highlights.len(), 1);
18144            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18145            assert_eq!(
18146                highlighted_edits.highlights[0].1.background_color,
18147                Some(cx.theme().status().created_background)
18148            );
18149        },
18150    )
18151    .await;
18152}
18153
18154async fn assert_highlighted_edits(
18155    text: &str,
18156    edits: Vec<(Range<Point>, String)>,
18157    include_deletions: bool,
18158    cx: &mut TestAppContext,
18159    assertion_fn: impl Fn(HighlightedText, &App),
18160) {
18161    let window = cx.add_window(|window, cx| {
18162        let buffer = MultiBuffer::build_simple(text, cx);
18163        Editor::new(EditorMode::full(), buffer, None, window, cx)
18164    });
18165    let cx = &mut VisualTestContext::from_window(*window, cx);
18166
18167    let (buffer, snapshot) = window
18168        .update(cx, |editor, _window, cx| {
18169            (
18170                editor.buffer().clone(),
18171                editor.buffer().read(cx).snapshot(cx),
18172            )
18173        })
18174        .unwrap();
18175
18176    let edits = edits
18177        .into_iter()
18178        .map(|(range, edit)| {
18179            (
18180                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18181                edit,
18182            )
18183        })
18184        .collect::<Vec<_>>();
18185
18186    let text_anchor_edits = edits
18187        .clone()
18188        .into_iter()
18189        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18190        .collect::<Vec<_>>();
18191
18192    let edit_preview = window
18193        .update(cx, |_, _window, cx| {
18194            buffer
18195                .read(cx)
18196                .as_singleton()
18197                .unwrap()
18198                .read(cx)
18199                .preview_edits(text_anchor_edits.into(), cx)
18200        })
18201        .unwrap()
18202        .await;
18203
18204    cx.update(|_window, cx| {
18205        let highlighted_edits = inline_completion_edit_text(
18206            &snapshot.as_singleton().unwrap().2,
18207            &edits,
18208            &edit_preview,
18209            include_deletions,
18210            cx,
18211        );
18212        assertion_fn(highlighted_edits, cx)
18213    });
18214}
18215
18216#[track_caller]
18217fn assert_breakpoint(
18218    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18219    path: &Arc<Path>,
18220    expected: Vec<(u32, Breakpoint)>,
18221) {
18222    if expected.len() == 0usize {
18223        assert!(!breakpoints.contains_key(path), "{}", path.display());
18224    } else {
18225        let mut breakpoint = breakpoints
18226            .get(path)
18227            .unwrap()
18228            .into_iter()
18229            .map(|breakpoint| {
18230                (
18231                    breakpoint.row,
18232                    Breakpoint {
18233                        message: breakpoint.message.clone(),
18234                        state: breakpoint.state,
18235                        condition: breakpoint.condition.clone(),
18236                        hit_condition: breakpoint.hit_condition.clone(),
18237                    },
18238                )
18239            })
18240            .collect::<Vec<_>>();
18241
18242        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18243
18244        assert_eq!(expected, breakpoint);
18245    }
18246}
18247
18248fn add_log_breakpoint_at_cursor(
18249    editor: &mut Editor,
18250    log_message: &str,
18251    window: &mut Window,
18252    cx: &mut Context<Editor>,
18253) {
18254    let (anchor, bp) = editor
18255        .breakpoints_at_cursors(window, cx)
18256        .first()
18257        .and_then(|(anchor, bp)| {
18258            if let Some(bp) = bp {
18259                Some((*anchor, bp.clone()))
18260            } else {
18261                None
18262            }
18263        })
18264        .unwrap_or_else(|| {
18265            let cursor_position: Point = editor.selections.newest(cx).head();
18266
18267            let breakpoint_position = editor
18268                .snapshot(window, cx)
18269                .display_snapshot
18270                .buffer_snapshot
18271                .anchor_before(Point::new(cursor_position.row, 0));
18272
18273            (breakpoint_position, Breakpoint::new_log(&log_message))
18274        });
18275
18276    editor.edit_breakpoint_at_anchor(
18277        anchor,
18278        bp,
18279        BreakpointEditAction::EditLogMessage(log_message.into()),
18280        cx,
18281    );
18282}
18283
18284#[gpui::test]
18285async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18286    init_test(cx, |_| {});
18287
18288    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18289    let fs = FakeFs::new(cx.executor());
18290    fs.insert_tree(
18291        path!("/a"),
18292        json!({
18293            "main.rs": sample_text,
18294        }),
18295    )
18296    .await;
18297    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18298    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18299    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18300
18301    let fs = FakeFs::new(cx.executor());
18302    fs.insert_tree(
18303        path!("/a"),
18304        json!({
18305            "main.rs": sample_text,
18306        }),
18307    )
18308    .await;
18309    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18310    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18311    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18312    let worktree_id = workspace
18313        .update(cx, |workspace, _window, cx| {
18314            workspace.project().update(cx, |project, cx| {
18315                project.worktrees(cx).next().unwrap().read(cx).id()
18316            })
18317        })
18318        .unwrap();
18319
18320    let buffer = project
18321        .update(cx, |project, cx| {
18322            project.open_buffer((worktree_id, "main.rs"), cx)
18323        })
18324        .await
18325        .unwrap();
18326
18327    let (editor, cx) = cx.add_window_view(|window, cx| {
18328        Editor::new(
18329            EditorMode::full(),
18330            MultiBuffer::build_from_buffer(buffer, cx),
18331            Some(project.clone()),
18332            window,
18333            cx,
18334        )
18335    });
18336
18337    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18338    let abs_path = project.read_with(cx, |project, cx| {
18339        project
18340            .absolute_path(&project_path, cx)
18341            .map(|path_buf| Arc::from(path_buf.to_owned()))
18342            .unwrap()
18343    });
18344
18345    // assert we can add breakpoint on the first line
18346    editor.update_in(cx, |editor, window, cx| {
18347        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18348        editor.move_to_end(&MoveToEnd, window, cx);
18349        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18350    });
18351
18352    let breakpoints = editor.update(cx, |editor, cx| {
18353        editor
18354            .breakpoint_store()
18355            .as_ref()
18356            .unwrap()
18357            .read(cx)
18358            .all_breakpoints(cx)
18359            .clone()
18360    });
18361
18362    assert_eq!(1, breakpoints.len());
18363    assert_breakpoint(
18364        &breakpoints,
18365        &abs_path,
18366        vec![
18367            (0, Breakpoint::new_standard()),
18368            (3, Breakpoint::new_standard()),
18369        ],
18370    );
18371
18372    editor.update_in(cx, |editor, window, cx| {
18373        editor.move_to_beginning(&MoveToBeginning, window, cx);
18374        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18375    });
18376
18377    let breakpoints = editor.update(cx, |editor, cx| {
18378        editor
18379            .breakpoint_store()
18380            .as_ref()
18381            .unwrap()
18382            .read(cx)
18383            .all_breakpoints(cx)
18384            .clone()
18385    });
18386
18387    assert_eq!(1, breakpoints.len());
18388    assert_breakpoint(
18389        &breakpoints,
18390        &abs_path,
18391        vec![(3, Breakpoint::new_standard())],
18392    );
18393
18394    editor.update_in(cx, |editor, window, cx| {
18395        editor.move_to_end(&MoveToEnd, window, cx);
18396        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18397    });
18398
18399    let breakpoints = editor.update(cx, |editor, cx| {
18400        editor
18401            .breakpoint_store()
18402            .as_ref()
18403            .unwrap()
18404            .read(cx)
18405            .all_breakpoints(cx)
18406            .clone()
18407    });
18408
18409    assert_eq!(0, breakpoints.len());
18410    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18411}
18412
18413#[gpui::test]
18414async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18415    init_test(cx, |_| {});
18416
18417    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18418
18419    let fs = FakeFs::new(cx.executor());
18420    fs.insert_tree(
18421        path!("/a"),
18422        json!({
18423            "main.rs": sample_text,
18424        }),
18425    )
18426    .await;
18427    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18428    let (workspace, cx) =
18429        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18430
18431    let worktree_id = workspace.update(cx, |workspace, cx| {
18432        workspace.project().update(cx, |project, cx| {
18433            project.worktrees(cx).next().unwrap().read(cx).id()
18434        })
18435    });
18436
18437    let buffer = project
18438        .update(cx, |project, cx| {
18439            project.open_buffer((worktree_id, "main.rs"), cx)
18440        })
18441        .await
18442        .unwrap();
18443
18444    let (editor, cx) = cx.add_window_view(|window, cx| {
18445        Editor::new(
18446            EditorMode::full(),
18447            MultiBuffer::build_from_buffer(buffer, cx),
18448            Some(project.clone()),
18449            window,
18450            cx,
18451        )
18452    });
18453
18454    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18455    let abs_path = project.read_with(cx, |project, cx| {
18456        project
18457            .absolute_path(&project_path, cx)
18458            .map(|path_buf| Arc::from(path_buf.to_owned()))
18459            .unwrap()
18460    });
18461
18462    editor.update_in(cx, |editor, window, cx| {
18463        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18464    });
18465
18466    let breakpoints = editor.update(cx, |editor, cx| {
18467        editor
18468            .breakpoint_store()
18469            .as_ref()
18470            .unwrap()
18471            .read(cx)
18472            .all_breakpoints(cx)
18473            .clone()
18474    });
18475
18476    assert_breakpoint(
18477        &breakpoints,
18478        &abs_path,
18479        vec![(0, Breakpoint::new_log("hello world"))],
18480    );
18481
18482    // Removing a log message from a log breakpoint should remove it
18483    editor.update_in(cx, |editor, window, cx| {
18484        add_log_breakpoint_at_cursor(editor, "", window, cx);
18485    });
18486
18487    let breakpoints = editor.update(cx, |editor, cx| {
18488        editor
18489            .breakpoint_store()
18490            .as_ref()
18491            .unwrap()
18492            .read(cx)
18493            .all_breakpoints(cx)
18494            .clone()
18495    });
18496
18497    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18498
18499    editor.update_in(cx, |editor, window, cx| {
18500        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18501        editor.move_to_end(&MoveToEnd, window, cx);
18502        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18503        // Not adding a log message to a standard breakpoint shouldn't remove it
18504        add_log_breakpoint_at_cursor(editor, "", window, cx);
18505    });
18506
18507    let breakpoints = editor.update(cx, |editor, cx| {
18508        editor
18509            .breakpoint_store()
18510            .as_ref()
18511            .unwrap()
18512            .read(cx)
18513            .all_breakpoints(cx)
18514            .clone()
18515    });
18516
18517    assert_breakpoint(
18518        &breakpoints,
18519        &abs_path,
18520        vec![
18521            (0, Breakpoint::new_standard()),
18522            (3, Breakpoint::new_standard()),
18523        ],
18524    );
18525
18526    editor.update_in(cx, |editor, window, cx| {
18527        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18528    });
18529
18530    let breakpoints = editor.update(cx, |editor, cx| {
18531        editor
18532            .breakpoint_store()
18533            .as_ref()
18534            .unwrap()
18535            .read(cx)
18536            .all_breakpoints(cx)
18537            .clone()
18538    });
18539
18540    assert_breakpoint(
18541        &breakpoints,
18542        &abs_path,
18543        vec![
18544            (0, Breakpoint::new_standard()),
18545            (3, Breakpoint::new_log("hello world")),
18546        ],
18547    );
18548
18549    editor.update_in(cx, |editor, window, cx| {
18550        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18551    });
18552
18553    let breakpoints = editor.update(cx, |editor, cx| {
18554        editor
18555            .breakpoint_store()
18556            .as_ref()
18557            .unwrap()
18558            .read(cx)
18559            .all_breakpoints(cx)
18560            .clone()
18561    });
18562
18563    assert_breakpoint(
18564        &breakpoints,
18565        &abs_path,
18566        vec![
18567            (0, Breakpoint::new_standard()),
18568            (3, Breakpoint::new_log("hello Earth!!")),
18569        ],
18570    );
18571}
18572
18573/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18574/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18575/// or when breakpoints were placed out of order. This tests for a regression too
18576#[gpui::test]
18577async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18578    init_test(cx, |_| {});
18579
18580    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18581    let fs = FakeFs::new(cx.executor());
18582    fs.insert_tree(
18583        path!("/a"),
18584        json!({
18585            "main.rs": sample_text,
18586        }),
18587    )
18588    .await;
18589    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18590    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18591    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18592
18593    let fs = FakeFs::new(cx.executor());
18594    fs.insert_tree(
18595        path!("/a"),
18596        json!({
18597            "main.rs": sample_text,
18598        }),
18599    )
18600    .await;
18601    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18602    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18603    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18604    let worktree_id = workspace
18605        .update(cx, |workspace, _window, cx| {
18606            workspace.project().update(cx, |project, cx| {
18607                project.worktrees(cx).next().unwrap().read(cx).id()
18608            })
18609        })
18610        .unwrap();
18611
18612    let buffer = project
18613        .update(cx, |project, cx| {
18614            project.open_buffer((worktree_id, "main.rs"), cx)
18615        })
18616        .await
18617        .unwrap();
18618
18619    let (editor, cx) = cx.add_window_view(|window, cx| {
18620        Editor::new(
18621            EditorMode::full(),
18622            MultiBuffer::build_from_buffer(buffer, cx),
18623            Some(project.clone()),
18624            window,
18625            cx,
18626        )
18627    });
18628
18629    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18630    let abs_path = project.read_with(cx, |project, cx| {
18631        project
18632            .absolute_path(&project_path, cx)
18633            .map(|path_buf| Arc::from(path_buf.to_owned()))
18634            .unwrap()
18635    });
18636
18637    // assert we can add breakpoint on the first line
18638    editor.update_in(cx, |editor, window, cx| {
18639        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18640        editor.move_to_end(&MoveToEnd, window, cx);
18641        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18642        editor.move_up(&MoveUp, window, cx);
18643        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18644    });
18645
18646    let breakpoints = editor.update(cx, |editor, cx| {
18647        editor
18648            .breakpoint_store()
18649            .as_ref()
18650            .unwrap()
18651            .read(cx)
18652            .all_breakpoints(cx)
18653            .clone()
18654    });
18655
18656    assert_eq!(1, breakpoints.len());
18657    assert_breakpoint(
18658        &breakpoints,
18659        &abs_path,
18660        vec![
18661            (0, Breakpoint::new_standard()),
18662            (2, Breakpoint::new_standard()),
18663            (3, Breakpoint::new_standard()),
18664        ],
18665    );
18666
18667    editor.update_in(cx, |editor, window, cx| {
18668        editor.move_to_beginning(&MoveToBeginning, window, cx);
18669        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18670        editor.move_to_end(&MoveToEnd, window, cx);
18671        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18672        // Disabling a breakpoint that doesn't exist should do nothing
18673        editor.move_up(&MoveUp, window, cx);
18674        editor.move_up(&MoveUp, window, cx);
18675        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18676    });
18677
18678    let breakpoints = editor.update(cx, |editor, cx| {
18679        editor
18680            .breakpoint_store()
18681            .as_ref()
18682            .unwrap()
18683            .read(cx)
18684            .all_breakpoints(cx)
18685            .clone()
18686    });
18687
18688    let disable_breakpoint = {
18689        let mut bp = Breakpoint::new_standard();
18690        bp.state = BreakpointState::Disabled;
18691        bp
18692    };
18693
18694    assert_eq!(1, breakpoints.len());
18695    assert_breakpoint(
18696        &breakpoints,
18697        &abs_path,
18698        vec![
18699            (0, disable_breakpoint.clone()),
18700            (2, Breakpoint::new_standard()),
18701            (3, disable_breakpoint.clone()),
18702        ],
18703    );
18704
18705    editor.update_in(cx, |editor, window, cx| {
18706        editor.move_to_beginning(&MoveToBeginning, window, cx);
18707        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18708        editor.move_to_end(&MoveToEnd, window, cx);
18709        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18710        editor.move_up(&MoveUp, window, cx);
18711        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18712    });
18713
18714    let breakpoints = editor.update(cx, |editor, cx| {
18715        editor
18716            .breakpoint_store()
18717            .as_ref()
18718            .unwrap()
18719            .read(cx)
18720            .all_breakpoints(cx)
18721            .clone()
18722    });
18723
18724    assert_eq!(1, breakpoints.len());
18725    assert_breakpoint(
18726        &breakpoints,
18727        &abs_path,
18728        vec![
18729            (0, Breakpoint::new_standard()),
18730            (2, disable_breakpoint),
18731            (3, Breakpoint::new_standard()),
18732        ],
18733    );
18734}
18735
18736#[gpui::test]
18737async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18738    init_test(cx, |_| {});
18739    let capabilities = lsp::ServerCapabilities {
18740        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18741            prepare_provider: Some(true),
18742            work_done_progress_options: Default::default(),
18743        })),
18744        ..Default::default()
18745    };
18746    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18747
18748    cx.set_state(indoc! {"
18749        struct Fˇoo {}
18750    "});
18751
18752    cx.update_editor(|editor, _, cx| {
18753        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18754        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18755        editor.highlight_background::<DocumentHighlightRead>(
18756            &[highlight_range],
18757            |c| c.editor_document_highlight_read_background,
18758            cx,
18759        );
18760    });
18761
18762    let mut prepare_rename_handler = cx
18763        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18764            move |_, _, _| async move {
18765                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18766                    start: lsp::Position {
18767                        line: 0,
18768                        character: 7,
18769                    },
18770                    end: lsp::Position {
18771                        line: 0,
18772                        character: 10,
18773                    },
18774                })))
18775            },
18776        );
18777    let prepare_rename_task = cx
18778        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18779        .expect("Prepare rename was not started");
18780    prepare_rename_handler.next().await.unwrap();
18781    prepare_rename_task.await.expect("Prepare rename failed");
18782
18783    let mut rename_handler =
18784        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18785            let edit = lsp::TextEdit {
18786                range: lsp::Range {
18787                    start: lsp::Position {
18788                        line: 0,
18789                        character: 7,
18790                    },
18791                    end: lsp::Position {
18792                        line: 0,
18793                        character: 10,
18794                    },
18795                },
18796                new_text: "FooRenamed".to_string(),
18797            };
18798            Ok(Some(lsp::WorkspaceEdit::new(
18799                // Specify the same edit twice
18800                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18801            )))
18802        });
18803    let rename_task = cx
18804        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18805        .expect("Confirm rename was not started");
18806    rename_handler.next().await.unwrap();
18807    rename_task.await.expect("Confirm rename failed");
18808    cx.run_until_parked();
18809
18810    // Despite two edits, only one is actually applied as those are identical
18811    cx.assert_editor_state(indoc! {"
18812        struct FooRenamedˇ {}
18813    "});
18814}
18815
18816#[gpui::test]
18817async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18818    init_test(cx, |_| {});
18819    // These capabilities indicate that the server does not support prepare rename.
18820    let capabilities = lsp::ServerCapabilities {
18821        rename_provider: Some(lsp::OneOf::Left(true)),
18822        ..Default::default()
18823    };
18824    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18825
18826    cx.set_state(indoc! {"
18827        struct Fˇoo {}
18828    "});
18829
18830    cx.update_editor(|editor, _window, cx| {
18831        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18832        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18833        editor.highlight_background::<DocumentHighlightRead>(
18834            &[highlight_range],
18835            |c| c.editor_document_highlight_read_background,
18836            cx,
18837        );
18838    });
18839
18840    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18841        .expect("Prepare rename was not started")
18842        .await
18843        .expect("Prepare rename failed");
18844
18845    let mut rename_handler =
18846        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18847            let edit = lsp::TextEdit {
18848                range: lsp::Range {
18849                    start: lsp::Position {
18850                        line: 0,
18851                        character: 7,
18852                    },
18853                    end: lsp::Position {
18854                        line: 0,
18855                        character: 10,
18856                    },
18857                },
18858                new_text: "FooRenamed".to_string(),
18859            };
18860            Ok(Some(lsp::WorkspaceEdit::new(
18861                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18862            )))
18863        });
18864    let rename_task = cx
18865        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18866        .expect("Confirm rename was not started");
18867    rename_handler.next().await.unwrap();
18868    rename_task.await.expect("Confirm rename failed");
18869    cx.run_until_parked();
18870
18871    // Correct range is renamed, as `surrounding_word` is used to find it.
18872    cx.assert_editor_state(indoc! {"
18873        struct FooRenamedˇ {}
18874    "});
18875}
18876
18877#[gpui::test]
18878async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18879    init_test(cx, |_| {});
18880    let mut cx = EditorTestContext::new(cx).await;
18881
18882    let language = Arc::new(
18883        Language::new(
18884            LanguageConfig::default(),
18885            Some(tree_sitter_html::LANGUAGE.into()),
18886        )
18887        .with_brackets_query(
18888            r#"
18889            ("<" @open "/>" @close)
18890            ("</" @open ">" @close)
18891            ("<" @open ">" @close)
18892            ("\"" @open "\"" @close)
18893            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18894        "#,
18895        )
18896        .unwrap(),
18897    );
18898    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18899
18900    cx.set_state(indoc! {"
18901        <span>ˇ</span>
18902    "});
18903    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18904    cx.assert_editor_state(indoc! {"
18905        <span>
18906        ˇ
18907        </span>
18908    "});
18909
18910    cx.set_state(indoc! {"
18911        <span><span></span>ˇ</span>
18912    "});
18913    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18914    cx.assert_editor_state(indoc! {"
18915        <span><span></span>
18916        ˇ</span>
18917    "});
18918
18919    cx.set_state(indoc! {"
18920        <span>ˇ
18921        </span>
18922    "});
18923    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18924    cx.assert_editor_state(indoc! {"
18925        <span>
18926        ˇ
18927        </span>
18928    "});
18929}
18930
18931#[gpui::test(iterations = 10)]
18932async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18933    init_test(cx, |_| {});
18934
18935    let fs = FakeFs::new(cx.executor());
18936    fs.insert_tree(
18937        path!("/dir"),
18938        json!({
18939            "a.ts": "a",
18940        }),
18941    )
18942    .await;
18943
18944    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18945    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18946    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18947
18948    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18949    language_registry.add(Arc::new(Language::new(
18950        LanguageConfig {
18951            name: "TypeScript".into(),
18952            matcher: LanguageMatcher {
18953                path_suffixes: vec!["ts".to_string()],
18954                ..Default::default()
18955            },
18956            ..Default::default()
18957        },
18958        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18959    )));
18960    let mut fake_language_servers = language_registry.register_fake_lsp(
18961        "TypeScript",
18962        FakeLspAdapter {
18963            capabilities: lsp::ServerCapabilities {
18964                code_lens_provider: Some(lsp::CodeLensOptions {
18965                    resolve_provider: Some(true),
18966                }),
18967                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18968                    commands: vec!["_the/command".to_string()],
18969                    ..lsp::ExecuteCommandOptions::default()
18970                }),
18971                ..lsp::ServerCapabilities::default()
18972            },
18973            ..FakeLspAdapter::default()
18974        },
18975    );
18976
18977    let (buffer, _handle) = project
18978        .update(cx, |p, cx| {
18979            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18980        })
18981        .await
18982        .unwrap();
18983    cx.executor().run_until_parked();
18984
18985    let fake_server = fake_language_servers.next().await.unwrap();
18986
18987    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18988    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18989    drop(buffer_snapshot);
18990    let actions = cx
18991        .update_window(*workspace, |_, window, cx| {
18992            project.code_actions(&buffer, anchor..anchor, window, cx)
18993        })
18994        .unwrap();
18995
18996    fake_server
18997        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18998            Ok(Some(vec![
18999                lsp::CodeLens {
19000                    range: lsp::Range::default(),
19001                    command: Some(lsp::Command {
19002                        title: "Code lens command".to_owned(),
19003                        command: "_the/command".to_owned(),
19004                        arguments: None,
19005                    }),
19006                    data: None,
19007                },
19008                lsp::CodeLens {
19009                    range: lsp::Range::default(),
19010                    command: Some(lsp::Command {
19011                        title: "Command not in capabilities".to_owned(),
19012                        command: "not in capabilities".to_owned(),
19013                        arguments: None,
19014                    }),
19015                    data: None,
19016                },
19017                lsp::CodeLens {
19018                    range: lsp::Range {
19019                        start: lsp::Position {
19020                            line: 1,
19021                            character: 1,
19022                        },
19023                        end: lsp::Position {
19024                            line: 1,
19025                            character: 1,
19026                        },
19027                    },
19028                    command: Some(lsp::Command {
19029                        title: "Command not in range".to_owned(),
19030                        command: "_the/command".to_owned(),
19031                        arguments: None,
19032                    }),
19033                    data: None,
19034                },
19035            ]))
19036        })
19037        .next()
19038        .await;
19039
19040    let actions = actions.await.unwrap();
19041    assert_eq!(
19042        actions.len(),
19043        1,
19044        "Should have only one valid action for the 0..0 range"
19045    );
19046    let action = actions[0].clone();
19047    let apply = project.update(cx, |project, cx| {
19048        project.apply_code_action(buffer.clone(), action, true, cx)
19049    });
19050
19051    // Resolving the code action does not populate its edits. In absence of
19052    // edits, we must execute the given command.
19053    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19054        |mut lens, _| async move {
19055            let lens_command = lens.command.as_mut().expect("should have a command");
19056            assert_eq!(lens_command.title, "Code lens command");
19057            lens_command.arguments = Some(vec![json!("the-argument")]);
19058            Ok(lens)
19059        },
19060    );
19061
19062    // While executing the command, the language server sends the editor
19063    // a `workspaceEdit` request.
19064    fake_server
19065        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19066            let fake = fake_server.clone();
19067            move |params, _| {
19068                assert_eq!(params.command, "_the/command");
19069                let fake = fake.clone();
19070                async move {
19071                    fake.server
19072                        .request::<lsp::request::ApplyWorkspaceEdit>(
19073                            lsp::ApplyWorkspaceEditParams {
19074                                label: None,
19075                                edit: lsp::WorkspaceEdit {
19076                                    changes: Some(
19077                                        [(
19078                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19079                                            vec![lsp::TextEdit {
19080                                                range: lsp::Range::new(
19081                                                    lsp::Position::new(0, 0),
19082                                                    lsp::Position::new(0, 0),
19083                                                ),
19084                                                new_text: "X".into(),
19085                                            }],
19086                                        )]
19087                                        .into_iter()
19088                                        .collect(),
19089                                    ),
19090                                    ..Default::default()
19091                                },
19092                            },
19093                        )
19094                        .await
19095                        .unwrap();
19096                    Ok(Some(json!(null)))
19097                }
19098            }
19099        })
19100        .next()
19101        .await;
19102
19103    // Applying the code lens command returns a project transaction containing the edits
19104    // sent by the language server in its `workspaceEdit` request.
19105    let transaction = apply.await.unwrap();
19106    assert!(transaction.0.contains_key(&buffer));
19107    buffer.update(cx, |buffer, cx| {
19108        assert_eq!(buffer.text(), "Xa");
19109        buffer.undo(cx);
19110        assert_eq!(buffer.text(), "a");
19111    });
19112}
19113
19114#[gpui::test]
19115async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19116    init_test(cx, |_| {});
19117
19118    let fs = FakeFs::new(cx.executor());
19119    let main_text = r#"fn main() {
19120println!("1");
19121println!("2");
19122println!("3");
19123println!("4");
19124println!("5");
19125}"#;
19126    let lib_text = "mod foo {}";
19127    fs.insert_tree(
19128        path!("/a"),
19129        json!({
19130            "lib.rs": lib_text,
19131            "main.rs": main_text,
19132        }),
19133    )
19134    .await;
19135
19136    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19137    let (workspace, cx) =
19138        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19139    let worktree_id = workspace.update(cx, |workspace, cx| {
19140        workspace.project().update(cx, |project, cx| {
19141            project.worktrees(cx).next().unwrap().read(cx).id()
19142        })
19143    });
19144
19145    let expected_ranges = vec![
19146        Point::new(0, 0)..Point::new(0, 0),
19147        Point::new(1, 0)..Point::new(1, 1),
19148        Point::new(2, 0)..Point::new(2, 2),
19149        Point::new(3, 0)..Point::new(3, 3),
19150    ];
19151
19152    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19153    let editor_1 = workspace
19154        .update_in(cx, |workspace, window, cx| {
19155            workspace.open_path(
19156                (worktree_id, "main.rs"),
19157                Some(pane_1.downgrade()),
19158                true,
19159                window,
19160                cx,
19161            )
19162        })
19163        .unwrap()
19164        .await
19165        .downcast::<Editor>()
19166        .unwrap();
19167    pane_1.update(cx, |pane, cx| {
19168        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19169        open_editor.update(cx, |editor, cx| {
19170            assert_eq!(
19171                editor.display_text(cx),
19172                main_text,
19173                "Original main.rs text on initial open",
19174            );
19175            assert_eq!(
19176                editor
19177                    .selections
19178                    .all::<Point>(cx)
19179                    .into_iter()
19180                    .map(|s| s.range())
19181                    .collect::<Vec<_>>(),
19182                vec![Point::zero()..Point::zero()],
19183                "Default selections on initial open",
19184            );
19185        })
19186    });
19187    editor_1.update_in(cx, |editor, window, cx| {
19188        editor.change_selections(None, window, cx, |s| {
19189            s.select_ranges(expected_ranges.clone());
19190        });
19191    });
19192
19193    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19194        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19195    });
19196    let editor_2 = workspace
19197        .update_in(cx, |workspace, window, cx| {
19198            workspace.open_path(
19199                (worktree_id, "main.rs"),
19200                Some(pane_2.downgrade()),
19201                true,
19202                window,
19203                cx,
19204            )
19205        })
19206        .unwrap()
19207        .await
19208        .downcast::<Editor>()
19209        .unwrap();
19210    pane_2.update(cx, |pane, cx| {
19211        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19212        open_editor.update(cx, |editor, cx| {
19213            assert_eq!(
19214                editor.display_text(cx),
19215                main_text,
19216                "Original main.rs text on initial open in another panel",
19217            );
19218            assert_eq!(
19219                editor
19220                    .selections
19221                    .all::<Point>(cx)
19222                    .into_iter()
19223                    .map(|s| s.range())
19224                    .collect::<Vec<_>>(),
19225                vec![Point::zero()..Point::zero()],
19226                "Default selections on initial open in another panel",
19227            );
19228        })
19229    });
19230
19231    editor_2.update_in(cx, |editor, window, cx| {
19232        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19233    });
19234
19235    let _other_editor_1 = workspace
19236        .update_in(cx, |workspace, window, cx| {
19237            workspace.open_path(
19238                (worktree_id, "lib.rs"),
19239                Some(pane_1.downgrade()),
19240                true,
19241                window,
19242                cx,
19243            )
19244        })
19245        .unwrap()
19246        .await
19247        .downcast::<Editor>()
19248        .unwrap();
19249    pane_1
19250        .update_in(cx, |pane, window, cx| {
19251            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19252                .unwrap()
19253        })
19254        .await
19255        .unwrap();
19256    drop(editor_1);
19257    pane_1.update(cx, |pane, cx| {
19258        pane.active_item()
19259            .unwrap()
19260            .downcast::<Editor>()
19261            .unwrap()
19262            .update(cx, |editor, cx| {
19263                assert_eq!(
19264                    editor.display_text(cx),
19265                    lib_text,
19266                    "Other file should be open and active",
19267                );
19268            });
19269        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19270    });
19271
19272    let _other_editor_2 = workspace
19273        .update_in(cx, |workspace, window, cx| {
19274            workspace.open_path(
19275                (worktree_id, "lib.rs"),
19276                Some(pane_2.downgrade()),
19277                true,
19278                window,
19279                cx,
19280            )
19281        })
19282        .unwrap()
19283        .await
19284        .downcast::<Editor>()
19285        .unwrap();
19286    pane_2
19287        .update_in(cx, |pane, window, cx| {
19288            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19289                .unwrap()
19290        })
19291        .await
19292        .unwrap();
19293    drop(editor_2);
19294    pane_2.update(cx, |pane, cx| {
19295        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19296        open_editor.update(cx, |editor, cx| {
19297            assert_eq!(
19298                editor.display_text(cx),
19299                lib_text,
19300                "Other file should be open and active in another panel too",
19301            );
19302        });
19303        assert_eq!(
19304            pane.items().count(),
19305            1,
19306            "No other editors should be open in another pane",
19307        );
19308    });
19309
19310    let _editor_1_reopened = workspace
19311        .update_in(cx, |workspace, window, cx| {
19312            workspace.open_path(
19313                (worktree_id, "main.rs"),
19314                Some(pane_1.downgrade()),
19315                true,
19316                window,
19317                cx,
19318            )
19319        })
19320        .unwrap()
19321        .await
19322        .downcast::<Editor>()
19323        .unwrap();
19324    let _editor_2_reopened = workspace
19325        .update_in(cx, |workspace, window, cx| {
19326            workspace.open_path(
19327                (worktree_id, "main.rs"),
19328                Some(pane_2.downgrade()),
19329                true,
19330                window,
19331                cx,
19332            )
19333        })
19334        .unwrap()
19335        .await
19336        .downcast::<Editor>()
19337        .unwrap();
19338    pane_1.update(cx, |pane, cx| {
19339        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19340        open_editor.update(cx, |editor, cx| {
19341            assert_eq!(
19342                editor.display_text(cx),
19343                main_text,
19344                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19345            );
19346            assert_eq!(
19347                editor
19348                    .selections
19349                    .all::<Point>(cx)
19350                    .into_iter()
19351                    .map(|s| s.range())
19352                    .collect::<Vec<_>>(),
19353                expected_ranges,
19354                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19355            );
19356        })
19357    });
19358    pane_2.update(cx, |pane, cx| {
19359        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19360        open_editor.update(cx, |editor, cx| {
19361            assert_eq!(
19362                editor.display_text(cx),
19363                r#"fn main() {
19364⋯rintln!("1");
19365⋯intln!("2");
19366⋯ntln!("3");
19367println!("4");
19368println!("5");
19369}"#,
19370                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19371            );
19372            assert_eq!(
19373                editor
19374                    .selections
19375                    .all::<Point>(cx)
19376                    .into_iter()
19377                    .map(|s| s.range())
19378                    .collect::<Vec<_>>(),
19379                vec![Point::zero()..Point::zero()],
19380                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19381            );
19382        })
19383    });
19384}
19385
19386#[gpui::test]
19387async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19388    init_test(cx, |_| {});
19389
19390    let fs = FakeFs::new(cx.executor());
19391    let main_text = r#"fn main() {
19392println!("1");
19393println!("2");
19394println!("3");
19395println!("4");
19396println!("5");
19397}"#;
19398    let lib_text = "mod foo {}";
19399    fs.insert_tree(
19400        path!("/a"),
19401        json!({
19402            "lib.rs": lib_text,
19403            "main.rs": main_text,
19404        }),
19405    )
19406    .await;
19407
19408    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19409    let (workspace, cx) =
19410        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19411    let worktree_id = workspace.update(cx, |workspace, cx| {
19412        workspace.project().update(cx, |project, cx| {
19413            project.worktrees(cx).next().unwrap().read(cx).id()
19414        })
19415    });
19416
19417    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19418    let editor = workspace
19419        .update_in(cx, |workspace, window, cx| {
19420            workspace.open_path(
19421                (worktree_id, "main.rs"),
19422                Some(pane.downgrade()),
19423                true,
19424                window,
19425                cx,
19426            )
19427        })
19428        .unwrap()
19429        .await
19430        .downcast::<Editor>()
19431        .unwrap();
19432    pane.update(cx, |pane, cx| {
19433        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19434        open_editor.update(cx, |editor, cx| {
19435            assert_eq!(
19436                editor.display_text(cx),
19437                main_text,
19438                "Original main.rs text on initial open",
19439            );
19440        })
19441    });
19442    editor.update_in(cx, |editor, window, cx| {
19443        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19444    });
19445
19446    cx.update_global(|store: &mut SettingsStore, cx| {
19447        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19448            s.restore_on_file_reopen = Some(false);
19449        });
19450    });
19451    editor.update_in(cx, |editor, window, cx| {
19452        editor.fold_ranges(
19453            vec![
19454                Point::new(1, 0)..Point::new(1, 1),
19455                Point::new(2, 0)..Point::new(2, 2),
19456                Point::new(3, 0)..Point::new(3, 3),
19457            ],
19458            false,
19459            window,
19460            cx,
19461        );
19462    });
19463    pane.update_in(cx, |pane, window, cx| {
19464        pane.close_all_items(&CloseAllItems::default(), window, cx)
19465            .unwrap()
19466    })
19467    .await
19468    .unwrap();
19469    pane.update(cx, |pane, _| {
19470        assert!(pane.active_item().is_none());
19471    });
19472    cx.update_global(|store: &mut SettingsStore, cx| {
19473        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19474            s.restore_on_file_reopen = Some(true);
19475        });
19476    });
19477
19478    let _editor_reopened = workspace
19479        .update_in(cx, |workspace, window, cx| {
19480            workspace.open_path(
19481                (worktree_id, "main.rs"),
19482                Some(pane.downgrade()),
19483                true,
19484                window,
19485                cx,
19486            )
19487        })
19488        .unwrap()
19489        .await
19490        .downcast::<Editor>()
19491        .unwrap();
19492    pane.update(cx, |pane, cx| {
19493        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19494        open_editor.update(cx, |editor, cx| {
19495            assert_eq!(
19496                editor.display_text(cx),
19497                main_text,
19498                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19499            );
19500        })
19501    });
19502}
19503
19504#[gpui::test]
19505async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19506    struct EmptyModalView {
19507        focus_handle: gpui::FocusHandle,
19508    }
19509    impl EventEmitter<DismissEvent> for EmptyModalView {}
19510    impl Render for EmptyModalView {
19511        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19512            div()
19513        }
19514    }
19515    impl Focusable for EmptyModalView {
19516        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19517            self.focus_handle.clone()
19518        }
19519    }
19520    impl workspace::ModalView for EmptyModalView {}
19521    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19522        EmptyModalView {
19523            focus_handle: cx.focus_handle(),
19524        }
19525    }
19526
19527    init_test(cx, |_| {});
19528
19529    let fs = FakeFs::new(cx.executor());
19530    let project = Project::test(fs, [], cx).await;
19531    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19532    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19533    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19534    let editor = cx.new_window_entity(|window, cx| {
19535        Editor::new(
19536            EditorMode::full(),
19537            buffer,
19538            Some(project.clone()),
19539            window,
19540            cx,
19541        )
19542    });
19543    workspace
19544        .update(cx, |workspace, window, cx| {
19545            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19546        })
19547        .unwrap();
19548    editor.update_in(cx, |editor, window, cx| {
19549        editor.open_context_menu(&OpenContextMenu, window, cx);
19550        assert!(editor.mouse_context_menu.is_some());
19551    });
19552    workspace
19553        .update(cx, |workspace, window, cx| {
19554            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19555        })
19556        .unwrap();
19557    cx.read(|cx| {
19558        assert!(editor.read(cx).mouse_context_menu.is_none());
19559    });
19560}
19561
19562fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19563    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19564    point..point
19565}
19566
19567fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19568    let (text, ranges) = marked_text_ranges(marked_text, true);
19569    assert_eq!(editor.text(cx), text);
19570    assert_eq!(
19571        editor.selections.ranges(cx),
19572        ranges,
19573        "Assert selections are {}",
19574        marked_text
19575    );
19576}
19577
19578pub fn handle_signature_help_request(
19579    cx: &mut EditorLspTestContext,
19580    mocked_response: lsp::SignatureHelp,
19581) -> impl Future<Output = ()> + use<> {
19582    let mut request =
19583        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19584            let mocked_response = mocked_response.clone();
19585            async move { Ok(Some(mocked_response)) }
19586        });
19587
19588    async move {
19589        request.next().await;
19590    }
19591}
19592
19593/// Handle completion request passing a marked string specifying where the completion
19594/// should be triggered from using '|' character, what range should be replaced, and what completions
19595/// should be returned using '<' and '>' to delimit the range.
19596///
19597/// Also see `handle_completion_request_with_insert_and_replace`.
19598#[track_caller]
19599pub fn handle_completion_request(
19600    cx: &mut EditorLspTestContext,
19601    marked_string: &str,
19602    completions: Vec<&'static str>,
19603    counter: Arc<AtomicUsize>,
19604) -> impl Future<Output = ()> {
19605    let complete_from_marker: TextRangeMarker = '|'.into();
19606    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19607    let (_, mut marked_ranges) = marked_text_ranges_by(
19608        marked_string,
19609        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19610    );
19611
19612    let complete_from_position =
19613        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19614    let replace_range =
19615        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19616
19617    let mut request =
19618        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19619            let completions = completions.clone();
19620            counter.fetch_add(1, atomic::Ordering::Release);
19621            async move {
19622                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19623                assert_eq!(
19624                    params.text_document_position.position,
19625                    complete_from_position
19626                );
19627                Ok(Some(lsp::CompletionResponse::Array(
19628                    completions
19629                        .iter()
19630                        .map(|completion_text| lsp::CompletionItem {
19631                            label: completion_text.to_string(),
19632                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19633                                range: replace_range,
19634                                new_text: completion_text.to_string(),
19635                            })),
19636                            ..Default::default()
19637                        })
19638                        .collect(),
19639                )))
19640            }
19641        });
19642
19643    async move {
19644        request.next().await;
19645    }
19646}
19647
19648/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19649/// given instead, which also contains an `insert` range.
19650///
19651/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19652/// that is, `replace_range.start..cursor_pos`.
19653pub fn handle_completion_request_with_insert_and_replace(
19654    cx: &mut EditorLspTestContext,
19655    marked_string: &str,
19656    completions: Vec<&'static str>,
19657    counter: Arc<AtomicUsize>,
19658) -> impl Future<Output = ()> {
19659    let complete_from_marker: TextRangeMarker = '|'.into();
19660    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19661    let (_, mut marked_ranges) = marked_text_ranges_by(
19662        marked_string,
19663        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19664    );
19665
19666    let complete_from_position =
19667        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19668    let replace_range =
19669        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19670
19671    let mut request =
19672        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19673            let completions = completions.clone();
19674            counter.fetch_add(1, atomic::Ordering::Release);
19675            async move {
19676                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19677                assert_eq!(
19678                    params.text_document_position.position, complete_from_position,
19679                    "marker `|` position doesn't match",
19680                );
19681                Ok(Some(lsp::CompletionResponse::Array(
19682                    completions
19683                        .iter()
19684                        .map(|completion_text| lsp::CompletionItem {
19685                            label: completion_text.to_string(),
19686                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19687                                lsp::InsertReplaceEdit {
19688                                    insert: lsp::Range {
19689                                        start: replace_range.start,
19690                                        end: complete_from_position,
19691                                    },
19692                                    replace: replace_range,
19693                                    new_text: completion_text.to_string(),
19694                                },
19695                            )),
19696                            ..Default::default()
19697                        })
19698                        .collect(),
19699                )))
19700            }
19701        });
19702
19703    async move {
19704        request.next().await;
19705    }
19706}
19707
19708fn handle_resolve_completion_request(
19709    cx: &mut EditorLspTestContext,
19710    edits: Option<Vec<(&'static str, &'static str)>>,
19711) -> impl Future<Output = ()> {
19712    let edits = edits.map(|edits| {
19713        edits
19714            .iter()
19715            .map(|(marked_string, new_text)| {
19716                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19717                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19718                lsp::TextEdit::new(replace_range, new_text.to_string())
19719            })
19720            .collect::<Vec<_>>()
19721    });
19722
19723    let mut request =
19724        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19725            let edits = edits.clone();
19726            async move {
19727                Ok(lsp::CompletionItem {
19728                    additional_text_edits: edits,
19729                    ..Default::default()
19730                })
19731            }
19732        });
19733
19734    async move {
19735        request.next().await;
19736    }
19737}
19738
19739pub(crate) fn update_test_language_settings(
19740    cx: &mut TestAppContext,
19741    f: impl Fn(&mut AllLanguageSettingsContent),
19742) {
19743    cx.update(|cx| {
19744        SettingsStore::update_global(cx, |store, cx| {
19745            store.update_user_settings::<AllLanguageSettings>(cx, f);
19746        });
19747    });
19748}
19749
19750pub(crate) fn update_test_project_settings(
19751    cx: &mut TestAppContext,
19752    f: impl Fn(&mut ProjectSettings),
19753) {
19754    cx.update(|cx| {
19755        SettingsStore::update_global(cx, |store, cx| {
19756            store.update_user_settings::<ProjectSettings>(cx, f);
19757        });
19758    });
19759}
19760
19761pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19762    cx.update(|cx| {
19763        assets::Assets.load_test_fonts(cx);
19764        let store = SettingsStore::test(cx);
19765        cx.set_global(store);
19766        theme::init(theme::LoadThemes::JustBase, cx);
19767        release_channel::init(SemanticVersion::default(), cx);
19768        client::init_settings(cx);
19769        language::init(cx);
19770        Project::init_settings(cx);
19771        workspace::init_settings(cx);
19772        crate::init(cx);
19773    });
19774
19775    update_test_language_settings(cx, f);
19776}
19777
19778#[track_caller]
19779fn assert_hunk_revert(
19780    not_reverted_text_with_selections: &str,
19781    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19782    expected_reverted_text_with_selections: &str,
19783    base_text: &str,
19784    cx: &mut EditorLspTestContext,
19785) {
19786    cx.set_state(not_reverted_text_with_selections);
19787    cx.set_head_text(base_text);
19788    cx.executor().run_until_parked();
19789
19790    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19791        let snapshot = editor.snapshot(window, cx);
19792        let reverted_hunk_statuses = snapshot
19793            .buffer_snapshot
19794            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19795            .map(|hunk| hunk.status().kind)
19796            .collect::<Vec<_>>();
19797
19798        editor.git_restore(&Default::default(), window, cx);
19799        reverted_hunk_statuses
19800    });
19801    cx.executor().run_until_parked();
19802    cx.assert_editor_state(expected_reverted_text_with_selections);
19803    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19804}