editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, DismissEvent, SemanticVersion, TestAppContext, UpdateGlobal,
   16    VisualTestContext, WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319#[gpui::test]
 1320fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1321    init_test(cx, |_| {});
 1322
 1323    let editor = cx.add_window(|window, cx| {
 1324        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1325        build_editor(buffer.clone(), window, cx)
 1326    });
 1327
 1328    assert_eq!('🟥'.len_utf8(), 4);
 1329    assert_eq!('α'.len_utf8(), 2);
 1330
 1331    _ = editor.update(cx, |editor, window, cx| {
 1332        editor.fold_creases(
 1333            vec![
 1334                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1337            ],
 1338            true,
 1339            window,
 1340            cx,
 1341        );
 1342        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1343
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧".len())]
 1353        );
 1354        editor.move_right(&MoveRight, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(0, "🟥🟧⋯".len())]
 1358        );
 1359
 1360        editor.move_down(&MoveDown, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯e".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "ab".len())]
 1374        );
 1375        editor.move_left(&MoveLeft, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(1, "a".len())]
 1379        );
 1380
 1381        editor.move_down(&MoveDown, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "α".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯".len())]
 1395        );
 1396        editor.move_right(&MoveRight, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(2, "αβ⋯ε".len())]
 1400        );
 1401
 1402        editor.move_up(&MoveUp, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(1, "ab⋯e".len())]
 1406        );
 1407        editor.move_down(&MoveDown, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβ⋯ε".len())]
 1411        );
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(1, "ab⋯e".len())]
 1416        );
 1417
 1418        editor.move_up(&MoveUp, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥🟧".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "🟥".len())]
 1427        );
 1428        editor.move_left(&MoveLeft, window, cx);
 1429        assert_eq!(
 1430            editor.selections.display_ranges(cx),
 1431            &[empty_range(0, "".len())]
 1432        );
 1433    });
 1434}
 1435
 1436#[gpui::test]
 1437fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1438    init_test(cx, |_| {});
 1439
 1440    let editor = cx.add_window(|window, cx| {
 1441        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1442        build_editor(buffer.clone(), window, cx)
 1443    });
 1444    _ = editor.update(cx, |editor, window, cx| {
 1445        editor.change_selections(None, window, cx, |s| {
 1446            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1447        });
 1448
 1449        // moving above start of document should move selection to start of document,
 1450        // but the next move down should still be at the original goal_x
 1451        editor.move_up(&MoveUp, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(0, "".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(1, "abcd".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(3, "abcd".len())]
 1473        );
 1474
 1475        editor.move_down(&MoveDown, window, cx);
 1476        assert_eq!(
 1477            editor.selections.display_ranges(cx),
 1478            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1479        );
 1480
 1481        // moving past end of document should not change goal_x
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_down(&MoveDown, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(5, "".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(3, "abcd".len())]
 1504        );
 1505
 1506        editor.move_up(&MoveUp, window, cx);
 1507        assert_eq!(
 1508            editor.selections.display_ranges(cx),
 1509            &[empty_range(2, "αβγ".len())]
 1510        );
 1511    });
 1512}
 1513
 1514#[gpui::test]
 1515fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1516    init_test(cx, |_| {});
 1517    let move_to_beg = MoveToBeginningOfLine {
 1518        stop_at_soft_wraps: true,
 1519        stop_at_indent: true,
 1520    };
 1521
 1522    let delete_to_beg = DeleteToBeginningOfLine {
 1523        stop_at_indent: false,
 1524    };
 1525
 1526    let move_to_end = MoveToEndOfLine {
 1527        stop_at_soft_wraps: true,
 1528    };
 1529
 1530    let editor = cx.add_window(|window, cx| {
 1531        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1532        build_editor(buffer, window, cx)
 1533    });
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.change_selections(None, window, cx, |s| {
 1536            s.select_display_ranges([
 1537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1538                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1539            ]);
 1540        });
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1571                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1572            ]
 1573        );
 1574    });
 1575
 1576    _ = editor.update(cx, |editor, window, cx| {
 1577        editor.move_to_end_of_line(&move_to_end, window, cx);
 1578        assert_eq!(
 1579            editor.selections.display_ranges(cx),
 1580            &[
 1581                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1582                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1583            ]
 1584        );
 1585    });
 1586
 1587    // Moving to the end of line again is a no-op.
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_to_end_of_line(&move_to_end, window, cx);
 1590        assert_eq!(
 1591            editor.selections.display_ranges(cx),
 1592            &[
 1593                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1594                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1595            ]
 1596        );
 1597    });
 1598
 1599    _ = editor.update(cx, |editor, window, cx| {
 1600        editor.move_left(&MoveLeft, window, cx);
 1601        editor.select_to_beginning_of_line(
 1602            &SelectToBeginningOfLine {
 1603                stop_at_soft_wraps: true,
 1604                stop_at_indent: true,
 1605            },
 1606            window,
 1607            cx,
 1608        );
 1609        assert_eq!(
 1610            editor.selections.display_ranges(cx),
 1611            &[
 1612                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1613                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1614            ]
 1615        );
 1616    });
 1617
 1618    _ = editor.update(cx, |editor, window, cx| {
 1619        editor.select_to_beginning_of_line(
 1620            &SelectToBeginningOfLine {
 1621                stop_at_soft_wraps: true,
 1622                stop_at_indent: true,
 1623            },
 1624            window,
 1625            cx,
 1626        );
 1627        assert_eq!(
 1628            editor.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635
 1636    _ = editor.update(cx, |editor, window, cx| {
 1637        editor.select_to_beginning_of_line(
 1638            &SelectToBeginningOfLine {
 1639                stop_at_soft_wraps: true,
 1640                stop_at_indent: true,
 1641            },
 1642            window,
 1643            cx,
 1644        );
 1645        assert_eq!(
 1646            editor.selections.display_ranges(cx),
 1647            &[
 1648                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1649                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1650            ]
 1651        );
 1652    });
 1653
 1654    _ = editor.update(cx, |editor, window, cx| {
 1655        editor.select_to_end_of_line(
 1656            &SelectToEndOfLine {
 1657                stop_at_soft_wraps: true,
 1658            },
 1659            window,
 1660            cx,
 1661        );
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "ab\n  de");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1678                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1679            ]
 1680        );
 1681    });
 1682
 1683    _ = editor.update(cx, |editor, window, cx| {
 1684        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1685        assert_eq!(editor.display_text(cx), "\n");
 1686        assert_eq!(
 1687            editor.selections.display_ranges(cx),
 1688            &[
 1689                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1691            ]
 1692        );
 1693    });
 1694}
 1695
 1696#[gpui::test]
 1697fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1698    init_test(cx, |_| {});
 1699    let move_to_beg = MoveToBeginningOfLine {
 1700        stop_at_soft_wraps: false,
 1701        stop_at_indent: false,
 1702    };
 1703
 1704    let move_to_end = MoveToEndOfLine {
 1705        stop_at_soft_wraps: false,
 1706    };
 1707
 1708    let editor = cx.add_window(|window, cx| {
 1709        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1710        build_editor(buffer, window, cx)
 1711    });
 1712
 1713    _ = editor.update(cx, |editor, window, cx| {
 1714        editor.set_wrap_width(Some(140.0.into()), cx);
 1715
 1716        // We expect the following lines after wrapping
 1717        // ```
 1718        // thequickbrownfox
 1719        // jumpedoverthelazydo
 1720        // gs
 1721        // ```
 1722        // The final `gs` was soft-wrapped onto a new line.
 1723        assert_eq!(
 1724            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1725            editor.display_text(cx),
 1726        );
 1727
 1728        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1729        // Start the cursor at the `k` on the first line
 1730        editor.change_selections(None, window, cx, |s| {
 1731            s.select_display_ranges([
 1732                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1733            ]);
 1734        });
 1735
 1736        // Moving to the beginning of the line should put us at the beginning of the line.
 1737        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1738        assert_eq!(
 1739            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1740            editor.selections.display_ranges(cx)
 1741        );
 1742
 1743        // Moving to the end of the line should put us at the end of the line.
 1744        editor.move_to_end_of_line(&move_to_end, window, cx);
 1745        assert_eq!(
 1746            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1747            editor.selections.display_ranges(cx)
 1748        );
 1749
 1750        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1751        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1752        editor.change_selections(None, window, cx, |s| {
 1753            s.select_display_ranges([
 1754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1755            ]);
 1756        });
 1757
 1758        // Moving to the beginning of the line should put us at the start of the second line of
 1759        // display text, i.e., the `j`.
 1760        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the beginning of the line again should be a no-op.
 1767        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772
 1773        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1774        // next display line.
 1775        editor.move_to_end_of_line(&move_to_end, window, cx);
 1776        assert_eq!(
 1777            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1778            editor.selections.display_ranges(cx)
 1779        );
 1780
 1781        // Moving to the end of the line again should be a no-op.
 1782        editor.move_to_end_of_line(&move_to_end, window, cx);
 1783        assert_eq!(
 1784            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1785            editor.selections.display_ranges(cx)
 1786        );
 1787    });
 1788}
 1789
 1790#[gpui::test]
 1791fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1792    init_test(cx, |_| {});
 1793
 1794    let move_to_beg = MoveToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let select_to_beg = SelectToBeginningOfLine {
 1800        stop_at_soft_wraps: true,
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let delete_to_beg = DeleteToBeginningOfLine {
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let move_to_end = MoveToEndOfLine {
 1809        stop_at_soft_wraps: false,
 1810    };
 1811
 1812    let editor = cx.add_window(|window, cx| {
 1813        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1814        build_editor(buffer, window, cx)
 1815    });
 1816
 1817    _ = editor.update(cx, |editor, window, cx| {
 1818        editor.change_selections(None, window, cx, |s| {
 1819            s.select_display_ranges([
 1820                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1821                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1822            ]);
 1823        });
 1824
 1825        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1826        // and the second cursor at the first non-whitespace character in the line.
 1827        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1828        assert_eq!(
 1829            editor.selections.display_ranges(cx),
 1830            &[
 1831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1833            ]
 1834        );
 1835
 1836        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1837        // and should move the second cursor to the beginning of the line.
 1838        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1839        assert_eq!(
 1840            editor.selections.display_ranges(cx),
 1841            &[
 1842                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1843                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1844            ]
 1845        );
 1846
 1847        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1848        // and should move the second cursor back to the first non-whitespace character in the line.
 1849        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1850        assert_eq!(
 1851            editor.selections.display_ranges(cx),
 1852            &[
 1853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1855            ]
 1856        );
 1857
 1858        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1859        // and to the first non-whitespace character in the line for the second cursor.
 1860        editor.move_to_end_of_line(&move_to_end, window, cx);
 1861        editor.move_left(&MoveLeft, window, cx);
 1862        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[
 1866                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1867                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1868            ]
 1869        );
 1870
 1871        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1872        // and should select to the beginning of the line for the second cursor.
 1873        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[
 1877                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1878                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1879            ]
 1880        );
 1881
 1882        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1883        // and should delete to the first non-whitespace character in the line for the second cursor.
 1884        editor.move_to_end_of_line(&move_to_end, window, cx);
 1885        editor.move_left(&MoveLeft, window, cx);
 1886        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1887        assert_eq!(editor.text(cx), "c\n  f");
 1888    });
 1889}
 1890
 1891#[gpui::test]
 1892fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1893    init_test(cx, |_| {});
 1894
 1895    let editor = cx.add_window(|window, cx| {
 1896        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1897        build_editor(buffer, window, cx)
 1898    });
 1899    _ = editor.update(cx, |editor, window, cx| {
 1900        editor.change_selections(None, window, cx, |s| {
 1901            s.select_display_ranges([
 1902                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1903                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1904            ])
 1905        });
 1906
 1907        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1908        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1923        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1930
 1931        editor.move_right(&MoveRight, window, cx);
 1932        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1933        assert_selection_ranges(
 1934            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1935            editor,
 1936            cx,
 1937        );
 1938
 1939        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1940        assert_selection_ranges(
 1941            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1942            editor,
 1943            cx,
 1944        );
 1945
 1946        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1947        assert_selection_ranges(
 1948            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1949            editor,
 1950            cx,
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1957    init_test(cx, |_| {});
 1958
 1959    let editor = cx.add_window(|window, cx| {
 1960        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1961        build_editor(buffer, window, cx)
 1962    });
 1963
 1964    _ = editor.update(cx, |editor, window, cx| {
 1965        editor.set_wrap_width(Some(140.0.into()), cx);
 1966        assert_eq!(
 1967            editor.display_text(cx),
 1968            "use one::{\n    two::three::\n    four::five\n};"
 1969        );
 1970
 1971        editor.change_selections(None, window, cx, |s| {
 1972            s.select_display_ranges([
 1973                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1974            ]);
 1975        });
 1976
 1977        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1978        assert_eq!(
 1979            editor.selections.display_ranges(cx),
 1980            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1981        );
 1982
 1983        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1984        assert_eq!(
 1985            editor.selections.display_ranges(cx),
 1986            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1987        );
 1988
 1989        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1990        assert_eq!(
 1991            editor.selections.display_ranges(cx),
 1992            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1993        );
 1994
 1995        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1996        assert_eq!(
 1997            editor.selections.display_ranges(cx),
 1998            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1999        );
 2000
 2001        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2002        assert_eq!(
 2003            editor.selections.display_ranges(cx),
 2004            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2005        );
 2006
 2007        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2008        assert_eq!(
 2009            editor.selections.display_ranges(cx),
 2010            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2011        );
 2012    });
 2013}
 2014
 2015#[gpui::test]
 2016async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2017    init_test(cx, |_| {});
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2028
 2029    cx.set_state(
 2030        &r#"ˇone
 2031        two
 2032
 2033        three
 2034        fourˇ
 2035        five
 2036
 2037        six"#
 2038            .unindent(),
 2039    );
 2040
 2041    cx.update_editor(|editor, window, cx| {
 2042        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2043    });
 2044    cx.assert_editor_state(
 2045        &r#"one
 2046        two
 2047        ˇ
 2048        three
 2049        four
 2050        five
 2051        ˇ
 2052        six"#
 2053            .unindent(),
 2054    );
 2055
 2056    cx.update_editor(|editor, window, cx| {
 2057        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"one
 2061        two
 2062
 2063        three
 2064        four
 2065        five
 2066        ˇ
 2067        sixˇ"#
 2068            .unindent(),
 2069    );
 2070
 2071    cx.update_editor(|editor, window, cx| {
 2072        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2073    });
 2074    cx.assert_editor_state(
 2075        &r#"one
 2076        two
 2077
 2078        three
 2079        four
 2080        five
 2081
 2082        sixˇ"#
 2083            .unindent(),
 2084    );
 2085
 2086    cx.update_editor(|editor, window, cx| {
 2087        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2088    });
 2089    cx.assert_editor_state(
 2090        &r#"one
 2091        two
 2092
 2093        three
 2094        four
 2095        five
 2096        ˇ
 2097        six"#
 2098            .unindent(),
 2099    );
 2100
 2101    cx.update_editor(|editor, window, cx| {
 2102        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2103    });
 2104    cx.assert_editor_state(
 2105        &r#"one
 2106        two
 2107        ˇ
 2108        three
 2109        four
 2110        five
 2111
 2112        six"#
 2113            .unindent(),
 2114    );
 2115
 2116    cx.update_editor(|editor, window, cx| {
 2117        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2118    });
 2119    cx.assert_editor_state(
 2120        &r#"ˇone
 2121        two
 2122
 2123        three
 2124        four
 2125        five
 2126
 2127        six"#
 2128            .unindent(),
 2129    );
 2130}
 2131
 2132#[gpui::test]
 2133async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135    let mut cx = EditorTestContext::new(cx).await;
 2136    let line_height = cx.editor(|editor, window, _| {
 2137        editor
 2138            .style()
 2139            .unwrap()
 2140            .text
 2141            .line_height_in_pixels(window.rem_size())
 2142    });
 2143    let window = cx.window;
 2144    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2145
 2146    cx.set_state(
 2147        r#"ˇone
 2148        two
 2149        three
 2150        four
 2151        five
 2152        six
 2153        seven
 2154        eight
 2155        nine
 2156        ten
 2157        "#,
 2158    );
 2159
 2160    cx.update_editor(|editor, window, cx| {
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 0.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 3.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 6.)
 2174        );
 2175        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2176        assert_eq!(
 2177            editor.snapshot(window, cx).scroll_position(),
 2178            gpui::Point::new(0., 3.)
 2179        );
 2180
 2181        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 1.)
 2185        );
 2186        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2187        assert_eq!(
 2188            editor.snapshot(window, cx).scroll_position(),
 2189            gpui::Point::new(0., 3.)
 2190        );
 2191    });
 2192}
 2193
 2194#[gpui::test]
 2195async fn test_autoscroll(cx: &mut TestAppContext) {
 2196    init_test(cx, |_| {});
 2197    let mut cx = EditorTestContext::new(cx).await;
 2198
 2199    let line_height = cx.update_editor(|editor, window, cx| {
 2200        editor.set_vertical_scroll_margin(2, cx);
 2201        editor
 2202            .style()
 2203            .unwrap()
 2204            .text
 2205            .line_height_in_pixels(window.rem_size())
 2206    });
 2207    let window = cx.window;
 2208    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2209
 2210    cx.set_state(
 2211        r#"ˇone
 2212            two
 2213            three
 2214            four
 2215            five
 2216            six
 2217            seven
 2218            eight
 2219            nine
 2220            ten
 2221        "#,
 2222    );
 2223    cx.update_editor(|editor, window, cx| {
 2224        assert_eq!(
 2225            editor.snapshot(window, cx).scroll_position(),
 2226            gpui::Point::new(0., 0.0)
 2227        );
 2228    });
 2229
 2230    // Add a cursor below the visible area. Since both cursors cannot fit
 2231    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2232    // allows the vertical scroll margin below that cursor.
 2233    cx.update_editor(|editor, window, cx| {
 2234        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2235            selections.select_ranges([
 2236                Point::new(0, 0)..Point::new(0, 0),
 2237                Point::new(6, 0)..Point::new(6, 0),
 2238            ]);
 2239        })
 2240    });
 2241    cx.update_editor(|editor, window, cx| {
 2242        assert_eq!(
 2243            editor.snapshot(window, cx).scroll_position(),
 2244            gpui::Point::new(0., 3.0)
 2245        );
 2246    });
 2247
 2248    // Move down. The editor cursor scrolls down to track the newest cursor.
 2249    cx.update_editor(|editor, window, cx| {
 2250        editor.move_down(&Default::default(), window, cx);
 2251    });
 2252    cx.update_editor(|editor, window, cx| {
 2253        assert_eq!(
 2254            editor.snapshot(window, cx).scroll_position(),
 2255            gpui::Point::new(0., 4.0)
 2256        );
 2257    });
 2258
 2259    // Add a cursor above the visible area. Since both cursors fit on screen,
 2260    // the editor scrolls to show both.
 2261    cx.update_editor(|editor, window, cx| {
 2262        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2263            selections.select_ranges([
 2264                Point::new(1, 0)..Point::new(1, 0),
 2265                Point::new(6, 0)..Point::new(6, 0),
 2266            ]);
 2267        })
 2268    });
 2269    cx.update_editor(|editor, window, cx| {
 2270        assert_eq!(
 2271            editor.snapshot(window, cx).scroll_position(),
 2272            gpui::Point::new(0., 1.0)
 2273        );
 2274    });
 2275}
 2276
 2277#[gpui::test]
 2278async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2279    init_test(cx, |_| {});
 2280    let mut cx = EditorTestContext::new(cx).await;
 2281
 2282    let line_height = cx.editor(|editor, window, _cx| {
 2283        editor
 2284            .style()
 2285            .unwrap()
 2286            .text
 2287            .line_height_in_pixels(window.rem_size())
 2288    });
 2289    let window = cx.window;
 2290    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2291    cx.set_state(
 2292        &r#"
 2293        ˇone
 2294        two
 2295        threeˇ
 2296        four
 2297        five
 2298        six
 2299        seven
 2300        eight
 2301        nine
 2302        ten
 2303        "#
 2304        .unindent(),
 2305    );
 2306
 2307    cx.update_editor(|editor, window, cx| {
 2308        editor.move_page_down(&MovePageDown::default(), window, cx)
 2309    });
 2310    cx.assert_editor_state(
 2311        &r#"
 2312        one
 2313        two
 2314        three
 2315        ˇfour
 2316        five
 2317        sixˇ
 2318        seven
 2319        eight
 2320        nine
 2321        ten
 2322        "#
 2323        .unindent(),
 2324    );
 2325
 2326    cx.update_editor(|editor, window, cx| {
 2327        editor.move_page_down(&MovePageDown::default(), window, cx)
 2328    });
 2329    cx.assert_editor_state(
 2330        &r#"
 2331        one
 2332        two
 2333        three
 2334        four
 2335        five
 2336        six
 2337        ˇseven
 2338        eight
 2339        nineˇ
 2340        ten
 2341        "#
 2342        .unindent(),
 2343    );
 2344
 2345    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2346    cx.assert_editor_state(
 2347        &r#"
 2348        one
 2349        two
 2350        three
 2351        ˇfour
 2352        five
 2353        sixˇ
 2354        seven
 2355        eight
 2356        nine
 2357        ten
 2358        "#
 2359        .unindent(),
 2360    );
 2361
 2362    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2363    cx.assert_editor_state(
 2364        &r#"
 2365        ˇone
 2366        two
 2367        threeˇ
 2368        four
 2369        five
 2370        six
 2371        seven
 2372        eight
 2373        nine
 2374        ten
 2375        "#
 2376        .unindent(),
 2377    );
 2378
 2379    // Test select collapsing
 2380    cx.update_editor(|editor, window, cx| {
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384    });
 2385    cx.assert_editor_state(
 2386        &r#"
 2387        one
 2388        two
 2389        three
 2390        four
 2391        five
 2392        six
 2393        seven
 2394        eight
 2395        nine
 2396        ˇten
 2397        ˇ"#
 2398        .unindent(),
 2399    );
 2400}
 2401
 2402#[gpui::test]
 2403async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2404    init_test(cx, |_| {});
 2405    let mut cx = EditorTestContext::new(cx).await;
 2406    cx.set_state("one «two threeˇ» four");
 2407    cx.update_editor(|editor, window, cx| {
 2408        editor.delete_to_beginning_of_line(
 2409            &DeleteToBeginningOfLine {
 2410                stop_at_indent: false,
 2411            },
 2412            window,
 2413            cx,
 2414        );
 2415        assert_eq!(editor.text(cx), " four");
 2416    });
 2417}
 2418
 2419#[gpui::test]
 2420fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2421    init_test(cx, |_| {});
 2422
 2423    let editor = cx.add_window(|window, cx| {
 2424        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2425        build_editor(buffer.clone(), window, cx)
 2426    });
 2427
 2428    _ = editor.update(cx, |editor, window, cx| {
 2429        editor.change_selections(None, window, cx, |s| {
 2430            s.select_display_ranges([
 2431                // an empty selection - the preceding word fragment is deleted
 2432                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2433                // characters selected - they are deleted
 2434                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2435            ])
 2436        });
 2437        editor.delete_to_previous_word_start(
 2438            &DeleteToPreviousWordStart {
 2439                ignore_newlines: false,
 2440            },
 2441            window,
 2442            cx,
 2443        );
 2444        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2445    });
 2446
 2447    _ = editor.update(cx, |editor, window, cx| {
 2448        editor.change_selections(None, window, cx, |s| {
 2449            s.select_display_ranges([
 2450                // an empty selection - the following word fragment is deleted
 2451                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2452                // characters selected - they are deleted
 2453                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2454            ])
 2455        });
 2456        editor.delete_to_next_word_end(
 2457            &DeleteToNextWordEnd {
 2458                ignore_newlines: false,
 2459            },
 2460            window,
 2461            cx,
 2462        );
 2463        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2464    });
 2465}
 2466
 2467#[gpui::test]
 2468fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2469    init_test(cx, |_| {});
 2470
 2471    let editor = cx.add_window(|window, cx| {
 2472        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2473        build_editor(buffer.clone(), window, cx)
 2474    });
 2475    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2476        ignore_newlines: false,
 2477    };
 2478    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2479        ignore_newlines: true,
 2480    };
 2481
 2482    _ = editor.update(cx, |editor, window, cx| {
 2483        editor.change_selections(None, window, cx, |s| {
 2484            s.select_display_ranges([
 2485                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2486            ])
 2487        });
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2500    });
 2501}
 2502
 2503#[gpui::test]
 2504fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2505    init_test(cx, |_| {});
 2506
 2507    let editor = cx.add_window(|window, cx| {
 2508        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2509        build_editor(buffer.clone(), window, cx)
 2510    });
 2511    let del_to_next_word_end = DeleteToNextWordEnd {
 2512        ignore_newlines: false,
 2513    };
 2514    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2515        ignore_newlines: true,
 2516    };
 2517
 2518    _ = editor.update(cx, |editor, window, cx| {
 2519        editor.change_selections(None, window, cx, |s| {
 2520            s.select_display_ranges([
 2521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2522            ])
 2523        });
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "one\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "\n   two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(
 2536            editor.buffer.read(cx).read(cx).text(),
 2537            "two\nthree\n   four"
 2538        );
 2539        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2541        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2545    });
 2546}
 2547
 2548#[gpui::test]
 2549fn test_newline(cx: &mut TestAppContext) {
 2550    init_test(cx, |_| {});
 2551
 2552    let editor = cx.add_window(|window, cx| {
 2553        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2554        build_editor(buffer.clone(), window, cx)
 2555    });
 2556
 2557    _ = editor.update(cx, |editor, window, cx| {
 2558        editor.change_selections(None, window, cx, |s| {
 2559            s.select_display_ranges([
 2560                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2562                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2563            ])
 2564        });
 2565
 2566        editor.newline(&Newline, window, cx);
 2567        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2568    });
 2569}
 2570
 2571#[gpui::test]
 2572fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|window, cx| {
 2576        let buffer = MultiBuffer::build_simple(
 2577            "
 2578                a
 2579                b(
 2580                    X
 2581                )
 2582                c(
 2583                    X
 2584                )
 2585            "
 2586            .unindent()
 2587            .as_str(),
 2588            cx,
 2589        );
 2590        let mut editor = build_editor(buffer.clone(), window, cx);
 2591        editor.change_selections(None, window, cx, |s| {
 2592            s.select_ranges([
 2593                Point::new(2, 4)..Point::new(2, 5),
 2594                Point::new(5, 4)..Point::new(5, 5),
 2595            ])
 2596        });
 2597        editor
 2598    });
 2599
 2600    _ = editor.update(cx, |editor, window, cx| {
 2601        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2602        editor.buffer.update(cx, |buffer, cx| {
 2603            buffer.edit(
 2604                [
 2605                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2606                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2607                ],
 2608                None,
 2609                cx,
 2610            );
 2611            assert_eq!(
 2612                buffer.read(cx).text(),
 2613                "
 2614                    a
 2615                    b()
 2616                    c()
 2617                "
 2618                .unindent()
 2619            );
 2620        });
 2621        assert_eq!(
 2622            editor.selections.ranges(cx),
 2623            &[
 2624                Point::new(1, 2)..Point::new(1, 2),
 2625                Point::new(2, 2)..Point::new(2, 2),
 2626            ],
 2627        );
 2628
 2629        editor.newline(&Newline, window, cx);
 2630        assert_eq!(
 2631            editor.text(cx),
 2632            "
 2633                a
 2634                b(
 2635                )
 2636                c(
 2637                )
 2638            "
 2639            .unindent()
 2640        );
 2641
 2642        // The selections are moved after the inserted newlines
 2643        assert_eq!(
 2644            editor.selections.ranges(cx),
 2645            &[
 2646                Point::new(2, 0)..Point::new(2, 0),
 2647                Point::new(4, 0)..Point::new(4, 0),
 2648            ],
 2649        );
 2650    });
 2651}
 2652
 2653#[gpui::test]
 2654async fn test_newline_above(cx: &mut TestAppContext) {
 2655    init_test(cx, |settings| {
 2656        settings.defaults.tab_size = NonZeroU32::new(4)
 2657    });
 2658
 2659    let language = Arc::new(
 2660        Language::new(
 2661            LanguageConfig::default(),
 2662            Some(tree_sitter_rust::LANGUAGE.into()),
 2663        )
 2664        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2665        .unwrap(),
 2666    );
 2667
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670    cx.set_state(indoc! {"
 2671        const a: ˇA = (
 2672 2673                «const_functionˇ»(ˇ),
 2674                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2675 2676        ˇ);ˇ
 2677    "});
 2678
 2679    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        ˇ
 2682        const a: A = (
 2683            ˇ
 2684            (
 2685                ˇ
 2686                ˇ
 2687                const_function(),
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                something_else,
 2693                ˇ
 2694            )
 2695            ˇ
 2696            ˇ
 2697        );
 2698    "});
 2699}
 2700
 2701#[gpui::test]
 2702async fn test_newline_below(cx: &mut TestAppContext) {
 2703    init_test(cx, |settings| {
 2704        settings.defaults.tab_size = NonZeroU32::new(4)
 2705    });
 2706
 2707    let language = Arc::new(
 2708        Language::new(
 2709            LanguageConfig::default(),
 2710            Some(tree_sitter_rust::LANGUAGE.into()),
 2711        )
 2712        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2713        .unwrap(),
 2714    );
 2715
 2716    let mut cx = EditorTestContext::new(cx).await;
 2717    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2718    cx.set_state(indoc! {"
 2719        const a: ˇA = (
 2720 2721                «const_functionˇ»(ˇ),
 2722                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2723 2724        ˇ);ˇ
 2725    "});
 2726
 2727    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2728    cx.assert_editor_state(indoc! {"
 2729        const a: A = (
 2730            ˇ
 2731            (
 2732                ˇ
 2733                const_function(),
 2734                ˇ
 2735                ˇ
 2736                something_else,
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741            )
 2742            ˇ
 2743        );
 2744        ˇ
 2745        ˇ
 2746    "});
 2747}
 2748
 2749#[gpui::test]
 2750async fn test_newline_comments(cx: &mut TestAppContext) {
 2751    init_test(cx, |settings| {
 2752        settings.defaults.tab_size = NonZeroU32::new(4)
 2753    });
 2754
 2755    let language = Arc::new(Language::new(
 2756        LanguageConfig {
 2757            line_comments: vec!["//".into()],
 2758            ..LanguageConfig::default()
 2759        },
 2760        None,
 2761    ));
 2762    {
 2763        let mut cx = EditorTestContext::new(cx).await;
 2764        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2765        cx.set_state(indoc! {"
 2766        // Fooˇ
 2767    "});
 2768
 2769        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2770        cx.assert_editor_state(indoc! {"
 2771        // Foo
 2772        //ˇ
 2773    "});
 2774        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2775        cx.set_state(indoc! {"
 2776        ˇ// Foo
 2777    "});
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(indoc! {"
 2780
 2781        ˇ// Foo
 2782    "});
 2783    }
 2784    // Ensure that comment continuations can be disabled.
 2785    update_test_language_settings(cx, |settings| {
 2786        settings.defaults.extend_comment_on_newline = Some(false);
 2787    });
 2788    let mut cx = EditorTestContext::new(cx).await;
 2789    cx.set_state(indoc! {"
 2790        // Fooˇ
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        ˇ
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2801    init_test(cx, |_| {});
 2802
 2803    let editor = cx.add_window(|window, cx| {
 2804        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2805        let mut editor = build_editor(buffer.clone(), window, cx);
 2806        editor.change_selections(None, window, cx, |s| {
 2807            s.select_ranges([3..4, 11..12, 19..20])
 2808        });
 2809        editor
 2810    });
 2811
 2812    _ = editor.update(cx, |editor, window, cx| {
 2813        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2814        editor.buffer.update(cx, |buffer, cx| {
 2815            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2816            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2817        });
 2818        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2819
 2820        editor.insert("Z", window, cx);
 2821        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2822
 2823        // The selections are moved after the inserted characters
 2824        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2825    });
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_tab(cx: &mut TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.tab_size = NonZeroU32::new(3)
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835    cx.set_state(indoc! {"
 2836        ˇabˇc
 2837        ˇ🏀ˇ🏀ˇefg
 2838 2839    "});
 2840    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2841    cx.assert_editor_state(indoc! {"
 2842           ˇab ˇc
 2843           ˇ🏀  ˇ🏀  ˇefg
 2844        d  ˇ
 2845    "});
 2846
 2847    cx.set_state(indoc! {"
 2848        a
 2849        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        a
 2854           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2855    "});
 2856}
 2857
 2858#[gpui::test]
 2859async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2860    init_test(cx, |_| {});
 2861
 2862    let mut cx = EditorTestContext::new(cx).await;
 2863    let language = Arc::new(
 2864        Language::new(
 2865            LanguageConfig::default(),
 2866            Some(tree_sitter_rust::LANGUAGE.into()),
 2867        )
 2868        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2869        .unwrap(),
 2870    );
 2871    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2872
 2873    // cursors that are already at the suggested indent level insert
 2874    // a soft tab. cursors that are to the left of the suggested indent
 2875    // auto-indent their line.
 2876    cx.set_state(indoc! {"
 2877        ˇ
 2878        const a: B = (
 2879            c(
 2880                d(
 2881        ˇ
 2882                )
 2883        ˇ
 2884        ˇ    )
 2885        );
 2886    "});
 2887    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889            ˇ
 2890        const a: B = (
 2891            c(
 2892                d(
 2893                    ˇ
 2894                )
 2895                ˇ
 2896            ˇ)
 2897        );
 2898    "});
 2899
 2900    // handle auto-indent when there are multiple cursors on the same line
 2901    cx.set_state(indoc! {"
 2902        const a: B = (
 2903            c(
 2904        ˇ    ˇ
 2905        ˇ    )
 2906        );
 2907    "});
 2908    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        const a: B = (
 2911            c(
 2912                ˇ
 2913            ˇ)
 2914        );
 2915    "});
 2916}
 2917
 2918#[gpui::test]
 2919async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2920    init_test(cx, |settings| {
 2921        settings.defaults.tab_size = NonZeroU32::new(3)
 2922    });
 2923
 2924    let mut cx = EditorTestContext::new(cx).await;
 2925    cx.set_state(indoc! {"
 2926         ˇ
 2927        \t ˇ
 2928        \t  ˇ
 2929        \t   ˇ
 2930         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2931    "});
 2932
 2933    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2934    cx.assert_editor_state(indoc! {"
 2935           ˇ
 2936        \t   ˇ
 2937        \t   ˇ
 2938        \t      ˇ
 2939         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2940    "});
 2941}
 2942
 2943#[gpui::test]
 2944async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2945    init_test(cx, |settings| {
 2946        settings.defaults.tab_size = NonZeroU32::new(4)
 2947    });
 2948
 2949    let language = Arc::new(
 2950        Language::new(
 2951            LanguageConfig::default(),
 2952            Some(tree_sitter_rust::LANGUAGE.into()),
 2953        )
 2954        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2955        .unwrap(),
 2956    );
 2957
 2958    let mut cx = EditorTestContext::new(cx).await;
 2959    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2960    cx.set_state(indoc! {"
 2961        fn a() {
 2962            if b {
 2963        \t ˇc
 2964            }
 2965        }
 2966    "});
 2967
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        fn a() {
 2971            if b {
 2972                ˇc
 2973            }
 2974        }
 2975    "});
 2976}
 2977
 2978#[gpui::test]
 2979async fn test_indent_outdent(cx: &mut TestAppContext) {
 2980    init_test(cx, |settings| {
 2981        settings.defaults.tab_size = NonZeroU32::new(4);
 2982    });
 2983
 2984    let mut cx = EditorTestContext::new(cx).await;
 2985
 2986    cx.set_state(indoc! {"
 2987          «oneˇ» «twoˇ»
 2988        three
 2989         four
 2990    "});
 2991    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2992    cx.assert_editor_state(indoc! {"
 2993            «oneˇ» «twoˇ»
 2994        three
 2995         four
 2996    "});
 2997
 2998    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        «oneˇ» «twoˇ»
 3001        three
 3002         four
 3003    "});
 3004
 3005    // select across line ending
 3006    cx.set_state(indoc! {"
 3007        one two
 3008        t«hree
 3009        ˇ» four
 3010    "});
 3011    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3012    cx.assert_editor_state(indoc! {"
 3013        one two
 3014            t«hree
 3015        ˇ» four
 3016    "});
 3017
 3018    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        one two
 3021        t«hree
 3022        ˇ» four
 3023    "});
 3024
 3025    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3026    cx.set_state(indoc! {"
 3027        one two
 3028        ˇthree
 3029            four
 3030    "});
 3031    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3032    cx.assert_editor_state(indoc! {"
 3033        one two
 3034            ˇthree
 3035            four
 3036    "});
 3037
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        ˇ    three
 3041            four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046        ˇthree
 3047            four
 3048    "});
 3049}
 3050
 3051#[gpui::test]
 3052async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3053    init_test(cx, |settings| {
 3054        settings.defaults.hard_tabs = Some(true);
 3055    });
 3056
 3057    let mut cx = EditorTestContext::new(cx).await;
 3058
 3059    // select two ranges on one line
 3060    cx.set_state(indoc! {"
 3061        «oneˇ» «twoˇ»
 3062        three
 3063        four
 3064    "});
 3065    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        \t«oneˇ» «twoˇ»
 3068        three
 3069        four
 3070    "});
 3071    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3072    cx.assert_editor_state(indoc! {"
 3073        \t\t«oneˇ» «twoˇ»
 3074        three
 3075        four
 3076    "});
 3077    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        \t«oneˇ» «twoˇ»
 3080        three
 3081        four
 3082    "});
 3083    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3084    cx.assert_editor_state(indoc! {"
 3085        «oneˇ» «twoˇ»
 3086        three
 3087        four
 3088    "});
 3089
 3090    // select across a line ending
 3091    cx.set_state(indoc! {"
 3092        one two
 3093        t«hree
 3094        ˇ»four
 3095    "});
 3096    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3097    cx.assert_editor_state(indoc! {"
 3098        one two
 3099        \tt«hree
 3100        ˇ»four
 3101    "});
 3102    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3103    cx.assert_editor_state(indoc! {"
 3104        one two
 3105        \t\tt«hree
 3106        ˇ»four
 3107    "});
 3108    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        one two
 3111        \tt«hree
 3112        ˇ»four
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        one two
 3117        t«hree
 3118        ˇ»four
 3119    "});
 3120
 3121    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3122    cx.set_state(indoc! {"
 3123        one two
 3124        ˇthree
 3125        four
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        one two
 3130        ˇthree
 3131        four
 3132    "});
 3133    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3134    cx.assert_editor_state(indoc! {"
 3135        one two
 3136        \tˇthree
 3137        four
 3138    "});
 3139    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3140    cx.assert_editor_state(indoc! {"
 3141        one two
 3142        ˇthree
 3143        four
 3144    "});
 3145}
 3146
 3147#[gpui::test]
 3148fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3149    init_test(cx, |settings| {
 3150        settings.languages.extend([
 3151            (
 3152                "TOML".into(),
 3153                LanguageSettingsContent {
 3154                    tab_size: NonZeroU32::new(2),
 3155                    ..Default::default()
 3156                },
 3157            ),
 3158            (
 3159                "Rust".into(),
 3160                LanguageSettingsContent {
 3161                    tab_size: NonZeroU32::new(4),
 3162                    ..Default::default()
 3163                },
 3164            ),
 3165        ]);
 3166    });
 3167
 3168    let toml_language = Arc::new(Language::new(
 3169        LanguageConfig {
 3170            name: "TOML".into(),
 3171            ..Default::default()
 3172        },
 3173        None,
 3174    ));
 3175    let rust_language = Arc::new(Language::new(
 3176        LanguageConfig {
 3177            name: "Rust".into(),
 3178            ..Default::default()
 3179        },
 3180        None,
 3181    ));
 3182
 3183    let toml_buffer =
 3184        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3185    let rust_buffer =
 3186        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3187    let multibuffer = cx.new(|cx| {
 3188        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3189        multibuffer.push_excerpts(
 3190            toml_buffer.clone(),
 3191            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3192            cx,
 3193        );
 3194        multibuffer.push_excerpts(
 3195            rust_buffer.clone(),
 3196            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3197            cx,
 3198        );
 3199        multibuffer
 3200    });
 3201
 3202    cx.add_window(|window, cx| {
 3203        let mut editor = build_editor(multibuffer, window, cx);
 3204
 3205        assert_eq!(
 3206            editor.text(cx),
 3207            indoc! {"
 3208                a = 1
 3209                b = 2
 3210
 3211                const c: usize = 3;
 3212            "}
 3213        );
 3214
 3215        select_ranges(
 3216            &mut editor,
 3217            indoc! {"
 3218                «aˇ» = 1
 3219                b = 2
 3220
 3221                «const c:ˇ» usize = 3;
 3222            "},
 3223            window,
 3224            cx,
 3225        );
 3226
 3227        editor.tab(&Tab, window, cx);
 3228        assert_text_with_selections(
 3229            &mut editor,
 3230            indoc! {"
 3231                  «aˇ» = 1
 3232                b = 2
 3233
 3234                    «const c:ˇ» usize = 3;
 3235            "},
 3236            cx,
 3237        );
 3238        editor.backtab(&Backtab, window, cx);
 3239        assert_text_with_selections(
 3240            &mut editor,
 3241            indoc! {"
 3242                «aˇ» = 1
 3243                b = 2
 3244
 3245                «const c:ˇ» usize = 3;
 3246            "},
 3247            cx,
 3248        );
 3249
 3250        editor
 3251    });
 3252}
 3253
 3254#[gpui::test]
 3255async fn test_backspace(cx: &mut TestAppContext) {
 3256    init_test(cx, |_| {});
 3257
 3258    let mut cx = EditorTestContext::new(cx).await;
 3259
 3260    // Basic backspace
 3261    cx.set_state(indoc! {"
 3262        onˇe two three
 3263        fou«rˇ» five six
 3264        seven «ˇeight nine
 3265        »ten
 3266    "});
 3267    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        oˇe two three
 3270        fouˇ five six
 3271        seven ˇten
 3272    "});
 3273
 3274    // Test backspace inside and around indents
 3275    cx.set_state(indoc! {"
 3276        zero
 3277            ˇone
 3278                ˇtwo
 3279            ˇ ˇ ˇ  three
 3280        ˇ  ˇ  four
 3281    "});
 3282    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3283    cx.assert_editor_state(indoc! {"
 3284        zero
 3285        ˇone
 3286            ˇtwo
 3287        ˇ  threeˇ  four
 3288    "});
 3289}
 3290
 3291#[gpui::test]
 3292async fn test_delete(cx: &mut TestAppContext) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296    cx.set_state(indoc! {"
 3297        onˇe two three
 3298        fou«rˇ» five six
 3299        seven «ˇeight nine
 3300        »ten
 3301    "});
 3302    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3303    cx.assert_editor_state(indoc! {"
 3304        onˇ two three
 3305        fouˇ five six
 3306        seven ˇten
 3307    "});
 3308}
 3309
 3310#[gpui::test]
 3311fn test_delete_line(cx: &mut TestAppContext) {
 3312    init_test(cx, |_| {});
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3322                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3323                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3324            ])
 3325        });
 3326        editor.delete_line(&DeleteLine, window, cx);
 3327        assert_eq!(editor.display_text(cx), "ghi");
 3328        assert_eq!(
 3329            editor.selections.display_ranges(cx),
 3330            vec![
 3331                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3332                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3333            ]
 3334        );
 3335    });
 3336
 3337    let editor = cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3339        build_editor(buffer, window, cx)
 3340    });
 3341    _ = editor.update(cx, |editor, window, cx| {
 3342        editor.change_selections(None, window, cx, |s| {
 3343            s.select_display_ranges([
 3344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3345            ])
 3346        });
 3347        editor.delete_line(&DeleteLine, window, cx);
 3348        assert_eq!(editor.display_text(cx), "ghi\n");
 3349        assert_eq!(
 3350            editor.selections.display_ranges(cx),
 3351            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3352        );
 3353    });
 3354}
 3355
 3356#[gpui::test]
 3357fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3358    init_test(cx, |_| {});
 3359
 3360    cx.add_window(|window, cx| {
 3361        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3362        let mut editor = build_editor(buffer.clone(), window, cx);
 3363        let buffer = buffer.read(cx).as_singleton().unwrap();
 3364
 3365        assert_eq!(
 3366            editor.selections.ranges::<Point>(cx),
 3367            &[Point::new(0, 0)..Point::new(0, 0)]
 3368        );
 3369
 3370        // When on single line, replace newline at end by space
 3371        editor.join_lines(&JoinLines, window, cx);
 3372        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3373        assert_eq!(
 3374            editor.selections.ranges::<Point>(cx),
 3375            &[Point::new(0, 3)..Point::new(0, 3)]
 3376        );
 3377
 3378        // When multiple lines are selected, remove newlines that are spanned by the selection
 3379        editor.change_selections(None, window, cx, |s| {
 3380            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3381        });
 3382        editor.join_lines(&JoinLines, window, cx);
 3383        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3384        assert_eq!(
 3385            editor.selections.ranges::<Point>(cx),
 3386            &[Point::new(0, 11)..Point::new(0, 11)]
 3387        );
 3388
 3389        // Undo should be transactional
 3390        editor.undo(&Undo, window, cx);
 3391        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3392        assert_eq!(
 3393            editor.selections.ranges::<Point>(cx),
 3394            &[Point::new(0, 5)..Point::new(2, 2)]
 3395        );
 3396
 3397        // When joining an empty line don't insert a space
 3398        editor.change_selections(None, window, cx, |s| {
 3399            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3400        });
 3401        editor.join_lines(&JoinLines, window, cx);
 3402        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3403        assert_eq!(
 3404            editor.selections.ranges::<Point>(cx),
 3405            [Point::new(2, 3)..Point::new(2, 3)]
 3406        );
 3407
 3408        // We can remove trailing newlines
 3409        editor.join_lines(&JoinLines, window, cx);
 3410        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3411        assert_eq!(
 3412            editor.selections.ranges::<Point>(cx),
 3413            [Point::new(2, 3)..Point::new(2, 3)]
 3414        );
 3415
 3416        // We don't blow up on the last line
 3417        editor.join_lines(&JoinLines, window, cx);
 3418        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3419        assert_eq!(
 3420            editor.selections.ranges::<Point>(cx),
 3421            [Point::new(2, 3)..Point::new(2, 3)]
 3422        );
 3423
 3424        // reset to test indentation
 3425        editor.buffer.update(cx, |buffer, cx| {
 3426            buffer.edit(
 3427                [
 3428                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3429                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3430                ],
 3431                None,
 3432                cx,
 3433            )
 3434        });
 3435
 3436        // We remove any leading spaces
 3437        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3438        editor.change_selections(None, window, cx, |s| {
 3439            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3440        });
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3443
 3444        // We don't insert a space for a line containing only spaces
 3445        editor.join_lines(&JoinLines, window, cx);
 3446        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3447
 3448        // We ignore any leading tabs
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3451
 3452        editor
 3453    });
 3454}
 3455
 3456#[gpui::test]
 3457fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    cx.add_window(|window, cx| {
 3461        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3462        let mut editor = build_editor(buffer.clone(), window, cx);
 3463        let buffer = buffer.read(cx).as_singleton().unwrap();
 3464
 3465        editor.change_selections(None, window, cx, |s| {
 3466            s.select_ranges([
 3467                Point::new(0, 2)..Point::new(1, 1),
 3468                Point::new(1, 2)..Point::new(1, 2),
 3469                Point::new(3, 1)..Point::new(3, 2),
 3470            ])
 3471        });
 3472
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3475
 3476        assert_eq!(
 3477            editor.selections.ranges::<Point>(cx),
 3478            [
 3479                Point::new(0, 7)..Point::new(0, 7),
 3480                Point::new(1, 3)..Point::new(1, 3)
 3481            ]
 3482        );
 3483        editor
 3484    });
 3485}
 3486
 3487#[gpui::test]
 3488async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3489    init_test(cx, |_| {});
 3490
 3491    let mut cx = EditorTestContext::new(cx).await;
 3492
 3493    let diff_base = r#"
 3494        Line 0
 3495        Line 1
 3496        Line 2
 3497        Line 3
 3498        "#
 3499    .unindent();
 3500
 3501    cx.set_state(
 3502        &r#"
 3503        ˇLine 0
 3504        Line 1
 3505        Line 2
 3506        Line 3
 3507        "#
 3508        .unindent(),
 3509    );
 3510
 3511    cx.set_head_text(&diff_base);
 3512    executor.run_until_parked();
 3513
 3514    // Join lines
 3515    cx.update_editor(|editor, window, cx| {
 3516        editor.join_lines(&JoinLines, window, cx);
 3517    });
 3518    executor.run_until_parked();
 3519
 3520    cx.assert_editor_state(
 3521        &r#"
 3522        Line 0ˇ Line 1
 3523        Line 2
 3524        Line 3
 3525        "#
 3526        .unindent(),
 3527    );
 3528    // Join again
 3529    cx.update_editor(|editor, window, cx| {
 3530        editor.join_lines(&JoinLines, window, cx);
 3531    });
 3532    executor.run_until_parked();
 3533
 3534    cx.assert_editor_state(
 3535        &r#"
 3536        Line 0 Line 1ˇ Line 2
 3537        Line 3
 3538        "#
 3539        .unindent(),
 3540    );
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_custom_newlines_cause_no_false_positive_diffs(
 3545    executor: BackgroundExecutor,
 3546    cx: &mut TestAppContext,
 3547) {
 3548    init_test(cx, |_| {});
 3549    let mut cx = EditorTestContext::new(cx).await;
 3550    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3551    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3552    executor.run_until_parked();
 3553
 3554    cx.update_editor(|editor, window, cx| {
 3555        let snapshot = editor.snapshot(window, cx);
 3556        assert_eq!(
 3557            snapshot
 3558                .buffer_snapshot
 3559                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3560                .collect::<Vec<_>>(),
 3561            Vec::new(),
 3562            "Should not have any diffs for files with custom newlines"
 3563        );
 3564    });
 3565}
 3566
 3567#[gpui::test]
 3568async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3569    init_test(cx, |_| {});
 3570
 3571    let mut cx = EditorTestContext::new(cx).await;
 3572
 3573    // Test sort_lines_case_insensitive()
 3574    cx.set_state(indoc! {"
 3575        «z
 3576        y
 3577        x
 3578        Z
 3579        Y
 3580        Xˇ»
 3581    "});
 3582    cx.update_editor(|e, window, cx| {
 3583        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «x
 3587        X
 3588        y
 3589        Y
 3590        z
 3591        Zˇ»
 3592    "});
 3593
 3594    // Test reverse_lines()
 3595    cx.set_state(indoc! {"
 3596        «5
 3597        4
 3598        3
 3599        2
 3600        1ˇ»
 3601    "});
 3602    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «1
 3605        2
 3606        3
 3607        4
 3608        5ˇ»
 3609    "});
 3610
 3611    // Skip testing shuffle_line()
 3612
 3613    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3614    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3615
 3616    // Don't manipulate when cursor is on single line, but expand the selection
 3617    cx.set_state(indoc! {"
 3618        ddˇdd
 3619        ccc
 3620        bb
 3621        a
 3622    "});
 3623    cx.update_editor(|e, window, cx| {
 3624        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3625    });
 3626    cx.assert_editor_state(indoc! {"
 3627        «ddddˇ»
 3628        ccc
 3629        bb
 3630        a
 3631    "});
 3632
 3633    // Basic manipulate case
 3634    // Start selection moves to column 0
 3635    // End of selection shrinks to fit shorter line
 3636    cx.set_state(indoc! {"
 3637        dd«d
 3638        ccc
 3639        bb
 3640        aaaaaˇ»
 3641    "});
 3642    cx.update_editor(|e, window, cx| {
 3643        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «aaaaa
 3647        bb
 3648        ccc
 3649        dddˇ»
 3650    "});
 3651
 3652    // Manipulate case with newlines
 3653    cx.set_state(indoc! {"
 3654        dd«d
 3655        ccc
 3656
 3657        bb
 3658        aaaaa
 3659
 3660        ˇ»
 3661    "});
 3662    cx.update_editor(|e, window, cx| {
 3663        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3664    });
 3665    cx.assert_editor_state(indoc! {"
 3666        «
 3667
 3668        aaaaa
 3669        bb
 3670        ccc
 3671        dddˇ»
 3672
 3673    "});
 3674
 3675    // Adding new line
 3676    cx.set_state(indoc! {"
 3677        aa«a
 3678        bbˇ»b
 3679    "});
 3680    cx.update_editor(|e, window, cx| {
 3681        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3682    });
 3683    cx.assert_editor_state(indoc! {"
 3684        «aaa
 3685        bbb
 3686        added_lineˇ»
 3687    "});
 3688
 3689    // Removing line
 3690    cx.set_state(indoc! {"
 3691        aa«a
 3692        bbbˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.manipulate_lines(window, cx, |lines| {
 3696            lines.pop();
 3697        })
 3698    });
 3699    cx.assert_editor_state(indoc! {"
 3700        «aaaˇ»
 3701    "});
 3702
 3703    // Removing all lines
 3704    cx.set_state(indoc! {"
 3705        aa«a
 3706        bbbˇ»
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.manipulate_lines(window, cx, |lines| {
 3710            lines.drain(..);
 3711        })
 3712    });
 3713    cx.assert_editor_state(indoc! {"
 3714        ˇ
 3715    "});
 3716}
 3717
 3718#[gpui::test]
 3719async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3720    init_test(cx, |_| {});
 3721
 3722    let mut cx = EditorTestContext::new(cx).await;
 3723
 3724    // Consider continuous selection as single selection
 3725    cx.set_state(indoc! {"
 3726        Aaa«aa
 3727        cˇ»c«c
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «Aaaaa
 3736        ccc
 3737        bb
 3738        aaaaaˇ»
 3739    "});
 3740
 3741    cx.set_state(indoc! {"
 3742        Aaa«aa
 3743        cˇ»c«c
 3744        bb
 3745        aaaˇ»aa
 3746    "});
 3747    cx.update_editor(|e, window, cx| {
 3748        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3749    });
 3750    cx.assert_editor_state(indoc! {"
 3751        «Aaaaa
 3752        ccc
 3753        bbˇ»
 3754    "});
 3755
 3756    // Consider non continuous selection as distinct dedup operations
 3757    cx.set_state(indoc! {"
 3758        «aaaaa
 3759        bb
 3760        aaaaa
 3761        aaaaaˇ»
 3762
 3763        aaa«aaˇ»
 3764    "});
 3765    cx.update_editor(|e, window, cx| {
 3766        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3767    });
 3768    cx.assert_editor_state(indoc! {"
 3769        «aaaaa
 3770        bbˇ»
 3771
 3772        «aaaaaˇ»
 3773    "});
 3774}
 3775
 3776#[gpui::test]
 3777async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3778    init_test(cx, |_| {});
 3779
 3780    let mut cx = EditorTestContext::new(cx).await;
 3781
 3782    cx.set_state(indoc! {"
 3783        «Aaa
 3784        aAa
 3785        Aaaˇ»
 3786    "});
 3787    cx.update_editor(|e, window, cx| {
 3788        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3789    });
 3790    cx.assert_editor_state(indoc! {"
 3791        «Aaa
 3792        aAaˇ»
 3793    "});
 3794
 3795    cx.set_state(indoc! {"
 3796        «Aaa
 3797        aAa
 3798        aaAˇ»
 3799    "});
 3800    cx.update_editor(|e, window, cx| {
 3801        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3802    });
 3803    cx.assert_editor_state(indoc! {"
 3804        «Aaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    // Manipulate with multiple selections on a single line
 3815    cx.set_state(indoc! {"
 3816        dd«dd
 3817        cˇ»c«c
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «aaaaa
 3826        bb
 3827        ccc
 3828        ddddˇ»
 3829    "});
 3830
 3831    // Manipulate with multiple disjoin selections
 3832    cx.set_state(indoc! {"
 3833 3834        4
 3835        3
 3836        2
 3837        1ˇ»
 3838
 3839        dd«dd
 3840        ccc
 3841        bb
 3842        aaaˇ»aa
 3843    "});
 3844    cx.update_editor(|e, window, cx| {
 3845        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3846    });
 3847    cx.assert_editor_state(indoc! {"
 3848        «1
 3849        2
 3850        3
 3851        4
 3852        5ˇ»
 3853
 3854        «aaaaa
 3855        bb
 3856        ccc
 3857        ddddˇ»
 3858    "});
 3859
 3860    // Adding lines on each selection
 3861    cx.set_state(indoc! {"
 3862 3863        1ˇ»
 3864
 3865        bb«bb
 3866        aaaˇ»aa
 3867    "});
 3868    cx.update_editor(|e, window, cx| {
 3869        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2
 3873        1
 3874        added lineˇ»
 3875
 3876        «bbbb
 3877        aaaaa
 3878        added lineˇ»
 3879    "});
 3880
 3881    // Removing lines on each selection
 3882    cx.set_state(indoc! {"
 3883 3884        1ˇ»
 3885
 3886        bb«bb
 3887        aaaˇ»aa
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.manipulate_lines(window, cx, |lines| {
 3891            lines.pop();
 3892        })
 3893    });
 3894    cx.assert_editor_state(indoc! {"
 3895        «2ˇ»
 3896
 3897        «bbbbˇ»
 3898    "});
 3899}
 3900
 3901#[gpui::test]
 3902async fn test_toggle_case(cx: &mut TestAppContext) {
 3903    init_test(cx, |_| {});
 3904
 3905    let mut cx = EditorTestContext::new(cx).await;
 3906
 3907    // If all lower case -> upper case
 3908    cx.set_state(indoc! {"
 3909        «hello worldˇ»
 3910    "});
 3911    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3912    cx.assert_editor_state(indoc! {"
 3913        «HELLO WORLDˇ»
 3914    "});
 3915
 3916    // If all upper case -> lower case
 3917    cx.set_state(indoc! {"
 3918        «HELLO WORLDˇ»
 3919    "});
 3920    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3921    cx.assert_editor_state(indoc! {"
 3922        «hello worldˇ»
 3923    "});
 3924
 3925    // If any upper case characters are identified -> lower case
 3926    // This matches JetBrains IDEs
 3927    cx.set_state(indoc! {"
 3928        «hEllo worldˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «hello worldˇ»
 3933    "});
 3934}
 3935
 3936#[gpui::test]
 3937async fn test_manipulate_text(cx: &mut TestAppContext) {
 3938    init_test(cx, |_| {});
 3939
 3940    let mut cx = EditorTestContext::new(cx).await;
 3941
 3942    // Test convert_to_upper_case()
 3943    cx.set_state(indoc! {"
 3944        «hello worldˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3947    cx.assert_editor_state(indoc! {"
 3948        «HELLO WORLDˇ»
 3949    "});
 3950
 3951    // Test convert_to_lower_case()
 3952    cx.set_state(indoc! {"
 3953        «HELLO WORLDˇ»
 3954    "});
 3955    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3956    cx.assert_editor_state(indoc! {"
 3957        «hello worldˇ»
 3958    "});
 3959
 3960    // Test multiple line, single selection case
 3961    cx.set_state(indoc! {"
 3962        «The quick brown
 3963        fox jumps over
 3964        the lazy dogˇ»
 3965    "});
 3966    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3967    cx.assert_editor_state(indoc! {"
 3968        «The Quick Brown
 3969        Fox Jumps Over
 3970        The Lazy Dogˇ»
 3971    "});
 3972
 3973    // Test multiple line, single selection case
 3974    cx.set_state(indoc! {"
 3975        «The quick brown
 3976        fox jumps over
 3977        the lazy dogˇ»
 3978    "});
 3979    cx.update_editor(|e, window, cx| {
 3980        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3981    });
 3982    cx.assert_editor_state(indoc! {"
 3983        «TheQuickBrown
 3984        FoxJumpsOver
 3985        TheLazyDogˇ»
 3986    "});
 3987
 3988    // From here on out, test more complex cases of manipulate_text()
 3989
 3990    // Test no selection case - should affect words cursors are in
 3991    // Cursor at beginning, middle, and end of word
 3992    cx.set_state(indoc! {"
 3993        ˇhello big beauˇtiful worldˇ
 3994    "});
 3995    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3996    cx.assert_editor_state(indoc! {"
 3997        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3998    "});
 3999
 4000    // Test multiple selections on a single line and across multiple lines
 4001    cx.set_state(indoc! {"
 4002        «Theˇ» quick «brown
 4003        foxˇ» jumps «overˇ»
 4004        the «lazyˇ» dog
 4005    "});
 4006    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4007    cx.assert_editor_state(indoc! {"
 4008        «THEˇ» quick «BROWN
 4009        FOXˇ» jumps «OVERˇ»
 4010        the «LAZYˇ» dog
 4011    "});
 4012
 4013    // Test case where text length grows
 4014    cx.set_state(indoc! {"
 4015        «tschüߡ»
 4016    "});
 4017    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4018    cx.assert_editor_state(indoc! {"
 4019        «TSCHÜSSˇ»
 4020    "});
 4021
 4022    // Test to make sure we don't crash when text shrinks
 4023    cx.set_state(indoc! {"
 4024        aaa_bbbˇ
 4025    "});
 4026    cx.update_editor(|e, window, cx| {
 4027        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4028    });
 4029    cx.assert_editor_state(indoc! {"
 4030        «aaaBbbˇ»
 4031    "});
 4032
 4033    // Test to make sure we all aware of the fact that each word can grow and shrink
 4034    // Final selections should be aware of this fact
 4035    cx.set_state(indoc! {"
 4036        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4037    "});
 4038    cx.update_editor(|e, window, cx| {
 4039        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4040    });
 4041    cx.assert_editor_state(indoc! {"
 4042        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4043    "});
 4044
 4045    cx.set_state(indoc! {"
 4046        «hElLo, WoRld!ˇ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| {
 4049        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4050    });
 4051    cx.assert_editor_state(indoc! {"
 4052        «HeLlO, wOrLD!ˇ»
 4053    "});
 4054}
 4055
 4056#[gpui::test]
 4057fn test_duplicate_line(cx: &mut TestAppContext) {
 4058    init_test(cx, |_| {});
 4059
 4060    let editor = cx.add_window(|window, cx| {
 4061        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4062        build_editor(buffer, window, cx)
 4063    });
 4064    _ = editor.update(cx, |editor, window, cx| {
 4065        editor.change_selections(None, window, cx, |s| {
 4066            s.select_display_ranges([
 4067                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4068                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4069                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4070                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4071            ])
 4072        });
 4073        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4074        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4075        assert_eq!(
 4076            editor.selections.display_ranges(cx),
 4077            vec![
 4078                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4079                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4080                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4081                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4082            ]
 4083        );
 4084    });
 4085
 4086    let editor = cx.add_window(|window, cx| {
 4087        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4088        build_editor(buffer, window, cx)
 4089    });
 4090    _ = editor.update(cx, |editor, window, cx| {
 4091        editor.change_selections(None, window, cx, |s| {
 4092            s.select_display_ranges([
 4093                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4094                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4095            ])
 4096        });
 4097        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4098        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4099        assert_eq!(
 4100            editor.selections.display_ranges(cx),
 4101            vec![
 4102                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4103                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4104            ]
 4105        );
 4106    });
 4107
 4108    // With `move_upwards` the selections stay in place, except for
 4109    // the lines inserted above them
 4110    let editor = cx.add_window(|window, cx| {
 4111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4112        build_editor(buffer, window, cx)
 4113    });
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.change_selections(None, window, cx, |s| {
 4116            s.select_display_ranges([
 4117                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4118                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4119                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4120                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4121            ])
 4122        });
 4123        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4124        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4125        assert_eq!(
 4126            editor.selections.display_ranges(cx),
 4127            vec![
 4128                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4129                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4130                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        editor.change_selections(None, window, cx, |s| {
 4142            s.select_display_ranges([
 4143                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4144                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4145            ])
 4146        });
 4147        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4148        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4149        assert_eq!(
 4150            editor.selections.display_ranges(cx),
 4151            vec![
 4152                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4153                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4154            ]
 4155        );
 4156    });
 4157
 4158    let editor = cx.add_window(|window, cx| {
 4159        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4160        build_editor(buffer, window, cx)
 4161    });
 4162    _ = editor.update(cx, |editor, window, cx| {
 4163        editor.change_selections(None, window, cx, |s| {
 4164            s.select_display_ranges([
 4165                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4166                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4167            ])
 4168        });
 4169        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4170        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4171        assert_eq!(
 4172            editor.selections.display_ranges(cx),
 4173            vec![
 4174                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4175                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4176            ]
 4177        );
 4178    });
 4179}
 4180
 4181#[gpui::test]
 4182fn test_move_line_up_down(cx: &mut TestAppContext) {
 4183    init_test(cx, |_| {});
 4184
 4185    let editor = cx.add_window(|window, cx| {
 4186        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4187        build_editor(buffer, window, cx)
 4188    });
 4189    _ = editor.update(cx, |editor, window, cx| {
 4190        editor.fold_creases(
 4191            vec![
 4192                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4193                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4194                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4195            ],
 4196            true,
 4197            window,
 4198            cx,
 4199        );
 4200        editor.change_selections(None, window, cx, |s| {
 4201            s.select_display_ranges([
 4202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4206            ])
 4207        });
 4208        assert_eq!(
 4209            editor.display_text(cx),
 4210            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4211        );
 4212
 4213        editor.move_line_up(&MoveLineUp, window, cx);
 4214        assert_eq!(
 4215            editor.display_text(cx),
 4216            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4217        );
 4218        assert_eq!(
 4219            editor.selections.display_ranges(cx),
 4220            vec![
 4221                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4222                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4223                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4224                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4225            ]
 4226        );
 4227    });
 4228
 4229    _ = editor.update(cx, |editor, window, cx| {
 4230        editor.move_line_down(&MoveLineDown, window, cx);
 4231        assert_eq!(
 4232            editor.display_text(cx),
 4233            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4234        );
 4235        assert_eq!(
 4236            editor.selections.display_ranges(cx),
 4237            vec![
 4238                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4239                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4240                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4241                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4242            ]
 4243        );
 4244    });
 4245
 4246    _ = editor.update(cx, |editor, window, cx| {
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248        assert_eq!(
 4249            editor.display_text(cx),
 4250            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4251        );
 4252        assert_eq!(
 4253            editor.selections.display_ranges(cx),
 4254            vec![
 4255                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4257                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4258                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4259            ]
 4260        );
 4261    });
 4262
 4263    _ = editor.update(cx, |editor, window, cx| {
 4264        editor.move_line_up(&MoveLineUp, window, cx);
 4265        assert_eq!(
 4266            editor.display_text(cx),
 4267            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4268        );
 4269        assert_eq!(
 4270            editor.selections.display_ranges(cx),
 4271            vec![
 4272                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4273                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4274                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4275                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4276            ]
 4277        );
 4278    });
 4279}
 4280
 4281#[gpui::test]
 4282fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4283    init_test(cx, |_| {});
 4284
 4285    let editor = cx.add_window(|window, cx| {
 4286        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4287        build_editor(buffer, window, cx)
 4288    });
 4289    _ = editor.update(cx, |editor, window, cx| {
 4290        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4291        editor.insert_blocks(
 4292            [BlockProperties {
 4293                style: BlockStyle::Fixed,
 4294                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4295                height: Some(1),
 4296                render: Arc::new(|_| div().into_any()),
 4297                priority: 0,
 4298            }],
 4299            Some(Autoscroll::fit()),
 4300            cx,
 4301        );
 4302        editor.change_selections(None, window, cx, |s| {
 4303            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4304        });
 4305        editor.move_line_down(&MoveLineDown, window, cx);
 4306    });
 4307}
 4308
 4309#[gpui::test]
 4310async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4311    init_test(cx, |_| {});
 4312
 4313    let mut cx = EditorTestContext::new(cx).await;
 4314    cx.set_state(
 4315        &"
 4316            ˇzero
 4317            one
 4318            two
 4319            three
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Create a four-line block that replaces three lines of text.
 4327    cx.update_editor(|editor, window, cx| {
 4328        let snapshot = editor.snapshot(window, cx);
 4329        let snapshot = &snapshot.buffer_snapshot;
 4330        let placement = BlockPlacement::Replace(
 4331            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4332        );
 4333        editor.insert_blocks(
 4334            [BlockProperties {
 4335                placement,
 4336                height: Some(4),
 4337                style: BlockStyle::Sticky,
 4338                render: Arc::new(|_| gpui::div().into_any_element()),
 4339                priority: 0,
 4340            }],
 4341            None,
 4342            cx,
 4343        );
 4344    });
 4345
 4346    // Move down so that the cursor touches the block.
 4347    cx.update_editor(|editor, window, cx| {
 4348        editor.move_down(&Default::default(), window, cx);
 4349    });
 4350    cx.assert_editor_state(
 4351        &"
 4352            zero
 4353            «one
 4354            two
 4355            threeˇ»
 4356            four
 4357            five
 4358        "
 4359        .unindent(),
 4360    );
 4361
 4362    // Move down past the block.
 4363    cx.update_editor(|editor, window, cx| {
 4364        editor.move_down(&Default::default(), window, cx);
 4365    });
 4366    cx.assert_editor_state(
 4367        &"
 4368            zero
 4369            one
 4370            two
 4371            three
 4372            ˇfour
 4373            five
 4374        "
 4375        .unindent(),
 4376    );
 4377}
 4378
 4379#[gpui::test]
 4380fn test_transpose(cx: &mut TestAppContext) {
 4381    init_test(cx, |_| {});
 4382
 4383    _ = cx.add_window(|window, cx| {
 4384        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4385        editor.set_style(EditorStyle::default(), window, cx);
 4386        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4387        editor.transpose(&Default::default(), window, cx);
 4388        assert_eq!(editor.text(cx), "bac");
 4389        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4390
 4391        editor.transpose(&Default::default(), window, cx);
 4392        assert_eq!(editor.text(cx), "bca");
 4393        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4394
 4395        editor.transpose(&Default::default(), window, cx);
 4396        assert_eq!(editor.text(cx), "bac");
 4397        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4398
 4399        editor
 4400    });
 4401
 4402    _ = cx.add_window(|window, cx| {
 4403        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4404        editor.set_style(EditorStyle::default(), window, cx);
 4405        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "acb\nde");
 4408        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4409
 4410        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "acbd\ne");
 4413        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "acbde\n");
 4417        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4418
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "acbd\ne");
 4421        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4422
 4423        editor
 4424    });
 4425
 4426    _ = cx.add_window(|window, cx| {
 4427        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4428        editor.set_style(EditorStyle::default(), window, cx);
 4429        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4430        editor.transpose(&Default::default(), window, cx);
 4431        assert_eq!(editor.text(cx), "bacd\ne");
 4432        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4433
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "bcade\n");
 4436        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4437
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "bcda\ne");
 4440        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4441
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "bcade\n");
 4444        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4445
 4446        editor.transpose(&Default::default(), window, cx);
 4447        assert_eq!(editor.text(cx), "bcaed\n");
 4448        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4449
 4450        editor
 4451    });
 4452
 4453    _ = cx.add_window(|window, cx| {
 4454        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4455        editor.set_style(EditorStyle::default(), window, cx);
 4456        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4457        editor.transpose(&Default::default(), window, cx);
 4458        assert_eq!(editor.text(cx), "🏀🍐✋");
 4459        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4460
 4461        editor.transpose(&Default::default(), window, cx);
 4462        assert_eq!(editor.text(cx), "🏀✋🍐");
 4463        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4464
 4465        editor.transpose(&Default::default(), window, cx);
 4466        assert_eq!(editor.text(cx), "🏀🍐✋");
 4467        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4468
 4469        editor
 4470    });
 4471}
 4472
 4473#[gpui::test]
 4474async fn test_rewrap(cx: &mut TestAppContext) {
 4475    init_test(cx, |settings| {
 4476        settings.languages.extend([
 4477            (
 4478                "Markdown".into(),
 4479                LanguageSettingsContent {
 4480                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4481                    ..Default::default()
 4482                },
 4483            ),
 4484            (
 4485                "Plain Text".into(),
 4486                LanguageSettingsContent {
 4487                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4488                    ..Default::default()
 4489                },
 4490            ),
 4491        ])
 4492    });
 4493
 4494    let mut cx = EditorTestContext::new(cx).await;
 4495
 4496    let language_with_c_comments = Arc::new(Language::new(
 4497        LanguageConfig {
 4498            line_comments: vec!["// ".into()],
 4499            ..LanguageConfig::default()
 4500        },
 4501        None,
 4502    ));
 4503    let language_with_pound_comments = Arc::new(Language::new(
 4504        LanguageConfig {
 4505            line_comments: vec!["# ".into()],
 4506            ..LanguageConfig::default()
 4507        },
 4508        None,
 4509    ));
 4510    let markdown_language = Arc::new(Language::new(
 4511        LanguageConfig {
 4512            name: "Markdown".into(),
 4513            ..LanguageConfig::default()
 4514        },
 4515        None,
 4516    ));
 4517    let language_with_doc_comments = Arc::new(Language::new(
 4518        LanguageConfig {
 4519            line_comments: vec!["// ".into(), "/// ".into()],
 4520            ..LanguageConfig::default()
 4521        },
 4522        Some(tree_sitter_rust::LANGUAGE.into()),
 4523    ));
 4524
 4525    let plaintext_language = Arc::new(Language::new(
 4526        LanguageConfig {
 4527            name: "Plain Text".into(),
 4528            ..LanguageConfig::default()
 4529        },
 4530        None,
 4531    ));
 4532
 4533    assert_rewrap(
 4534        indoc! {"
 4535            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4536        "},
 4537        indoc! {"
 4538            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4539            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4540            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4541            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4542            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4543            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4544            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4545            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4546            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4547            // porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        language_with_c_comments.clone(),
 4550        &mut cx,
 4551    );
 4552
 4553    // Test that rewrapping works inside of a selection
 4554    assert_rewrap(
 4555        indoc! {"
 4556            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4557        "},
 4558        indoc! {"
 4559            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4560            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4561            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4562            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4563            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4564            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4565            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4566            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4567            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4568            // porttitor id. Aliquam id accumsan eros.ˇ»
 4569        "},
 4570        language_with_c_comments.clone(),
 4571        &mut cx,
 4572    );
 4573
 4574    // Test that cursors that expand to the same region are collapsed.
 4575    assert_rewrap(
 4576        indoc! {"
 4577            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4578            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4579            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4580            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4581        "},
 4582        indoc! {"
 4583            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4584            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4585            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4586            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4587            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4588            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4589            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4590            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4591            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4592            // porttitor id. Aliquam id accumsan eros.
 4593        "},
 4594        language_with_c_comments.clone(),
 4595        &mut cx,
 4596    );
 4597
 4598    // Test that non-contiguous selections are treated separately.
 4599    assert_rewrap(
 4600        indoc! {"
 4601            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4602            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4603            //
 4604            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4605            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4606        "},
 4607        indoc! {"
 4608            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4609            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4610            // auctor, eu lacinia sapien scelerisque.
 4611            //
 4612            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4613            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4614            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4615            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4616            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4617            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4618            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4619        "},
 4620        language_with_c_comments.clone(),
 4621        &mut cx,
 4622    );
 4623
 4624    // Test that different comment prefixes are supported.
 4625    assert_rewrap(
 4626        indoc! {"
 4627            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4628        "},
 4629        indoc! {"
 4630            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4631            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4632            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4633            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4634            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4635            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4636            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4637            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4638            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4639            # accumsan eros.
 4640        "},
 4641        language_with_pound_comments.clone(),
 4642        &mut cx,
 4643    );
 4644
 4645    // Test that rewrapping is ignored outside of comments in most languages.
 4646    assert_rewrap(
 4647        indoc! {"
 4648            /// Adds two numbers.
 4649            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4650            fn add(a: u32, b: u32) -> u32 {
 4651                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4652            }
 4653        "},
 4654        indoc! {"
 4655            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4656            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4657            fn add(a: u32, b: u32) -> u32 {
 4658                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4659            }
 4660        "},
 4661        language_with_doc_comments.clone(),
 4662        &mut cx,
 4663    );
 4664
 4665    // Test that rewrapping works in Markdown and Plain Text languages.
 4666    assert_rewrap(
 4667        indoc! {"
 4668            # Hello
 4669
 4670            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4671        "},
 4672        indoc! {"
 4673            # Hello
 4674
 4675            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4676            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4677            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4678            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4679            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4680            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4681            Integer sit amet scelerisque nisi.
 4682        "},
 4683        markdown_language,
 4684        &mut cx,
 4685    );
 4686
 4687    assert_rewrap(
 4688        indoc! {"
 4689            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4690        "},
 4691        indoc! {"
 4692            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4693            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4694            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4695            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4696            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4697            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4698            Integer sit amet scelerisque nisi.
 4699        "},
 4700        plaintext_language,
 4701        &mut cx,
 4702    );
 4703
 4704    // Test rewrapping unaligned comments in a selection.
 4705    assert_rewrap(
 4706        indoc! {"
 4707            fn foo() {
 4708                if true {
 4709            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4710            // Praesent semper egestas tellus id dignissim.ˇ»
 4711                    do_something();
 4712                } else {
 4713                    //
 4714                }
 4715            }
 4716        "},
 4717        indoc! {"
 4718            fn foo() {
 4719                if true {
 4720            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4721                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4722                    // egestas tellus id dignissim.ˇ»
 4723                    do_something();
 4724                } else {
 4725                    //
 4726                }
 4727            }
 4728        "},
 4729        language_with_doc_comments.clone(),
 4730        &mut cx,
 4731    );
 4732
 4733    assert_rewrap(
 4734        indoc! {"
 4735            fn foo() {
 4736                if true {
 4737            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4738            // Praesent semper egestas tellus id dignissim.»
 4739                    do_something();
 4740                } else {
 4741                    //
 4742                }
 4743
 4744            }
 4745        "},
 4746        indoc! {"
 4747            fn foo() {
 4748                if true {
 4749            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4750                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4751                    // egestas tellus id dignissim.»
 4752                    do_something();
 4753                } else {
 4754                    //
 4755                }
 4756
 4757            }
 4758        "},
 4759        language_with_doc_comments.clone(),
 4760        &mut cx,
 4761    );
 4762
 4763    #[track_caller]
 4764    fn assert_rewrap(
 4765        unwrapped_text: &str,
 4766        wrapped_text: &str,
 4767        language: Arc<Language>,
 4768        cx: &mut EditorTestContext,
 4769    ) {
 4770        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4771        cx.set_state(unwrapped_text);
 4772        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4773        cx.assert_editor_state(wrapped_text);
 4774    }
 4775}
 4776
 4777#[gpui::test]
 4778async fn test_hard_wrap(cx: &mut TestAppContext) {
 4779    init_test(cx, |_| {});
 4780    let mut cx = EditorTestContext::new(cx).await;
 4781
 4782    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4783    cx.update_editor(|editor, _, cx| {
 4784        editor.set_hard_wrap(Some(14), cx);
 4785    });
 4786
 4787    cx.set_state(indoc!(
 4788        "
 4789        one two three ˇ
 4790        "
 4791    ));
 4792    cx.simulate_input("four");
 4793    cx.run_until_parked();
 4794
 4795    cx.assert_editor_state(indoc!(
 4796        "
 4797        one two three
 4798        fourˇ
 4799        "
 4800    ));
 4801
 4802    cx.update_editor(|editor, window, cx| {
 4803        editor.newline(&Default::default(), window, cx);
 4804    });
 4805    cx.run_until_parked();
 4806    cx.assert_editor_state(indoc!(
 4807        "
 4808        one two three
 4809        four
 4810        ˇ
 4811        "
 4812    ));
 4813
 4814    cx.simulate_input("five");
 4815    cx.run_until_parked();
 4816    cx.assert_editor_state(indoc!(
 4817        "
 4818        one two three
 4819        four
 4820        fiveˇ
 4821        "
 4822    ));
 4823
 4824    cx.update_editor(|editor, window, cx| {
 4825        editor.newline(&Default::default(), window, cx);
 4826    });
 4827    cx.run_until_parked();
 4828    cx.simulate_input("# ");
 4829    cx.run_until_parked();
 4830    cx.assert_editor_state(indoc!(
 4831        "
 4832        one two three
 4833        four
 4834        five
 4835        # ˇ
 4836        "
 4837    ));
 4838
 4839    cx.update_editor(|editor, window, cx| {
 4840        editor.newline(&Default::default(), window, cx);
 4841    });
 4842    cx.run_until_parked();
 4843    cx.assert_editor_state(indoc!(
 4844        "
 4845        one two three
 4846        four
 4847        five
 4848        #\x20
 4849 4850        "
 4851    ));
 4852
 4853    cx.simulate_input(" 6");
 4854    cx.run_until_parked();
 4855    cx.assert_editor_state(indoc!(
 4856        "
 4857        one two three
 4858        four
 4859        five
 4860        #
 4861        # 6ˇ
 4862        "
 4863    ));
 4864}
 4865
 4866#[gpui::test]
 4867async fn test_clipboard(cx: &mut TestAppContext) {
 4868    init_test(cx, |_| {});
 4869
 4870    let mut cx = EditorTestContext::new(cx).await;
 4871
 4872    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4873    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4874    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4875
 4876    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4877    cx.set_state("two ˇfour ˇsix ˇ");
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4880
 4881    // Paste again but with only two cursors. Since the number of cursors doesn't
 4882    // match the number of slices in the clipboard, the entire clipboard text
 4883    // is pasted at each cursor.
 4884    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4885    cx.update_editor(|e, window, cx| {
 4886        e.handle_input("( ", window, cx);
 4887        e.paste(&Paste, window, cx);
 4888        e.handle_input(") ", window, cx);
 4889    });
 4890    cx.assert_editor_state(
 4891        &([
 4892            "( one✅ ",
 4893            "three ",
 4894            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4895            "three ",
 4896            "five ) ˇ",
 4897        ]
 4898        .join("\n")),
 4899    );
 4900
 4901    // Cut with three selections, one of which is full-line.
 4902    cx.set_state(indoc! {"
 4903        1«2ˇ»3
 4904        4ˇ567
 4905        «8ˇ»9"});
 4906    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4907    cx.assert_editor_state(indoc! {"
 4908        1ˇ3
 4909        ˇ9"});
 4910
 4911    // Paste with three selections, noticing how the copied selection that was full-line
 4912    // gets inserted before the second cursor.
 4913    cx.set_state(indoc! {"
 4914        1ˇ3
 4915 4916        «oˇ»ne"});
 4917    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        12ˇ3
 4920        4567
 4921 4922        8ˇne"});
 4923
 4924    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4925    cx.set_state(indoc! {"
 4926        The quick brown
 4927        fox juˇmps over
 4928        the lazy dog"});
 4929    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4930    assert_eq!(
 4931        cx.read_from_clipboard()
 4932            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4933        Some("fox jumps over\n".to_string())
 4934    );
 4935
 4936    // Paste with three selections, noticing how the copied full-line selection is inserted
 4937    // before the empty selections but replaces the selection that is non-empty.
 4938    cx.set_state(indoc! {"
 4939        Tˇhe quick brown
 4940        «foˇ»x jumps over
 4941        tˇhe lazy dog"});
 4942    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4943    cx.assert_editor_state(indoc! {"
 4944        fox jumps over
 4945        Tˇhe quick brown
 4946        fox jumps over
 4947        ˇx jumps over
 4948        fox jumps over
 4949        tˇhe lazy dog"});
 4950}
 4951
 4952#[gpui::test]
 4953async fn test_copy_trim(cx: &mut TestAppContext) {
 4954    init_test(cx, |_| {});
 4955
 4956    let mut cx = EditorTestContext::new(cx).await;
 4957    cx.set_state(
 4958        r#"            «for selection in selections.iter() {
 4959            let mut start = selection.start;
 4960            let mut end = selection.end;
 4961            let is_entire_line = selection.is_empty();
 4962            if is_entire_line {
 4963                start = Point::new(start.row, 0);ˇ»
 4964                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4965            }
 4966        "#,
 4967    );
 4968    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4969    assert_eq!(
 4970        cx.read_from_clipboard()
 4971            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4972        Some(
 4973            "for selection in selections.iter() {
 4974            let mut start = selection.start;
 4975            let mut end = selection.end;
 4976            let is_entire_line = selection.is_empty();
 4977            if is_entire_line {
 4978                start = Point::new(start.row, 0);"
 4979                .to_string()
 4980        ),
 4981        "Regular copying preserves all indentation selected",
 4982    );
 4983    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4984    assert_eq!(
 4985        cx.read_from_clipboard()
 4986            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4987        Some(
 4988            "for selection in selections.iter() {
 4989let mut start = selection.start;
 4990let mut end = selection.end;
 4991let is_entire_line = selection.is_empty();
 4992if is_entire_line {
 4993    start = Point::new(start.row, 0);"
 4994                .to_string()
 4995        ),
 4996        "Copying with stripping should strip all leading whitespaces"
 4997    );
 4998
 4999    cx.set_state(
 5000        r#"       «     for selection in selections.iter() {
 5001            let mut start = selection.start;
 5002            let mut end = selection.end;
 5003            let is_entire_line = selection.is_empty();
 5004            if is_entire_line {
 5005                start = Point::new(start.row, 0);ˇ»
 5006                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5007            }
 5008        "#,
 5009    );
 5010    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5011    assert_eq!(
 5012        cx.read_from_clipboard()
 5013            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5014        Some(
 5015            "     for selection in selections.iter() {
 5016            let mut start = selection.start;
 5017            let mut end = selection.end;
 5018            let is_entire_line = selection.is_empty();
 5019            if is_entire_line {
 5020                start = Point::new(start.row, 0);"
 5021                .to_string()
 5022        ),
 5023        "Regular copying preserves all indentation selected",
 5024    );
 5025    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5026    assert_eq!(
 5027        cx.read_from_clipboard()
 5028            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5029        Some(
 5030            "for selection in selections.iter() {
 5031let mut start = selection.start;
 5032let mut end = selection.end;
 5033let is_entire_line = selection.is_empty();
 5034if is_entire_line {
 5035    start = Point::new(start.row, 0);"
 5036                .to_string()
 5037        ),
 5038        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5039    );
 5040
 5041    cx.set_state(
 5042        r#"       «ˇ     for selection in selections.iter() {
 5043            let mut start = selection.start;
 5044            let mut end = selection.end;
 5045            let is_entire_line = selection.is_empty();
 5046            if is_entire_line {
 5047                start = Point::new(start.row, 0);»
 5048                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5049            }
 5050        "#,
 5051    );
 5052    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5053    assert_eq!(
 5054        cx.read_from_clipboard()
 5055            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5056        Some(
 5057            "     for selection in selections.iter() {
 5058            let mut start = selection.start;
 5059            let mut end = selection.end;
 5060            let is_entire_line = selection.is_empty();
 5061            if is_entire_line {
 5062                start = Point::new(start.row, 0);"
 5063                .to_string()
 5064        ),
 5065        "Regular copying for reverse selection works the same",
 5066    );
 5067    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5068    assert_eq!(
 5069        cx.read_from_clipboard()
 5070            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5071        Some(
 5072            "for selection in selections.iter() {
 5073let mut start = selection.start;
 5074let mut end = selection.end;
 5075let is_entire_line = selection.is_empty();
 5076if is_entire_line {
 5077    start = Point::new(start.row, 0);"
 5078                .to_string()
 5079        ),
 5080        "Copying with stripping for reverse selection works the same"
 5081    );
 5082
 5083    cx.set_state(
 5084        r#"            for selection «in selections.iter() {
 5085            let mut start = selection.start;
 5086            let mut end = selection.end;
 5087            let is_entire_line = selection.is_empty();
 5088            if is_entire_line {
 5089                start = Point::new(start.row, 0);ˇ»
 5090                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5091            }
 5092        "#,
 5093    );
 5094    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5095    assert_eq!(
 5096        cx.read_from_clipboard()
 5097            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5098        Some(
 5099            "in selections.iter() {
 5100            let mut start = selection.start;
 5101            let mut end = selection.end;
 5102            let is_entire_line = selection.is_empty();
 5103            if is_entire_line {
 5104                start = Point::new(start.row, 0);"
 5105                .to_string()
 5106        ),
 5107        "When selecting past the indent, the copying works as usual",
 5108    );
 5109    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5110    assert_eq!(
 5111        cx.read_from_clipboard()
 5112            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5113        Some(
 5114            "in selections.iter() {
 5115            let mut start = selection.start;
 5116            let mut end = selection.end;
 5117            let is_entire_line = selection.is_empty();
 5118            if is_entire_line {
 5119                start = Point::new(start.row, 0);"
 5120                .to_string()
 5121        ),
 5122        "When selecting past the indent, nothing is trimmed"
 5123    );
 5124
 5125    cx.set_state(
 5126        r#"            «for selection in selections.iter() {
 5127            let mut start = selection.start;
 5128
 5129            let mut end = selection.end;
 5130            let is_entire_line = selection.is_empty();
 5131            if is_entire_line {
 5132                start = Point::new(start.row, 0);
 5133ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5134            }
 5135        "#,
 5136    );
 5137    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5138    assert_eq!(
 5139        cx.read_from_clipboard()
 5140            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5141        Some(
 5142            "for selection in selections.iter() {
 5143let mut start = selection.start;
 5144
 5145let mut end = selection.end;
 5146let is_entire_line = selection.is_empty();
 5147if is_entire_line {
 5148    start = Point::new(start.row, 0);
 5149"
 5150            .to_string()
 5151        ),
 5152        "Copying with stripping should ignore empty lines"
 5153    );
 5154}
 5155
 5156#[gpui::test]
 5157async fn test_paste_multiline(cx: &mut TestAppContext) {
 5158    init_test(cx, |_| {});
 5159
 5160    let mut cx = EditorTestContext::new(cx).await;
 5161    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5162
 5163    // Cut an indented block, without the leading whitespace.
 5164    cx.set_state(indoc! {"
 5165        const a: B = (
 5166            c(),
 5167            «d(
 5168                e,
 5169                f
 5170            )ˇ»
 5171        );
 5172    "});
 5173    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5174    cx.assert_editor_state(indoc! {"
 5175        const a: B = (
 5176            c(),
 5177            ˇ
 5178        );
 5179    "});
 5180
 5181    // Paste it at the same position.
 5182    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5183    cx.assert_editor_state(indoc! {"
 5184        const a: B = (
 5185            c(),
 5186            d(
 5187                e,
 5188                f
 5189 5190        );
 5191    "});
 5192
 5193    // Paste it at a line with a lower indent level.
 5194    cx.set_state(indoc! {"
 5195        ˇ
 5196        const a: B = (
 5197            c(),
 5198        );
 5199    "});
 5200    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5201    cx.assert_editor_state(indoc! {"
 5202        d(
 5203            e,
 5204            f
 5205 5206        const a: B = (
 5207            c(),
 5208        );
 5209    "});
 5210
 5211    // Cut an indented block, with the leading whitespace.
 5212    cx.set_state(indoc! {"
 5213        const a: B = (
 5214            c(),
 5215        «    d(
 5216                e,
 5217                f
 5218            )
 5219        ˇ»);
 5220    "});
 5221    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5222    cx.assert_editor_state(indoc! {"
 5223        const a: B = (
 5224            c(),
 5225        ˇ);
 5226    "});
 5227
 5228    // Paste it at the same position.
 5229    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5230    cx.assert_editor_state(indoc! {"
 5231        const a: B = (
 5232            c(),
 5233            d(
 5234                e,
 5235                f
 5236            )
 5237        ˇ);
 5238    "});
 5239
 5240    // Paste it at a line with a higher indent level.
 5241    cx.set_state(indoc! {"
 5242        const a: B = (
 5243            c(),
 5244            d(
 5245                e,
 5246 5247            )
 5248        );
 5249    "});
 5250    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5251    cx.assert_editor_state(indoc! {"
 5252        const a: B = (
 5253            c(),
 5254            d(
 5255                e,
 5256                f    d(
 5257                    e,
 5258                    f
 5259                )
 5260        ˇ
 5261            )
 5262        );
 5263    "});
 5264
 5265    // Copy an indented block, starting mid-line
 5266    cx.set_state(indoc! {"
 5267        const a: B = (
 5268            c(),
 5269            somethin«g(
 5270                e,
 5271                f
 5272            )ˇ»
 5273        );
 5274    "});
 5275    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5276
 5277    // Paste it on a line with a lower indent level
 5278    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5279    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5280    cx.assert_editor_state(indoc! {"
 5281        const a: B = (
 5282            c(),
 5283            something(
 5284                e,
 5285                f
 5286            )
 5287        );
 5288        g(
 5289            e,
 5290            f
 5291"});
 5292}
 5293
 5294#[gpui::test]
 5295async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5296    init_test(cx, |_| {});
 5297
 5298    cx.write_to_clipboard(ClipboardItem::new_string(
 5299        "    d(\n        e\n    );\n".into(),
 5300    ));
 5301
 5302    let mut cx = EditorTestContext::new(cx).await;
 5303    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5304
 5305    cx.set_state(indoc! {"
 5306        fn a() {
 5307            b();
 5308            if c() {
 5309                ˇ
 5310            }
 5311        }
 5312    "});
 5313
 5314    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5315    cx.assert_editor_state(indoc! {"
 5316        fn a() {
 5317            b();
 5318            if c() {
 5319                d(
 5320                    e
 5321                );
 5322        ˇ
 5323            }
 5324        }
 5325    "});
 5326
 5327    cx.set_state(indoc! {"
 5328        fn a() {
 5329            b();
 5330            ˇ
 5331        }
 5332    "});
 5333
 5334    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5335    cx.assert_editor_state(indoc! {"
 5336        fn a() {
 5337            b();
 5338            d(
 5339                e
 5340            );
 5341        ˇ
 5342        }
 5343    "});
 5344}
 5345
 5346#[gpui::test]
 5347fn test_select_all(cx: &mut TestAppContext) {
 5348    init_test(cx, |_| {});
 5349
 5350    let editor = cx.add_window(|window, cx| {
 5351        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5352        build_editor(buffer, window, cx)
 5353    });
 5354    _ = editor.update(cx, |editor, window, cx| {
 5355        editor.select_all(&SelectAll, window, cx);
 5356        assert_eq!(
 5357            editor.selections.display_ranges(cx),
 5358            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5359        );
 5360    });
 5361}
 5362
 5363#[gpui::test]
 5364fn test_select_line(cx: &mut TestAppContext) {
 5365    init_test(cx, |_| {});
 5366
 5367    let editor = cx.add_window(|window, cx| {
 5368        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5369        build_editor(buffer, window, cx)
 5370    });
 5371    _ = editor.update(cx, |editor, window, cx| {
 5372        editor.change_selections(None, window, cx, |s| {
 5373            s.select_display_ranges([
 5374                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5375                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5376                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5377                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5378            ])
 5379        });
 5380        editor.select_line(&SelectLine, window, cx);
 5381        assert_eq!(
 5382            editor.selections.display_ranges(cx),
 5383            vec![
 5384                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5385                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5386            ]
 5387        );
 5388    });
 5389
 5390    _ = editor.update(cx, |editor, window, cx| {
 5391        editor.select_line(&SelectLine, window, cx);
 5392        assert_eq!(
 5393            editor.selections.display_ranges(cx),
 5394            vec![
 5395                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5396                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5397            ]
 5398        );
 5399    });
 5400
 5401    _ = editor.update(cx, |editor, window, cx| {
 5402        editor.select_line(&SelectLine, window, cx);
 5403        assert_eq!(
 5404            editor.selections.display_ranges(cx),
 5405            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5406        );
 5407    });
 5408}
 5409
 5410#[gpui::test]
 5411async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5412    init_test(cx, |_| {});
 5413    let mut cx = EditorTestContext::new(cx).await;
 5414
 5415    #[track_caller]
 5416    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5417        cx.set_state(initial_state);
 5418        cx.update_editor(|e, window, cx| {
 5419            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5420        });
 5421        cx.assert_editor_state(expected_state);
 5422    }
 5423
 5424    // Selection starts and ends at the middle of lines, left-to-right
 5425    test(
 5426        &mut cx,
 5427        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5428        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5429    );
 5430    // Same thing, right-to-left
 5431    test(
 5432        &mut cx,
 5433        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5434        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5435    );
 5436
 5437    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5438    test(
 5439        &mut cx,
 5440        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5441        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5442    );
 5443    // Same thing, right-to-left
 5444    test(
 5445        &mut cx,
 5446        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5447        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5448    );
 5449
 5450    // Whole buffer, left-to-right, last line ends with newline
 5451    test(
 5452        &mut cx,
 5453        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5454        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5455    );
 5456    // Same thing, right-to-left
 5457    test(
 5458        &mut cx,
 5459        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5460        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5461    );
 5462
 5463    // Starts at the end of a line, ends at the start of another
 5464    test(
 5465        &mut cx,
 5466        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5467        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5468    );
 5469}
 5470
 5471#[gpui::test]
 5472async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5473    init_test(cx, |_| {});
 5474
 5475    let editor = cx.add_window(|window, cx| {
 5476        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5477        build_editor(buffer, window, cx)
 5478    });
 5479
 5480    // setup
 5481    _ = editor.update(cx, |editor, window, cx| {
 5482        editor.fold_creases(
 5483            vec![
 5484                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5485                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5486                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5487            ],
 5488            true,
 5489            window,
 5490            cx,
 5491        );
 5492        assert_eq!(
 5493            editor.display_text(cx),
 5494            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5495        );
 5496    });
 5497
 5498    _ = editor.update(cx, |editor, window, cx| {
 5499        editor.change_selections(None, window, cx, |s| {
 5500            s.select_display_ranges([
 5501                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5502                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5503                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5504                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5505            ])
 5506        });
 5507        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5508        assert_eq!(
 5509            editor.display_text(cx),
 5510            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5511        );
 5512    });
 5513    EditorTestContext::for_editor(editor, cx)
 5514        .await
 5515        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5516
 5517    _ = editor.update(cx, |editor, window, cx| {
 5518        editor.change_selections(None, window, cx, |s| {
 5519            s.select_display_ranges([
 5520                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5521            ])
 5522        });
 5523        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5524        assert_eq!(
 5525            editor.display_text(cx),
 5526            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5527        );
 5528        assert_eq!(
 5529            editor.selections.display_ranges(cx),
 5530            [
 5531                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5532                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5533                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5534                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5535                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5536                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5537                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5538            ]
 5539        );
 5540    });
 5541    EditorTestContext::for_editor(editor, cx)
 5542        .await
 5543        .assert_editor_state(
 5544            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5545        );
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    cx.set_state(indoc!(
 5555        r#"abc
 5556           defˇghi
 5557
 5558           jk
 5559           nlmo
 5560           "#
 5561    ));
 5562
 5563    cx.update_editor(|editor, window, cx| {
 5564        editor.add_selection_above(&Default::default(), window, cx);
 5565    });
 5566
 5567    cx.assert_editor_state(indoc!(
 5568        r#"abcˇ
 5569           defˇghi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_above(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abcˇ
 5582            defˇghi
 5583
 5584            jk
 5585            nlmo
 5586            "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           defˇghi
 5596
 5597           jk
 5598           nlmo
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.undo_selection(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abcˇ
 5608           defˇghi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.redo_selection(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           defˇghi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    cx.update_editor(|editor, window, cx| {
 5629        editor.add_selection_below(&Default::default(), window, cx);
 5630    });
 5631
 5632    cx.assert_editor_state(indoc!(
 5633        r#"abc
 5634           defˇghi
 5635
 5636           jk
 5637           nlmˇo
 5638           "#
 5639    ));
 5640
 5641    cx.update_editor(|editor, window, cx| {
 5642        editor.add_selection_below(&Default::default(), window, cx);
 5643    });
 5644
 5645    cx.assert_editor_state(indoc!(
 5646        r#"abc
 5647           defˇghi
 5648
 5649           jk
 5650           nlmˇo
 5651           "#
 5652    ));
 5653
 5654    // change selections
 5655    cx.set_state(indoc!(
 5656        r#"abc
 5657           def«ˇg»hi
 5658
 5659           jk
 5660           nlmo
 5661           "#
 5662    ));
 5663
 5664    cx.update_editor(|editor, window, cx| {
 5665        editor.add_selection_below(&Default::default(), window, cx);
 5666    });
 5667
 5668    cx.assert_editor_state(indoc!(
 5669        r#"abc
 5670           def«ˇg»hi
 5671
 5672           jk
 5673           nlm«ˇo»
 5674           "#
 5675    ));
 5676
 5677    cx.update_editor(|editor, window, cx| {
 5678        editor.add_selection_below(&Default::default(), window, cx);
 5679    });
 5680
 5681    cx.assert_editor_state(indoc!(
 5682        r#"abc
 5683           def«ˇg»hi
 5684
 5685           jk
 5686           nlm«ˇo»
 5687           "#
 5688    ));
 5689
 5690    cx.update_editor(|editor, window, cx| {
 5691        editor.add_selection_above(&Default::default(), window, cx);
 5692    });
 5693
 5694    cx.assert_editor_state(indoc!(
 5695        r#"abc
 5696           def«ˇg»hi
 5697
 5698           jk
 5699           nlmo
 5700           "#
 5701    ));
 5702
 5703    cx.update_editor(|editor, window, cx| {
 5704        editor.add_selection_above(&Default::default(), window, cx);
 5705    });
 5706
 5707    cx.assert_editor_state(indoc!(
 5708        r#"abc
 5709           def«ˇg»hi
 5710
 5711           jk
 5712           nlmo
 5713           "#
 5714    ));
 5715
 5716    // Change selections again
 5717    cx.set_state(indoc!(
 5718        r#"a«bc
 5719           defgˇ»hi
 5720
 5721           jk
 5722           nlmo
 5723           "#
 5724    ));
 5725
 5726    cx.update_editor(|editor, window, cx| {
 5727        editor.add_selection_below(&Default::default(), window, cx);
 5728    });
 5729
 5730    cx.assert_editor_state(indoc!(
 5731        r#"a«bcˇ»
 5732           d«efgˇ»hi
 5733
 5734           j«kˇ»
 5735           nlmo
 5736           "#
 5737    ));
 5738
 5739    cx.update_editor(|editor, window, cx| {
 5740        editor.add_selection_below(&Default::default(), window, cx);
 5741    });
 5742    cx.assert_editor_state(indoc!(
 5743        r#"a«bcˇ»
 5744           d«efgˇ»hi
 5745
 5746           j«kˇ»
 5747           n«lmoˇ»
 5748           "#
 5749    ));
 5750    cx.update_editor(|editor, window, cx| {
 5751        editor.add_selection_above(&Default::default(), window, cx);
 5752    });
 5753
 5754    cx.assert_editor_state(indoc!(
 5755        r#"a«bcˇ»
 5756           d«efgˇ»hi
 5757
 5758           j«kˇ»
 5759           nlmo
 5760           "#
 5761    ));
 5762
 5763    // Change selections again
 5764    cx.set_state(indoc!(
 5765        r#"abc
 5766           d«ˇefghi
 5767
 5768           jk
 5769           nlm»o
 5770           "#
 5771    ));
 5772
 5773    cx.update_editor(|editor, window, cx| {
 5774        editor.add_selection_above(&Default::default(), window, cx);
 5775    });
 5776
 5777    cx.assert_editor_state(indoc!(
 5778        r#"a«ˇbc»
 5779           d«ˇef»ghi
 5780
 5781           j«ˇk»
 5782           n«ˇlm»o
 5783           "#
 5784    ));
 5785
 5786    cx.update_editor(|editor, window, cx| {
 5787        editor.add_selection_below(&Default::default(), window, cx);
 5788    });
 5789
 5790    cx.assert_editor_state(indoc!(
 5791        r#"abc
 5792           d«ˇef»ghi
 5793
 5794           j«ˇk»
 5795           n«ˇlm»o
 5796           "#
 5797    ));
 5798}
 5799
 5800#[gpui::test]
 5801async fn test_select_next(cx: &mut TestAppContext) {
 5802    init_test(cx, |_| {});
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5806
 5807    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5808        .unwrap();
 5809    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5810
 5811    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5812        .unwrap();
 5813    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5814
 5815    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5816    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5817
 5818    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5819    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5820
 5821    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5822        .unwrap();
 5823    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5824
 5825    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5826        .unwrap();
 5827    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5828
 5829    // Test selection direction should be preserved
 5830    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5831
 5832    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5833        .unwrap();
 5834    cx.assert_editor_state("abc\n«ˇabc» «ˇabc»\ndefabc\nabc");
 5835}
 5836
 5837#[gpui::test]
 5838async fn test_select_all_matches(cx: &mut TestAppContext) {
 5839    init_test(cx, |_| {});
 5840
 5841    let mut cx = EditorTestContext::new(cx).await;
 5842
 5843    // Test caret-only selections
 5844    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5845    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5846        .unwrap();
 5847    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5848
 5849    // Test left-to-right selections
 5850    cx.set_state("abc\n«abcˇ»\nabc");
 5851    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5852        .unwrap();
 5853    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5854
 5855    // Test right-to-left selections
 5856    cx.set_state("abc\n«ˇabc»\nabc");
 5857    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5858        .unwrap();
 5859    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5860
 5861    // Test selecting whitespace with caret selection
 5862    cx.set_state("abc\nˇ   abc\nabc");
 5863    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5866
 5867    // Test selecting whitespace with left-to-right selection
 5868    cx.set_state("abc\n«ˇ  »abc\nabc");
 5869    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5870        .unwrap();
 5871    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5872
 5873    // Test no matches with right-to-left selection
 5874    cx.set_state("abc\n«  ˇ»abc\nabc");
 5875    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5876        .unwrap();
 5877    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5878}
 5879
 5880#[gpui::test]
 5881async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5882    init_test(cx, |_| {});
 5883
 5884    let mut cx = EditorTestContext::new(cx).await;
 5885
 5886    let large_body_1 = "\nd".repeat(200);
 5887    let large_body_2 = "\ne".repeat(200);
 5888
 5889    cx.set_state(&format!(
 5890        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5891    ));
 5892    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5893        let scroll_position = editor.scroll_position(cx);
 5894        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5895        scroll_position
 5896    });
 5897
 5898    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5899        .unwrap();
 5900    cx.assert_editor_state(&format!(
 5901        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5902    ));
 5903    let scroll_position_after_selection =
 5904        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5905    assert_eq!(
 5906        initial_scroll_position, scroll_position_after_selection,
 5907        "Scroll position should not change after selecting all matches"
 5908    );
 5909}
 5910
 5911#[gpui::test]
 5912async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5913    init_test(cx, |_| {});
 5914
 5915    let mut cx = EditorLspTestContext::new_rust(
 5916        lsp::ServerCapabilities {
 5917            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5918            ..Default::default()
 5919        },
 5920        cx,
 5921    )
 5922    .await;
 5923
 5924    cx.set_state(indoc! {"
 5925        line 1
 5926        line 2
 5927        linˇe 3
 5928        line 4
 5929        line 5
 5930    "});
 5931
 5932    // Make an edit
 5933    cx.update_editor(|editor, window, cx| {
 5934        editor.handle_input("X", window, cx);
 5935    });
 5936
 5937    // Move cursor to a different position
 5938    cx.update_editor(|editor, window, cx| {
 5939        editor.change_selections(None, window, cx, |s| {
 5940            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5941        });
 5942    });
 5943
 5944    cx.assert_editor_state(indoc! {"
 5945        line 1
 5946        line 2
 5947        linXe 3
 5948        line 4
 5949        liˇne 5
 5950    "});
 5951
 5952    cx.lsp
 5953        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5954            Ok(Some(vec![lsp::TextEdit::new(
 5955                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5956                "PREFIX ".to_string(),
 5957            )]))
 5958        });
 5959
 5960    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5961        .unwrap()
 5962        .await
 5963        .unwrap();
 5964
 5965    cx.assert_editor_state(indoc! {"
 5966        PREFIX line 1
 5967        line 2
 5968        linXe 3
 5969        line 4
 5970        liˇne 5
 5971    "});
 5972
 5973    // Undo formatting
 5974    cx.update_editor(|editor, window, cx| {
 5975        editor.undo(&Default::default(), window, cx);
 5976    });
 5977
 5978    // Verify cursor moved back to position after edit
 5979    cx.assert_editor_state(indoc! {"
 5980        line 1
 5981        line 2
 5982        linXˇe 3
 5983        line 4
 5984        line 5
 5985    "});
 5986}
 5987
 5988#[gpui::test]
 5989async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5990    init_test(cx, |_| {});
 5991
 5992    let mut cx = EditorTestContext::new(cx).await;
 5993    cx.set_state(
 5994        r#"let foo = 2;
 5995lˇet foo = 2;
 5996let fooˇ = 2;
 5997let foo = 2;
 5998let foo = ˇ2;"#,
 5999    );
 6000
 6001    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6002        .unwrap();
 6003    cx.assert_editor_state(
 6004        r#"let foo = 2;
 6005«letˇ» foo = 2;
 6006let «fooˇ» = 2;
 6007let foo = 2;
 6008let foo = «2ˇ»;"#,
 6009    );
 6010
 6011    // noop for multiple selections with different contents
 6012    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6013        .unwrap();
 6014    cx.assert_editor_state(
 6015        r#"let foo = 2;
 6016«letˇ» foo = 2;
 6017let «fooˇ» = 2;
 6018let foo = 2;
 6019let foo = «2ˇ»;"#,
 6020    );
 6021
 6022    // Test last selection direction should be preserved
 6023    cx.set_state(
 6024        r#"let foo = 2;
 6025let foo = 2;
 6026let «fooˇ» = 2;
 6027let «ˇfoo» = 2;
 6028let foo = 2;"#,
 6029    );
 6030
 6031    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6032        .unwrap();
 6033    cx.assert_editor_state(
 6034        r#"let foo = 2;
 6035let foo = 2;
 6036let «fooˇ» = 2;
 6037let «ˇfoo» = 2;
 6038let «ˇfoo» = 2;"#,
 6039    );
 6040}
 6041
 6042#[gpui::test]
 6043async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6044    init_test(cx, |_| {});
 6045
 6046    let mut cx =
 6047        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6048
 6049    cx.assert_editor_state(indoc! {"
 6050        ˇbbb
 6051        ccc
 6052
 6053        bbb
 6054        ccc
 6055        "});
 6056    cx.dispatch_action(SelectPrevious::default());
 6057    cx.assert_editor_state(indoc! {"
 6058                «bbbˇ»
 6059                ccc
 6060
 6061                bbb
 6062                ccc
 6063                "});
 6064    cx.dispatch_action(SelectPrevious::default());
 6065    cx.assert_editor_state(indoc! {"
 6066                «bbbˇ»
 6067                ccc
 6068
 6069                «bbbˇ»
 6070                ccc
 6071                "});
 6072}
 6073
 6074#[gpui::test]
 6075async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6076    init_test(cx, |_| {});
 6077
 6078    let mut cx = EditorTestContext::new(cx).await;
 6079    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6080
 6081    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6082        .unwrap();
 6083    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6084
 6085    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6086        .unwrap();
 6087    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6088
 6089    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6090    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6091
 6092    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6093    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6094
 6095    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6096        .unwrap();
 6097    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6098
 6099    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6100        .unwrap();
 6101    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 6102
 6103    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6104        .unwrap();
 6105    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6106}
 6107
 6108#[gpui::test]
 6109async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6110    init_test(cx, |_| {});
 6111
 6112    let mut cx = EditorTestContext::new(cx).await;
 6113    cx.set_state("");
 6114
 6115    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6116        .unwrap();
 6117    cx.assert_editor_state("«aˇ»");
 6118    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6119        .unwrap();
 6120    cx.assert_editor_state("«aˇ»");
 6121}
 6122
 6123#[gpui::test]
 6124async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6125    init_test(cx, |_| {});
 6126
 6127    let mut cx = EditorTestContext::new(cx).await;
 6128    cx.set_state(
 6129        r#"let foo = 2;
 6130lˇet foo = 2;
 6131let fooˇ = 2;
 6132let foo = 2;
 6133let foo = ˇ2;"#,
 6134    );
 6135
 6136    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6137        .unwrap();
 6138    cx.assert_editor_state(
 6139        r#"let foo = 2;
 6140«letˇ» foo = 2;
 6141let «fooˇ» = 2;
 6142let foo = 2;
 6143let foo = «2ˇ»;"#,
 6144    );
 6145
 6146    // noop for multiple selections with different contents
 6147    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6148        .unwrap();
 6149    cx.assert_editor_state(
 6150        r#"let foo = 2;
 6151«letˇ» foo = 2;
 6152let «fooˇ» = 2;
 6153let foo = 2;
 6154let foo = «2ˇ»;"#,
 6155    );
 6156}
 6157
 6158#[gpui::test]
 6159async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6160    init_test(cx, |_| {});
 6161
 6162    let mut cx = EditorTestContext::new(cx).await;
 6163    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6164
 6165    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6166        .unwrap();
 6167    // selection direction is preserved
 6168    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6169
 6170    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6171        .unwrap();
 6172    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6173
 6174    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6175    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\nabc");
 6176
 6177    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6178    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndefabc\n«ˇabc»");
 6179
 6180    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6181        .unwrap();
 6182    cx.assert_editor_state("«ˇabc»\n«ˇabc» abc\ndef«ˇabc»\n«ˇabc»");
 6183
 6184    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6185        .unwrap();
 6186    cx.assert_editor_state("«ˇabc»\n«ˇabc» «ˇabc»\ndef«ˇabc»\n«ˇabc»");
 6187}
 6188
 6189#[gpui::test]
 6190async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6191    init_test(cx, |_| {});
 6192
 6193    let language = Arc::new(Language::new(
 6194        LanguageConfig::default(),
 6195        Some(tree_sitter_rust::LANGUAGE.into()),
 6196    ));
 6197
 6198    let text = r#"
 6199        use mod1::mod2::{mod3, mod4};
 6200
 6201        fn fn_1(param1: bool, param2: &str) {
 6202            let var1 = "text";
 6203        }
 6204    "#
 6205    .unindent();
 6206
 6207    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6208    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6209    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6210
 6211    editor
 6212        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6213        .await;
 6214
 6215    editor.update_in(cx, |editor, window, cx| {
 6216        editor.change_selections(None, window, cx, |s| {
 6217            s.select_display_ranges([
 6218                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6219                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6220                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6221            ]);
 6222        });
 6223        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6224    });
 6225    editor.update(cx, |editor, cx| {
 6226        assert_text_with_selections(
 6227            editor,
 6228            indoc! {r#"
 6229                use mod1::mod2::{mod3, «mod4ˇ»};
 6230
 6231                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6232                    let var1 = "«ˇtext»";
 6233                }
 6234            "#},
 6235            cx,
 6236        );
 6237    });
 6238
 6239    editor.update_in(cx, |editor, window, cx| {
 6240        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6241    });
 6242    editor.update(cx, |editor, cx| {
 6243        assert_text_with_selections(
 6244            editor,
 6245            indoc! {r#"
 6246                use mod1::mod2::«{mod3, mod4}ˇ»;
 6247
 6248                «ˇfn fn_1(param1: bool, param2: &str) {
 6249                    let var1 = "text";
 6250 6251            "#},
 6252            cx,
 6253        );
 6254    });
 6255
 6256    editor.update_in(cx, |editor, window, cx| {
 6257        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6258    });
 6259    assert_eq!(
 6260        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6261        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6262    );
 6263
 6264    // Trying to expand the selected syntax node one more time has no effect.
 6265    editor.update_in(cx, |editor, window, cx| {
 6266        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6267    });
 6268    assert_eq!(
 6269        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6270        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6271    );
 6272
 6273    editor.update_in(cx, |editor, window, cx| {
 6274        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6275    });
 6276    editor.update(cx, |editor, cx| {
 6277        assert_text_with_selections(
 6278            editor,
 6279            indoc! {r#"
 6280                use mod1::mod2::«{mod3, mod4}ˇ»;
 6281
 6282                «ˇfn fn_1(param1: bool, param2: &str) {
 6283                    let var1 = "text";
 6284 6285            "#},
 6286            cx,
 6287        );
 6288    });
 6289
 6290    editor.update_in(cx, |editor, window, cx| {
 6291        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6292    });
 6293    editor.update(cx, |editor, cx| {
 6294        assert_text_with_selections(
 6295            editor,
 6296            indoc! {r#"
 6297                use mod1::mod2::{mod3, «mod4ˇ»};
 6298
 6299                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6300                    let var1 = "«ˇtext»";
 6301                }
 6302            "#},
 6303            cx,
 6304        );
 6305    });
 6306
 6307    editor.update_in(cx, |editor, window, cx| {
 6308        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6309    });
 6310    editor.update(cx, |editor, cx| {
 6311        assert_text_with_selections(
 6312            editor,
 6313            indoc! {r#"
 6314                use mod1::mod2::{mod3, mo«ˇ»d4};
 6315
 6316                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6317                    let var1 = "te«ˇ»xt";
 6318                }
 6319            "#},
 6320            cx,
 6321        );
 6322    });
 6323
 6324    // Trying to shrink the selected syntax node one more time has no effect.
 6325    editor.update_in(cx, |editor, window, cx| {
 6326        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6327    });
 6328    editor.update_in(cx, |editor, _, cx| {
 6329        assert_text_with_selections(
 6330            editor,
 6331            indoc! {r#"
 6332                use mod1::mod2::{mod3, mo«ˇ»d4};
 6333
 6334                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6335                    let var1 = "te«ˇ»xt";
 6336                }
 6337            "#},
 6338            cx,
 6339        );
 6340    });
 6341
 6342    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6343    // a fold.
 6344    editor.update_in(cx, |editor, window, cx| {
 6345        editor.fold_creases(
 6346            vec![
 6347                Crease::simple(
 6348                    Point::new(0, 21)..Point::new(0, 24),
 6349                    FoldPlaceholder::test(),
 6350                ),
 6351                Crease::simple(
 6352                    Point::new(3, 20)..Point::new(3, 22),
 6353                    FoldPlaceholder::test(),
 6354                ),
 6355            ],
 6356            true,
 6357            window,
 6358            cx,
 6359        );
 6360        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6361    });
 6362    editor.update(cx, |editor, cx| {
 6363        assert_text_with_selections(
 6364            editor,
 6365            indoc! {r#"
 6366                use mod1::mod2::«{mod3, mod4}ˇ»;
 6367
 6368                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6369                    let var1 = "«ˇtext»";
 6370                }
 6371            "#},
 6372            cx,
 6373        );
 6374    });
 6375}
 6376
 6377#[gpui::test]
 6378async fn test_select_larger_smaller_syntax_node_for_string(cx: &mut TestAppContext) {
 6379    init_test(cx, |_| {});
 6380
 6381    let language = Arc::new(Language::new(
 6382        LanguageConfig::default(),
 6383        Some(tree_sitter_rust::LANGUAGE.into()),
 6384    ));
 6385
 6386    let text = r#"
 6387        use mod1::mod2::{mod3, mod4};
 6388
 6389        fn fn_1(param1: bool, param2: &str) {
 6390            let var1 = "hello world";
 6391        }
 6392    "#
 6393    .unindent();
 6394
 6395    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6396    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6397    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6398
 6399    editor
 6400        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6401        .await;
 6402
 6403    // Test 1: Cursor on a letter of a string word
 6404    editor.update_in(cx, |editor, window, cx| {
 6405        editor.change_selections(None, window, cx, |s| {
 6406            s.select_display_ranges([
 6407                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 17)
 6408            ]);
 6409        });
 6410    });
 6411    editor.update_in(cx, |editor, window, cx| {
 6412        assert_text_with_selections(
 6413            editor,
 6414            indoc! {r#"
 6415                use mod1::mod2::{mod3, mod4};
 6416
 6417                fn fn_1(param1: bool, param2: &str) {
 6418                    let var1 = "hˇello world";
 6419                }
 6420            "#},
 6421            cx,
 6422        );
 6423        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6424        assert_text_with_selections(
 6425            editor,
 6426            indoc! {r#"
 6427                use mod1::mod2::{mod3, mod4};
 6428
 6429                fn fn_1(param1: bool, param2: &str) {
 6430                    let var1 = "«ˇhello» world";
 6431                }
 6432            "#},
 6433            cx,
 6434        );
 6435    });
 6436
 6437    // Test 2: Partial selection within a word
 6438    editor.update_in(cx, |editor, window, cx| {
 6439        editor.change_selections(None, window, cx, |s| {
 6440            s.select_display_ranges([
 6441                DisplayPoint::new(DisplayRow(3), 17)..DisplayPoint::new(DisplayRow(3), 19)
 6442            ]);
 6443        });
 6444    });
 6445    editor.update_in(cx, |editor, window, cx| {
 6446        assert_text_with_selections(
 6447            editor,
 6448            indoc! {r#"
 6449                use mod1::mod2::{mod3, mod4};
 6450
 6451                fn fn_1(param1: bool, param2: &str) {
 6452                    let var1 = "h«elˇ»lo world";
 6453                }
 6454            "#},
 6455            cx,
 6456        );
 6457        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6458        assert_text_with_selections(
 6459            editor,
 6460            indoc! {r#"
 6461                use mod1::mod2::{mod3, mod4};
 6462
 6463                fn fn_1(param1: bool, param2: &str) {
 6464                    let var1 = "«ˇhello» world";
 6465                }
 6466            "#},
 6467            cx,
 6468        );
 6469    });
 6470
 6471    // Test 3: Complete word already selected
 6472    editor.update_in(cx, |editor, window, cx| {
 6473        editor.change_selections(None, window, cx, |s| {
 6474            s.select_display_ranges([
 6475                DisplayPoint::new(DisplayRow(3), 16)..DisplayPoint::new(DisplayRow(3), 21)
 6476            ]);
 6477        });
 6478    });
 6479    editor.update_in(cx, |editor, window, cx| {
 6480        assert_text_with_selections(
 6481            editor,
 6482            indoc! {r#"
 6483                use mod1::mod2::{mod3, mod4};
 6484
 6485                fn fn_1(param1: bool, param2: &str) {
 6486                    let var1 = "«helloˇ» world";
 6487                }
 6488            "#},
 6489            cx,
 6490        );
 6491        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6492        assert_text_with_selections(
 6493            editor,
 6494            indoc! {r#"
 6495                use mod1::mod2::{mod3, mod4};
 6496
 6497                fn fn_1(param1: bool, param2: &str) {
 6498                    let var1 = "«hello worldˇ»";
 6499                }
 6500            "#},
 6501            cx,
 6502        );
 6503    });
 6504
 6505    // Test 4: Selection spanning across words
 6506    editor.update_in(cx, |editor, window, cx| {
 6507        editor.change_selections(None, window, cx, |s| {
 6508            s.select_display_ranges([
 6509                DisplayPoint::new(DisplayRow(3), 19)..DisplayPoint::new(DisplayRow(3), 24)
 6510            ]);
 6511        });
 6512    });
 6513    editor.update_in(cx, |editor, window, cx| {
 6514        assert_text_with_selections(
 6515            editor,
 6516            indoc! {r#"
 6517                use mod1::mod2::{mod3, mod4};
 6518
 6519                fn fn_1(param1: bool, param2: &str) {
 6520                    let var1 = "hel«lo woˇ»rld";
 6521                }
 6522            "#},
 6523            cx,
 6524        );
 6525        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6526        assert_text_with_selections(
 6527            editor,
 6528            indoc! {r#"
 6529                use mod1::mod2::{mod3, mod4};
 6530
 6531                fn fn_1(param1: bool, param2: &str) {
 6532                    let var1 = "«ˇhello world»";
 6533                }
 6534            "#},
 6535            cx,
 6536        );
 6537    });
 6538
 6539    // Test 5: Expansion beyond string
 6540    editor.update_in(cx, |editor, window, cx| {
 6541        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6542        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6543        assert_text_with_selections(
 6544            editor,
 6545            indoc! {r#"
 6546                use mod1::mod2::{mod3, mod4};
 6547
 6548                fn fn_1(param1: bool, param2: &str) {
 6549                    «ˇlet var1 = "hello world";»
 6550                }
 6551            "#},
 6552            cx,
 6553        );
 6554    });
 6555}
 6556
 6557#[gpui::test]
 6558async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6559    init_test(cx, |_| {});
 6560
 6561    let base_text = r#"
 6562        impl A {
 6563            // this is an uncommitted comment
 6564
 6565            fn b() {
 6566                c();
 6567            }
 6568
 6569            // this is another uncommitted comment
 6570
 6571            fn d() {
 6572                // e
 6573                // f
 6574            }
 6575        }
 6576
 6577        fn g() {
 6578            // h
 6579        }
 6580    "#
 6581    .unindent();
 6582
 6583    let text = r#"
 6584        ˇimpl A {
 6585
 6586            fn b() {
 6587                c();
 6588            }
 6589
 6590            fn d() {
 6591                // e
 6592                // f
 6593            }
 6594        }
 6595
 6596        fn g() {
 6597            // h
 6598        }
 6599    "#
 6600    .unindent();
 6601
 6602    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6603    cx.set_state(&text);
 6604    cx.set_head_text(&base_text);
 6605    cx.update_editor(|editor, window, cx| {
 6606        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6607    });
 6608
 6609    cx.assert_state_with_diff(
 6610        "
 6611        ˇimpl A {
 6612      -     // this is an uncommitted comment
 6613
 6614            fn b() {
 6615                c();
 6616            }
 6617
 6618      -     // this is another uncommitted comment
 6619      -
 6620            fn d() {
 6621                // e
 6622                // f
 6623            }
 6624        }
 6625
 6626        fn g() {
 6627            // h
 6628        }
 6629    "
 6630        .unindent(),
 6631    );
 6632
 6633    let expected_display_text = "
 6634        impl A {
 6635            // this is an uncommitted comment
 6636
 6637            fn b() {
 6638 6639            }
 6640
 6641            // this is another uncommitted comment
 6642
 6643            fn d() {
 6644 6645            }
 6646        }
 6647
 6648        fn g() {
 6649 6650        }
 6651        "
 6652    .unindent();
 6653
 6654    cx.update_editor(|editor, window, cx| {
 6655        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6656        assert_eq!(editor.display_text(cx), expected_display_text);
 6657    });
 6658}
 6659
 6660#[gpui::test]
 6661async fn test_autoindent(cx: &mut TestAppContext) {
 6662    init_test(cx, |_| {});
 6663
 6664    let language = Arc::new(
 6665        Language::new(
 6666            LanguageConfig {
 6667                brackets: BracketPairConfig {
 6668                    pairs: vec![
 6669                        BracketPair {
 6670                            start: "{".to_string(),
 6671                            end: "}".to_string(),
 6672                            close: false,
 6673                            surround: false,
 6674                            newline: true,
 6675                        },
 6676                        BracketPair {
 6677                            start: "(".to_string(),
 6678                            end: ")".to_string(),
 6679                            close: false,
 6680                            surround: false,
 6681                            newline: true,
 6682                        },
 6683                    ],
 6684                    ..Default::default()
 6685                },
 6686                ..Default::default()
 6687            },
 6688            Some(tree_sitter_rust::LANGUAGE.into()),
 6689        )
 6690        .with_indents_query(
 6691            r#"
 6692                (_ "(" ")" @end) @indent
 6693                (_ "{" "}" @end) @indent
 6694            "#,
 6695        )
 6696        .unwrap(),
 6697    );
 6698
 6699    let text = "fn a() {}";
 6700
 6701    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6702    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6703    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6704    editor
 6705        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6706        .await;
 6707
 6708    editor.update_in(cx, |editor, window, cx| {
 6709        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6710        editor.newline(&Newline, window, cx);
 6711        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6712        assert_eq!(
 6713            editor.selections.ranges(cx),
 6714            &[
 6715                Point::new(1, 4)..Point::new(1, 4),
 6716                Point::new(3, 4)..Point::new(3, 4),
 6717                Point::new(5, 0)..Point::new(5, 0)
 6718            ]
 6719        );
 6720    });
 6721}
 6722
 6723#[gpui::test]
 6724async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6725    init_test(cx, |_| {});
 6726
 6727    {
 6728        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6729        cx.set_state(indoc! {"
 6730            impl A {
 6731
 6732                fn b() {}
 6733
 6734            «fn c() {
 6735
 6736            }ˇ»
 6737            }
 6738        "});
 6739
 6740        cx.update_editor(|editor, window, cx| {
 6741            editor.autoindent(&Default::default(), window, cx);
 6742        });
 6743
 6744        cx.assert_editor_state(indoc! {"
 6745            impl A {
 6746
 6747                fn b() {}
 6748
 6749                «fn c() {
 6750
 6751                }ˇ»
 6752            }
 6753        "});
 6754    }
 6755
 6756    {
 6757        let mut cx = EditorTestContext::new_multibuffer(
 6758            cx,
 6759            [indoc! { "
 6760                impl A {
 6761                «
 6762                // a
 6763                fn b(){}
 6764                »
 6765                «
 6766                    }
 6767                    fn c(){}
 6768                »
 6769            "}],
 6770        );
 6771
 6772        let buffer = cx.update_editor(|editor, _, cx| {
 6773            let buffer = editor.buffer().update(cx, |buffer, _| {
 6774                buffer.all_buffers().iter().next().unwrap().clone()
 6775            });
 6776            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6777            buffer
 6778        });
 6779
 6780        cx.run_until_parked();
 6781        cx.update_editor(|editor, window, cx| {
 6782            editor.select_all(&Default::default(), window, cx);
 6783            editor.autoindent(&Default::default(), window, cx)
 6784        });
 6785        cx.run_until_parked();
 6786
 6787        cx.update(|_, cx| {
 6788            assert_eq!(
 6789                buffer.read(cx).text(),
 6790                indoc! { "
 6791                    impl A {
 6792
 6793                        // a
 6794                        fn b(){}
 6795
 6796
 6797                    }
 6798                    fn c(){}
 6799
 6800                " }
 6801            )
 6802        });
 6803    }
 6804}
 6805
 6806#[gpui::test]
 6807async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6808    init_test(cx, |_| {});
 6809
 6810    let mut cx = EditorTestContext::new(cx).await;
 6811
 6812    let language = Arc::new(Language::new(
 6813        LanguageConfig {
 6814            brackets: BracketPairConfig {
 6815                pairs: vec![
 6816                    BracketPair {
 6817                        start: "{".to_string(),
 6818                        end: "}".to_string(),
 6819                        close: true,
 6820                        surround: true,
 6821                        newline: true,
 6822                    },
 6823                    BracketPair {
 6824                        start: "(".to_string(),
 6825                        end: ")".to_string(),
 6826                        close: true,
 6827                        surround: true,
 6828                        newline: true,
 6829                    },
 6830                    BracketPair {
 6831                        start: "/*".to_string(),
 6832                        end: " */".to_string(),
 6833                        close: true,
 6834                        surround: true,
 6835                        newline: true,
 6836                    },
 6837                    BracketPair {
 6838                        start: "[".to_string(),
 6839                        end: "]".to_string(),
 6840                        close: false,
 6841                        surround: false,
 6842                        newline: true,
 6843                    },
 6844                    BracketPair {
 6845                        start: "\"".to_string(),
 6846                        end: "\"".to_string(),
 6847                        close: true,
 6848                        surround: true,
 6849                        newline: false,
 6850                    },
 6851                    BracketPair {
 6852                        start: "<".to_string(),
 6853                        end: ">".to_string(),
 6854                        close: false,
 6855                        surround: true,
 6856                        newline: true,
 6857                    },
 6858                ],
 6859                ..Default::default()
 6860            },
 6861            autoclose_before: "})]".to_string(),
 6862            ..Default::default()
 6863        },
 6864        Some(tree_sitter_rust::LANGUAGE.into()),
 6865    ));
 6866
 6867    cx.language_registry().add(language.clone());
 6868    cx.update_buffer(|buffer, cx| {
 6869        buffer.set_language(Some(language), cx);
 6870    });
 6871
 6872    cx.set_state(
 6873        &r#"
 6874            🏀ˇ
 6875            εˇ
 6876            ❤️ˇ
 6877        "#
 6878        .unindent(),
 6879    );
 6880
 6881    // autoclose multiple nested brackets at multiple cursors
 6882    cx.update_editor(|editor, window, cx| {
 6883        editor.handle_input("{", window, cx);
 6884        editor.handle_input("{", window, cx);
 6885        editor.handle_input("{", window, cx);
 6886    });
 6887    cx.assert_editor_state(
 6888        &"
 6889            🏀{{{ˇ}}}
 6890            ε{{{ˇ}}}
 6891            ❤️{{{ˇ}}}
 6892        "
 6893        .unindent(),
 6894    );
 6895
 6896    // insert a different closing bracket
 6897    cx.update_editor(|editor, window, cx| {
 6898        editor.handle_input(")", window, cx);
 6899    });
 6900    cx.assert_editor_state(
 6901        &"
 6902            🏀{{{)ˇ}}}
 6903            ε{{{)ˇ}}}
 6904            ❤️{{{)ˇ}}}
 6905        "
 6906        .unindent(),
 6907    );
 6908
 6909    // skip over the auto-closed brackets when typing a closing bracket
 6910    cx.update_editor(|editor, window, cx| {
 6911        editor.move_right(&MoveRight, window, cx);
 6912        editor.handle_input("}", window, cx);
 6913        editor.handle_input("}", window, cx);
 6914        editor.handle_input("}", window, cx);
 6915    });
 6916    cx.assert_editor_state(
 6917        &"
 6918            🏀{{{)}}}}ˇ
 6919            ε{{{)}}}}ˇ
 6920            ❤️{{{)}}}}ˇ
 6921        "
 6922        .unindent(),
 6923    );
 6924
 6925    // autoclose multi-character pairs
 6926    cx.set_state(
 6927        &"
 6928            ˇ
 6929            ˇ
 6930        "
 6931        .unindent(),
 6932    );
 6933    cx.update_editor(|editor, window, cx| {
 6934        editor.handle_input("/", window, cx);
 6935        editor.handle_input("*", window, cx);
 6936    });
 6937    cx.assert_editor_state(
 6938        &"
 6939            /*ˇ */
 6940            /*ˇ */
 6941        "
 6942        .unindent(),
 6943    );
 6944
 6945    // one cursor autocloses a multi-character pair, one cursor
 6946    // does not autoclose.
 6947    cx.set_state(
 6948        &"
 6949 6950            ˇ
 6951        "
 6952        .unindent(),
 6953    );
 6954    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6955    cx.assert_editor_state(
 6956        &"
 6957            /*ˇ */
 6958 6959        "
 6960        .unindent(),
 6961    );
 6962
 6963    // Don't autoclose if the next character isn't whitespace and isn't
 6964    // listed in the language's "autoclose_before" section.
 6965    cx.set_state("ˇa b");
 6966    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6967    cx.assert_editor_state("{ˇa b");
 6968
 6969    // Don't autoclose if `close` is false for the bracket pair
 6970    cx.set_state("ˇ");
 6971    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6972    cx.assert_editor_state("");
 6973
 6974    // Surround with brackets if text is selected
 6975    cx.set_state("«aˇ» b");
 6976    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6977    cx.assert_editor_state("{«aˇ»} b");
 6978
 6979    // Autoclose when not immediately after a word character
 6980    cx.set_state("a ˇ");
 6981    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6982    cx.assert_editor_state("a \"ˇ\"");
 6983
 6984    // Autoclose pair where the start and end characters are the same
 6985    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6986    cx.assert_editor_state("a \"\"ˇ");
 6987
 6988    // Don't autoclose when immediately after a word character
 6989    cx.set_state("");
 6990    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6991    cx.assert_editor_state("a\"ˇ");
 6992
 6993    // Do autoclose when after a non-word character
 6994    cx.set_state("");
 6995    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6996    cx.assert_editor_state("{\"ˇ\"");
 6997
 6998    // Non identical pairs autoclose regardless of preceding character
 6999    cx.set_state("");
 7000    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 7001    cx.assert_editor_state("a{ˇ}");
 7002
 7003    // Don't autoclose pair if autoclose is disabled
 7004    cx.set_state("ˇ");
 7005    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7006    cx.assert_editor_state("");
 7007
 7008    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 7009    cx.set_state("«aˇ» b");
 7010    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 7011    cx.assert_editor_state("<«aˇ»> b");
 7012}
 7013
 7014#[gpui::test]
 7015async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 7016    init_test(cx, |settings| {
 7017        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7018    });
 7019
 7020    let mut cx = EditorTestContext::new(cx).await;
 7021
 7022    let language = Arc::new(Language::new(
 7023        LanguageConfig {
 7024            brackets: BracketPairConfig {
 7025                pairs: vec![
 7026                    BracketPair {
 7027                        start: "{".to_string(),
 7028                        end: "}".to_string(),
 7029                        close: true,
 7030                        surround: true,
 7031                        newline: true,
 7032                    },
 7033                    BracketPair {
 7034                        start: "(".to_string(),
 7035                        end: ")".to_string(),
 7036                        close: true,
 7037                        surround: true,
 7038                        newline: true,
 7039                    },
 7040                    BracketPair {
 7041                        start: "[".to_string(),
 7042                        end: "]".to_string(),
 7043                        close: false,
 7044                        surround: false,
 7045                        newline: true,
 7046                    },
 7047                ],
 7048                ..Default::default()
 7049            },
 7050            autoclose_before: "})]".to_string(),
 7051            ..Default::default()
 7052        },
 7053        Some(tree_sitter_rust::LANGUAGE.into()),
 7054    ));
 7055
 7056    cx.language_registry().add(language.clone());
 7057    cx.update_buffer(|buffer, cx| {
 7058        buffer.set_language(Some(language), cx);
 7059    });
 7060
 7061    cx.set_state(
 7062        &"
 7063            ˇ
 7064            ˇ
 7065            ˇ
 7066        "
 7067        .unindent(),
 7068    );
 7069
 7070    // ensure only matching closing brackets are skipped over
 7071    cx.update_editor(|editor, window, cx| {
 7072        editor.handle_input("}", window, cx);
 7073        editor.move_left(&MoveLeft, window, cx);
 7074        editor.handle_input(")", window, cx);
 7075        editor.move_left(&MoveLeft, window, cx);
 7076    });
 7077    cx.assert_editor_state(
 7078        &"
 7079            ˇ)}
 7080            ˇ)}
 7081            ˇ)}
 7082        "
 7083        .unindent(),
 7084    );
 7085
 7086    // skip-over closing brackets at multiple cursors
 7087    cx.update_editor(|editor, window, cx| {
 7088        editor.handle_input(")", window, cx);
 7089        editor.handle_input("}", window, cx);
 7090    });
 7091    cx.assert_editor_state(
 7092        &"
 7093            )}ˇ
 7094            )}ˇ
 7095            )}ˇ
 7096        "
 7097        .unindent(),
 7098    );
 7099
 7100    // ignore non-close brackets
 7101    cx.update_editor(|editor, window, cx| {
 7102        editor.handle_input("]", window, cx);
 7103        editor.move_left(&MoveLeft, window, cx);
 7104        editor.handle_input("]", window, cx);
 7105    });
 7106    cx.assert_editor_state(
 7107        &"
 7108            )}]ˇ]
 7109            )}]ˇ]
 7110            )}]ˇ]
 7111        "
 7112        .unindent(),
 7113    );
 7114}
 7115
 7116#[gpui::test]
 7117async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 7118    init_test(cx, |_| {});
 7119
 7120    let mut cx = EditorTestContext::new(cx).await;
 7121
 7122    let html_language = Arc::new(
 7123        Language::new(
 7124            LanguageConfig {
 7125                name: "HTML".into(),
 7126                brackets: BracketPairConfig {
 7127                    pairs: vec![
 7128                        BracketPair {
 7129                            start: "<".into(),
 7130                            end: ">".into(),
 7131                            close: true,
 7132                            ..Default::default()
 7133                        },
 7134                        BracketPair {
 7135                            start: "{".into(),
 7136                            end: "}".into(),
 7137                            close: true,
 7138                            ..Default::default()
 7139                        },
 7140                        BracketPair {
 7141                            start: "(".into(),
 7142                            end: ")".into(),
 7143                            close: true,
 7144                            ..Default::default()
 7145                        },
 7146                    ],
 7147                    ..Default::default()
 7148                },
 7149                autoclose_before: "})]>".into(),
 7150                ..Default::default()
 7151            },
 7152            Some(tree_sitter_html::LANGUAGE.into()),
 7153        )
 7154        .with_injection_query(
 7155            r#"
 7156            (script_element
 7157                (raw_text) @injection.content
 7158                (#set! injection.language "javascript"))
 7159            "#,
 7160        )
 7161        .unwrap(),
 7162    );
 7163
 7164    let javascript_language = Arc::new(Language::new(
 7165        LanguageConfig {
 7166            name: "JavaScript".into(),
 7167            brackets: BracketPairConfig {
 7168                pairs: vec![
 7169                    BracketPair {
 7170                        start: "/*".into(),
 7171                        end: " */".into(),
 7172                        close: true,
 7173                        ..Default::default()
 7174                    },
 7175                    BracketPair {
 7176                        start: "{".into(),
 7177                        end: "}".into(),
 7178                        close: true,
 7179                        ..Default::default()
 7180                    },
 7181                    BracketPair {
 7182                        start: "(".into(),
 7183                        end: ")".into(),
 7184                        close: true,
 7185                        ..Default::default()
 7186                    },
 7187                ],
 7188                ..Default::default()
 7189            },
 7190            autoclose_before: "})]>".into(),
 7191            ..Default::default()
 7192        },
 7193        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 7194    ));
 7195
 7196    cx.language_registry().add(html_language.clone());
 7197    cx.language_registry().add(javascript_language.clone());
 7198
 7199    cx.update_buffer(|buffer, cx| {
 7200        buffer.set_language(Some(html_language), cx);
 7201    });
 7202
 7203    cx.set_state(
 7204        &r#"
 7205            <body>ˇ
 7206                <script>
 7207                    var x = 1;ˇ
 7208                </script>
 7209            </body>ˇ
 7210        "#
 7211        .unindent(),
 7212    );
 7213
 7214    // Precondition: different languages are active at different locations.
 7215    cx.update_editor(|editor, window, cx| {
 7216        let snapshot = editor.snapshot(window, cx);
 7217        let cursors = editor.selections.ranges::<usize>(cx);
 7218        let languages = cursors
 7219            .iter()
 7220            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7221            .collect::<Vec<_>>();
 7222        assert_eq!(
 7223            languages,
 7224            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7225        );
 7226    });
 7227
 7228    // Angle brackets autoclose in HTML, but not JavaScript.
 7229    cx.update_editor(|editor, window, cx| {
 7230        editor.handle_input("<", window, cx);
 7231        editor.handle_input("a", window, cx);
 7232    });
 7233    cx.assert_editor_state(
 7234        &r#"
 7235            <body><aˇ>
 7236                <script>
 7237                    var x = 1;<aˇ
 7238                </script>
 7239            </body><aˇ>
 7240        "#
 7241        .unindent(),
 7242    );
 7243
 7244    // Curly braces and parens autoclose in both HTML and JavaScript.
 7245    cx.update_editor(|editor, window, cx| {
 7246        editor.handle_input(" b=", window, cx);
 7247        editor.handle_input("{", window, cx);
 7248        editor.handle_input("c", window, cx);
 7249        editor.handle_input("(", window, cx);
 7250    });
 7251    cx.assert_editor_state(
 7252        &r#"
 7253            <body><a b={c(ˇ)}>
 7254                <script>
 7255                    var x = 1;<a b={c(ˇ)}
 7256                </script>
 7257            </body><a b={c(ˇ)}>
 7258        "#
 7259        .unindent(),
 7260    );
 7261
 7262    // Brackets that were already autoclosed are skipped.
 7263    cx.update_editor(|editor, window, cx| {
 7264        editor.handle_input(")", window, cx);
 7265        editor.handle_input("d", window, cx);
 7266        editor.handle_input("}", window, cx);
 7267    });
 7268    cx.assert_editor_state(
 7269        &r#"
 7270            <body><a b={c()d}ˇ>
 7271                <script>
 7272                    var x = 1;<a b={c()d}ˇ
 7273                </script>
 7274            </body><a b={c()d}ˇ>
 7275        "#
 7276        .unindent(),
 7277    );
 7278    cx.update_editor(|editor, window, cx| {
 7279        editor.handle_input(">", window, cx);
 7280    });
 7281    cx.assert_editor_state(
 7282        &r#"
 7283            <body><a b={c()d}>ˇ
 7284                <script>
 7285                    var x = 1;<a b={c()d}>ˇ
 7286                </script>
 7287            </body><a b={c()d}>ˇ
 7288        "#
 7289        .unindent(),
 7290    );
 7291
 7292    // Reset
 7293    cx.set_state(
 7294        &r#"
 7295            <body>ˇ
 7296                <script>
 7297                    var x = 1;ˇ
 7298                </script>
 7299            </body>ˇ
 7300        "#
 7301        .unindent(),
 7302    );
 7303
 7304    cx.update_editor(|editor, window, cx| {
 7305        editor.handle_input("<", window, cx);
 7306    });
 7307    cx.assert_editor_state(
 7308        &r#"
 7309            <body><ˇ>
 7310                <script>
 7311                    var x = 1;<ˇ
 7312                </script>
 7313            </body><ˇ>
 7314        "#
 7315        .unindent(),
 7316    );
 7317
 7318    // When backspacing, the closing angle brackets are removed.
 7319    cx.update_editor(|editor, window, cx| {
 7320        editor.backspace(&Backspace, window, cx);
 7321    });
 7322    cx.assert_editor_state(
 7323        &r#"
 7324            <body>ˇ
 7325                <script>
 7326                    var x = 1;ˇ
 7327                </script>
 7328            </body>ˇ
 7329        "#
 7330        .unindent(),
 7331    );
 7332
 7333    // Block comments autoclose in JavaScript, but not HTML.
 7334    cx.update_editor(|editor, window, cx| {
 7335        editor.handle_input("/", 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
 7350#[gpui::test]
 7351async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7352    init_test(cx, |_| {});
 7353
 7354    let mut cx = EditorTestContext::new(cx).await;
 7355
 7356    let rust_language = Arc::new(
 7357        Language::new(
 7358            LanguageConfig {
 7359                name: "Rust".into(),
 7360                brackets: serde_json::from_value(json!([
 7361                    { "start": "{", "end": "}", "close": true, "newline": true },
 7362                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7363                ]))
 7364                .unwrap(),
 7365                autoclose_before: "})]>".into(),
 7366                ..Default::default()
 7367            },
 7368            Some(tree_sitter_rust::LANGUAGE.into()),
 7369        )
 7370        .with_override_query("(string_literal) @string")
 7371        .unwrap(),
 7372    );
 7373
 7374    cx.language_registry().add(rust_language.clone());
 7375    cx.update_buffer(|buffer, cx| {
 7376        buffer.set_language(Some(rust_language), cx);
 7377    });
 7378
 7379    cx.set_state(
 7380        &r#"
 7381            let x = ˇ
 7382        "#
 7383        .unindent(),
 7384    );
 7385
 7386    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7387    cx.update_editor(|editor, window, cx| {
 7388        editor.handle_input("\"", window, cx);
 7389    });
 7390    cx.assert_editor_state(
 7391        &r#"
 7392            let x = "ˇ"
 7393        "#
 7394        .unindent(),
 7395    );
 7396
 7397    // Inserting another quotation mark. The cursor moves across the existing
 7398    // automatically-inserted quotation mark.
 7399    cx.update_editor(|editor, window, cx| {
 7400        editor.handle_input("\"", window, cx);
 7401    });
 7402    cx.assert_editor_state(
 7403        &r#"
 7404            let x = ""ˇ
 7405        "#
 7406        .unindent(),
 7407    );
 7408
 7409    // Reset
 7410    cx.set_state(
 7411        &r#"
 7412            let x = ˇ
 7413        "#
 7414        .unindent(),
 7415    );
 7416
 7417    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7418    cx.update_editor(|editor, window, cx| {
 7419        editor.handle_input("\"", window, cx);
 7420        editor.handle_input(" ", window, cx);
 7421        editor.move_left(&Default::default(), window, cx);
 7422        editor.handle_input("\\", window, cx);
 7423        editor.handle_input("\"", window, cx);
 7424    });
 7425    cx.assert_editor_state(
 7426        &r#"
 7427            let x = "\"ˇ "
 7428        "#
 7429        .unindent(),
 7430    );
 7431
 7432    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7433    // mark. Nothing is inserted.
 7434    cx.update_editor(|editor, window, cx| {
 7435        editor.move_right(&Default::default(), window, cx);
 7436        editor.handle_input("\"", window, cx);
 7437    });
 7438    cx.assert_editor_state(
 7439        &r#"
 7440            let x = "\" "ˇ
 7441        "#
 7442        .unindent(),
 7443    );
 7444}
 7445
 7446#[gpui::test]
 7447async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7448    init_test(cx, |_| {});
 7449
 7450    let language = Arc::new(Language::new(
 7451        LanguageConfig {
 7452            brackets: BracketPairConfig {
 7453                pairs: vec![
 7454                    BracketPair {
 7455                        start: "{".to_string(),
 7456                        end: "}".to_string(),
 7457                        close: true,
 7458                        surround: true,
 7459                        newline: true,
 7460                    },
 7461                    BracketPair {
 7462                        start: "/* ".to_string(),
 7463                        end: "*/".to_string(),
 7464                        close: true,
 7465                        surround: true,
 7466                        ..Default::default()
 7467                    },
 7468                ],
 7469                ..Default::default()
 7470            },
 7471            ..Default::default()
 7472        },
 7473        Some(tree_sitter_rust::LANGUAGE.into()),
 7474    ));
 7475
 7476    let text = r#"
 7477        a
 7478        b
 7479        c
 7480    "#
 7481    .unindent();
 7482
 7483    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7484    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7485    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7486    editor
 7487        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7488        .await;
 7489
 7490    editor.update_in(cx, |editor, window, cx| {
 7491        editor.change_selections(None, window, cx, |s| {
 7492            s.select_display_ranges([
 7493                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7494                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7495                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7496            ])
 7497        });
 7498
 7499        editor.handle_input("{", window, cx);
 7500        editor.handle_input("{", window, cx);
 7501        editor.handle_input("{", window, cx);
 7502        assert_eq!(
 7503            editor.text(cx),
 7504            "
 7505                {{{a}}}
 7506                {{{b}}}
 7507                {{{c}}}
 7508            "
 7509            .unindent()
 7510        );
 7511        assert_eq!(
 7512            editor.selections.display_ranges(cx),
 7513            [
 7514                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7515                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7516                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7517            ]
 7518        );
 7519
 7520        editor.undo(&Undo, window, cx);
 7521        editor.undo(&Undo, window, cx);
 7522        editor.undo(&Undo, window, cx);
 7523        assert_eq!(
 7524            editor.text(cx),
 7525            "
 7526                a
 7527                b
 7528                c
 7529            "
 7530            .unindent()
 7531        );
 7532        assert_eq!(
 7533            editor.selections.display_ranges(cx),
 7534            [
 7535                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7536                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7537                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7538            ]
 7539        );
 7540
 7541        // Ensure inserting the first character of a multi-byte bracket pair
 7542        // doesn't surround the selections with the bracket.
 7543        editor.handle_input("/", window, cx);
 7544        assert_eq!(
 7545            editor.text(cx),
 7546            "
 7547                /
 7548                /
 7549                /
 7550            "
 7551            .unindent()
 7552        );
 7553        assert_eq!(
 7554            editor.selections.display_ranges(cx),
 7555            [
 7556                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7557                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7558                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7559            ]
 7560        );
 7561
 7562        editor.undo(&Undo, window, cx);
 7563        assert_eq!(
 7564            editor.text(cx),
 7565            "
 7566                a
 7567                b
 7568                c
 7569            "
 7570            .unindent()
 7571        );
 7572        assert_eq!(
 7573            editor.selections.display_ranges(cx),
 7574            [
 7575                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7576                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7577                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7578            ]
 7579        );
 7580
 7581        // Ensure inserting the last character of a multi-byte bracket pair
 7582        // doesn't surround the selections with the bracket.
 7583        editor.handle_input("*", window, cx);
 7584        assert_eq!(
 7585            editor.text(cx),
 7586            "
 7587                *
 7588                *
 7589                *
 7590            "
 7591            .unindent()
 7592        );
 7593        assert_eq!(
 7594            editor.selections.display_ranges(cx),
 7595            [
 7596                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7597                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7598                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7599            ]
 7600        );
 7601    });
 7602}
 7603
 7604#[gpui::test]
 7605async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7606    init_test(cx, |_| {});
 7607
 7608    let language = Arc::new(Language::new(
 7609        LanguageConfig {
 7610            brackets: BracketPairConfig {
 7611                pairs: vec![BracketPair {
 7612                    start: "{".to_string(),
 7613                    end: "}".to_string(),
 7614                    close: true,
 7615                    surround: true,
 7616                    newline: true,
 7617                }],
 7618                ..Default::default()
 7619            },
 7620            autoclose_before: "}".to_string(),
 7621            ..Default::default()
 7622        },
 7623        Some(tree_sitter_rust::LANGUAGE.into()),
 7624    ));
 7625
 7626    let text = r#"
 7627        a
 7628        b
 7629        c
 7630    "#
 7631    .unindent();
 7632
 7633    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7634    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7635    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7636    editor
 7637        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7638        .await;
 7639
 7640    editor.update_in(cx, |editor, window, cx| {
 7641        editor.change_selections(None, window, cx, |s| {
 7642            s.select_ranges([
 7643                Point::new(0, 1)..Point::new(0, 1),
 7644                Point::new(1, 1)..Point::new(1, 1),
 7645                Point::new(2, 1)..Point::new(2, 1),
 7646            ])
 7647        });
 7648
 7649        editor.handle_input("{", window, cx);
 7650        editor.handle_input("{", window, cx);
 7651        editor.handle_input("_", window, cx);
 7652        assert_eq!(
 7653            editor.text(cx),
 7654            "
 7655                a{{_}}
 7656                b{{_}}
 7657                c{{_}}
 7658            "
 7659            .unindent()
 7660        );
 7661        assert_eq!(
 7662            editor.selections.ranges::<Point>(cx),
 7663            [
 7664                Point::new(0, 4)..Point::new(0, 4),
 7665                Point::new(1, 4)..Point::new(1, 4),
 7666                Point::new(2, 4)..Point::new(2, 4)
 7667            ]
 7668        );
 7669
 7670        editor.backspace(&Default::default(), window, cx);
 7671        editor.backspace(&Default::default(), window, cx);
 7672        assert_eq!(
 7673            editor.text(cx),
 7674            "
 7675                a{}
 7676                b{}
 7677                c{}
 7678            "
 7679            .unindent()
 7680        );
 7681        assert_eq!(
 7682            editor.selections.ranges::<Point>(cx),
 7683            [
 7684                Point::new(0, 2)..Point::new(0, 2),
 7685                Point::new(1, 2)..Point::new(1, 2),
 7686                Point::new(2, 2)..Point::new(2, 2)
 7687            ]
 7688        );
 7689
 7690        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7691        assert_eq!(
 7692            editor.text(cx),
 7693            "
 7694                a
 7695                b
 7696                c
 7697            "
 7698            .unindent()
 7699        );
 7700        assert_eq!(
 7701            editor.selections.ranges::<Point>(cx),
 7702            [
 7703                Point::new(0, 1)..Point::new(0, 1),
 7704                Point::new(1, 1)..Point::new(1, 1),
 7705                Point::new(2, 1)..Point::new(2, 1)
 7706            ]
 7707        );
 7708    });
 7709}
 7710
 7711#[gpui::test]
 7712async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7713    init_test(cx, |settings| {
 7714        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7715    });
 7716
 7717    let mut cx = EditorTestContext::new(cx).await;
 7718
 7719    let language = Arc::new(Language::new(
 7720        LanguageConfig {
 7721            brackets: BracketPairConfig {
 7722                pairs: vec![
 7723                    BracketPair {
 7724                        start: "{".to_string(),
 7725                        end: "}".to_string(),
 7726                        close: true,
 7727                        surround: true,
 7728                        newline: true,
 7729                    },
 7730                    BracketPair {
 7731                        start: "(".to_string(),
 7732                        end: ")".to_string(),
 7733                        close: true,
 7734                        surround: true,
 7735                        newline: true,
 7736                    },
 7737                    BracketPair {
 7738                        start: "[".to_string(),
 7739                        end: "]".to_string(),
 7740                        close: false,
 7741                        surround: true,
 7742                        newline: true,
 7743                    },
 7744                ],
 7745                ..Default::default()
 7746            },
 7747            autoclose_before: "})]".to_string(),
 7748            ..Default::default()
 7749        },
 7750        Some(tree_sitter_rust::LANGUAGE.into()),
 7751    ));
 7752
 7753    cx.language_registry().add(language.clone());
 7754    cx.update_buffer(|buffer, cx| {
 7755        buffer.set_language(Some(language), cx);
 7756    });
 7757
 7758    cx.set_state(
 7759        &"
 7760            {(ˇ)}
 7761            [[ˇ]]
 7762            {(ˇ)}
 7763        "
 7764        .unindent(),
 7765    );
 7766
 7767    cx.update_editor(|editor, window, cx| {
 7768        editor.backspace(&Default::default(), window, cx);
 7769        editor.backspace(&Default::default(), window, cx);
 7770    });
 7771
 7772    cx.assert_editor_state(
 7773        &"
 7774            ˇ
 7775            ˇ]]
 7776            ˇ
 7777        "
 7778        .unindent(),
 7779    );
 7780
 7781    cx.update_editor(|editor, window, cx| {
 7782        editor.handle_input("{", window, cx);
 7783        editor.handle_input("{", window, cx);
 7784        editor.move_right(&MoveRight, window, cx);
 7785        editor.move_right(&MoveRight, window, cx);
 7786        editor.move_left(&MoveLeft, window, cx);
 7787        editor.move_left(&MoveLeft, window, cx);
 7788        editor.backspace(&Default::default(), window, cx);
 7789    });
 7790
 7791    cx.assert_editor_state(
 7792        &"
 7793            {ˇ}
 7794            {ˇ}]]
 7795            {ˇ}
 7796        "
 7797        .unindent(),
 7798    );
 7799
 7800    cx.update_editor(|editor, window, cx| {
 7801        editor.backspace(&Default::default(), window, cx);
 7802    });
 7803
 7804    cx.assert_editor_state(
 7805        &"
 7806            ˇ
 7807            ˇ]]
 7808            ˇ
 7809        "
 7810        .unindent(),
 7811    );
 7812}
 7813
 7814#[gpui::test]
 7815async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7816    init_test(cx, |_| {});
 7817
 7818    let language = Arc::new(Language::new(
 7819        LanguageConfig::default(),
 7820        Some(tree_sitter_rust::LANGUAGE.into()),
 7821    ));
 7822
 7823    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7824    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7825    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7826    editor
 7827        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7828        .await;
 7829
 7830    editor.update_in(cx, |editor, window, cx| {
 7831        editor.set_auto_replace_emoji_shortcode(true);
 7832
 7833        editor.handle_input("Hello ", window, cx);
 7834        editor.handle_input(":wave", window, cx);
 7835        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7836
 7837        editor.handle_input(":", window, cx);
 7838        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7839
 7840        editor.handle_input(" :smile", window, cx);
 7841        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7842
 7843        editor.handle_input(":", window, cx);
 7844        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7845
 7846        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7847        editor.handle_input(":wave", window, cx);
 7848        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7849
 7850        editor.handle_input(":", window, cx);
 7851        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7852
 7853        editor.handle_input(":1", window, cx);
 7854        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7855
 7856        editor.handle_input(":", window, cx);
 7857        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7858
 7859        // Ensure shortcode does not get replaced when it is part of a word
 7860        editor.handle_input(" Test:wave", window, cx);
 7861        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7862
 7863        editor.handle_input(":", window, cx);
 7864        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7865
 7866        editor.set_auto_replace_emoji_shortcode(false);
 7867
 7868        // Ensure shortcode does not get replaced when auto replace is off
 7869        editor.handle_input(" :wave", window, cx);
 7870        assert_eq!(
 7871            editor.text(cx),
 7872            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7873        );
 7874
 7875        editor.handle_input(":", window, cx);
 7876        assert_eq!(
 7877            editor.text(cx),
 7878            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7879        );
 7880    });
 7881}
 7882
 7883#[gpui::test]
 7884async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7885    init_test(cx, |_| {});
 7886
 7887    let (text, insertion_ranges) = marked_text_ranges(
 7888        indoc! {"
 7889            ˇ
 7890        "},
 7891        false,
 7892    );
 7893
 7894    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7895    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7896
 7897    _ = editor.update_in(cx, |editor, window, cx| {
 7898        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7899
 7900        editor
 7901            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7902            .unwrap();
 7903
 7904        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7905            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7906            assert_eq!(editor.text(cx), expected_text);
 7907            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7908        }
 7909
 7910        assert(
 7911            editor,
 7912            cx,
 7913            indoc! {"
 7914            type «» =•
 7915            "},
 7916        );
 7917
 7918        assert!(editor.context_menu_visible(), "There should be a matches");
 7919    });
 7920}
 7921
 7922#[gpui::test]
 7923async fn test_snippets(cx: &mut TestAppContext) {
 7924    init_test(cx, |_| {});
 7925
 7926    let (text, insertion_ranges) = marked_text_ranges(
 7927        indoc! {"
 7928            a.ˇ b
 7929            a.ˇ b
 7930            a.ˇ b
 7931        "},
 7932        false,
 7933    );
 7934
 7935    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7936    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7937
 7938    editor.update_in(cx, |editor, window, cx| {
 7939        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7940
 7941        editor
 7942            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7943            .unwrap();
 7944
 7945        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7946            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7947            assert_eq!(editor.text(cx), expected_text);
 7948            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7949        }
 7950
 7951        assert(
 7952            editor,
 7953            cx,
 7954            indoc! {"
 7955                a.f(«one», two, «three») b
 7956                a.f(«one», two, «three») b
 7957                a.f(«one», two, «three») b
 7958            "},
 7959        );
 7960
 7961        // Can't move earlier than the first tab stop
 7962        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7963        assert(
 7964            editor,
 7965            cx,
 7966            indoc! {"
 7967                a.f(«one», two, «three») b
 7968                a.f(«one», two, «three») b
 7969                a.f(«one», two, «three») b
 7970            "},
 7971        );
 7972
 7973        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7974        assert(
 7975            editor,
 7976            cx,
 7977            indoc! {"
 7978                a.f(one, «two», three) b
 7979                a.f(one, «two», three) b
 7980                a.f(one, «two», three) b
 7981            "},
 7982        );
 7983
 7984        editor.move_to_prev_snippet_tabstop(window, cx);
 7985        assert(
 7986            editor,
 7987            cx,
 7988            indoc! {"
 7989                a.f(«one», two, «three») b
 7990                a.f(«one», two, «three») b
 7991                a.f(«one», two, «three») b
 7992            "},
 7993        );
 7994
 7995        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7996        assert(
 7997            editor,
 7998            cx,
 7999            indoc! {"
 8000                a.f(one, «two», three) b
 8001                a.f(one, «two», three) b
 8002                a.f(one, «two», three) b
 8003            "},
 8004        );
 8005        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 8006        assert(
 8007            editor,
 8008            cx,
 8009            indoc! {"
 8010                a.f(one, two, three)ˇ b
 8011                a.f(one, two, three)ˇ b
 8012                a.f(one, two, three)ˇ b
 8013            "},
 8014        );
 8015
 8016        // As soon as the last tab stop is reached, snippet state is gone
 8017        editor.move_to_prev_snippet_tabstop(window, cx);
 8018        assert(
 8019            editor,
 8020            cx,
 8021            indoc! {"
 8022                a.f(one, two, three)ˇ b
 8023                a.f(one, two, three)ˇ b
 8024                a.f(one, two, three)ˇ b
 8025            "},
 8026        );
 8027    });
 8028}
 8029
 8030#[gpui::test]
 8031async fn test_document_format_during_save(cx: &mut TestAppContext) {
 8032    init_test(cx, |_| {});
 8033
 8034    let fs = FakeFs::new(cx.executor());
 8035    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8036
 8037    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 8038
 8039    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8040    language_registry.add(rust_lang());
 8041    let mut fake_servers = language_registry.register_fake_lsp(
 8042        "Rust",
 8043        FakeLspAdapter {
 8044            capabilities: lsp::ServerCapabilities {
 8045                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8046                ..Default::default()
 8047            },
 8048            ..Default::default()
 8049        },
 8050    );
 8051
 8052    let buffer = project
 8053        .update(cx, |project, cx| {
 8054            project.open_local_buffer(path!("/file.rs"), cx)
 8055        })
 8056        .await
 8057        .unwrap();
 8058
 8059    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8060    let (editor, cx) = cx.add_window_view(|window, cx| {
 8061        build_editor_with_project(project.clone(), buffer, window, cx)
 8062    });
 8063    editor.update_in(cx, |editor, window, cx| {
 8064        editor.set_text("one\ntwo\nthree\n", window, cx)
 8065    });
 8066    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8067
 8068    cx.executor().start_waiting();
 8069    let fake_server = fake_servers.next().await.unwrap();
 8070
 8071    {
 8072        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8073            move |params, _| async move {
 8074                assert_eq!(
 8075                    params.text_document.uri,
 8076                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8077                );
 8078                assert_eq!(params.options.tab_size, 4);
 8079                Ok(Some(vec![lsp::TextEdit::new(
 8080                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8081                    ", ".to_string(),
 8082                )]))
 8083            },
 8084        );
 8085        let save = editor
 8086            .update_in(cx, |editor, window, cx| {
 8087                editor.save(true, project.clone(), window, cx)
 8088            })
 8089            .unwrap();
 8090        cx.executor().start_waiting();
 8091        save.await;
 8092
 8093        assert_eq!(
 8094            editor.update(cx, |editor, cx| editor.text(cx)),
 8095            "one, two\nthree\n"
 8096        );
 8097        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8098    }
 8099
 8100    {
 8101        editor.update_in(cx, |editor, window, cx| {
 8102            editor.set_text("one\ntwo\nthree\n", window, cx)
 8103        });
 8104        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8105
 8106        // Ensure we can still save even if formatting hangs.
 8107        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8108            move |params, _| async move {
 8109                assert_eq!(
 8110                    params.text_document.uri,
 8111                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8112                );
 8113                futures::future::pending::<()>().await;
 8114                unreachable!()
 8115            },
 8116        );
 8117        let save = editor
 8118            .update_in(cx, |editor, window, cx| {
 8119                editor.save(true, project.clone(), window, cx)
 8120            })
 8121            .unwrap();
 8122        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8123        cx.executor().start_waiting();
 8124        save.await;
 8125        assert_eq!(
 8126            editor.update(cx, |editor, cx| editor.text(cx)),
 8127            "one\ntwo\nthree\n"
 8128        );
 8129    }
 8130
 8131    // For non-dirty buffer, no formatting request should be sent
 8132    {
 8133        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8134
 8135        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 8136            panic!("Should not be invoked on non-dirty buffer");
 8137        });
 8138        let save = editor
 8139            .update_in(cx, |editor, window, cx| {
 8140                editor.save(true, project.clone(), window, cx)
 8141            })
 8142            .unwrap();
 8143        cx.executor().start_waiting();
 8144        save.await;
 8145    }
 8146
 8147    // Set rust language override and assert overridden tabsize is sent to language server
 8148    update_test_language_settings(cx, |settings| {
 8149        settings.languages.insert(
 8150            "Rust".into(),
 8151            LanguageSettingsContent {
 8152                tab_size: NonZeroU32::new(8),
 8153                ..Default::default()
 8154            },
 8155        );
 8156    });
 8157
 8158    {
 8159        editor.update_in(cx, |editor, window, cx| {
 8160            editor.set_text("somehting_new\n", window, cx)
 8161        });
 8162        assert!(cx.read(|cx| editor.is_dirty(cx)));
 8163        let _formatting_request_signal = fake_server
 8164            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8165                assert_eq!(
 8166                    params.text_document.uri,
 8167                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8168                );
 8169                assert_eq!(params.options.tab_size, 8);
 8170                Ok(Some(vec![]))
 8171            });
 8172        let save = editor
 8173            .update_in(cx, |editor, window, cx| {
 8174                editor.save(true, project.clone(), window, cx)
 8175            })
 8176            .unwrap();
 8177        cx.executor().start_waiting();
 8178        save.await;
 8179    }
 8180}
 8181
 8182#[gpui::test]
 8183async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 8184    init_test(cx, |_| {});
 8185
 8186    let cols = 4;
 8187    let rows = 10;
 8188    let sample_text_1 = sample_text(rows, cols, 'a');
 8189    assert_eq!(
 8190        sample_text_1,
 8191        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 8192    );
 8193    let sample_text_2 = sample_text(rows, cols, 'l');
 8194    assert_eq!(
 8195        sample_text_2,
 8196        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 8197    );
 8198    let sample_text_3 = sample_text(rows, cols, 'v');
 8199    assert_eq!(
 8200        sample_text_3,
 8201        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 8202    );
 8203
 8204    let fs = FakeFs::new(cx.executor());
 8205    fs.insert_tree(
 8206        path!("/a"),
 8207        json!({
 8208            "main.rs": sample_text_1,
 8209            "other.rs": sample_text_2,
 8210            "lib.rs": sample_text_3,
 8211        }),
 8212    )
 8213    .await;
 8214
 8215    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8216    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8217    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8218
 8219    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8220    language_registry.add(rust_lang());
 8221    let mut fake_servers = language_registry.register_fake_lsp(
 8222        "Rust",
 8223        FakeLspAdapter {
 8224            capabilities: lsp::ServerCapabilities {
 8225                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8226                ..Default::default()
 8227            },
 8228            ..Default::default()
 8229        },
 8230    );
 8231
 8232    let worktree = project.update(cx, |project, cx| {
 8233        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8234        assert_eq!(worktrees.len(), 1);
 8235        worktrees.pop().unwrap()
 8236    });
 8237    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8238
 8239    let buffer_1 = project
 8240        .update(cx, |project, cx| {
 8241            project.open_buffer((worktree_id, "main.rs"), cx)
 8242        })
 8243        .await
 8244        .unwrap();
 8245    let buffer_2 = project
 8246        .update(cx, |project, cx| {
 8247            project.open_buffer((worktree_id, "other.rs"), cx)
 8248        })
 8249        .await
 8250        .unwrap();
 8251    let buffer_3 = project
 8252        .update(cx, |project, cx| {
 8253            project.open_buffer((worktree_id, "lib.rs"), cx)
 8254        })
 8255        .await
 8256        .unwrap();
 8257
 8258    let multi_buffer = cx.new(|cx| {
 8259        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8260        multi_buffer.push_excerpts(
 8261            buffer_1.clone(),
 8262            [
 8263                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8264                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8265                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8266            ],
 8267            cx,
 8268        );
 8269        multi_buffer.push_excerpts(
 8270            buffer_2.clone(),
 8271            [
 8272                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8273                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8274                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8275            ],
 8276            cx,
 8277        );
 8278        multi_buffer.push_excerpts(
 8279            buffer_3.clone(),
 8280            [
 8281                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8282                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8283                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8284            ],
 8285            cx,
 8286        );
 8287        multi_buffer
 8288    });
 8289    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8290        Editor::new(
 8291            EditorMode::full(),
 8292            multi_buffer,
 8293            Some(project.clone()),
 8294            window,
 8295            cx,
 8296        )
 8297    });
 8298
 8299    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8300        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8301            s.select_ranges(Some(1..2))
 8302        });
 8303        editor.insert("|one|two|three|", window, cx);
 8304    });
 8305    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8306    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8307        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8308            s.select_ranges(Some(60..70))
 8309        });
 8310        editor.insert("|four|five|six|", window, cx);
 8311    });
 8312    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8313
 8314    // First two buffers should be edited, but not the third one.
 8315    assert_eq!(
 8316        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8317        "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}",
 8318    );
 8319    buffer_1.update(cx, |buffer, _| {
 8320        assert!(buffer.is_dirty());
 8321        assert_eq!(
 8322            buffer.text(),
 8323            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8324        )
 8325    });
 8326    buffer_2.update(cx, |buffer, _| {
 8327        assert!(buffer.is_dirty());
 8328        assert_eq!(
 8329            buffer.text(),
 8330            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8331        )
 8332    });
 8333    buffer_3.update(cx, |buffer, _| {
 8334        assert!(!buffer.is_dirty());
 8335        assert_eq!(buffer.text(), sample_text_3,)
 8336    });
 8337    cx.executor().run_until_parked();
 8338
 8339    cx.executor().start_waiting();
 8340    let save = multi_buffer_editor
 8341        .update_in(cx, |editor, window, cx| {
 8342            editor.save(true, project.clone(), window, cx)
 8343        })
 8344        .unwrap();
 8345
 8346    let fake_server = fake_servers.next().await.unwrap();
 8347    fake_server
 8348        .server
 8349        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8350            Ok(Some(vec![lsp::TextEdit::new(
 8351                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8352                format!("[{} formatted]", params.text_document.uri),
 8353            )]))
 8354        })
 8355        .detach();
 8356    save.await;
 8357
 8358    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8359    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8360    assert_eq!(
 8361        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8362        uri!(
 8363            "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}"
 8364        ),
 8365    );
 8366    buffer_1.update(cx, |buffer, _| {
 8367        assert!(!buffer.is_dirty());
 8368        assert_eq!(
 8369            buffer.text(),
 8370            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8371        )
 8372    });
 8373    buffer_2.update(cx, |buffer, _| {
 8374        assert!(!buffer.is_dirty());
 8375        assert_eq!(
 8376            buffer.text(),
 8377            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8378        )
 8379    });
 8380    buffer_3.update(cx, |buffer, _| {
 8381        assert!(!buffer.is_dirty());
 8382        assert_eq!(buffer.text(), sample_text_3,)
 8383    });
 8384}
 8385
 8386#[gpui::test]
 8387async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8388    init_test(cx, |_| {});
 8389
 8390    let fs = FakeFs::new(cx.executor());
 8391    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8392
 8393    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8394
 8395    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8396    language_registry.add(rust_lang());
 8397    let mut fake_servers = language_registry.register_fake_lsp(
 8398        "Rust",
 8399        FakeLspAdapter {
 8400            capabilities: lsp::ServerCapabilities {
 8401                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8402                ..Default::default()
 8403            },
 8404            ..Default::default()
 8405        },
 8406    );
 8407
 8408    let buffer = project
 8409        .update(cx, |project, cx| {
 8410            project.open_local_buffer(path!("/file.rs"), cx)
 8411        })
 8412        .await
 8413        .unwrap();
 8414
 8415    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8416    let (editor, cx) = cx.add_window_view(|window, cx| {
 8417        build_editor_with_project(project.clone(), buffer, window, cx)
 8418    });
 8419    editor.update_in(cx, |editor, window, cx| {
 8420        editor.set_text("one\ntwo\nthree\n", window, cx)
 8421    });
 8422    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8423
 8424    cx.executor().start_waiting();
 8425    let fake_server = fake_servers.next().await.unwrap();
 8426
 8427    let save = editor
 8428        .update_in(cx, |editor, window, cx| {
 8429            editor.save(true, project.clone(), window, cx)
 8430        })
 8431        .unwrap();
 8432    fake_server
 8433        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8434            assert_eq!(
 8435                params.text_document.uri,
 8436                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8437            );
 8438            assert_eq!(params.options.tab_size, 4);
 8439            Ok(Some(vec![lsp::TextEdit::new(
 8440                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8441                ", ".to_string(),
 8442            )]))
 8443        })
 8444        .next()
 8445        .await;
 8446    cx.executor().start_waiting();
 8447    save.await;
 8448    assert_eq!(
 8449        editor.update(cx, |editor, cx| editor.text(cx)),
 8450        "one, two\nthree\n"
 8451    );
 8452    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8453
 8454    editor.update_in(cx, |editor, window, cx| {
 8455        editor.set_text("one\ntwo\nthree\n", window, cx)
 8456    });
 8457    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8458
 8459    // Ensure we can still save even if formatting hangs.
 8460    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8461        move |params, _| async move {
 8462            assert_eq!(
 8463                params.text_document.uri,
 8464                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8465            );
 8466            futures::future::pending::<()>().await;
 8467            unreachable!()
 8468        },
 8469    );
 8470    let save = editor
 8471        .update_in(cx, |editor, window, cx| {
 8472            editor.save(true, project.clone(), window, cx)
 8473        })
 8474        .unwrap();
 8475    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8476    cx.executor().start_waiting();
 8477    save.await;
 8478    assert_eq!(
 8479        editor.update(cx, |editor, cx| editor.text(cx)),
 8480        "one\ntwo\nthree\n"
 8481    );
 8482    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8483
 8484    // For non-dirty buffer, no formatting request should be sent
 8485    let save = editor
 8486        .update_in(cx, |editor, window, cx| {
 8487            editor.save(true, project.clone(), window, cx)
 8488        })
 8489        .unwrap();
 8490    let _pending_format_request = fake_server
 8491        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8492            panic!("Should not be invoked on non-dirty buffer");
 8493        })
 8494        .next();
 8495    cx.executor().start_waiting();
 8496    save.await;
 8497
 8498    // Set Rust language override and assert overridden tabsize is sent to language server
 8499    update_test_language_settings(cx, |settings| {
 8500        settings.languages.insert(
 8501            "Rust".into(),
 8502            LanguageSettingsContent {
 8503                tab_size: NonZeroU32::new(8),
 8504                ..Default::default()
 8505            },
 8506        );
 8507    });
 8508
 8509    editor.update_in(cx, |editor, window, cx| {
 8510        editor.set_text("somehting_new\n", window, cx)
 8511    });
 8512    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8513    let save = editor
 8514        .update_in(cx, |editor, window, cx| {
 8515            editor.save(true, project.clone(), window, cx)
 8516        })
 8517        .unwrap();
 8518    fake_server
 8519        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8520            assert_eq!(
 8521                params.text_document.uri,
 8522                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8523            );
 8524            assert_eq!(params.options.tab_size, 8);
 8525            Ok(Some(vec![]))
 8526        })
 8527        .next()
 8528        .await;
 8529    cx.executor().start_waiting();
 8530    save.await;
 8531}
 8532
 8533#[gpui::test]
 8534async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8535    init_test(cx, |settings| {
 8536        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8537            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8538        ))
 8539    });
 8540
 8541    let fs = FakeFs::new(cx.executor());
 8542    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8543
 8544    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8545
 8546    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8547    language_registry.add(Arc::new(Language::new(
 8548        LanguageConfig {
 8549            name: "Rust".into(),
 8550            matcher: LanguageMatcher {
 8551                path_suffixes: vec!["rs".to_string()],
 8552                ..Default::default()
 8553            },
 8554            ..LanguageConfig::default()
 8555        },
 8556        Some(tree_sitter_rust::LANGUAGE.into()),
 8557    )));
 8558    update_test_language_settings(cx, |settings| {
 8559        // Enable Prettier formatting for the same buffer, and ensure
 8560        // LSP is called instead of Prettier.
 8561        settings.defaults.prettier = Some(PrettierSettings {
 8562            allowed: true,
 8563            ..PrettierSettings::default()
 8564        });
 8565    });
 8566    let mut fake_servers = language_registry.register_fake_lsp(
 8567        "Rust",
 8568        FakeLspAdapter {
 8569            capabilities: lsp::ServerCapabilities {
 8570                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8571                ..Default::default()
 8572            },
 8573            ..Default::default()
 8574        },
 8575    );
 8576
 8577    let buffer = project
 8578        .update(cx, |project, cx| {
 8579            project.open_local_buffer(path!("/file.rs"), cx)
 8580        })
 8581        .await
 8582        .unwrap();
 8583
 8584    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8585    let (editor, cx) = cx.add_window_view(|window, cx| {
 8586        build_editor_with_project(project.clone(), buffer, window, cx)
 8587    });
 8588    editor.update_in(cx, |editor, window, cx| {
 8589        editor.set_text("one\ntwo\nthree\n", window, cx)
 8590    });
 8591
 8592    cx.executor().start_waiting();
 8593    let fake_server = fake_servers.next().await.unwrap();
 8594
 8595    let format = editor
 8596        .update_in(cx, |editor, window, cx| {
 8597            editor.perform_format(
 8598                project.clone(),
 8599                FormatTrigger::Manual,
 8600                FormatTarget::Buffers,
 8601                window,
 8602                cx,
 8603            )
 8604        })
 8605        .unwrap();
 8606    fake_server
 8607        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8608            assert_eq!(
 8609                params.text_document.uri,
 8610                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8611            );
 8612            assert_eq!(params.options.tab_size, 4);
 8613            Ok(Some(vec![lsp::TextEdit::new(
 8614                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8615                ", ".to_string(),
 8616            )]))
 8617        })
 8618        .next()
 8619        .await;
 8620    cx.executor().start_waiting();
 8621    format.await;
 8622    assert_eq!(
 8623        editor.update(cx, |editor, cx| editor.text(cx)),
 8624        "one, two\nthree\n"
 8625    );
 8626
 8627    editor.update_in(cx, |editor, window, cx| {
 8628        editor.set_text("one\ntwo\nthree\n", window, cx)
 8629    });
 8630    // Ensure we don't lock if formatting hangs.
 8631    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8632        move |params, _| async move {
 8633            assert_eq!(
 8634                params.text_document.uri,
 8635                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8636            );
 8637            futures::future::pending::<()>().await;
 8638            unreachable!()
 8639        },
 8640    );
 8641    let format = editor
 8642        .update_in(cx, |editor, window, cx| {
 8643            editor.perform_format(
 8644                project,
 8645                FormatTrigger::Manual,
 8646                FormatTarget::Buffers,
 8647                window,
 8648                cx,
 8649            )
 8650        })
 8651        .unwrap();
 8652    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8653    cx.executor().start_waiting();
 8654    format.await;
 8655    assert_eq!(
 8656        editor.update(cx, |editor, cx| editor.text(cx)),
 8657        "one\ntwo\nthree\n"
 8658    );
 8659}
 8660
 8661#[gpui::test]
 8662async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8663    init_test(cx, |settings| {
 8664        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8665        settings.defaults.formatter =
 8666            Some(language_settings::SelectedFormatter::List(FormatterList(
 8667                vec![
 8668                    Formatter::LanguageServer { name: None },
 8669                    Formatter::CodeActions(
 8670                        [
 8671                            ("code-action-1".into(), true),
 8672                            ("code-action-2".into(), true),
 8673                        ]
 8674                        .into_iter()
 8675                        .collect(),
 8676                    ),
 8677                ]
 8678                .into(),
 8679            )))
 8680    });
 8681
 8682    let fs = FakeFs::new(cx.executor());
 8683    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8684        .await;
 8685
 8686    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8687    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8688    language_registry.add(rust_lang());
 8689
 8690    let mut fake_servers = language_registry.register_fake_lsp(
 8691        "Rust",
 8692        FakeLspAdapter {
 8693            capabilities: lsp::ServerCapabilities {
 8694                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8695                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8696                    commands: vec!["the-command-for-code-action-1".into()],
 8697                    ..Default::default()
 8698                }),
 8699                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8700                ..Default::default()
 8701            },
 8702            ..Default::default()
 8703        },
 8704    );
 8705
 8706    let buffer = project
 8707        .update(cx, |project, cx| {
 8708            project.open_local_buffer(path!("/file.rs"), cx)
 8709        })
 8710        .await
 8711        .unwrap();
 8712
 8713    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8714    let (editor, cx) = cx.add_window_view(|window, cx| {
 8715        build_editor_with_project(project.clone(), buffer, window, cx)
 8716    });
 8717
 8718    cx.executor().start_waiting();
 8719
 8720    let fake_server = fake_servers.next().await.unwrap();
 8721    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8722        move |_params, _| async move {
 8723            Ok(Some(vec![lsp::TextEdit::new(
 8724                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8725                "applied-formatting\n".to_string(),
 8726            )]))
 8727        },
 8728    );
 8729    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8730        move |params, _| async move {
 8731            assert_eq!(
 8732                params.context.only,
 8733                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8734            );
 8735            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8736            Ok(Some(vec![
 8737                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8738                    kind: Some("code-action-1".into()),
 8739                    edit: Some(lsp::WorkspaceEdit::new(
 8740                        [(
 8741                            uri.clone(),
 8742                            vec![lsp::TextEdit::new(
 8743                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8744                                "applied-code-action-1-edit\n".to_string(),
 8745                            )],
 8746                        )]
 8747                        .into_iter()
 8748                        .collect(),
 8749                    )),
 8750                    command: Some(lsp::Command {
 8751                        command: "the-command-for-code-action-1".into(),
 8752                        ..Default::default()
 8753                    }),
 8754                    ..Default::default()
 8755                }),
 8756                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8757                    kind: Some("code-action-2".into()),
 8758                    edit: Some(lsp::WorkspaceEdit::new(
 8759                        [(
 8760                            uri.clone(),
 8761                            vec![lsp::TextEdit::new(
 8762                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8763                                "applied-code-action-2-edit\n".to_string(),
 8764                            )],
 8765                        )]
 8766                        .into_iter()
 8767                        .collect(),
 8768                    )),
 8769                    ..Default::default()
 8770                }),
 8771            ]))
 8772        },
 8773    );
 8774
 8775    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8776        move |params, _| async move { Ok(params) }
 8777    });
 8778
 8779    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8780    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8781        let fake = fake_server.clone();
 8782        let lock = command_lock.clone();
 8783        move |params, _| {
 8784            assert_eq!(params.command, "the-command-for-code-action-1");
 8785            let fake = fake.clone();
 8786            let lock = lock.clone();
 8787            async move {
 8788                lock.lock().await;
 8789                fake.server
 8790                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8791                        label: None,
 8792                        edit: lsp::WorkspaceEdit {
 8793                            changes: Some(
 8794                                [(
 8795                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8796                                    vec![lsp::TextEdit {
 8797                                        range: lsp::Range::new(
 8798                                            lsp::Position::new(0, 0),
 8799                                            lsp::Position::new(0, 0),
 8800                                        ),
 8801                                        new_text: "applied-code-action-1-command\n".into(),
 8802                                    }],
 8803                                )]
 8804                                .into_iter()
 8805                                .collect(),
 8806                            ),
 8807                            ..Default::default()
 8808                        },
 8809                    })
 8810                    .await
 8811                    .unwrap();
 8812                Ok(Some(json!(null)))
 8813            }
 8814        }
 8815    });
 8816
 8817    cx.executor().start_waiting();
 8818    editor
 8819        .update_in(cx, |editor, window, cx| {
 8820            editor.perform_format(
 8821                project.clone(),
 8822                FormatTrigger::Manual,
 8823                FormatTarget::Buffers,
 8824                window,
 8825                cx,
 8826            )
 8827        })
 8828        .unwrap()
 8829        .await;
 8830    editor.update(cx, |editor, cx| {
 8831        assert_eq!(
 8832            editor.text(cx),
 8833            r#"
 8834                applied-code-action-2-edit
 8835                applied-code-action-1-command
 8836                applied-code-action-1-edit
 8837                applied-formatting
 8838                one
 8839                two
 8840                three
 8841            "#
 8842            .unindent()
 8843        );
 8844    });
 8845
 8846    editor.update_in(cx, |editor, window, cx| {
 8847        editor.undo(&Default::default(), window, cx);
 8848        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8849    });
 8850
 8851    // Perform a manual edit while waiting for an LSP command
 8852    // that's being run as part of a formatting code action.
 8853    let lock_guard = command_lock.lock().await;
 8854    let format = editor
 8855        .update_in(cx, |editor, window, cx| {
 8856            editor.perform_format(
 8857                project.clone(),
 8858                FormatTrigger::Manual,
 8859                FormatTarget::Buffers,
 8860                window,
 8861                cx,
 8862            )
 8863        })
 8864        .unwrap();
 8865    cx.run_until_parked();
 8866    editor.update(cx, |editor, cx| {
 8867        assert_eq!(
 8868            editor.text(cx),
 8869            r#"
 8870                applied-code-action-1-edit
 8871                applied-formatting
 8872                one
 8873                two
 8874                three
 8875            "#
 8876            .unindent()
 8877        );
 8878
 8879        editor.buffer.update(cx, |buffer, cx| {
 8880            let ix = buffer.len(cx);
 8881            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8882        });
 8883    });
 8884
 8885    // Allow the LSP command to proceed. Because the buffer was edited,
 8886    // the second code action will not be run.
 8887    drop(lock_guard);
 8888    format.await;
 8889    editor.update_in(cx, |editor, window, cx| {
 8890        assert_eq!(
 8891            editor.text(cx),
 8892            r#"
 8893                applied-code-action-1-command
 8894                applied-code-action-1-edit
 8895                applied-formatting
 8896                one
 8897                two
 8898                three
 8899                edited
 8900            "#
 8901            .unindent()
 8902        );
 8903
 8904        // The manual edit is undone first, because it is the last thing the user did
 8905        // (even though the command completed afterwards).
 8906        editor.undo(&Default::default(), window, cx);
 8907        assert_eq!(
 8908            editor.text(cx),
 8909            r#"
 8910                applied-code-action-1-command
 8911                applied-code-action-1-edit
 8912                applied-formatting
 8913                one
 8914                two
 8915                three
 8916            "#
 8917            .unindent()
 8918        );
 8919
 8920        // All the formatting (including the command, which completed after the manual edit)
 8921        // is undone together.
 8922        editor.undo(&Default::default(), window, cx);
 8923        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8924    });
 8925}
 8926
 8927#[gpui::test]
 8928async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8929    init_test(cx, |settings| {
 8930        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8931            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8932        ))
 8933    });
 8934
 8935    let fs = FakeFs::new(cx.executor());
 8936    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8937
 8938    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8939
 8940    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8941    language_registry.add(Arc::new(Language::new(
 8942        LanguageConfig {
 8943            name: "TypeScript".into(),
 8944            matcher: LanguageMatcher {
 8945                path_suffixes: vec!["ts".to_string()],
 8946                ..Default::default()
 8947            },
 8948            ..LanguageConfig::default()
 8949        },
 8950        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8951    )));
 8952    update_test_language_settings(cx, |settings| {
 8953        settings.defaults.prettier = Some(PrettierSettings {
 8954            allowed: true,
 8955            ..PrettierSettings::default()
 8956        });
 8957    });
 8958    let mut fake_servers = language_registry.register_fake_lsp(
 8959        "TypeScript",
 8960        FakeLspAdapter {
 8961            capabilities: lsp::ServerCapabilities {
 8962                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8963                ..Default::default()
 8964            },
 8965            ..Default::default()
 8966        },
 8967    );
 8968
 8969    let buffer = project
 8970        .update(cx, |project, cx| {
 8971            project.open_local_buffer(path!("/file.ts"), cx)
 8972        })
 8973        .await
 8974        .unwrap();
 8975
 8976    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8977    let (editor, cx) = cx.add_window_view(|window, cx| {
 8978        build_editor_with_project(project.clone(), buffer, window, cx)
 8979    });
 8980    editor.update_in(cx, |editor, window, cx| {
 8981        editor.set_text(
 8982            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8983            window,
 8984            cx,
 8985        )
 8986    });
 8987
 8988    cx.executor().start_waiting();
 8989    let fake_server = fake_servers.next().await.unwrap();
 8990
 8991    let format = editor
 8992        .update_in(cx, |editor, window, cx| {
 8993            editor.perform_code_action_kind(
 8994                project.clone(),
 8995                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8996                window,
 8997                cx,
 8998            )
 8999        })
 9000        .unwrap();
 9001    fake_server
 9002        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 9003            assert_eq!(
 9004                params.text_document.uri,
 9005                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9006            );
 9007            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 9008                lsp::CodeAction {
 9009                    title: "Organize Imports".to_string(),
 9010                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 9011                    edit: Some(lsp::WorkspaceEdit {
 9012                        changes: Some(
 9013                            [(
 9014                                params.text_document.uri.clone(),
 9015                                vec![lsp::TextEdit::new(
 9016                                    lsp::Range::new(
 9017                                        lsp::Position::new(1, 0),
 9018                                        lsp::Position::new(2, 0),
 9019                                    ),
 9020                                    "".to_string(),
 9021                                )],
 9022                            )]
 9023                            .into_iter()
 9024                            .collect(),
 9025                        ),
 9026                        ..Default::default()
 9027                    }),
 9028                    ..Default::default()
 9029                },
 9030            )]))
 9031        })
 9032        .next()
 9033        .await;
 9034    cx.executor().start_waiting();
 9035    format.await;
 9036    assert_eq!(
 9037        editor.update(cx, |editor, cx| editor.text(cx)),
 9038        "import { a } from 'module';\n\nconst x = a;\n"
 9039    );
 9040
 9041    editor.update_in(cx, |editor, window, cx| {
 9042        editor.set_text(
 9043            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 9044            window,
 9045            cx,
 9046        )
 9047    });
 9048    // Ensure we don't lock if code action hangs.
 9049    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 9050        move |params, _| async move {
 9051            assert_eq!(
 9052                params.text_document.uri,
 9053                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 9054            );
 9055            futures::future::pending::<()>().await;
 9056            unreachable!()
 9057        },
 9058    );
 9059    let format = editor
 9060        .update_in(cx, |editor, window, cx| {
 9061            editor.perform_code_action_kind(
 9062                project,
 9063                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 9064                window,
 9065                cx,
 9066            )
 9067        })
 9068        .unwrap();
 9069    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 9070    cx.executor().start_waiting();
 9071    format.await;
 9072    assert_eq!(
 9073        editor.update(cx, |editor, cx| editor.text(cx)),
 9074        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 9075    );
 9076}
 9077
 9078#[gpui::test]
 9079async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 9080    init_test(cx, |_| {});
 9081
 9082    let mut cx = EditorLspTestContext::new_rust(
 9083        lsp::ServerCapabilities {
 9084            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9085            ..Default::default()
 9086        },
 9087        cx,
 9088    )
 9089    .await;
 9090
 9091    cx.set_state(indoc! {"
 9092        one.twoˇ
 9093    "});
 9094
 9095    // The format request takes a long time. When it completes, it inserts
 9096    // a newline and an indent before the `.`
 9097    cx.lsp
 9098        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 9099            let executor = cx.background_executor().clone();
 9100            async move {
 9101                executor.timer(Duration::from_millis(100)).await;
 9102                Ok(Some(vec![lsp::TextEdit {
 9103                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 9104                    new_text: "\n    ".into(),
 9105                }]))
 9106            }
 9107        });
 9108
 9109    // Submit a format request.
 9110    let format_1 = cx
 9111        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9112        .unwrap();
 9113    cx.executor().run_until_parked();
 9114
 9115    // Submit a second format request.
 9116    let format_2 = cx
 9117        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9118        .unwrap();
 9119    cx.executor().run_until_parked();
 9120
 9121    // Wait for both format requests to complete
 9122    cx.executor().advance_clock(Duration::from_millis(200));
 9123    cx.executor().start_waiting();
 9124    format_1.await.unwrap();
 9125    cx.executor().start_waiting();
 9126    format_2.await.unwrap();
 9127
 9128    // The formatting edits only happens once.
 9129    cx.assert_editor_state(indoc! {"
 9130        one
 9131            .twoˇ
 9132    "});
 9133}
 9134
 9135#[gpui::test]
 9136async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 9137    init_test(cx, |settings| {
 9138        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 9139    });
 9140
 9141    let mut cx = EditorLspTestContext::new_rust(
 9142        lsp::ServerCapabilities {
 9143            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 9144            ..Default::default()
 9145        },
 9146        cx,
 9147    )
 9148    .await;
 9149
 9150    // Set up a buffer white some trailing whitespace and no trailing newline.
 9151    cx.set_state(
 9152        &[
 9153            "one ",   //
 9154            "twoˇ",   //
 9155            "three ", //
 9156            "four",   //
 9157        ]
 9158        .join("\n"),
 9159    );
 9160
 9161    // Submit a format request.
 9162    let format = cx
 9163        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 9164        .unwrap();
 9165
 9166    // Record which buffer changes have been sent to the language server
 9167    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 9168    cx.lsp
 9169        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 9170            let buffer_changes = buffer_changes.clone();
 9171            move |params, _| {
 9172                buffer_changes.lock().extend(
 9173                    params
 9174                        .content_changes
 9175                        .into_iter()
 9176                        .map(|e| (e.range.unwrap(), e.text)),
 9177                );
 9178            }
 9179        });
 9180
 9181    // Handle formatting requests to the language server.
 9182    cx.lsp
 9183        .set_request_handler::<lsp::request::Formatting, _, _>({
 9184            let buffer_changes = buffer_changes.clone();
 9185            move |_, _| {
 9186                // When formatting is requested, trailing whitespace has already been stripped,
 9187                // and the trailing newline has already been added.
 9188                assert_eq!(
 9189                    &buffer_changes.lock()[1..],
 9190                    &[
 9191                        (
 9192                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 9193                            "".into()
 9194                        ),
 9195                        (
 9196                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 9197                            "".into()
 9198                        ),
 9199                        (
 9200                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 9201                            "\n".into()
 9202                        ),
 9203                    ]
 9204                );
 9205
 9206                // Insert blank lines between each line of the buffer.
 9207                async move {
 9208                    Ok(Some(vec![
 9209                        lsp::TextEdit {
 9210                            range: lsp::Range::new(
 9211                                lsp::Position::new(1, 0),
 9212                                lsp::Position::new(1, 0),
 9213                            ),
 9214                            new_text: "\n".into(),
 9215                        },
 9216                        lsp::TextEdit {
 9217                            range: lsp::Range::new(
 9218                                lsp::Position::new(2, 0),
 9219                                lsp::Position::new(2, 0),
 9220                            ),
 9221                            new_text: "\n".into(),
 9222                        },
 9223                    ]))
 9224                }
 9225            }
 9226        });
 9227
 9228    // After formatting the buffer, the trailing whitespace is stripped,
 9229    // a newline is appended, and the edits provided by the language server
 9230    // have been applied.
 9231    format.await.unwrap();
 9232    cx.assert_editor_state(
 9233        &[
 9234            "one",   //
 9235            "",      //
 9236            "twoˇ",  //
 9237            "",      //
 9238            "three", //
 9239            "four",  //
 9240            "",      //
 9241        ]
 9242        .join("\n"),
 9243    );
 9244
 9245    // Undoing the formatting undoes the trailing whitespace removal, the
 9246    // trailing newline, and the LSP edits.
 9247    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9248    cx.assert_editor_state(
 9249        &[
 9250            "one ",   //
 9251            "twoˇ",   //
 9252            "three ", //
 9253            "four",   //
 9254        ]
 9255        .join("\n"),
 9256    );
 9257}
 9258
 9259#[gpui::test]
 9260async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9261    cx: &mut TestAppContext,
 9262) {
 9263    init_test(cx, |_| {});
 9264
 9265    cx.update(|cx| {
 9266        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9267            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9268                settings.auto_signature_help = Some(true);
 9269            });
 9270        });
 9271    });
 9272
 9273    let mut cx = EditorLspTestContext::new_rust(
 9274        lsp::ServerCapabilities {
 9275            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9276                ..Default::default()
 9277            }),
 9278            ..Default::default()
 9279        },
 9280        cx,
 9281    )
 9282    .await;
 9283
 9284    let language = Language::new(
 9285        LanguageConfig {
 9286            name: "Rust".into(),
 9287            brackets: BracketPairConfig {
 9288                pairs: vec![
 9289                    BracketPair {
 9290                        start: "{".to_string(),
 9291                        end: "}".to_string(),
 9292                        close: true,
 9293                        surround: true,
 9294                        newline: true,
 9295                    },
 9296                    BracketPair {
 9297                        start: "(".to_string(),
 9298                        end: ")".to_string(),
 9299                        close: true,
 9300                        surround: true,
 9301                        newline: true,
 9302                    },
 9303                    BracketPair {
 9304                        start: "/*".to_string(),
 9305                        end: " */".to_string(),
 9306                        close: true,
 9307                        surround: true,
 9308                        newline: true,
 9309                    },
 9310                    BracketPair {
 9311                        start: "[".to_string(),
 9312                        end: "]".to_string(),
 9313                        close: false,
 9314                        surround: false,
 9315                        newline: true,
 9316                    },
 9317                    BracketPair {
 9318                        start: "\"".to_string(),
 9319                        end: "\"".to_string(),
 9320                        close: true,
 9321                        surround: true,
 9322                        newline: false,
 9323                    },
 9324                    BracketPair {
 9325                        start: "<".to_string(),
 9326                        end: ">".to_string(),
 9327                        close: false,
 9328                        surround: true,
 9329                        newline: true,
 9330                    },
 9331                ],
 9332                ..Default::default()
 9333            },
 9334            autoclose_before: "})]".to_string(),
 9335            ..Default::default()
 9336        },
 9337        Some(tree_sitter_rust::LANGUAGE.into()),
 9338    );
 9339    let language = Arc::new(language);
 9340
 9341    cx.language_registry().add(language.clone());
 9342    cx.update_buffer(|buffer, cx| {
 9343        buffer.set_language(Some(language), cx);
 9344    });
 9345
 9346    cx.set_state(
 9347        &r#"
 9348            fn main() {
 9349                sampleˇ
 9350            }
 9351        "#
 9352        .unindent(),
 9353    );
 9354
 9355    cx.update_editor(|editor, window, cx| {
 9356        editor.handle_input("(", window, cx);
 9357    });
 9358    cx.assert_editor_state(
 9359        &"
 9360            fn main() {
 9361                sample(ˇ)
 9362            }
 9363        "
 9364        .unindent(),
 9365    );
 9366
 9367    let mocked_response = lsp::SignatureHelp {
 9368        signatures: vec![lsp::SignatureInformation {
 9369            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9370            documentation: None,
 9371            parameters: Some(vec![
 9372                lsp::ParameterInformation {
 9373                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9374                    documentation: None,
 9375                },
 9376                lsp::ParameterInformation {
 9377                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9378                    documentation: None,
 9379                },
 9380            ]),
 9381            active_parameter: None,
 9382        }],
 9383        active_signature: Some(0),
 9384        active_parameter: Some(0),
 9385    };
 9386    handle_signature_help_request(&mut cx, mocked_response).await;
 9387
 9388    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9389        .await;
 9390
 9391    cx.editor(|editor, _, _| {
 9392        let signature_help_state = editor.signature_help_state.popover().cloned();
 9393        assert_eq!(
 9394            signature_help_state.unwrap().label,
 9395            "param1: u8, param2: u8"
 9396        );
 9397    });
 9398}
 9399
 9400#[gpui::test]
 9401async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9402    init_test(cx, |_| {});
 9403
 9404    cx.update(|cx| {
 9405        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9406            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9407                settings.auto_signature_help = Some(false);
 9408                settings.show_signature_help_after_edits = Some(false);
 9409            });
 9410        });
 9411    });
 9412
 9413    let mut cx = EditorLspTestContext::new_rust(
 9414        lsp::ServerCapabilities {
 9415            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9416                ..Default::default()
 9417            }),
 9418            ..Default::default()
 9419        },
 9420        cx,
 9421    )
 9422    .await;
 9423
 9424    let language = Language::new(
 9425        LanguageConfig {
 9426            name: "Rust".into(),
 9427            brackets: BracketPairConfig {
 9428                pairs: vec![
 9429                    BracketPair {
 9430                        start: "{".to_string(),
 9431                        end: "}".to_string(),
 9432                        close: true,
 9433                        surround: true,
 9434                        newline: true,
 9435                    },
 9436                    BracketPair {
 9437                        start: "(".to_string(),
 9438                        end: ")".to_string(),
 9439                        close: true,
 9440                        surround: true,
 9441                        newline: true,
 9442                    },
 9443                    BracketPair {
 9444                        start: "/*".to_string(),
 9445                        end: " */".to_string(),
 9446                        close: true,
 9447                        surround: true,
 9448                        newline: true,
 9449                    },
 9450                    BracketPair {
 9451                        start: "[".to_string(),
 9452                        end: "]".to_string(),
 9453                        close: false,
 9454                        surround: false,
 9455                        newline: true,
 9456                    },
 9457                    BracketPair {
 9458                        start: "\"".to_string(),
 9459                        end: "\"".to_string(),
 9460                        close: true,
 9461                        surround: true,
 9462                        newline: false,
 9463                    },
 9464                    BracketPair {
 9465                        start: "<".to_string(),
 9466                        end: ">".to_string(),
 9467                        close: false,
 9468                        surround: true,
 9469                        newline: true,
 9470                    },
 9471                ],
 9472                ..Default::default()
 9473            },
 9474            autoclose_before: "})]".to_string(),
 9475            ..Default::default()
 9476        },
 9477        Some(tree_sitter_rust::LANGUAGE.into()),
 9478    );
 9479    let language = Arc::new(language);
 9480
 9481    cx.language_registry().add(language.clone());
 9482    cx.update_buffer(|buffer, cx| {
 9483        buffer.set_language(Some(language), cx);
 9484    });
 9485
 9486    // Ensure that signature_help is not called when no signature help is enabled.
 9487    cx.set_state(
 9488        &r#"
 9489            fn main() {
 9490                sampleˇ
 9491            }
 9492        "#
 9493        .unindent(),
 9494    );
 9495    cx.update_editor(|editor, window, cx| {
 9496        editor.handle_input("(", window, cx);
 9497    });
 9498    cx.assert_editor_state(
 9499        &"
 9500            fn main() {
 9501                sample(ˇ)
 9502            }
 9503        "
 9504        .unindent(),
 9505    );
 9506    cx.editor(|editor, _, _| {
 9507        assert!(editor.signature_help_state.task().is_none());
 9508    });
 9509
 9510    let mocked_response = lsp::SignatureHelp {
 9511        signatures: vec![lsp::SignatureInformation {
 9512            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9513            documentation: None,
 9514            parameters: Some(vec![
 9515                lsp::ParameterInformation {
 9516                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9517                    documentation: None,
 9518                },
 9519                lsp::ParameterInformation {
 9520                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9521                    documentation: None,
 9522                },
 9523            ]),
 9524            active_parameter: None,
 9525        }],
 9526        active_signature: Some(0),
 9527        active_parameter: Some(0),
 9528    };
 9529
 9530    // Ensure that signature_help is called when enabled afte edits
 9531    cx.update(|_, cx| {
 9532        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9533            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9534                settings.auto_signature_help = Some(false);
 9535                settings.show_signature_help_after_edits = Some(true);
 9536            });
 9537        });
 9538    });
 9539    cx.set_state(
 9540        &r#"
 9541            fn main() {
 9542                sampleˇ
 9543            }
 9544        "#
 9545        .unindent(),
 9546    );
 9547    cx.update_editor(|editor, window, cx| {
 9548        editor.handle_input("(", window, cx);
 9549    });
 9550    cx.assert_editor_state(
 9551        &"
 9552            fn main() {
 9553                sample(ˇ)
 9554            }
 9555        "
 9556        .unindent(),
 9557    );
 9558    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9559    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9560        .await;
 9561    cx.update_editor(|editor, _, _| {
 9562        let signature_help_state = editor.signature_help_state.popover().cloned();
 9563        assert!(signature_help_state.is_some());
 9564        assert_eq!(
 9565            signature_help_state.unwrap().label,
 9566            "param1: u8, param2: u8"
 9567        );
 9568        editor.signature_help_state = SignatureHelpState::default();
 9569    });
 9570
 9571    // Ensure that signature_help is called when auto signature help override is enabled
 9572    cx.update(|_, cx| {
 9573        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9574            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9575                settings.auto_signature_help = Some(true);
 9576                settings.show_signature_help_after_edits = Some(false);
 9577            });
 9578        });
 9579    });
 9580    cx.set_state(
 9581        &r#"
 9582            fn main() {
 9583                sampleˇ
 9584            }
 9585        "#
 9586        .unindent(),
 9587    );
 9588    cx.update_editor(|editor, window, cx| {
 9589        editor.handle_input("(", window, cx);
 9590    });
 9591    cx.assert_editor_state(
 9592        &"
 9593            fn main() {
 9594                sample(ˇ)
 9595            }
 9596        "
 9597        .unindent(),
 9598    );
 9599    handle_signature_help_request(&mut cx, mocked_response).await;
 9600    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9601        .await;
 9602    cx.editor(|editor, _, _| {
 9603        let signature_help_state = editor.signature_help_state.popover().cloned();
 9604        assert!(signature_help_state.is_some());
 9605        assert_eq!(
 9606            signature_help_state.unwrap().label,
 9607            "param1: u8, param2: u8"
 9608        );
 9609    });
 9610}
 9611
 9612#[gpui::test]
 9613async fn test_signature_help(cx: &mut TestAppContext) {
 9614    init_test(cx, |_| {});
 9615    cx.update(|cx| {
 9616        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9617            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9618                settings.auto_signature_help = Some(true);
 9619            });
 9620        });
 9621    });
 9622
 9623    let mut cx = EditorLspTestContext::new_rust(
 9624        lsp::ServerCapabilities {
 9625            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9626                ..Default::default()
 9627            }),
 9628            ..Default::default()
 9629        },
 9630        cx,
 9631    )
 9632    .await;
 9633
 9634    // A test that directly calls `show_signature_help`
 9635    cx.update_editor(|editor, window, cx| {
 9636        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9637    });
 9638
 9639    let mocked_response = lsp::SignatureHelp {
 9640        signatures: vec![lsp::SignatureInformation {
 9641            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9642            documentation: None,
 9643            parameters: Some(vec![
 9644                lsp::ParameterInformation {
 9645                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9646                    documentation: None,
 9647                },
 9648                lsp::ParameterInformation {
 9649                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9650                    documentation: None,
 9651                },
 9652            ]),
 9653            active_parameter: None,
 9654        }],
 9655        active_signature: Some(0),
 9656        active_parameter: Some(0),
 9657    };
 9658    handle_signature_help_request(&mut cx, mocked_response).await;
 9659
 9660    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9661        .await;
 9662
 9663    cx.editor(|editor, _, _| {
 9664        let signature_help_state = editor.signature_help_state.popover().cloned();
 9665        assert!(signature_help_state.is_some());
 9666        assert_eq!(
 9667            signature_help_state.unwrap().label,
 9668            "param1: u8, param2: u8"
 9669        );
 9670    });
 9671
 9672    // When exiting outside from inside the brackets, `signature_help` is closed.
 9673    cx.set_state(indoc! {"
 9674        fn main() {
 9675            sample(ˇ);
 9676        }
 9677
 9678        fn sample(param1: u8, param2: u8) {}
 9679    "});
 9680
 9681    cx.update_editor(|editor, window, cx| {
 9682        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9683    });
 9684
 9685    let mocked_response = lsp::SignatureHelp {
 9686        signatures: Vec::new(),
 9687        active_signature: None,
 9688        active_parameter: None,
 9689    };
 9690    handle_signature_help_request(&mut cx, mocked_response).await;
 9691
 9692    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9693        .await;
 9694
 9695    cx.editor(|editor, _, _| {
 9696        assert!(!editor.signature_help_state.is_shown());
 9697    });
 9698
 9699    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9700    cx.set_state(indoc! {"
 9701        fn main() {
 9702            sample(ˇ);
 9703        }
 9704
 9705        fn sample(param1: u8, param2: u8) {}
 9706    "});
 9707
 9708    let mocked_response = lsp::SignatureHelp {
 9709        signatures: vec![lsp::SignatureInformation {
 9710            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9711            documentation: None,
 9712            parameters: Some(vec![
 9713                lsp::ParameterInformation {
 9714                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9715                    documentation: None,
 9716                },
 9717                lsp::ParameterInformation {
 9718                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9719                    documentation: None,
 9720                },
 9721            ]),
 9722            active_parameter: None,
 9723        }],
 9724        active_signature: Some(0),
 9725        active_parameter: Some(0),
 9726    };
 9727    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9728    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9729        .await;
 9730    cx.editor(|editor, _, _| {
 9731        assert!(editor.signature_help_state.is_shown());
 9732    });
 9733
 9734    // Restore the popover with more parameter input
 9735    cx.set_state(indoc! {"
 9736        fn main() {
 9737            sample(param1, param2ˇ);
 9738        }
 9739
 9740        fn sample(param1: u8, param2: u8) {}
 9741    "});
 9742
 9743    let mocked_response = lsp::SignatureHelp {
 9744        signatures: vec![lsp::SignatureInformation {
 9745            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9746            documentation: None,
 9747            parameters: Some(vec![
 9748                lsp::ParameterInformation {
 9749                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9750                    documentation: None,
 9751                },
 9752                lsp::ParameterInformation {
 9753                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9754                    documentation: None,
 9755                },
 9756            ]),
 9757            active_parameter: None,
 9758        }],
 9759        active_signature: Some(0),
 9760        active_parameter: Some(1),
 9761    };
 9762    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9763    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9764        .await;
 9765
 9766    // When selecting a range, the popover is gone.
 9767    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9768    cx.update_editor(|editor, window, cx| {
 9769        editor.change_selections(None, window, cx, |s| {
 9770            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9771        })
 9772    });
 9773    cx.assert_editor_state(indoc! {"
 9774        fn main() {
 9775            sample(param1, «ˇparam2»);
 9776        }
 9777
 9778        fn sample(param1: u8, param2: u8) {}
 9779    "});
 9780    cx.editor(|editor, _, _| {
 9781        assert!(!editor.signature_help_state.is_shown());
 9782    });
 9783
 9784    // When unselecting again, the popover is back if within the brackets.
 9785    cx.update_editor(|editor, window, cx| {
 9786        editor.change_selections(None, window, cx, |s| {
 9787            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9788        })
 9789    });
 9790    cx.assert_editor_state(indoc! {"
 9791        fn main() {
 9792            sample(param1, ˇparam2);
 9793        }
 9794
 9795        fn sample(param1: u8, param2: u8) {}
 9796    "});
 9797    handle_signature_help_request(&mut cx, mocked_response).await;
 9798    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9799        .await;
 9800    cx.editor(|editor, _, _| {
 9801        assert!(editor.signature_help_state.is_shown());
 9802    });
 9803
 9804    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9805    cx.update_editor(|editor, window, cx| {
 9806        editor.change_selections(None, window, cx, |s| {
 9807            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9808            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9809        })
 9810    });
 9811    cx.assert_editor_state(indoc! {"
 9812        fn main() {
 9813            sample(param1, ˇparam2);
 9814        }
 9815
 9816        fn sample(param1: u8, param2: u8) {}
 9817    "});
 9818
 9819    let mocked_response = lsp::SignatureHelp {
 9820        signatures: vec![lsp::SignatureInformation {
 9821            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9822            documentation: None,
 9823            parameters: Some(vec![
 9824                lsp::ParameterInformation {
 9825                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9826                    documentation: None,
 9827                },
 9828                lsp::ParameterInformation {
 9829                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9830                    documentation: None,
 9831                },
 9832            ]),
 9833            active_parameter: None,
 9834        }],
 9835        active_signature: Some(0),
 9836        active_parameter: Some(1),
 9837    };
 9838    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9839    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9840        .await;
 9841    cx.update_editor(|editor, _, cx| {
 9842        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9843    });
 9844    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9845        .await;
 9846    cx.update_editor(|editor, window, cx| {
 9847        editor.change_selections(None, window, cx, |s| {
 9848            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9849        })
 9850    });
 9851    cx.assert_editor_state(indoc! {"
 9852        fn main() {
 9853            sample(param1, «ˇparam2»);
 9854        }
 9855
 9856        fn sample(param1: u8, param2: u8) {}
 9857    "});
 9858    cx.update_editor(|editor, window, cx| {
 9859        editor.change_selections(None, window, cx, |s| {
 9860            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9861        })
 9862    });
 9863    cx.assert_editor_state(indoc! {"
 9864        fn main() {
 9865            sample(param1, ˇparam2);
 9866        }
 9867
 9868        fn sample(param1: u8, param2: u8) {}
 9869    "});
 9870    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9871        .await;
 9872}
 9873
 9874#[gpui::test]
 9875async fn test_completion_mode(cx: &mut TestAppContext) {
 9876    init_test(cx, |_| {});
 9877    let mut cx = EditorLspTestContext::new_rust(
 9878        lsp::ServerCapabilities {
 9879            completion_provider: Some(lsp::CompletionOptions {
 9880                resolve_provider: Some(true),
 9881                ..Default::default()
 9882            }),
 9883            ..Default::default()
 9884        },
 9885        cx,
 9886    )
 9887    .await;
 9888
 9889    struct Run {
 9890        run_description: &'static str,
 9891        initial_state: String,
 9892        buffer_marked_text: String,
 9893        completion_text: &'static str,
 9894        expected_with_insert_mode: String,
 9895        expected_with_replace_mode: String,
 9896        expected_with_replace_subsequence_mode: String,
 9897        expected_with_replace_suffix_mode: String,
 9898    }
 9899
 9900    let runs = [
 9901        Run {
 9902            run_description: "Start of word matches completion text",
 9903            initial_state: "before ediˇ after".into(),
 9904            buffer_marked_text: "before <edi|> after".into(),
 9905            completion_text: "editor",
 9906            expected_with_insert_mode: "before editorˇ after".into(),
 9907            expected_with_replace_mode: "before editorˇ after".into(),
 9908            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9909            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9910        },
 9911        Run {
 9912            run_description: "Accept same text at the middle of the word",
 9913            initial_state: "before ediˇtor after".into(),
 9914            buffer_marked_text: "before <edi|tor> after".into(),
 9915            completion_text: "editor",
 9916            expected_with_insert_mode: "before editorˇtor after".into(),
 9917            expected_with_replace_mode: "before editorˇ after".into(),
 9918            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9919            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9920        },
 9921        Run {
 9922            run_description: "End of word matches completion text -- cursor at end",
 9923            initial_state: "before torˇ after".into(),
 9924            buffer_marked_text: "before <tor|> after".into(),
 9925            completion_text: "editor",
 9926            expected_with_insert_mode: "before editorˇ after".into(),
 9927            expected_with_replace_mode: "before editorˇ after".into(),
 9928            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9929            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9930        },
 9931        Run {
 9932            run_description: "End of word matches completion text -- cursor at start",
 9933            initial_state: "before ˇtor after".into(),
 9934            buffer_marked_text: "before <|tor> after".into(),
 9935            completion_text: "editor",
 9936            expected_with_insert_mode: "before editorˇtor after".into(),
 9937            expected_with_replace_mode: "before editorˇ after".into(),
 9938            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9939            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9940        },
 9941        Run {
 9942            run_description: "Prepend text containing whitespace",
 9943            initial_state: "pˇfield: bool".into(),
 9944            buffer_marked_text: "<p|field>: bool".into(),
 9945            completion_text: "pub ",
 9946            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9947            expected_with_replace_mode: "pub ˇ: bool".into(),
 9948            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9949            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9950        },
 9951        Run {
 9952            run_description: "Add element to start of list",
 9953            initial_state: "[element_ˇelement_2]".into(),
 9954            buffer_marked_text: "[<element_|element_2>]".into(),
 9955            completion_text: "element_1",
 9956            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9957            expected_with_replace_mode: "[element_1ˇ]".into(),
 9958            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9959            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9960        },
 9961        Run {
 9962            run_description: "Add element to start of list -- first and second elements are equal",
 9963            initial_state: "[elˇelement]".into(),
 9964            buffer_marked_text: "[<el|element>]".into(),
 9965            completion_text: "element",
 9966            expected_with_insert_mode: "[elementˇelement]".into(),
 9967            expected_with_replace_mode: "[elementˇ]".into(),
 9968            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9969            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9970        },
 9971        Run {
 9972            run_description: "Ends with matching suffix",
 9973            initial_state: "SubˇError".into(),
 9974            buffer_marked_text: "<Sub|Error>".into(),
 9975            completion_text: "SubscriptionError",
 9976            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9977            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9978            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9979            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9980        },
 9981        Run {
 9982            run_description: "Suffix is a subsequence -- contiguous",
 9983            initial_state: "SubˇErr".into(),
 9984            buffer_marked_text: "<Sub|Err>".into(),
 9985            completion_text: "SubscriptionError",
 9986            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9987            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9988            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9989            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9990        },
 9991        Run {
 9992            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9993            initial_state: "Suˇscrirr".into(),
 9994            buffer_marked_text: "<Su|scrirr>".into(),
 9995            completion_text: "SubscriptionError",
 9996            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9997            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9998            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9999            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
10000        },
10001        Run {
10002            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
10003            initial_state: "foo(indˇix)".into(),
10004            buffer_marked_text: "foo(<ind|ix>)".into(),
10005            completion_text: "node_index",
10006            expected_with_insert_mode: "foo(node_indexˇix)".into(),
10007            expected_with_replace_mode: "foo(node_indexˇ)".into(),
10008            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
10009            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
10010        },
10011    ];
10012
10013    for run in runs {
10014        let run_variations = [
10015            (LspInsertMode::Insert, run.expected_with_insert_mode),
10016            (LspInsertMode::Replace, run.expected_with_replace_mode),
10017            (
10018                LspInsertMode::ReplaceSubsequence,
10019                run.expected_with_replace_subsequence_mode,
10020            ),
10021            (
10022                LspInsertMode::ReplaceSuffix,
10023                run.expected_with_replace_suffix_mode,
10024            ),
10025        ];
10026
10027        for (lsp_insert_mode, expected_text) in run_variations {
10028            eprintln!(
10029                "run = {:?}, mode = {lsp_insert_mode:.?}",
10030                run.run_description,
10031            );
10032
10033            update_test_language_settings(&mut cx, |settings| {
10034                settings.defaults.completions = Some(CompletionSettings {
10035                    lsp_insert_mode,
10036                    words: WordsCompletionMode::Disabled,
10037                    lsp: true,
10038                    lsp_fetch_timeout_ms: 0,
10039                });
10040            });
10041
10042            cx.set_state(&run.initial_state);
10043            cx.update_editor(|editor, window, cx| {
10044                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10045            });
10046
10047            let counter = Arc::new(AtomicUsize::new(0));
10048            handle_completion_request_with_insert_and_replace(
10049                &mut cx,
10050                &run.buffer_marked_text,
10051                vec![run.completion_text],
10052                counter.clone(),
10053            )
10054            .await;
10055            cx.condition(|editor, _| editor.context_menu_visible())
10056                .await;
10057            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10058
10059            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10060                editor
10061                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
10062                    .unwrap()
10063            });
10064            cx.assert_editor_state(&expected_text);
10065            handle_resolve_completion_request(&mut cx, None).await;
10066            apply_additional_edits.await.unwrap();
10067        }
10068    }
10069}
10070
10071#[gpui::test]
10072async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
10073    init_test(cx, |_| {});
10074    let mut cx = EditorLspTestContext::new_rust(
10075        lsp::ServerCapabilities {
10076            completion_provider: Some(lsp::CompletionOptions {
10077                resolve_provider: Some(true),
10078                ..Default::default()
10079            }),
10080            ..Default::default()
10081        },
10082        cx,
10083    )
10084    .await;
10085
10086    let initial_state = "SubˇError";
10087    let buffer_marked_text = "<Sub|Error>";
10088    let completion_text = "SubscriptionError";
10089    let expected_with_insert_mode = "SubscriptionErrorˇError";
10090    let expected_with_replace_mode = "SubscriptionErrorˇ";
10091
10092    update_test_language_settings(&mut cx, |settings| {
10093        settings.defaults.completions = Some(CompletionSettings {
10094            words: WordsCompletionMode::Disabled,
10095            // set the opposite here to ensure that the action is overriding the default behavior
10096            lsp_insert_mode: LspInsertMode::Insert,
10097            lsp: true,
10098            lsp_fetch_timeout_ms: 0,
10099        });
10100    });
10101
10102    cx.set_state(initial_state);
10103    cx.update_editor(|editor, window, cx| {
10104        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10105    });
10106
10107    let counter = Arc::new(AtomicUsize::new(0));
10108    handle_completion_request_with_insert_and_replace(
10109        &mut cx,
10110        &buffer_marked_text,
10111        vec![completion_text],
10112        counter.clone(),
10113    )
10114    .await;
10115    cx.condition(|editor, _| editor.context_menu_visible())
10116        .await;
10117    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10118
10119    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10120        editor
10121            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10122            .unwrap()
10123    });
10124    cx.assert_editor_state(&expected_with_replace_mode);
10125    handle_resolve_completion_request(&mut cx, None).await;
10126    apply_additional_edits.await.unwrap();
10127
10128    update_test_language_settings(&mut cx, |settings| {
10129        settings.defaults.completions = Some(CompletionSettings {
10130            words: WordsCompletionMode::Disabled,
10131            // set the opposite here to ensure that the action is overriding the default behavior
10132            lsp_insert_mode: LspInsertMode::Replace,
10133            lsp: true,
10134            lsp_fetch_timeout_ms: 0,
10135        });
10136    });
10137
10138    cx.set_state(initial_state);
10139    cx.update_editor(|editor, window, cx| {
10140        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10141    });
10142    handle_completion_request_with_insert_and_replace(
10143        &mut cx,
10144        &buffer_marked_text,
10145        vec![completion_text],
10146        counter.clone(),
10147    )
10148    .await;
10149    cx.condition(|editor, _| editor.context_menu_visible())
10150        .await;
10151    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10152
10153    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10154        editor
10155            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
10156            .unwrap()
10157    });
10158    cx.assert_editor_state(&expected_with_insert_mode);
10159    handle_resolve_completion_request(&mut cx, None).await;
10160    apply_additional_edits.await.unwrap();
10161}
10162
10163#[gpui::test]
10164async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
10165    init_test(cx, |_| {});
10166    let mut cx = EditorLspTestContext::new_rust(
10167        lsp::ServerCapabilities {
10168            completion_provider: Some(lsp::CompletionOptions {
10169                resolve_provider: Some(true),
10170                ..Default::default()
10171            }),
10172            ..Default::default()
10173        },
10174        cx,
10175    )
10176    .await;
10177
10178    // scenario: surrounding text matches completion text
10179    let completion_text = "to_offset";
10180    let initial_state = indoc! {"
10181        1. buf.to_offˇsuffix
10182        2. buf.to_offˇsuf
10183        3. buf.to_offˇfix
10184        4. buf.to_offˇ
10185        5. into_offˇensive
10186        6. ˇsuffix
10187        7. let ˇ //
10188        8. aaˇzz
10189        9. buf.to_off«zzzzzˇ»suffix
10190        10. buf.«ˇzzzzz»suffix
10191        11. to_off«ˇzzzzz»
10192
10193        buf.to_offˇsuffix  // newest cursor
10194    "};
10195    let completion_marked_buffer = indoc! {"
10196        1. buf.to_offsuffix
10197        2. buf.to_offsuf
10198        3. buf.to_offfix
10199        4. buf.to_off
10200        5. into_offensive
10201        6. suffix
10202        7. let  //
10203        8. aazz
10204        9. buf.to_offzzzzzsuffix
10205        10. buf.zzzzzsuffix
10206        11. to_offzzzzz
10207
10208        buf.<to_off|suffix>  // newest cursor
10209    "};
10210    let expected = indoc! {"
10211        1. buf.to_offsetˇ
10212        2. buf.to_offsetˇsuf
10213        3. buf.to_offsetˇfix
10214        4. buf.to_offsetˇ
10215        5. into_offsetˇensive
10216        6. to_offsetˇsuffix
10217        7. let to_offsetˇ //
10218        8. aato_offsetˇzz
10219        9. buf.to_offsetˇ
10220        10. buf.to_offsetˇsuffix
10221        11. to_offsetˇ
10222
10223        buf.to_offsetˇ  // newest cursor
10224    "};
10225    cx.set_state(initial_state);
10226    cx.update_editor(|editor, window, cx| {
10227        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10228    });
10229    handle_completion_request_with_insert_and_replace(
10230        &mut cx,
10231        completion_marked_buffer,
10232        vec![completion_text],
10233        Arc::new(AtomicUsize::new(0)),
10234    )
10235    .await;
10236    cx.condition(|editor, _| editor.context_menu_visible())
10237        .await;
10238    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10239        editor
10240            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10241            .unwrap()
10242    });
10243    cx.assert_editor_state(expected);
10244    handle_resolve_completion_request(&mut cx, None).await;
10245    apply_additional_edits.await.unwrap();
10246
10247    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10248    let completion_text = "foo_and_bar";
10249    let initial_state = indoc! {"
10250        1. ooanbˇ
10251        2. zooanbˇ
10252        3. ooanbˇz
10253        4. zooanbˇz
10254        5. ooanˇ
10255        6. oanbˇ
10256
10257        ooanbˇ
10258    "};
10259    let completion_marked_buffer = indoc! {"
10260        1. ooanb
10261        2. zooanb
10262        3. ooanbz
10263        4. zooanbz
10264        5. ooan
10265        6. oanb
10266
10267        <ooanb|>
10268    "};
10269    let expected = indoc! {"
10270        1. foo_and_barˇ
10271        2. zfoo_and_barˇ
10272        3. foo_and_barˇz
10273        4. zfoo_and_barˇz
10274        5. ooanfoo_and_barˇ
10275        6. oanbfoo_and_barˇ
10276
10277        foo_and_barˇ
10278    "};
10279    cx.set_state(initial_state);
10280    cx.update_editor(|editor, window, cx| {
10281        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10282    });
10283    handle_completion_request_with_insert_and_replace(
10284        &mut cx,
10285        completion_marked_buffer,
10286        vec![completion_text],
10287        Arc::new(AtomicUsize::new(0)),
10288    )
10289    .await;
10290    cx.condition(|editor, _| editor.context_menu_visible())
10291        .await;
10292    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10293        editor
10294            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10295            .unwrap()
10296    });
10297    cx.assert_editor_state(expected);
10298    handle_resolve_completion_request(&mut cx, None).await;
10299    apply_additional_edits.await.unwrap();
10300
10301    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10302    // (expects the same as if it was inserted at the end)
10303    let completion_text = "foo_and_bar";
10304    let initial_state = indoc! {"
10305        1. ooˇanb
10306        2. zooˇanb
10307        3. ooˇanbz
10308        4. zooˇanbz
10309
10310        ooˇanb
10311    "};
10312    let completion_marked_buffer = indoc! {"
10313        1. ooanb
10314        2. zooanb
10315        3. ooanbz
10316        4. zooanbz
10317
10318        <oo|anb>
10319    "};
10320    let expected = indoc! {"
10321        1. foo_and_barˇ
10322        2. zfoo_and_barˇ
10323        3. foo_and_barˇz
10324        4. zfoo_and_barˇz
10325
10326        foo_and_barˇ
10327    "};
10328    cx.set_state(initial_state);
10329    cx.update_editor(|editor, window, cx| {
10330        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10331    });
10332    handle_completion_request_with_insert_and_replace(
10333        &mut cx,
10334        completion_marked_buffer,
10335        vec![completion_text],
10336        Arc::new(AtomicUsize::new(0)),
10337    )
10338    .await;
10339    cx.condition(|editor, _| editor.context_menu_visible())
10340        .await;
10341    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10342        editor
10343            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10344            .unwrap()
10345    });
10346    cx.assert_editor_state(expected);
10347    handle_resolve_completion_request(&mut cx, None).await;
10348    apply_additional_edits.await.unwrap();
10349}
10350
10351// This used to crash
10352#[gpui::test]
10353async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10354    init_test(cx, |_| {});
10355
10356    let buffer_text = indoc! {"
10357        fn main() {
10358            10.satu;
10359
10360            //
10361            // separate cursors so they open in different excerpts (manually reproducible)
10362            //
10363
10364            10.satu20;
10365        }
10366    "};
10367    let multibuffer_text_with_selections = indoc! {"
10368        fn main() {
10369            10.satuˇ;
10370
10371            //
10372
10373            //
10374
10375            10.satuˇ20;
10376        }
10377    "};
10378    let expected_multibuffer = indoc! {"
10379        fn main() {
10380            10.saturating_sub()ˇ;
10381
10382            //
10383
10384            //
10385
10386            10.saturating_sub()ˇ;
10387        }
10388    "};
10389
10390    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10391    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10392
10393    let fs = FakeFs::new(cx.executor());
10394    fs.insert_tree(
10395        path!("/a"),
10396        json!({
10397            "main.rs": buffer_text,
10398        }),
10399    )
10400    .await;
10401
10402    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10403    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10404    language_registry.add(rust_lang());
10405    let mut fake_servers = language_registry.register_fake_lsp(
10406        "Rust",
10407        FakeLspAdapter {
10408            capabilities: lsp::ServerCapabilities {
10409                completion_provider: Some(lsp::CompletionOptions {
10410                    resolve_provider: None,
10411                    ..lsp::CompletionOptions::default()
10412                }),
10413                ..lsp::ServerCapabilities::default()
10414            },
10415            ..FakeLspAdapter::default()
10416        },
10417    );
10418    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10419    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10420    let buffer = project
10421        .update(cx, |project, cx| {
10422            project.open_local_buffer(path!("/a/main.rs"), cx)
10423        })
10424        .await
10425        .unwrap();
10426
10427    let multi_buffer = cx.new(|cx| {
10428        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10429        multi_buffer.push_excerpts(
10430            buffer.clone(),
10431            [ExcerptRange::new(0..first_excerpt_end)],
10432            cx,
10433        );
10434        multi_buffer.push_excerpts(
10435            buffer.clone(),
10436            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10437            cx,
10438        );
10439        multi_buffer
10440    });
10441
10442    let editor = workspace
10443        .update(cx, |_, window, cx| {
10444            cx.new(|cx| {
10445                Editor::new(
10446                    EditorMode::Full {
10447                        scale_ui_elements_with_buffer_font_size: false,
10448                        show_active_line_background: false,
10449                        sized_by_content: false,
10450                    },
10451                    multi_buffer.clone(),
10452                    Some(project.clone()),
10453                    window,
10454                    cx,
10455                )
10456            })
10457        })
10458        .unwrap();
10459
10460    let pane = workspace
10461        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10462        .unwrap();
10463    pane.update_in(cx, |pane, window, cx| {
10464        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10465    });
10466
10467    let fake_server = fake_servers.next().await.unwrap();
10468
10469    editor.update_in(cx, |editor, window, cx| {
10470        editor.change_selections(None, window, cx, |s| {
10471            s.select_ranges([
10472                Point::new(1, 11)..Point::new(1, 11),
10473                Point::new(7, 11)..Point::new(7, 11),
10474            ])
10475        });
10476
10477        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10478    });
10479
10480    editor.update_in(cx, |editor, window, cx| {
10481        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10482    });
10483
10484    fake_server
10485        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10486            let completion_item = lsp::CompletionItem {
10487                label: "saturating_sub()".into(),
10488                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10489                    lsp::InsertReplaceEdit {
10490                        new_text: "saturating_sub()".to_owned(),
10491                        insert: lsp::Range::new(
10492                            lsp::Position::new(7, 7),
10493                            lsp::Position::new(7, 11),
10494                        ),
10495                        replace: lsp::Range::new(
10496                            lsp::Position::new(7, 7),
10497                            lsp::Position::new(7, 13),
10498                        ),
10499                    },
10500                )),
10501                ..lsp::CompletionItem::default()
10502            };
10503
10504            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10505        })
10506        .next()
10507        .await
10508        .unwrap();
10509
10510    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10511        .await;
10512
10513    editor
10514        .update_in(cx, |editor, window, cx| {
10515            editor
10516                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10517                .unwrap()
10518        })
10519        .await
10520        .unwrap();
10521
10522    editor.update(cx, |editor, cx| {
10523        assert_text_with_selections(editor, expected_multibuffer, cx);
10524    })
10525}
10526
10527#[gpui::test]
10528async fn test_completion(cx: &mut TestAppContext) {
10529    init_test(cx, |_| {});
10530
10531    let mut cx = EditorLspTestContext::new_rust(
10532        lsp::ServerCapabilities {
10533            completion_provider: Some(lsp::CompletionOptions {
10534                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10535                resolve_provider: Some(true),
10536                ..Default::default()
10537            }),
10538            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10539            ..Default::default()
10540        },
10541        cx,
10542    )
10543    .await;
10544    let counter = Arc::new(AtomicUsize::new(0));
10545
10546    cx.set_state(indoc! {"
10547        oneˇ
10548        two
10549        three
10550    "});
10551    cx.simulate_keystroke(".");
10552    handle_completion_request(
10553        &mut cx,
10554        indoc! {"
10555            one.|<>
10556            two
10557            three
10558        "},
10559        vec!["first_completion", "second_completion"],
10560        counter.clone(),
10561    )
10562    .await;
10563    cx.condition(|editor, _| editor.context_menu_visible())
10564        .await;
10565    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10566
10567    let _handler = handle_signature_help_request(
10568        &mut cx,
10569        lsp::SignatureHelp {
10570            signatures: vec![lsp::SignatureInformation {
10571                label: "test signature".to_string(),
10572                documentation: None,
10573                parameters: Some(vec![lsp::ParameterInformation {
10574                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10575                    documentation: None,
10576                }]),
10577                active_parameter: None,
10578            }],
10579            active_signature: None,
10580            active_parameter: None,
10581        },
10582    );
10583    cx.update_editor(|editor, window, cx| {
10584        assert!(
10585            !editor.signature_help_state.is_shown(),
10586            "No signature help was called for"
10587        );
10588        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10589    });
10590    cx.run_until_parked();
10591    cx.update_editor(|editor, _, _| {
10592        assert!(
10593            !editor.signature_help_state.is_shown(),
10594            "No signature help should be shown when completions menu is open"
10595        );
10596    });
10597
10598    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10599        editor.context_menu_next(&Default::default(), window, cx);
10600        editor
10601            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10602            .unwrap()
10603    });
10604    cx.assert_editor_state(indoc! {"
10605        one.second_completionˇ
10606        two
10607        three
10608    "});
10609
10610    handle_resolve_completion_request(
10611        &mut cx,
10612        Some(vec![
10613            (
10614                //This overlaps with the primary completion edit which is
10615                //misbehavior from the LSP spec, test that we filter it out
10616                indoc! {"
10617                    one.second_ˇcompletion
10618                    two
10619                    threeˇ
10620                "},
10621                "overlapping additional edit",
10622            ),
10623            (
10624                indoc! {"
10625                    one.second_completion
10626                    two
10627                    threeˇ
10628                "},
10629                "\nadditional edit",
10630            ),
10631        ]),
10632    )
10633    .await;
10634    apply_additional_edits.await.unwrap();
10635    cx.assert_editor_state(indoc! {"
10636        one.second_completionˇ
10637        two
10638        three
10639        additional edit
10640    "});
10641
10642    cx.set_state(indoc! {"
10643        one.second_completion
10644        twoˇ
10645        threeˇ
10646        additional edit
10647    "});
10648    cx.simulate_keystroke(" ");
10649    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10650    cx.simulate_keystroke("s");
10651    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10652
10653    cx.assert_editor_state(indoc! {"
10654        one.second_completion
10655        two sˇ
10656        three sˇ
10657        additional edit
10658    "});
10659    handle_completion_request(
10660        &mut cx,
10661        indoc! {"
10662            one.second_completion
10663            two s
10664            three <s|>
10665            additional edit
10666        "},
10667        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10668        counter.clone(),
10669    )
10670    .await;
10671    cx.condition(|editor, _| editor.context_menu_visible())
10672        .await;
10673    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10674
10675    cx.simulate_keystroke("i");
10676
10677    handle_completion_request(
10678        &mut cx,
10679        indoc! {"
10680            one.second_completion
10681            two si
10682            three <si|>
10683            additional edit
10684        "},
10685        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10686        counter.clone(),
10687    )
10688    .await;
10689    cx.condition(|editor, _| editor.context_menu_visible())
10690        .await;
10691    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10692
10693    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10694        editor
10695            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10696            .unwrap()
10697    });
10698    cx.assert_editor_state(indoc! {"
10699        one.second_completion
10700        two sixth_completionˇ
10701        three sixth_completionˇ
10702        additional edit
10703    "});
10704
10705    apply_additional_edits.await.unwrap();
10706
10707    update_test_language_settings(&mut cx, |settings| {
10708        settings.defaults.show_completions_on_input = Some(false);
10709    });
10710    cx.set_state("editorˇ");
10711    cx.simulate_keystroke(".");
10712    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10713    cx.simulate_keystrokes("c l o");
10714    cx.assert_editor_state("editor.cloˇ");
10715    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10716    cx.update_editor(|editor, window, cx| {
10717        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10718    });
10719    handle_completion_request(
10720        &mut cx,
10721        "editor.<clo|>",
10722        vec!["close", "clobber"],
10723        counter.clone(),
10724    )
10725    .await;
10726    cx.condition(|editor, _| editor.context_menu_visible())
10727        .await;
10728    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10729
10730    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10731        editor
10732            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10733            .unwrap()
10734    });
10735    cx.assert_editor_state("editor.closeˇ");
10736    handle_resolve_completion_request(&mut cx, None).await;
10737    apply_additional_edits.await.unwrap();
10738}
10739
10740#[gpui::test]
10741async fn test_word_completion(cx: &mut TestAppContext) {
10742    let lsp_fetch_timeout_ms = 10;
10743    init_test(cx, |language_settings| {
10744        language_settings.defaults.completions = Some(CompletionSettings {
10745            words: WordsCompletionMode::Fallback,
10746            lsp: true,
10747            lsp_fetch_timeout_ms: 10,
10748            lsp_insert_mode: LspInsertMode::Insert,
10749        });
10750    });
10751
10752    let mut cx = EditorLspTestContext::new_rust(
10753        lsp::ServerCapabilities {
10754            completion_provider: Some(lsp::CompletionOptions {
10755                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10756                ..lsp::CompletionOptions::default()
10757            }),
10758            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10759            ..lsp::ServerCapabilities::default()
10760        },
10761        cx,
10762    )
10763    .await;
10764
10765    let throttle_completions = Arc::new(AtomicBool::new(false));
10766
10767    let lsp_throttle_completions = throttle_completions.clone();
10768    let _completion_requests_handler =
10769        cx.lsp
10770            .server
10771            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10772                let lsp_throttle_completions = lsp_throttle_completions.clone();
10773                let cx = cx.clone();
10774                async move {
10775                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10776                        cx.background_executor()
10777                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10778                            .await;
10779                    }
10780                    Ok(Some(lsp::CompletionResponse::Array(vec![
10781                        lsp::CompletionItem {
10782                            label: "first".into(),
10783                            ..lsp::CompletionItem::default()
10784                        },
10785                        lsp::CompletionItem {
10786                            label: "last".into(),
10787                            ..lsp::CompletionItem::default()
10788                        },
10789                    ])))
10790                }
10791            });
10792
10793    cx.set_state(indoc! {"
10794        oneˇ
10795        two
10796        three
10797    "});
10798    cx.simulate_keystroke(".");
10799    cx.executor().run_until_parked();
10800    cx.condition(|editor, _| editor.context_menu_visible())
10801        .await;
10802    cx.update_editor(|editor, window, cx| {
10803        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10804        {
10805            assert_eq!(
10806                completion_menu_entries(&menu),
10807                &["first", "last"],
10808                "When LSP server is fast to reply, no fallback word completions are used"
10809            );
10810        } else {
10811            panic!("expected completion menu to be open");
10812        }
10813        editor.cancel(&Cancel, window, cx);
10814    });
10815    cx.executor().run_until_parked();
10816    cx.condition(|editor, _| !editor.context_menu_visible())
10817        .await;
10818
10819    throttle_completions.store(true, atomic::Ordering::Release);
10820    cx.simulate_keystroke(".");
10821    cx.executor()
10822        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10823    cx.executor().run_until_parked();
10824    cx.condition(|editor, _| editor.context_menu_visible())
10825        .await;
10826    cx.update_editor(|editor, _, _| {
10827        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10828        {
10829            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10830                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10831        } else {
10832            panic!("expected completion menu to be open");
10833        }
10834    });
10835}
10836
10837#[gpui::test]
10838async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10839    init_test(cx, |language_settings| {
10840        language_settings.defaults.completions = Some(CompletionSettings {
10841            words: WordsCompletionMode::Enabled,
10842            lsp: true,
10843            lsp_fetch_timeout_ms: 0,
10844            lsp_insert_mode: LspInsertMode::Insert,
10845        });
10846    });
10847
10848    let mut cx = EditorLspTestContext::new_rust(
10849        lsp::ServerCapabilities {
10850            completion_provider: Some(lsp::CompletionOptions {
10851                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10852                ..lsp::CompletionOptions::default()
10853            }),
10854            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10855            ..lsp::ServerCapabilities::default()
10856        },
10857        cx,
10858    )
10859    .await;
10860
10861    let _completion_requests_handler =
10862        cx.lsp
10863            .server
10864            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10865                Ok(Some(lsp::CompletionResponse::Array(vec![
10866                    lsp::CompletionItem {
10867                        label: "first".into(),
10868                        ..lsp::CompletionItem::default()
10869                    },
10870                    lsp::CompletionItem {
10871                        label: "last".into(),
10872                        ..lsp::CompletionItem::default()
10873                    },
10874                ])))
10875            });
10876
10877    cx.set_state(indoc! {"ˇ
10878        first
10879        last
10880        second
10881    "});
10882    cx.simulate_keystroke(".");
10883    cx.executor().run_until_parked();
10884    cx.condition(|editor, _| editor.context_menu_visible())
10885        .await;
10886    cx.update_editor(|editor, _, _| {
10887        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10888        {
10889            assert_eq!(
10890                completion_menu_entries(&menu),
10891                &["first", "last", "second"],
10892                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10893            );
10894        } else {
10895            panic!("expected completion menu to be open");
10896        }
10897    });
10898}
10899
10900#[gpui::test]
10901async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10902    init_test(cx, |language_settings| {
10903        language_settings.defaults.completions = Some(CompletionSettings {
10904            words: WordsCompletionMode::Disabled,
10905            lsp: true,
10906            lsp_fetch_timeout_ms: 0,
10907            lsp_insert_mode: LspInsertMode::Insert,
10908        });
10909    });
10910
10911    let mut cx = EditorLspTestContext::new_rust(
10912        lsp::ServerCapabilities {
10913            completion_provider: Some(lsp::CompletionOptions {
10914                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10915                ..lsp::CompletionOptions::default()
10916            }),
10917            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10918            ..lsp::ServerCapabilities::default()
10919        },
10920        cx,
10921    )
10922    .await;
10923
10924    let _completion_requests_handler =
10925        cx.lsp
10926            .server
10927            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10928                panic!("LSP completions should not be queried when dealing with word completions")
10929            });
10930
10931    cx.set_state(indoc! {"ˇ
10932        first
10933        last
10934        second
10935    "});
10936    cx.update_editor(|editor, window, cx| {
10937        editor.show_word_completions(&ShowWordCompletions, window, cx);
10938    });
10939    cx.executor().run_until_parked();
10940    cx.condition(|editor, _| editor.context_menu_visible())
10941        .await;
10942    cx.update_editor(|editor, _, _| {
10943        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10944        {
10945            assert_eq!(
10946                completion_menu_entries(&menu),
10947                &["first", "last", "second"],
10948                "`ShowWordCompletions` action should show word completions"
10949            );
10950        } else {
10951            panic!("expected completion menu to be open");
10952        }
10953    });
10954
10955    cx.simulate_keystroke("l");
10956    cx.executor().run_until_parked();
10957    cx.condition(|editor, _| editor.context_menu_visible())
10958        .await;
10959    cx.update_editor(|editor, _, _| {
10960        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10961        {
10962            assert_eq!(
10963                completion_menu_entries(&menu),
10964                &["last"],
10965                "After showing word completions, further editing should filter them and not query the LSP"
10966            );
10967        } else {
10968            panic!("expected completion menu to be open");
10969        }
10970    });
10971}
10972
10973#[gpui::test]
10974async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10975    init_test(cx, |language_settings| {
10976        language_settings.defaults.completions = Some(CompletionSettings {
10977            words: WordsCompletionMode::Fallback,
10978            lsp: false,
10979            lsp_fetch_timeout_ms: 0,
10980            lsp_insert_mode: LspInsertMode::Insert,
10981        });
10982    });
10983
10984    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10985
10986    cx.set_state(indoc! {"ˇ
10987        0_usize
10988        let
10989        33
10990        4.5f32
10991    "});
10992    cx.update_editor(|editor, window, cx| {
10993        editor.show_completions(&ShowCompletions::default(), window, cx);
10994    });
10995    cx.executor().run_until_parked();
10996    cx.condition(|editor, _| editor.context_menu_visible())
10997        .await;
10998    cx.update_editor(|editor, window, cx| {
10999        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11000        {
11001            assert_eq!(
11002                completion_menu_entries(&menu),
11003                &["let"],
11004                "With no digits in the completion query, no digits should be in the word completions"
11005            );
11006        } else {
11007            panic!("expected completion menu to be open");
11008        }
11009        editor.cancel(&Cancel, window, cx);
11010    });
11011
11012    cx.set_state(indoc! {"11013        0_usize
11014        let
11015        3
11016        33.35f32
11017    "});
11018    cx.update_editor(|editor, window, cx| {
11019        editor.show_completions(&ShowCompletions::default(), window, cx);
11020    });
11021    cx.executor().run_until_parked();
11022    cx.condition(|editor, _| editor.context_menu_visible())
11023        .await;
11024    cx.update_editor(|editor, _, _| {
11025        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11026        {
11027            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
11028                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
11029        } else {
11030            panic!("expected completion menu to be open");
11031        }
11032    });
11033}
11034
11035fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
11036    let position = || lsp::Position {
11037        line: params.text_document_position.position.line,
11038        character: params.text_document_position.position.character,
11039    };
11040    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11041        range: lsp::Range {
11042            start: position(),
11043            end: position(),
11044        },
11045        new_text: text.to_string(),
11046    }))
11047}
11048
11049#[gpui::test]
11050async fn test_multiline_completion(cx: &mut TestAppContext) {
11051    init_test(cx, |_| {});
11052
11053    let fs = FakeFs::new(cx.executor());
11054    fs.insert_tree(
11055        path!("/a"),
11056        json!({
11057            "main.ts": "a",
11058        }),
11059    )
11060    .await;
11061
11062    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
11063    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
11064    let typescript_language = Arc::new(Language::new(
11065        LanguageConfig {
11066            name: "TypeScript".into(),
11067            matcher: LanguageMatcher {
11068                path_suffixes: vec!["ts".to_string()],
11069                ..LanguageMatcher::default()
11070            },
11071            line_comments: vec!["// ".into()],
11072            ..LanguageConfig::default()
11073        },
11074        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
11075    ));
11076    language_registry.add(typescript_language.clone());
11077    let mut fake_servers = language_registry.register_fake_lsp(
11078        "TypeScript",
11079        FakeLspAdapter {
11080            capabilities: lsp::ServerCapabilities {
11081                completion_provider: Some(lsp::CompletionOptions {
11082                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
11083                    ..lsp::CompletionOptions::default()
11084                }),
11085                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
11086                ..lsp::ServerCapabilities::default()
11087            },
11088            // Emulate vtsls label generation
11089            label_for_completion: Some(Box::new(|item, _| {
11090                let text = if let Some(description) = item
11091                    .label_details
11092                    .as_ref()
11093                    .and_then(|label_details| label_details.description.as_ref())
11094                {
11095                    format!("{} {}", item.label, description)
11096                } else if let Some(detail) = &item.detail {
11097                    format!("{} {}", item.label, detail)
11098                } else {
11099                    item.label.clone()
11100                };
11101                let len = text.len();
11102                Some(language::CodeLabel {
11103                    text,
11104                    runs: Vec::new(),
11105                    filter_range: 0..len,
11106                })
11107            })),
11108            ..FakeLspAdapter::default()
11109        },
11110    );
11111    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11112    let cx = &mut VisualTestContext::from_window(*workspace, cx);
11113    let worktree_id = workspace
11114        .update(cx, |workspace, _window, cx| {
11115            workspace.project().update(cx, |project, cx| {
11116                project.worktrees(cx).next().unwrap().read(cx).id()
11117            })
11118        })
11119        .unwrap();
11120    let _buffer = project
11121        .update(cx, |project, cx| {
11122            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
11123        })
11124        .await
11125        .unwrap();
11126    let editor = workspace
11127        .update(cx, |workspace, window, cx| {
11128            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
11129        })
11130        .unwrap()
11131        .await
11132        .unwrap()
11133        .downcast::<Editor>()
11134        .unwrap();
11135    let fake_server = fake_servers.next().await.unwrap();
11136
11137    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
11138    let multiline_label_2 = "a\nb\nc\n";
11139    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
11140    let multiline_description = "d\ne\nf\n";
11141    let multiline_detail_2 = "g\nh\ni\n";
11142
11143    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
11144        move |params, _| async move {
11145            Ok(Some(lsp::CompletionResponse::Array(vec![
11146                lsp::CompletionItem {
11147                    label: multiline_label.to_string(),
11148                    text_edit: gen_text_edit(&params, "new_text_1"),
11149                    ..lsp::CompletionItem::default()
11150                },
11151                lsp::CompletionItem {
11152                    label: "single line label 1".to_string(),
11153                    detail: Some(multiline_detail.to_string()),
11154                    text_edit: gen_text_edit(&params, "new_text_2"),
11155                    ..lsp::CompletionItem::default()
11156                },
11157                lsp::CompletionItem {
11158                    label: "single line label 2".to_string(),
11159                    label_details: Some(lsp::CompletionItemLabelDetails {
11160                        description: Some(multiline_description.to_string()),
11161                        detail: None,
11162                    }),
11163                    text_edit: gen_text_edit(&params, "new_text_2"),
11164                    ..lsp::CompletionItem::default()
11165                },
11166                lsp::CompletionItem {
11167                    label: multiline_label_2.to_string(),
11168                    detail: Some(multiline_detail_2.to_string()),
11169                    text_edit: gen_text_edit(&params, "new_text_3"),
11170                    ..lsp::CompletionItem::default()
11171                },
11172                lsp::CompletionItem {
11173                    label: "Label with many     spaces and \t but without newlines".to_string(),
11174                    detail: Some(
11175                        "Details with many     spaces and \t but without newlines".to_string(),
11176                    ),
11177                    text_edit: gen_text_edit(&params, "new_text_4"),
11178                    ..lsp::CompletionItem::default()
11179                },
11180            ])))
11181        },
11182    );
11183
11184    editor.update_in(cx, |editor, window, cx| {
11185        cx.focus_self(window);
11186        editor.move_to_end(&MoveToEnd, window, cx);
11187        editor.handle_input(".", window, cx);
11188    });
11189    cx.run_until_parked();
11190    completion_handle.next().await.unwrap();
11191
11192    editor.update(cx, |editor, _| {
11193        assert!(editor.context_menu_visible());
11194        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11195        {
11196            let completion_labels = menu
11197                .completions
11198                .borrow()
11199                .iter()
11200                .map(|c| c.label.text.clone())
11201                .collect::<Vec<_>>();
11202            assert_eq!(
11203                completion_labels,
11204                &[
11205                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
11206                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
11207                    "single line label 2 d e f ",
11208                    "a b c g h i ",
11209                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11210                ],
11211                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11212            );
11213
11214            for completion in menu
11215                .completions
11216                .borrow()
11217                .iter() {
11218                    assert_eq!(
11219                        completion.label.filter_range,
11220                        0..completion.label.text.len(),
11221                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11222                    );
11223                }
11224        } else {
11225            panic!("expected completion menu to be open");
11226        }
11227    });
11228}
11229
11230#[gpui::test]
11231async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11232    init_test(cx, |_| {});
11233    let mut cx = EditorLspTestContext::new_rust(
11234        lsp::ServerCapabilities {
11235            completion_provider: Some(lsp::CompletionOptions {
11236                trigger_characters: Some(vec![".".to_string()]),
11237                ..Default::default()
11238            }),
11239            ..Default::default()
11240        },
11241        cx,
11242    )
11243    .await;
11244    cx.lsp
11245        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11246            Ok(Some(lsp::CompletionResponse::Array(vec![
11247                lsp::CompletionItem {
11248                    label: "first".into(),
11249                    ..Default::default()
11250                },
11251                lsp::CompletionItem {
11252                    label: "last".into(),
11253                    ..Default::default()
11254                },
11255            ])))
11256        });
11257    cx.set_state("variableˇ");
11258    cx.simulate_keystroke(".");
11259    cx.executor().run_until_parked();
11260
11261    cx.update_editor(|editor, _, _| {
11262        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11263        {
11264            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11265        } else {
11266            panic!("expected completion menu to be open");
11267        }
11268    });
11269
11270    cx.update_editor(|editor, window, cx| {
11271        editor.move_page_down(&MovePageDown::default(), window, cx);
11272        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11273        {
11274            assert!(
11275                menu.selected_item == 1,
11276                "expected PageDown to select the last item from the context menu"
11277            );
11278        } else {
11279            panic!("expected completion menu to stay open after PageDown");
11280        }
11281    });
11282
11283    cx.update_editor(|editor, window, cx| {
11284        editor.move_page_up(&MovePageUp::default(), window, cx);
11285        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11286        {
11287            assert!(
11288                menu.selected_item == 0,
11289                "expected PageUp to select the first item from the context menu"
11290            );
11291        } else {
11292            panic!("expected completion menu to stay open after PageUp");
11293        }
11294    });
11295}
11296
11297#[gpui::test]
11298async fn test_as_is_completions(cx: &mut TestAppContext) {
11299    init_test(cx, |_| {});
11300    let mut cx = EditorLspTestContext::new_rust(
11301        lsp::ServerCapabilities {
11302            completion_provider: Some(lsp::CompletionOptions {
11303                ..Default::default()
11304            }),
11305            ..Default::default()
11306        },
11307        cx,
11308    )
11309    .await;
11310    cx.lsp
11311        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11312            Ok(Some(lsp::CompletionResponse::Array(vec![
11313                lsp::CompletionItem {
11314                    label: "unsafe".into(),
11315                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11316                        range: lsp::Range {
11317                            start: lsp::Position {
11318                                line: 1,
11319                                character: 2,
11320                            },
11321                            end: lsp::Position {
11322                                line: 1,
11323                                character: 3,
11324                            },
11325                        },
11326                        new_text: "unsafe".to_string(),
11327                    })),
11328                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11329                    ..Default::default()
11330                },
11331            ])))
11332        });
11333    cx.set_state("fn a() {}\n");
11334    cx.executor().run_until_parked();
11335    cx.update_editor(|editor, window, cx| {
11336        editor.show_completions(
11337            &ShowCompletions {
11338                trigger: Some("\n".into()),
11339            },
11340            window,
11341            cx,
11342        );
11343    });
11344    cx.executor().run_until_parked();
11345
11346    cx.update_editor(|editor, window, cx| {
11347        editor.confirm_completion(&Default::default(), window, cx)
11348    });
11349    cx.executor().run_until_parked();
11350    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11351}
11352
11353#[gpui::test]
11354async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11355    init_test(cx, |_| {});
11356
11357    let mut cx = EditorLspTestContext::new_rust(
11358        lsp::ServerCapabilities {
11359            completion_provider: Some(lsp::CompletionOptions {
11360                trigger_characters: Some(vec![".".to_string()]),
11361                resolve_provider: Some(true),
11362                ..Default::default()
11363            }),
11364            ..Default::default()
11365        },
11366        cx,
11367    )
11368    .await;
11369
11370    cx.set_state("fn main() { let a = 2ˇ; }");
11371    cx.simulate_keystroke(".");
11372    let completion_item = lsp::CompletionItem {
11373        label: "Some".into(),
11374        kind: Some(lsp::CompletionItemKind::SNIPPET),
11375        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11376        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11377            kind: lsp::MarkupKind::Markdown,
11378            value: "```rust\nSome(2)\n```".to_string(),
11379        })),
11380        deprecated: Some(false),
11381        sort_text: Some("Some".to_string()),
11382        filter_text: Some("Some".to_string()),
11383        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11384        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11385            range: lsp::Range {
11386                start: lsp::Position {
11387                    line: 0,
11388                    character: 22,
11389                },
11390                end: lsp::Position {
11391                    line: 0,
11392                    character: 22,
11393                },
11394            },
11395            new_text: "Some(2)".to_string(),
11396        })),
11397        additional_text_edits: Some(vec![lsp::TextEdit {
11398            range: lsp::Range {
11399                start: lsp::Position {
11400                    line: 0,
11401                    character: 20,
11402                },
11403                end: lsp::Position {
11404                    line: 0,
11405                    character: 22,
11406                },
11407            },
11408            new_text: "".to_string(),
11409        }]),
11410        ..Default::default()
11411    };
11412
11413    let closure_completion_item = completion_item.clone();
11414    let counter = Arc::new(AtomicUsize::new(0));
11415    let counter_clone = counter.clone();
11416    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11417        let task_completion_item = closure_completion_item.clone();
11418        counter_clone.fetch_add(1, atomic::Ordering::Release);
11419        async move {
11420            Ok(Some(lsp::CompletionResponse::Array(vec![
11421                task_completion_item,
11422            ])))
11423        }
11424    });
11425
11426    cx.condition(|editor, _| editor.context_menu_visible())
11427        .await;
11428    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11429    assert!(request.next().await.is_some());
11430    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11431
11432    cx.simulate_keystrokes("S o m");
11433    cx.condition(|editor, _| editor.context_menu_visible())
11434        .await;
11435    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11436    assert!(request.next().await.is_some());
11437    assert!(request.next().await.is_some());
11438    assert!(request.next().await.is_some());
11439    request.close();
11440    assert!(request.next().await.is_none());
11441    assert_eq!(
11442        counter.load(atomic::Ordering::Acquire),
11443        4,
11444        "With the completions menu open, only one LSP request should happen per input"
11445    );
11446}
11447
11448#[gpui::test]
11449async fn test_toggle_comment(cx: &mut TestAppContext) {
11450    init_test(cx, |_| {});
11451    let mut cx = EditorTestContext::new(cx).await;
11452    let language = Arc::new(Language::new(
11453        LanguageConfig {
11454            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11455            ..Default::default()
11456        },
11457        Some(tree_sitter_rust::LANGUAGE.into()),
11458    ));
11459    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11460
11461    // If multiple selections intersect a line, the line is only toggled once.
11462    cx.set_state(indoc! {"
11463        fn a() {
11464            «//b();
11465            ˇ»// «c();
11466            //ˇ»  d();
11467        }
11468    "});
11469
11470    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11471
11472    cx.assert_editor_state(indoc! {"
11473        fn a() {
11474            «b();
11475            c();
11476            ˇ» d();
11477        }
11478    "});
11479
11480    // The comment prefix is inserted at the same column for every line in a
11481    // selection.
11482    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11483
11484    cx.assert_editor_state(indoc! {"
11485        fn a() {
11486            // «b();
11487            // c();
11488            ˇ»//  d();
11489        }
11490    "});
11491
11492    // If a selection ends at the beginning of a line, that line is not toggled.
11493    cx.set_selections_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    // If a selection span a single line and is empty, the line is toggled.
11512    cx.set_state(indoc! {"
11513        fn a() {
11514            a();
11515            b();
11516        ˇ
11517        }
11518    "});
11519
11520    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11521
11522    cx.assert_editor_state(indoc! {"
11523        fn a() {
11524            a();
11525            b();
11526        //•ˇ
11527        }
11528    "});
11529
11530    // If a selection span multiple lines, empty lines are not toggled.
11531    cx.set_state(indoc! {"
11532        fn a() {
11533            «a();
11534
11535            c();ˇ»
11536        }
11537    "});
11538
11539    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11540
11541    cx.assert_editor_state(indoc! {"
11542        fn a() {
11543            // «a();
11544
11545            // c();ˇ»
11546        }
11547    "});
11548
11549    // If a selection includes multiple comment prefixes, all lines are uncommented.
11550    cx.set_state(indoc! {"
11551        fn a() {
11552            «// a();
11553            /// b();
11554            //! c();ˇ»
11555        }
11556    "});
11557
11558    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11559
11560    cx.assert_editor_state(indoc! {"
11561        fn a() {
11562            «a();
11563            b();
11564            c();ˇ»
11565        }
11566    "});
11567}
11568
11569#[gpui::test]
11570async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11571    init_test(cx, |_| {});
11572    let mut cx = EditorTestContext::new(cx).await;
11573    let language = Arc::new(Language::new(
11574        LanguageConfig {
11575            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11576            ..Default::default()
11577        },
11578        Some(tree_sitter_rust::LANGUAGE.into()),
11579    ));
11580    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11581
11582    let toggle_comments = &ToggleComments {
11583        advance_downwards: false,
11584        ignore_indent: true,
11585    };
11586
11587    // If multiple selections intersect a line, the line is only toggled once.
11588    cx.set_state(indoc! {"
11589        fn a() {
11590        //    «b();
11591        //    c();
11592        //    ˇ» d();
11593        }
11594    "});
11595
11596    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11597
11598    cx.assert_editor_state(indoc! {"
11599        fn a() {
11600            «b();
11601            c();
11602            ˇ» d();
11603        }
11604    "});
11605
11606    // The comment prefix is inserted at the beginning of each line
11607    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11608
11609    cx.assert_editor_state(indoc! {"
11610        fn a() {
11611        //    «b();
11612        //    c();
11613        //    ˇ» d();
11614        }
11615    "});
11616
11617    // If a selection ends at the beginning of a line, that line is not toggled.
11618    cx.set_selections_state(indoc! {"
11619        fn a() {
11620        //    b();
11621        //    «c();
11622        ˇ»//     d();
11623        }
11624    "});
11625
11626    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11627
11628    cx.assert_editor_state(indoc! {"
11629        fn a() {
11630        //    b();
11631            «c();
11632        ˇ»//     d();
11633        }
11634    "});
11635
11636    // If a selection span a single line and is empty, the line is toggled.
11637    cx.set_state(indoc! {"
11638        fn a() {
11639            a();
11640            b();
11641        ˇ
11642        }
11643    "});
11644
11645    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11646
11647    cx.assert_editor_state(indoc! {"
11648        fn a() {
11649            a();
11650            b();
11651        //ˇ
11652        }
11653    "});
11654
11655    // If a selection span multiple lines, empty lines are not toggled.
11656    cx.set_state(indoc! {"
11657        fn a() {
11658            «a();
11659
11660            c();ˇ»
11661        }
11662    "});
11663
11664    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11665
11666    cx.assert_editor_state(indoc! {"
11667        fn a() {
11668        //    «a();
11669
11670        //    c();ˇ»
11671        }
11672    "});
11673
11674    // If a selection includes multiple comment prefixes, all lines are uncommented.
11675    cx.set_state(indoc! {"
11676        fn a() {
11677        //    «a();
11678        ///    b();
11679        //!    c();ˇ»
11680        }
11681    "});
11682
11683    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11684
11685    cx.assert_editor_state(indoc! {"
11686        fn a() {
11687            «a();
11688            b();
11689            c();ˇ»
11690        }
11691    "});
11692}
11693
11694#[gpui::test]
11695async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11696    init_test(cx, |_| {});
11697
11698    let language = Arc::new(Language::new(
11699        LanguageConfig {
11700            line_comments: vec!["// ".into()],
11701            ..Default::default()
11702        },
11703        Some(tree_sitter_rust::LANGUAGE.into()),
11704    ));
11705
11706    let mut cx = EditorTestContext::new(cx).await;
11707
11708    cx.language_registry().add(language.clone());
11709    cx.update_buffer(|buffer, cx| {
11710        buffer.set_language(Some(language), cx);
11711    });
11712
11713    let toggle_comments = &ToggleComments {
11714        advance_downwards: true,
11715        ignore_indent: false,
11716    };
11717
11718    // Single cursor on one line -> advance
11719    // Cursor moves horizontally 3 characters as well on non-blank line
11720    cx.set_state(indoc!(
11721        "fn a() {
11722             ˇdog();
11723             cat();
11724        }"
11725    ));
11726    cx.update_editor(|editor, window, cx| {
11727        editor.toggle_comments(toggle_comments, window, cx);
11728    });
11729    cx.assert_editor_state(indoc!(
11730        "fn a() {
11731             // dog();
11732             catˇ();
11733        }"
11734    ));
11735
11736    // Single selection on one line -> don't advance
11737    cx.set_state(indoc!(
11738        "fn a() {
11739             «dog()ˇ»;
11740             cat();
11741        }"
11742    ));
11743    cx.update_editor(|editor, window, cx| {
11744        editor.toggle_comments(toggle_comments, window, cx);
11745    });
11746    cx.assert_editor_state(indoc!(
11747        "fn a() {
11748             // «dog()ˇ»;
11749             cat();
11750        }"
11751    ));
11752
11753    // Multiple cursors on one line -> advance
11754    cx.set_state(indoc!(
11755        "fn a() {
11756             ˇdˇog();
11757             cat();
11758        }"
11759    ));
11760    cx.update_editor(|editor, window, cx| {
11761        editor.toggle_comments(toggle_comments, window, cx);
11762    });
11763    cx.assert_editor_state(indoc!(
11764        "fn a() {
11765             // dog();
11766             catˇ(ˇ);
11767        }"
11768    ));
11769
11770    // Multiple cursors on one line, with selection -> don't advance
11771    cx.set_state(indoc!(
11772        "fn a() {
11773             ˇdˇog«()ˇ»;
11774             cat();
11775        }"
11776    ));
11777    cx.update_editor(|editor, window, cx| {
11778        editor.toggle_comments(toggle_comments, window, cx);
11779    });
11780    cx.assert_editor_state(indoc!(
11781        "fn a() {
11782             // ˇdˇog«()ˇ»;
11783             cat();
11784        }"
11785    ));
11786
11787    // Single cursor on one line -> advance
11788    // Cursor moves to column 0 on blank line
11789    cx.set_state(indoc!(
11790        "fn a() {
11791             ˇdog();
11792
11793             cat();
11794        }"
11795    ));
11796    cx.update_editor(|editor, window, cx| {
11797        editor.toggle_comments(toggle_comments, window, cx);
11798    });
11799    cx.assert_editor_state(indoc!(
11800        "fn a() {
11801             // dog();
11802        ˇ
11803             cat();
11804        }"
11805    ));
11806
11807    // Single cursor on one line -> advance
11808    // Cursor starts and ends at column 0
11809    cx.set_state(indoc!(
11810        "fn a() {
11811         ˇ    dog();
11812             cat();
11813        }"
11814    ));
11815    cx.update_editor(|editor, window, cx| {
11816        editor.toggle_comments(toggle_comments, window, cx);
11817    });
11818    cx.assert_editor_state(indoc!(
11819        "fn a() {
11820             // dog();
11821         ˇ    cat();
11822        }"
11823    ));
11824}
11825
11826#[gpui::test]
11827async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11828    init_test(cx, |_| {});
11829
11830    let mut cx = EditorTestContext::new(cx).await;
11831
11832    let html_language = Arc::new(
11833        Language::new(
11834            LanguageConfig {
11835                name: "HTML".into(),
11836                block_comment: Some(("<!-- ".into(), " -->".into())),
11837                ..Default::default()
11838            },
11839            Some(tree_sitter_html::LANGUAGE.into()),
11840        )
11841        .with_injection_query(
11842            r#"
11843            (script_element
11844                (raw_text) @injection.content
11845                (#set! injection.language "javascript"))
11846            "#,
11847        )
11848        .unwrap(),
11849    );
11850
11851    let javascript_language = Arc::new(Language::new(
11852        LanguageConfig {
11853            name: "JavaScript".into(),
11854            line_comments: vec!["// ".into()],
11855            ..Default::default()
11856        },
11857        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11858    ));
11859
11860    cx.language_registry().add(html_language.clone());
11861    cx.language_registry().add(javascript_language.clone());
11862    cx.update_buffer(|buffer, cx| {
11863        buffer.set_language(Some(html_language), cx);
11864    });
11865
11866    // Toggle comments for empty selections
11867    cx.set_state(
11868        &r#"
11869            <p>A</p>ˇ
11870            <p>B</p>ˇ
11871            <p>C</p>ˇ
11872        "#
11873        .unindent(),
11874    );
11875    cx.update_editor(|editor, window, cx| {
11876        editor.toggle_comments(&ToggleComments::default(), window, cx)
11877    });
11878    cx.assert_editor_state(
11879        &r#"
11880            <!-- <p>A</p>ˇ -->
11881            <!-- <p>B</p>ˇ -->
11882            <!-- <p>C</p>ˇ -->
11883        "#
11884        .unindent(),
11885    );
11886    cx.update_editor(|editor, window, cx| {
11887        editor.toggle_comments(&ToggleComments::default(), window, cx)
11888    });
11889    cx.assert_editor_state(
11890        &r#"
11891            <p>A</p>ˇ
11892            <p>B</p>ˇ
11893            <p>C</p>ˇ
11894        "#
11895        .unindent(),
11896    );
11897
11898    // Toggle comments for mixture of empty and non-empty selections, where
11899    // multiple selections occupy a given line.
11900    cx.set_state(
11901        &r#"
11902            <p>A«</p>
11903            <p>ˇ»B</p>ˇ
11904            <p>C«</p>
11905            <p>ˇ»D</p>ˇ
11906        "#
11907        .unindent(),
11908    );
11909
11910    cx.update_editor(|editor, window, cx| {
11911        editor.toggle_comments(&ToggleComments::default(), window, cx)
11912    });
11913    cx.assert_editor_state(
11914        &r#"
11915            <!-- <p>A«</p>
11916            <p>ˇ»B</p>ˇ -->
11917            <!-- <p>C«</p>
11918            <p>ˇ»D</p>ˇ -->
11919        "#
11920        .unindent(),
11921    );
11922    cx.update_editor(|editor, window, cx| {
11923        editor.toggle_comments(&ToggleComments::default(), window, cx)
11924    });
11925    cx.assert_editor_state(
11926        &r#"
11927            <p>A«</p>
11928            <p>ˇ»B</p>ˇ
11929            <p>C«</p>
11930            <p>ˇ»D</p>ˇ
11931        "#
11932        .unindent(),
11933    );
11934
11935    // Toggle comments when different languages are active for different
11936    // selections.
11937    cx.set_state(
11938        &r#"
11939            ˇ<script>
11940                ˇvar x = new Y();
11941            ˇ</script>
11942        "#
11943        .unindent(),
11944    );
11945    cx.executor().run_until_parked();
11946    cx.update_editor(|editor, window, cx| {
11947        editor.toggle_comments(&ToggleComments::default(), window, cx)
11948    });
11949    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11950    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11951    cx.assert_editor_state(
11952        &r#"
11953            <!-- ˇ<script> -->
11954                // ˇvar x = new Y();
11955            <!-- ˇ</script> -->
11956        "#
11957        .unindent(),
11958    );
11959}
11960
11961#[gpui::test]
11962fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11963    init_test(cx, |_| {});
11964
11965    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11966    let multibuffer = cx.new(|cx| {
11967        let mut multibuffer = MultiBuffer::new(ReadWrite);
11968        multibuffer.push_excerpts(
11969            buffer.clone(),
11970            [
11971                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11972                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11973            ],
11974            cx,
11975        );
11976        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11977        multibuffer
11978    });
11979
11980    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11981    editor.update_in(cx, |editor, window, cx| {
11982        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11983        editor.change_selections(None, window, cx, |s| {
11984            s.select_ranges([
11985                Point::new(0, 0)..Point::new(0, 0),
11986                Point::new(1, 0)..Point::new(1, 0),
11987            ])
11988        });
11989
11990        editor.handle_input("X", window, cx);
11991        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11992        assert_eq!(
11993            editor.selections.ranges(cx),
11994            [
11995                Point::new(0, 1)..Point::new(0, 1),
11996                Point::new(1, 1)..Point::new(1, 1),
11997            ]
11998        );
11999
12000        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
12001        editor.change_selections(None, window, cx, |s| {
12002            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
12003        });
12004        editor.backspace(&Default::default(), window, cx);
12005        assert_eq!(editor.text(cx), "Xa\nbbb");
12006        assert_eq!(
12007            editor.selections.ranges(cx),
12008            [Point::new(1, 0)..Point::new(1, 0)]
12009        );
12010
12011        editor.change_selections(None, window, cx, |s| {
12012            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
12013        });
12014        editor.backspace(&Default::default(), window, cx);
12015        assert_eq!(editor.text(cx), "X\nbb");
12016        assert_eq!(
12017            editor.selections.ranges(cx),
12018            [Point::new(0, 1)..Point::new(0, 1)]
12019        );
12020    });
12021}
12022
12023#[gpui::test]
12024fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
12025    init_test(cx, |_| {});
12026
12027    let markers = vec![('[', ']').into(), ('(', ')').into()];
12028    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
12029        indoc! {"
12030            [aaaa
12031            (bbbb]
12032            cccc)",
12033        },
12034        markers.clone(),
12035    );
12036    let excerpt_ranges = markers.into_iter().map(|marker| {
12037        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
12038        ExcerptRange::new(context.clone())
12039    });
12040    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
12041    let multibuffer = cx.new(|cx| {
12042        let mut multibuffer = MultiBuffer::new(ReadWrite);
12043        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
12044        multibuffer
12045    });
12046
12047    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
12048    editor.update_in(cx, |editor, window, cx| {
12049        let (expected_text, selection_ranges) = marked_text_ranges(
12050            indoc! {"
12051                aaaa
12052                bˇbbb
12053                bˇbbˇb
12054                cccc"
12055            },
12056            true,
12057        );
12058        assert_eq!(editor.text(cx), expected_text);
12059        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
12060
12061        editor.handle_input("X", window, cx);
12062
12063        let (expected_text, expected_selections) = marked_text_ranges(
12064            indoc! {"
12065                aaaa
12066                bXˇbbXb
12067                bXˇbbXˇb
12068                cccc"
12069            },
12070            false,
12071        );
12072        assert_eq!(editor.text(cx), expected_text);
12073        assert_eq!(editor.selections.ranges(cx), expected_selections);
12074
12075        editor.newline(&Newline, window, cx);
12076        let (expected_text, expected_selections) = marked_text_ranges(
12077            indoc! {"
12078                aaaa
12079                bX
12080                ˇbbX
12081                b
12082                bX
12083                ˇbbX
12084                ˇb
12085                cccc"
12086            },
12087            false,
12088        );
12089        assert_eq!(editor.text(cx), expected_text);
12090        assert_eq!(editor.selections.ranges(cx), expected_selections);
12091    });
12092}
12093
12094#[gpui::test]
12095fn test_refresh_selections(cx: &mut TestAppContext) {
12096    init_test(cx, |_| {});
12097
12098    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12099    let mut excerpt1_id = None;
12100    let multibuffer = cx.new(|cx| {
12101        let mut multibuffer = MultiBuffer::new(ReadWrite);
12102        excerpt1_id = multibuffer
12103            .push_excerpts(
12104                buffer.clone(),
12105                [
12106                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12107                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12108                ],
12109                cx,
12110            )
12111            .into_iter()
12112            .next();
12113        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12114        multibuffer
12115    });
12116
12117    let editor = cx.add_window(|window, cx| {
12118        let mut editor = build_editor(multibuffer.clone(), window, cx);
12119        let snapshot = editor.snapshot(window, cx);
12120        editor.change_selections(None, window, cx, |s| {
12121            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
12122        });
12123        editor.begin_selection(
12124            Point::new(2, 1).to_display_point(&snapshot),
12125            true,
12126            1,
12127            window,
12128            cx,
12129        );
12130        assert_eq!(
12131            editor.selections.ranges(cx),
12132            [
12133                Point::new(1, 3)..Point::new(1, 3),
12134                Point::new(2, 1)..Point::new(2, 1),
12135            ]
12136        );
12137        editor
12138    });
12139
12140    // Refreshing selections is a no-op when excerpts haven't changed.
12141    _ = editor.update(cx, |editor, window, cx| {
12142        editor.change_selections(None, window, cx, |s| s.refresh());
12143        assert_eq!(
12144            editor.selections.ranges(cx),
12145            [
12146                Point::new(1, 3)..Point::new(1, 3),
12147                Point::new(2, 1)..Point::new(2, 1),
12148            ]
12149        );
12150    });
12151
12152    multibuffer.update(cx, |multibuffer, cx| {
12153        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12154    });
12155    _ = editor.update(cx, |editor, window, cx| {
12156        // Removing an excerpt causes the first selection to become degenerate.
12157        assert_eq!(
12158            editor.selections.ranges(cx),
12159            [
12160                Point::new(0, 0)..Point::new(0, 0),
12161                Point::new(0, 1)..Point::new(0, 1)
12162            ]
12163        );
12164
12165        // Refreshing selections will relocate the first selection to the original buffer
12166        // location.
12167        editor.change_selections(None, window, cx, |s| s.refresh());
12168        assert_eq!(
12169            editor.selections.ranges(cx),
12170            [
12171                Point::new(0, 1)..Point::new(0, 1),
12172                Point::new(0, 3)..Point::new(0, 3)
12173            ]
12174        );
12175        assert!(editor.selections.pending_anchor().is_some());
12176    });
12177}
12178
12179#[gpui::test]
12180fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12181    init_test(cx, |_| {});
12182
12183    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12184    let mut excerpt1_id = None;
12185    let multibuffer = cx.new(|cx| {
12186        let mut multibuffer = MultiBuffer::new(ReadWrite);
12187        excerpt1_id = multibuffer
12188            .push_excerpts(
12189                buffer.clone(),
12190                [
12191                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12192                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12193                ],
12194                cx,
12195            )
12196            .into_iter()
12197            .next();
12198        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12199        multibuffer
12200    });
12201
12202    let editor = cx.add_window(|window, cx| {
12203        let mut editor = build_editor(multibuffer.clone(), window, cx);
12204        let snapshot = editor.snapshot(window, cx);
12205        editor.begin_selection(
12206            Point::new(1, 3).to_display_point(&snapshot),
12207            false,
12208            1,
12209            window,
12210            cx,
12211        );
12212        assert_eq!(
12213            editor.selections.ranges(cx),
12214            [Point::new(1, 3)..Point::new(1, 3)]
12215        );
12216        editor
12217    });
12218
12219    multibuffer.update(cx, |multibuffer, cx| {
12220        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12221    });
12222    _ = editor.update(cx, |editor, window, cx| {
12223        assert_eq!(
12224            editor.selections.ranges(cx),
12225            [Point::new(0, 0)..Point::new(0, 0)]
12226        );
12227
12228        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12229        editor.change_selections(None, window, cx, |s| s.refresh());
12230        assert_eq!(
12231            editor.selections.ranges(cx),
12232            [Point::new(0, 3)..Point::new(0, 3)]
12233        );
12234        assert!(editor.selections.pending_anchor().is_some());
12235    });
12236}
12237
12238#[gpui::test]
12239async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12240    init_test(cx, |_| {});
12241
12242    let language = Arc::new(
12243        Language::new(
12244            LanguageConfig {
12245                brackets: BracketPairConfig {
12246                    pairs: vec![
12247                        BracketPair {
12248                            start: "{".to_string(),
12249                            end: "}".to_string(),
12250                            close: true,
12251                            surround: true,
12252                            newline: true,
12253                        },
12254                        BracketPair {
12255                            start: "/* ".to_string(),
12256                            end: " */".to_string(),
12257                            close: true,
12258                            surround: true,
12259                            newline: true,
12260                        },
12261                    ],
12262                    ..Default::default()
12263                },
12264                ..Default::default()
12265            },
12266            Some(tree_sitter_rust::LANGUAGE.into()),
12267        )
12268        .with_indents_query("")
12269        .unwrap(),
12270    );
12271
12272    let text = concat!(
12273        "{   }\n",     //
12274        "  x\n",       //
12275        "  /*   */\n", //
12276        "x\n",         //
12277        "{{} }\n",     //
12278    );
12279
12280    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12281    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12282    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12283    editor
12284        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12285        .await;
12286
12287    editor.update_in(cx, |editor, window, cx| {
12288        editor.change_selections(None, window, cx, |s| {
12289            s.select_display_ranges([
12290                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12291                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12292                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12293            ])
12294        });
12295        editor.newline(&Newline, window, cx);
12296
12297        assert_eq!(
12298            editor.buffer().read(cx).read(cx).text(),
12299            concat!(
12300                "{ \n",    // Suppress rustfmt
12301                "\n",      //
12302                "}\n",     //
12303                "  x\n",   //
12304                "  /* \n", //
12305                "  \n",    //
12306                "  */\n",  //
12307                "x\n",     //
12308                "{{} \n",  //
12309                "}\n",     //
12310            )
12311        );
12312    });
12313}
12314
12315#[gpui::test]
12316fn test_highlighted_ranges(cx: &mut TestAppContext) {
12317    init_test(cx, |_| {});
12318
12319    let editor = cx.add_window(|window, cx| {
12320        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12321        build_editor(buffer.clone(), window, cx)
12322    });
12323
12324    _ = editor.update(cx, |editor, window, cx| {
12325        struct Type1;
12326        struct Type2;
12327
12328        let buffer = editor.buffer.read(cx).snapshot(cx);
12329
12330        let anchor_range =
12331            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12332
12333        editor.highlight_background::<Type1>(
12334            &[
12335                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12336                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12337                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12338                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12339            ],
12340            |_| Hsla::red(),
12341            cx,
12342        );
12343        editor.highlight_background::<Type2>(
12344            &[
12345                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12346                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12347                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12348                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12349            ],
12350            |_| Hsla::green(),
12351            cx,
12352        );
12353
12354        let snapshot = editor.snapshot(window, cx);
12355        let mut highlighted_ranges = editor.background_highlights_in_range(
12356            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12357            &snapshot,
12358            cx.theme().colors(),
12359        );
12360        // Enforce a consistent ordering based on color without relying on the ordering of the
12361        // highlight's `TypeId` which is non-executor.
12362        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12363        assert_eq!(
12364            highlighted_ranges,
12365            &[
12366                (
12367                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12368                    Hsla::red(),
12369                ),
12370                (
12371                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12372                    Hsla::red(),
12373                ),
12374                (
12375                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12376                    Hsla::green(),
12377                ),
12378                (
12379                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12380                    Hsla::green(),
12381                ),
12382            ]
12383        );
12384        assert_eq!(
12385            editor.background_highlights_in_range(
12386                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12387                &snapshot,
12388                cx.theme().colors(),
12389            ),
12390            &[(
12391                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12392                Hsla::red(),
12393            )]
12394        );
12395    });
12396}
12397
12398#[gpui::test]
12399async fn test_following(cx: &mut TestAppContext) {
12400    init_test(cx, |_| {});
12401
12402    let fs = FakeFs::new(cx.executor());
12403    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12404
12405    let buffer = project.update(cx, |project, cx| {
12406        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12407        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12408    });
12409    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12410    let follower = cx.update(|cx| {
12411        cx.open_window(
12412            WindowOptions {
12413                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12414                    gpui::Point::new(px(0.), px(0.)),
12415                    gpui::Point::new(px(10.), px(80.)),
12416                ))),
12417                ..Default::default()
12418            },
12419            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12420        )
12421        .unwrap()
12422    });
12423
12424    let is_still_following = Rc::new(RefCell::new(true));
12425    let follower_edit_event_count = Rc::new(RefCell::new(0));
12426    let pending_update = Rc::new(RefCell::new(None));
12427    let leader_entity = leader.root(cx).unwrap();
12428    let follower_entity = follower.root(cx).unwrap();
12429    _ = follower.update(cx, {
12430        let update = pending_update.clone();
12431        let is_still_following = is_still_following.clone();
12432        let follower_edit_event_count = follower_edit_event_count.clone();
12433        |_, window, cx| {
12434            cx.subscribe_in(
12435                &leader_entity,
12436                window,
12437                move |_, leader, event, window, cx| {
12438                    leader.read(cx).add_event_to_update_proto(
12439                        event,
12440                        &mut update.borrow_mut(),
12441                        window,
12442                        cx,
12443                    );
12444                },
12445            )
12446            .detach();
12447
12448            cx.subscribe_in(
12449                &follower_entity,
12450                window,
12451                move |_, _, event: &EditorEvent, _window, _cx| {
12452                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12453                        *is_still_following.borrow_mut() = false;
12454                    }
12455
12456                    if let EditorEvent::BufferEdited = event {
12457                        *follower_edit_event_count.borrow_mut() += 1;
12458                    }
12459                },
12460            )
12461            .detach();
12462        }
12463    });
12464
12465    // Update the selections only
12466    _ = leader.update(cx, |leader, window, cx| {
12467        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12468    });
12469    follower
12470        .update(cx, |follower, window, cx| {
12471            follower.apply_update_proto(
12472                &project,
12473                pending_update.borrow_mut().take().unwrap(),
12474                window,
12475                cx,
12476            )
12477        })
12478        .unwrap()
12479        .await
12480        .unwrap();
12481    _ = follower.update(cx, |follower, _, cx| {
12482        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12483    });
12484    assert!(*is_still_following.borrow());
12485    assert_eq!(*follower_edit_event_count.borrow(), 0);
12486
12487    // Update the scroll position only
12488    _ = leader.update(cx, |leader, window, cx| {
12489        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12490    });
12491    follower
12492        .update(cx, |follower, window, cx| {
12493            follower.apply_update_proto(
12494                &project,
12495                pending_update.borrow_mut().take().unwrap(),
12496                window,
12497                cx,
12498            )
12499        })
12500        .unwrap()
12501        .await
12502        .unwrap();
12503    assert_eq!(
12504        follower
12505            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12506            .unwrap(),
12507        gpui::Point::new(1.5, 3.5)
12508    );
12509    assert!(*is_still_following.borrow());
12510    assert_eq!(*follower_edit_event_count.borrow(), 0);
12511
12512    // Update the selections and scroll position. The follower's scroll position is updated
12513    // via autoscroll, not via the leader's exact scroll position.
12514    _ = leader.update(cx, |leader, window, cx| {
12515        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12516        leader.request_autoscroll(Autoscroll::newest(), cx);
12517        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12518    });
12519    follower
12520        .update(cx, |follower, window, cx| {
12521            follower.apply_update_proto(
12522                &project,
12523                pending_update.borrow_mut().take().unwrap(),
12524                window,
12525                cx,
12526            )
12527        })
12528        .unwrap()
12529        .await
12530        .unwrap();
12531    _ = follower.update(cx, |follower, _, cx| {
12532        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12533        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12534    });
12535    assert!(*is_still_following.borrow());
12536
12537    // Creating a pending selection that precedes another selection
12538    _ = leader.update(cx, |leader, window, cx| {
12539        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12540        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12541    });
12542    follower
12543        .update(cx, |follower, window, cx| {
12544            follower.apply_update_proto(
12545                &project,
12546                pending_update.borrow_mut().take().unwrap(),
12547                window,
12548                cx,
12549            )
12550        })
12551        .unwrap()
12552        .await
12553        .unwrap();
12554    _ = follower.update(cx, |follower, _, cx| {
12555        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12556    });
12557    assert!(*is_still_following.borrow());
12558
12559    // Extend the pending selection so that it surrounds another selection
12560    _ = leader.update(cx, |leader, window, cx| {
12561        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12562    });
12563    follower
12564        .update(cx, |follower, window, cx| {
12565            follower.apply_update_proto(
12566                &project,
12567                pending_update.borrow_mut().take().unwrap(),
12568                window,
12569                cx,
12570            )
12571        })
12572        .unwrap()
12573        .await
12574        .unwrap();
12575    _ = follower.update(cx, |follower, _, cx| {
12576        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12577    });
12578
12579    // Scrolling locally breaks the follow
12580    _ = follower.update(cx, |follower, window, cx| {
12581        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12582        follower.set_scroll_anchor(
12583            ScrollAnchor {
12584                anchor: top_anchor,
12585                offset: gpui::Point::new(0.0, 0.5),
12586            },
12587            window,
12588            cx,
12589        );
12590    });
12591    assert!(!(*is_still_following.borrow()));
12592}
12593
12594#[gpui::test]
12595async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12596    init_test(cx, |_| {});
12597
12598    let fs = FakeFs::new(cx.executor());
12599    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12600    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12601    let pane = workspace
12602        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12603        .unwrap();
12604
12605    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12606
12607    let leader = pane.update_in(cx, |_, window, cx| {
12608        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12609        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12610    });
12611
12612    // Start following the editor when it has no excerpts.
12613    let mut state_message =
12614        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12615    let workspace_entity = workspace.root(cx).unwrap();
12616    let follower_1 = cx
12617        .update_window(*workspace.deref(), |_, window, cx| {
12618            Editor::from_state_proto(
12619                workspace_entity,
12620                ViewId {
12621                    creator: Default::default(),
12622                    id: 0,
12623                },
12624                &mut state_message,
12625                window,
12626                cx,
12627            )
12628        })
12629        .unwrap()
12630        .unwrap()
12631        .await
12632        .unwrap();
12633
12634    let update_message = Rc::new(RefCell::new(None));
12635    follower_1.update_in(cx, {
12636        let update = update_message.clone();
12637        |_, window, cx| {
12638            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12639                leader.read(cx).add_event_to_update_proto(
12640                    event,
12641                    &mut update.borrow_mut(),
12642                    window,
12643                    cx,
12644                );
12645            })
12646            .detach();
12647        }
12648    });
12649
12650    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12651        (
12652            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12653            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12654        )
12655    });
12656
12657    // Insert some excerpts.
12658    leader.update(cx, |leader, cx| {
12659        leader.buffer.update(cx, |multibuffer, cx| {
12660            multibuffer.set_excerpts_for_path(
12661                PathKey::namespaced(1, Arc::from(Path::new("b.txt"))),
12662                buffer_1.clone(),
12663                vec![
12664                    Point::row_range(0..3),
12665                    Point::row_range(1..6),
12666                    Point::row_range(12..15),
12667                ],
12668                0,
12669                cx,
12670            );
12671            multibuffer.set_excerpts_for_path(
12672                PathKey::namespaced(1, Arc::from(Path::new("a.txt"))),
12673                buffer_2.clone(),
12674                vec![Point::row_range(0..6), Point::row_range(8..12)],
12675                0,
12676                cx,
12677            );
12678        });
12679    });
12680
12681    // Apply the update of adding the excerpts.
12682    follower_1
12683        .update_in(cx, |follower, window, cx| {
12684            follower.apply_update_proto(
12685                &project,
12686                update_message.borrow().clone().unwrap(),
12687                window,
12688                cx,
12689            )
12690        })
12691        .await
12692        .unwrap();
12693    assert_eq!(
12694        follower_1.update(cx, |editor, cx| editor.text(cx)),
12695        leader.update(cx, |editor, cx| editor.text(cx))
12696    );
12697    update_message.borrow_mut().take();
12698
12699    // Start following separately after it already has excerpts.
12700    let mut state_message =
12701        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12702    let workspace_entity = workspace.root(cx).unwrap();
12703    let follower_2 = cx
12704        .update_window(*workspace.deref(), |_, window, cx| {
12705            Editor::from_state_proto(
12706                workspace_entity,
12707                ViewId {
12708                    creator: Default::default(),
12709                    id: 0,
12710                },
12711                &mut state_message,
12712                window,
12713                cx,
12714            )
12715        })
12716        .unwrap()
12717        .unwrap()
12718        .await
12719        .unwrap();
12720    assert_eq!(
12721        follower_2.update(cx, |editor, cx| editor.text(cx)),
12722        leader.update(cx, |editor, cx| editor.text(cx))
12723    );
12724
12725    // Remove some excerpts.
12726    leader.update(cx, |leader, cx| {
12727        leader.buffer.update(cx, |multibuffer, cx| {
12728            let excerpt_ids = multibuffer.excerpt_ids();
12729            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12730            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12731        });
12732    });
12733
12734    // Apply the update of removing the excerpts.
12735    follower_1
12736        .update_in(cx, |follower, window, cx| {
12737            follower.apply_update_proto(
12738                &project,
12739                update_message.borrow().clone().unwrap(),
12740                window,
12741                cx,
12742            )
12743        })
12744        .await
12745        .unwrap();
12746    follower_2
12747        .update_in(cx, |follower, window, cx| {
12748            follower.apply_update_proto(
12749                &project,
12750                update_message.borrow().clone().unwrap(),
12751                window,
12752                cx,
12753            )
12754        })
12755        .await
12756        .unwrap();
12757    update_message.borrow_mut().take();
12758    assert_eq!(
12759        follower_1.update(cx, |editor, cx| editor.text(cx)),
12760        leader.update(cx, |editor, cx| editor.text(cx))
12761    );
12762}
12763
12764#[gpui::test]
12765async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12766    init_test(cx, |_| {});
12767
12768    let mut cx = EditorTestContext::new(cx).await;
12769    let lsp_store =
12770        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12771
12772    cx.set_state(indoc! {"
12773        ˇfn func(abc def: i32) -> u32 {
12774        }
12775    "});
12776
12777    cx.update(|_, cx| {
12778        lsp_store.update(cx, |lsp_store, cx| {
12779            lsp_store
12780                .update_diagnostics(
12781                    LanguageServerId(0),
12782                    lsp::PublishDiagnosticsParams {
12783                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12784                        version: None,
12785                        diagnostics: vec![
12786                            lsp::Diagnostic {
12787                                range: lsp::Range::new(
12788                                    lsp::Position::new(0, 11),
12789                                    lsp::Position::new(0, 12),
12790                                ),
12791                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12792                                ..Default::default()
12793                            },
12794                            lsp::Diagnostic {
12795                                range: lsp::Range::new(
12796                                    lsp::Position::new(0, 12),
12797                                    lsp::Position::new(0, 15),
12798                                ),
12799                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12800                                ..Default::default()
12801                            },
12802                            lsp::Diagnostic {
12803                                range: lsp::Range::new(
12804                                    lsp::Position::new(0, 25),
12805                                    lsp::Position::new(0, 28),
12806                                ),
12807                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12808                                ..Default::default()
12809                            },
12810                        ],
12811                    },
12812                    &[],
12813                    cx,
12814                )
12815                .unwrap()
12816        });
12817    });
12818
12819    executor.run_until_parked();
12820
12821    cx.update_editor(|editor, window, cx| {
12822        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12823    });
12824
12825    cx.assert_editor_state(indoc! {"
12826        fn func(abc def: i32) -> ˇu32 {
12827        }
12828    "});
12829
12830    cx.update_editor(|editor, window, cx| {
12831        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12832    });
12833
12834    cx.assert_editor_state(indoc! {"
12835        fn func(abc ˇdef: i32) -> u32 {
12836        }
12837    "});
12838
12839    cx.update_editor(|editor, window, cx| {
12840        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12841    });
12842
12843    cx.assert_editor_state(indoc! {"
12844        fn func(abcˇ def: i32) -> u32 {
12845        }
12846    "});
12847
12848    cx.update_editor(|editor, window, cx| {
12849        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12850    });
12851
12852    cx.assert_editor_state(indoc! {"
12853        fn func(abc def: i32) -> ˇu32 {
12854        }
12855    "});
12856}
12857
12858#[gpui::test]
12859async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12860    init_test(cx, |_| {});
12861
12862    let mut cx = EditorTestContext::new(cx).await;
12863
12864    let diff_base = r#"
12865        use some::mod;
12866
12867        const A: u32 = 42;
12868
12869        fn main() {
12870            println!("hello");
12871
12872            println!("world");
12873        }
12874        "#
12875    .unindent();
12876
12877    // Edits are modified, removed, modified, added
12878    cx.set_state(
12879        &r#"
12880        use some::modified;
12881
12882        ˇ
12883        fn main() {
12884            println!("hello there");
12885
12886            println!("around the");
12887            println!("world");
12888        }
12889        "#
12890        .unindent(),
12891    );
12892
12893    cx.set_head_text(&diff_base);
12894    executor.run_until_parked();
12895
12896    cx.update_editor(|editor, window, cx| {
12897        //Wrap around the bottom of the buffer
12898        for _ in 0..3 {
12899            editor.go_to_next_hunk(&GoToHunk, window, cx);
12900        }
12901    });
12902
12903    cx.assert_editor_state(
12904        &r#"
12905        ˇuse some::modified;
12906
12907
12908        fn main() {
12909            println!("hello there");
12910
12911            println!("around the");
12912            println!("world");
12913        }
12914        "#
12915        .unindent(),
12916    );
12917
12918    cx.update_editor(|editor, window, cx| {
12919        //Wrap around the top of the buffer
12920        for _ in 0..2 {
12921            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12922        }
12923    });
12924
12925    cx.assert_editor_state(
12926        &r#"
12927        use some::modified;
12928
12929
12930        fn main() {
12931        ˇ    println!("hello there");
12932
12933            println!("around the");
12934            println!("world");
12935        }
12936        "#
12937        .unindent(),
12938    );
12939
12940    cx.update_editor(|editor, window, cx| {
12941        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12942    });
12943
12944    cx.assert_editor_state(
12945        &r#"
12946        use some::modified;
12947
12948        ˇ
12949        fn main() {
12950            println!("hello there");
12951
12952            println!("around the");
12953            println!("world");
12954        }
12955        "#
12956        .unindent(),
12957    );
12958
12959    cx.update_editor(|editor, window, cx| {
12960        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12961    });
12962
12963    cx.assert_editor_state(
12964        &r#"
12965        ˇuse some::modified;
12966
12967
12968        fn main() {
12969            println!("hello there");
12970
12971            println!("around the");
12972            println!("world");
12973        }
12974        "#
12975        .unindent(),
12976    );
12977
12978    cx.update_editor(|editor, window, cx| {
12979        for _ in 0..2 {
12980            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12981        }
12982    });
12983
12984    cx.assert_editor_state(
12985        &r#"
12986        use some::modified;
12987
12988
12989        fn main() {
12990        ˇ    println!("hello there");
12991
12992            println!("around the");
12993            println!("world");
12994        }
12995        "#
12996        .unindent(),
12997    );
12998
12999    cx.update_editor(|editor, window, cx| {
13000        editor.fold(&Fold, window, cx);
13001    });
13002
13003    cx.update_editor(|editor, window, cx| {
13004        editor.go_to_next_hunk(&GoToHunk, window, cx);
13005    });
13006
13007    cx.assert_editor_state(
13008        &r#"
13009        ˇuse some::modified;
13010
13011
13012        fn main() {
13013            println!("hello there");
13014
13015            println!("around the");
13016            println!("world");
13017        }
13018        "#
13019        .unindent(),
13020    );
13021}
13022
13023#[test]
13024fn test_split_words() {
13025    fn split(text: &str) -> Vec<&str> {
13026        split_words(text).collect()
13027    }
13028
13029    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
13030    assert_eq!(split("hello_world"), &["hello_", "world"]);
13031    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
13032    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
13033    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
13034    assert_eq!(split("helloworld"), &["helloworld"]);
13035
13036    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
13037}
13038
13039#[gpui::test]
13040async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
13041    init_test(cx, |_| {});
13042
13043    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
13044    let mut assert = |before, after| {
13045        let _state_context = cx.set_state(before);
13046        cx.run_until_parked();
13047        cx.update_editor(|editor, window, cx| {
13048            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
13049        });
13050        cx.run_until_parked();
13051        cx.assert_editor_state(after);
13052    };
13053
13054    // Outside bracket jumps to outside of matching bracket
13055    assert("console.logˇ(var);", "console.log(var)ˇ;");
13056    assert("console.log(var)ˇ;", "console.logˇ(var);");
13057
13058    // Inside bracket jumps to inside of matching bracket
13059    assert("console.log(ˇvar);", "console.log(varˇ);");
13060    assert("console.log(varˇ);", "console.log(ˇvar);");
13061
13062    // When outside a bracket and inside, favor jumping to the inside bracket
13063    assert(
13064        "console.log('foo', [1, 2, 3]ˇ);",
13065        "console.log(ˇ'foo', [1, 2, 3]);",
13066    );
13067    assert(
13068        "console.log(ˇ'foo', [1, 2, 3]);",
13069        "console.log('foo', [1, 2, 3]ˇ);",
13070    );
13071
13072    // Bias forward if two options are equally likely
13073    assert(
13074        "let result = curried_fun()ˇ();",
13075        "let result = curried_fun()()ˇ;",
13076    );
13077
13078    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
13079    assert(
13080        indoc! {"
13081            function test() {
13082                console.log('test')ˇ
13083            }"},
13084        indoc! {"
13085            function test() {
13086                console.logˇ('test')
13087            }"},
13088    );
13089}
13090
13091#[gpui::test]
13092async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
13093    init_test(cx, |_| {});
13094
13095    let fs = FakeFs::new(cx.executor());
13096    fs.insert_tree(
13097        path!("/a"),
13098        json!({
13099            "main.rs": "fn main() { let a = 5; }",
13100            "other.rs": "// Test file",
13101        }),
13102    )
13103    .await;
13104    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13105
13106    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13107    language_registry.add(Arc::new(Language::new(
13108        LanguageConfig {
13109            name: "Rust".into(),
13110            matcher: LanguageMatcher {
13111                path_suffixes: vec!["rs".to_string()],
13112                ..Default::default()
13113            },
13114            brackets: BracketPairConfig {
13115                pairs: vec![BracketPair {
13116                    start: "{".to_string(),
13117                    end: "}".to_string(),
13118                    close: true,
13119                    surround: true,
13120                    newline: true,
13121                }],
13122                disabled_scopes_by_bracket_ix: Vec::new(),
13123            },
13124            ..Default::default()
13125        },
13126        Some(tree_sitter_rust::LANGUAGE.into()),
13127    )));
13128    let mut fake_servers = language_registry.register_fake_lsp(
13129        "Rust",
13130        FakeLspAdapter {
13131            capabilities: lsp::ServerCapabilities {
13132                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13133                    first_trigger_character: "{".to_string(),
13134                    more_trigger_character: None,
13135                }),
13136                ..Default::default()
13137            },
13138            ..Default::default()
13139        },
13140    );
13141
13142    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13143
13144    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13145
13146    let worktree_id = workspace
13147        .update(cx, |workspace, _, cx| {
13148            workspace.project().update(cx, |project, cx| {
13149                project.worktrees(cx).next().unwrap().read(cx).id()
13150            })
13151        })
13152        .unwrap();
13153
13154    let buffer = project
13155        .update(cx, |project, cx| {
13156            project.open_local_buffer(path!("/a/main.rs"), cx)
13157        })
13158        .await
13159        .unwrap();
13160    let editor_handle = workspace
13161        .update(cx, |workspace, window, cx| {
13162            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13163        })
13164        .unwrap()
13165        .await
13166        .unwrap()
13167        .downcast::<Editor>()
13168        .unwrap();
13169
13170    cx.executor().start_waiting();
13171    let fake_server = fake_servers.next().await.unwrap();
13172
13173    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13174        |params, _| async move {
13175            assert_eq!(
13176                params.text_document_position.text_document.uri,
13177                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13178            );
13179            assert_eq!(
13180                params.text_document_position.position,
13181                lsp::Position::new(0, 21),
13182            );
13183
13184            Ok(Some(vec![lsp::TextEdit {
13185                new_text: "]".to_string(),
13186                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13187            }]))
13188        },
13189    );
13190
13191    editor_handle.update_in(cx, |editor, window, cx| {
13192        window.focus(&editor.focus_handle(cx));
13193        editor.change_selections(None, window, cx, |s| {
13194            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13195        });
13196        editor.handle_input("{", window, cx);
13197    });
13198
13199    cx.executor().run_until_parked();
13200
13201    buffer.update(cx, |buffer, _| {
13202        assert_eq!(
13203            buffer.text(),
13204            "fn main() { let a = {5}; }",
13205            "No extra braces from on type formatting should appear in the buffer"
13206        )
13207    });
13208}
13209
13210#[gpui::test]
13211async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13212    init_test(cx, |_| {});
13213
13214    let fs = FakeFs::new(cx.executor());
13215    fs.insert_tree(
13216        path!("/a"),
13217        json!({
13218            "main.rs": "fn main() { let a = 5; }",
13219            "other.rs": "// Test file",
13220        }),
13221    )
13222    .await;
13223
13224    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13225
13226    let server_restarts = Arc::new(AtomicUsize::new(0));
13227    let closure_restarts = Arc::clone(&server_restarts);
13228    let language_server_name = "test language server";
13229    let language_name: LanguageName = "Rust".into();
13230
13231    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13232    language_registry.add(Arc::new(Language::new(
13233        LanguageConfig {
13234            name: language_name.clone(),
13235            matcher: LanguageMatcher {
13236                path_suffixes: vec!["rs".to_string()],
13237                ..Default::default()
13238            },
13239            ..Default::default()
13240        },
13241        Some(tree_sitter_rust::LANGUAGE.into()),
13242    )));
13243    let mut fake_servers = language_registry.register_fake_lsp(
13244        "Rust",
13245        FakeLspAdapter {
13246            name: language_server_name,
13247            initialization_options: Some(json!({
13248                "testOptionValue": true
13249            })),
13250            initializer: Some(Box::new(move |fake_server| {
13251                let task_restarts = Arc::clone(&closure_restarts);
13252                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13253                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13254                    futures::future::ready(Ok(()))
13255                });
13256            })),
13257            ..Default::default()
13258        },
13259    );
13260
13261    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13262    let _buffer = project
13263        .update(cx, |project, cx| {
13264            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13265        })
13266        .await
13267        .unwrap();
13268    let _fake_server = fake_servers.next().await.unwrap();
13269    update_test_language_settings(cx, |language_settings| {
13270        language_settings.languages.insert(
13271            language_name.clone(),
13272            LanguageSettingsContent {
13273                tab_size: NonZeroU32::new(8),
13274                ..Default::default()
13275            },
13276        );
13277    });
13278    cx.executor().run_until_parked();
13279    assert_eq!(
13280        server_restarts.load(atomic::Ordering::Acquire),
13281        0,
13282        "Should not restart LSP server on an unrelated change"
13283    );
13284
13285    update_test_project_settings(cx, |project_settings| {
13286        project_settings.lsp.insert(
13287            "Some other server name".into(),
13288            LspSettings {
13289                binary: None,
13290                settings: None,
13291                initialization_options: Some(json!({
13292                    "some other init value": false
13293                })),
13294                enable_lsp_tasks: false,
13295            },
13296        );
13297    });
13298    cx.executor().run_until_parked();
13299    assert_eq!(
13300        server_restarts.load(atomic::Ordering::Acquire),
13301        0,
13302        "Should not restart LSP server on an unrelated LSP settings change"
13303    );
13304
13305    update_test_project_settings(cx, |project_settings| {
13306        project_settings.lsp.insert(
13307            language_server_name.into(),
13308            LspSettings {
13309                binary: None,
13310                settings: None,
13311                initialization_options: Some(json!({
13312                    "anotherInitValue": false
13313                })),
13314                enable_lsp_tasks: false,
13315            },
13316        );
13317    });
13318    cx.executor().run_until_parked();
13319    assert_eq!(
13320        server_restarts.load(atomic::Ordering::Acquire),
13321        1,
13322        "Should restart LSP server on a related LSP settings change"
13323    );
13324
13325    update_test_project_settings(cx, |project_settings| {
13326        project_settings.lsp.insert(
13327            language_server_name.into(),
13328            LspSettings {
13329                binary: None,
13330                settings: None,
13331                initialization_options: Some(json!({
13332                    "anotherInitValue": false
13333                })),
13334                enable_lsp_tasks: false,
13335            },
13336        );
13337    });
13338    cx.executor().run_until_parked();
13339    assert_eq!(
13340        server_restarts.load(atomic::Ordering::Acquire),
13341        1,
13342        "Should not restart LSP server on a related LSP settings change that is the same"
13343    );
13344
13345    update_test_project_settings(cx, |project_settings| {
13346        project_settings.lsp.insert(
13347            language_server_name.into(),
13348            LspSettings {
13349                binary: None,
13350                settings: None,
13351                initialization_options: None,
13352                enable_lsp_tasks: false,
13353            },
13354        );
13355    });
13356    cx.executor().run_until_parked();
13357    assert_eq!(
13358        server_restarts.load(atomic::Ordering::Acquire),
13359        2,
13360        "Should restart LSP server on another related LSP settings change"
13361    );
13362}
13363
13364#[gpui::test]
13365async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13366    init_test(cx, |_| {});
13367
13368    let mut cx = EditorLspTestContext::new_rust(
13369        lsp::ServerCapabilities {
13370            completion_provider: Some(lsp::CompletionOptions {
13371                trigger_characters: Some(vec![".".to_string()]),
13372                resolve_provider: Some(true),
13373                ..Default::default()
13374            }),
13375            ..Default::default()
13376        },
13377        cx,
13378    )
13379    .await;
13380
13381    cx.set_state("fn main() { let a = 2ˇ; }");
13382    cx.simulate_keystroke(".");
13383    let completion_item = lsp::CompletionItem {
13384        label: "some".into(),
13385        kind: Some(lsp::CompletionItemKind::SNIPPET),
13386        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13387        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13388            kind: lsp::MarkupKind::Markdown,
13389            value: "```rust\nSome(2)\n```".to_string(),
13390        })),
13391        deprecated: Some(false),
13392        sort_text: Some("fffffff2".to_string()),
13393        filter_text: Some("some".to_string()),
13394        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13395        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13396            range: lsp::Range {
13397                start: lsp::Position {
13398                    line: 0,
13399                    character: 22,
13400                },
13401                end: lsp::Position {
13402                    line: 0,
13403                    character: 22,
13404                },
13405            },
13406            new_text: "Some(2)".to_string(),
13407        })),
13408        additional_text_edits: Some(vec![lsp::TextEdit {
13409            range: lsp::Range {
13410                start: lsp::Position {
13411                    line: 0,
13412                    character: 20,
13413                },
13414                end: lsp::Position {
13415                    line: 0,
13416                    character: 22,
13417                },
13418            },
13419            new_text: "".to_string(),
13420        }]),
13421        ..Default::default()
13422    };
13423
13424    let closure_completion_item = completion_item.clone();
13425    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13426        let task_completion_item = closure_completion_item.clone();
13427        async move {
13428            Ok(Some(lsp::CompletionResponse::Array(vec![
13429                task_completion_item,
13430            ])))
13431        }
13432    });
13433
13434    request.next().await;
13435
13436    cx.condition(|editor, _| editor.context_menu_visible())
13437        .await;
13438    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13439        editor
13440            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13441            .unwrap()
13442    });
13443    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13444
13445    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13446        let task_completion_item = completion_item.clone();
13447        async move { Ok(task_completion_item) }
13448    })
13449    .next()
13450    .await
13451    .unwrap();
13452    apply_additional_edits.await.unwrap();
13453    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13454}
13455
13456#[gpui::test]
13457async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13458    init_test(cx, |_| {});
13459
13460    let mut cx = EditorLspTestContext::new_rust(
13461        lsp::ServerCapabilities {
13462            completion_provider: Some(lsp::CompletionOptions {
13463                trigger_characters: Some(vec![".".to_string()]),
13464                resolve_provider: Some(true),
13465                ..Default::default()
13466            }),
13467            ..Default::default()
13468        },
13469        cx,
13470    )
13471    .await;
13472
13473    cx.set_state("fn main() { let a = 2ˇ; }");
13474    cx.simulate_keystroke(".");
13475
13476    let item1 = lsp::CompletionItem {
13477        label: "method id()".to_string(),
13478        filter_text: Some("id".to_string()),
13479        detail: None,
13480        documentation: None,
13481        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13482            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13483            new_text: ".id".to_string(),
13484        })),
13485        ..lsp::CompletionItem::default()
13486    };
13487
13488    let item2 = lsp::CompletionItem {
13489        label: "other".to_string(),
13490        filter_text: Some("other".to_string()),
13491        detail: None,
13492        documentation: None,
13493        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13494            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13495            new_text: ".other".to_string(),
13496        })),
13497        ..lsp::CompletionItem::default()
13498    };
13499
13500    let item1 = item1.clone();
13501    cx.set_request_handler::<lsp::request::Completion, _, _>({
13502        let item1 = item1.clone();
13503        move |_, _, _| {
13504            let item1 = item1.clone();
13505            let item2 = item2.clone();
13506            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13507        }
13508    })
13509    .next()
13510    .await;
13511
13512    cx.condition(|editor, _| editor.context_menu_visible())
13513        .await;
13514    cx.update_editor(|editor, _, _| {
13515        let context_menu = editor.context_menu.borrow_mut();
13516        let context_menu = context_menu
13517            .as_ref()
13518            .expect("Should have the context menu deployed");
13519        match context_menu {
13520            CodeContextMenu::Completions(completions_menu) => {
13521                let completions = completions_menu.completions.borrow_mut();
13522                assert_eq!(
13523                    completions
13524                        .iter()
13525                        .map(|completion| &completion.label.text)
13526                        .collect::<Vec<_>>(),
13527                    vec!["method id()", "other"]
13528                )
13529            }
13530            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13531        }
13532    });
13533
13534    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13535        let item1 = item1.clone();
13536        move |_, item_to_resolve, _| {
13537            let item1 = item1.clone();
13538            async move {
13539                if item1 == item_to_resolve {
13540                    Ok(lsp::CompletionItem {
13541                        label: "method id()".to_string(),
13542                        filter_text: Some("id".to_string()),
13543                        detail: Some("Now resolved!".to_string()),
13544                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13545                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13546                            range: lsp::Range::new(
13547                                lsp::Position::new(0, 22),
13548                                lsp::Position::new(0, 22),
13549                            ),
13550                            new_text: ".id".to_string(),
13551                        })),
13552                        ..lsp::CompletionItem::default()
13553                    })
13554                } else {
13555                    Ok(item_to_resolve)
13556                }
13557            }
13558        }
13559    })
13560    .next()
13561    .await
13562    .unwrap();
13563    cx.run_until_parked();
13564
13565    cx.update_editor(|editor, window, cx| {
13566        editor.context_menu_next(&Default::default(), window, cx);
13567    });
13568
13569    cx.update_editor(|editor, _, _| {
13570        let context_menu = editor.context_menu.borrow_mut();
13571        let context_menu = context_menu
13572            .as_ref()
13573            .expect("Should have the context menu deployed");
13574        match context_menu {
13575            CodeContextMenu::Completions(completions_menu) => {
13576                let completions = completions_menu.completions.borrow_mut();
13577                assert_eq!(
13578                    completions
13579                        .iter()
13580                        .map(|completion| &completion.label.text)
13581                        .collect::<Vec<_>>(),
13582                    vec!["method id() Now resolved!", "other"],
13583                    "Should update first completion label, but not second as the filter text did not match."
13584                );
13585            }
13586            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13587        }
13588    });
13589}
13590
13591#[gpui::test]
13592async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13593    init_test(cx, |_| {});
13594
13595    let mut cx = EditorLspTestContext::new_rust(
13596        lsp::ServerCapabilities {
13597            completion_provider: Some(lsp::CompletionOptions {
13598                trigger_characters: Some(vec![".".to_string()]),
13599                resolve_provider: Some(true),
13600                ..Default::default()
13601            }),
13602            ..Default::default()
13603        },
13604        cx,
13605    )
13606    .await;
13607
13608    cx.set_state("fn main() { let a = 2ˇ; }");
13609    cx.simulate_keystroke(".");
13610
13611    let unresolved_item_1 = lsp::CompletionItem {
13612        label: "id".to_string(),
13613        filter_text: Some("id".to_string()),
13614        detail: None,
13615        documentation: None,
13616        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13617            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13618            new_text: ".id".to_string(),
13619        })),
13620        ..lsp::CompletionItem::default()
13621    };
13622    let resolved_item_1 = lsp::CompletionItem {
13623        additional_text_edits: Some(vec![lsp::TextEdit {
13624            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13625            new_text: "!!".to_string(),
13626        }]),
13627        ..unresolved_item_1.clone()
13628    };
13629    let unresolved_item_2 = lsp::CompletionItem {
13630        label: "other".to_string(),
13631        filter_text: Some("other".to_string()),
13632        detail: None,
13633        documentation: None,
13634        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13635            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13636            new_text: ".other".to_string(),
13637        })),
13638        ..lsp::CompletionItem::default()
13639    };
13640    let resolved_item_2 = lsp::CompletionItem {
13641        additional_text_edits: Some(vec![lsp::TextEdit {
13642            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13643            new_text: "??".to_string(),
13644        }]),
13645        ..unresolved_item_2.clone()
13646    };
13647
13648    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13649    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13650    cx.lsp
13651        .server
13652        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13653            let unresolved_item_1 = unresolved_item_1.clone();
13654            let resolved_item_1 = resolved_item_1.clone();
13655            let unresolved_item_2 = unresolved_item_2.clone();
13656            let resolved_item_2 = resolved_item_2.clone();
13657            let resolve_requests_1 = resolve_requests_1.clone();
13658            let resolve_requests_2 = resolve_requests_2.clone();
13659            move |unresolved_request, _| {
13660                let unresolved_item_1 = unresolved_item_1.clone();
13661                let resolved_item_1 = resolved_item_1.clone();
13662                let unresolved_item_2 = unresolved_item_2.clone();
13663                let resolved_item_2 = resolved_item_2.clone();
13664                let resolve_requests_1 = resolve_requests_1.clone();
13665                let resolve_requests_2 = resolve_requests_2.clone();
13666                async move {
13667                    if unresolved_request == unresolved_item_1 {
13668                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13669                        Ok(resolved_item_1.clone())
13670                    } else if unresolved_request == unresolved_item_2 {
13671                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13672                        Ok(resolved_item_2.clone())
13673                    } else {
13674                        panic!("Unexpected completion item {unresolved_request:?}")
13675                    }
13676                }
13677            }
13678        })
13679        .detach();
13680
13681    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13682        let unresolved_item_1 = unresolved_item_1.clone();
13683        let unresolved_item_2 = unresolved_item_2.clone();
13684        async move {
13685            Ok(Some(lsp::CompletionResponse::Array(vec![
13686                unresolved_item_1,
13687                unresolved_item_2,
13688            ])))
13689        }
13690    })
13691    .next()
13692    .await;
13693
13694    cx.condition(|editor, _| editor.context_menu_visible())
13695        .await;
13696    cx.update_editor(|editor, _, _| {
13697        let context_menu = editor.context_menu.borrow_mut();
13698        let context_menu = context_menu
13699            .as_ref()
13700            .expect("Should have the context menu deployed");
13701        match context_menu {
13702            CodeContextMenu::Completions(completions_menu) => {
13703                let completions = completions_menu.completions.borrow_mut();
13704                assert_eq!(
13705                    completions
13706                        .iter()
13707                        .map(|completion| &completion.label.text)
13708                        .collect::<Vec<_>>(),
13709                    vec!["id", "other"]
13710                )
13711            }
13712            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13713        }
13714    });
13715    cx.run_until_parked();
13716
13717    cx.update_editor(|editor, window, cx| {
13718        editor.context_menu_next(&ContextMenuNext, window, cx);
13719    });
13720    cx.run_until_parked();
13721    cx.update_editor(|editor, window, cx| {
13722        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13723    });
13724    cx.run_until_parked();
13725    cx.update_editor(|editor, window, cx| {
13726        editor.context_menu_next(&ContextMenuNext, window, cx);
13727    });
13728    cx.run_until_parked();
13729    cx.update_editor(|editor, window, cx| {
13730        editor
13731            .compose_completion(&ComposeCompletion::default(), window, cx)
13732            .expect("No task returned")
13733    })
13734    .await
13735    .expect("Completion failed");
13736    cx.run_until_parked();
13737
13738    cx.update_editor(|editor, _, cx| {
13739        assert_eq!(
13740            resolve_requests_1.load(atomic::Ordering::Acquire),
13741            1,
13742            "Should always resolve once despite multiple selections"
13743        );
13744        assert_eq!(
13745            resolve_requests_2.load(atomic::Ordering::Acquire),
13746            1,
13747            "Should always resolve once after multiple selections and applying the completion"
13748        );
13749        assert_eq!(
13750            editor.text(cx),
13751            "fn main() { let a = ??.other; }",
13752            "Should use resolved data when applying the completion"
13753        );
13754    });
13755}
13756
13757#[gpui::test]
13758async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13759    init_test(cx, |_| {});
13760
13761    let item_0 = lsp::CompletionItem {
13762        label: "abs".into(),
13763        insert_text: Some("abs".into()),
13764        data: Some(json!({ "very": "special"})),
13765        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13766        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13767            lsp::InsertReplaceEdit {
13768                new_text: "abs".to_string(),
13769                insert: lsp::Range::default(),
13770                replace: lsp::Range::default(),
13771            },
13772        )),
13773        ..lsp::CompletionItem::default()
13774    };
13775    let items = iter::once(item_0.clone())
13776        .chain((11..51).map(|i| lsp::CompletionItem {
13777            label: format!("item_{}", i),
13778            insert_text: Some(format!("item_{}", i)),
13779            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13780            ..lsp::CompletionItem::default()
13781        }))
13782        .collect::<Vec<_>>();
13783
13784    let default_commit_characters = vec!["?".to_string()];
13785    let default_data = json!({ "default": "data"});
13786    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13787    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13788    let default_edit_range = lsp::Range {
13789        start: lsp::Position {
13790            line: 0,
13791            character: 5,
13792        },
13793        end: lsp::Position {
13794            line: 0,
13795            character: 5,
13796        },
13797    };
13798
13799    let mut cx = EditorLspTestContext::new_rust(
13800        lsp::ServerCapabilities {
13801            completion_provider: Some(lsp::CompletionOptions {
13802                trigger_characters: Some(vec![".".to_string()]),
13803                resolve_provider: Some(true),
13804                ..Default::default()
13805            }),
13806            ..Default::default()
13807        },
13808        cx,
13809    )
13810    .await;
13811
13812    cx.set_state("fn main() { let a = 2ˇ; }");
13813    cx.simulate_keystroke(".");
13814
13815    let completion_data = default_data.clone();
13816    let completion_characters = default_commit_characters.clone();
13817    let completion_items = items.clone();
13818    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13819        let default_data = completion_data.clone();
13820        let default_commit_characters = completion_characters.clone();
13821        let items = completion_items.clone();
13822        async move {
13823            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13824                items,
13825                item_defaults: Some(lsp::CompletionListItemDefaults {
13826                    data: Some(default_data.clone()),
13827                    commit_characters: Some(default_commit_characters.clone()),
13828                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13829                        default_edit_range,
13830                    )),
13831                    insert_text_format: Some(default_insert_text_format),
13832                    insert_text_mode: Some(default_insert_text_mode),
13833                }),
13834                ..lsp::CompletionList::default()
13835            })))
13836        }
13837    })
13838    .next()
13839    .await;
13840
13841    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13842    cx.lsp
13843        .server
13844        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13845            let closure_resolved_items = resolved_items.clone();
13846            move |item_to_resolve, _| {
13847                let closure_resolved_items = closure_resolved_items.clone();
13848                async move {
13849                    closure_resolved_items.lock().push(item_to_resolve.clone());
13850                    Ok(item_to_resolve)
13851                }
13852            }
13853        })
13854        .detach();
13855
13856    cx.condition(|editor, _| editor.context_menu_visible())
13857        .await;
13858    cx.run_until_parked();
13859    cx.update_editor(|editor, _, _| {
13860        let menu = editor.context_menu.borrow_mut();
13861        match menu.as_ref().expect("should have the completions menu") {
13862            CodeContextMenu::Completions(completions_menu) => {
13863                assert_eq!(
13864                    completions_menu
13865                        .entries
13866                        .borrow()
13867                        .iter()
13868                        .map(|mat| mat.string.clone())
13869                        .collect::<Vec<String>>(),
13870                    items
13871                        .iter()
13872                        .map(|completion| completion.label.clone())
13873                        .collect::<Vec<String>>()
13874                );
13875            }
13876            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13877        }
13878    });
13879    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13880    // with 4 from the end.
13881    assert_eq!(
13882        *resolved_items.lock(),
13883        [&items[0..16], &items[items.len() - 4..items.len()]]
13884            .concat()
13885            .iter()
13886            .cloned()
13887            .map(|mut item| {
13888                if item.data.is_none() {
13889                    item.data = Some(default_data.clone());
13890                }
13891                item
13892            })
13893            .collect::<Vec<lsp::CompletionItem>>(),
13894        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13895    );
13896    resolved_items.lock().clear();
13897
13898    cx.update_editor(|editor, window, cx| {
13899        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13900    });
13901    cx.run_until_parked();
13902    // Completions that have already been resolved are skipped.
13903    assert_eq!(
13904        *resolved_items.lock(),
13905        items[items.len() - 16..items.len() - 4]
13906            .iter()
13907            .cloned()
13908            .map(|mut item| {
13909                if item.data.is_none() {
13910                    item.data = Some(default_data.clone());
13911                }
13912                item
13913            })
13914            .collect::<Vec<lsp::CompletionItem>>()
13915    );
13916    resolved_items.lock().clear();
13917}
13918
13919#[gpui::test]
13920async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13921    init_test(cx, |_| {});
13922
13923    let mut cx = EditorLspTestContext::new(
13924        Language::new(
13925            LanguageConfig {
13926                matcher: LanguageMatcher {
13927                    path_suffixes: vec!["jsx".into()],
13928                    ..Default::default()
13929                },
13930                overrides: [(
13931                    "element".into(),
13932                    LanguageConfigOverride {
13933                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13934                        ..Default::default()
13935                    },
13936                )]
13937                .into_iter()
13938                .collect(),
13939                ..Default::default()
13940            },
13941            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13942        )
13943        .with_override_query("(jsx_self_closing_element) @element")
13944        .unwrap(),
13945        lsp::ServerCapabilities {
13946            completion_provider: Some(lsp::CompletionOptions {
13947                trigger_characters: Some(vec![":".to_string()]),
13948                ..Default::default()
13949            }),
13950            ..Default::default()
13951        },
13952        cx,
13953    )
13954    .await;
13955
13956    cx.lsp
13957        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13958            Ok(Some(lsp::CompletionResponse::Array(vec![
13959                lsp::CompletionItem {
13960                    label: "bg-blue".into(),
13961                    ..Default::default()
13962                },
13963                lsp::CompletionItem {
13964                    label: "bg-red".into(),
13965                    ..Default::default()
13966                },
13967                lsp::CompletionItem {
13968                    label: "bg-yellow".into(),
13969                    ..Default::default()
13970                },
13971            ])))
13972        });
13973
13974    cx.set_state(r#"<p class="bgˇ" />"#);
13975
13976    // Trigger completion when typing a dash, because the dash is an extra
13977    // word character in the 'element' scope, which contains the cursor.
13978    cx.simulate_keystroke("-");
13979    cx.executor().run_until_parked();
13980    cx.update_editor(|editor, _, _| {
13981        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13982        {
13983            assert_eq!(
13984                completion_menu_entries(&menu),
13985                &["bg-red", "bg-blue", "bg-yellow"]
13986            );
13987        } else {
13988            panic!("expected completion menu to be open");
13989        }
13990    });
13991
13992    cx.simulate_keystroke("l");
13993    cx.executor().run_until_parked();
13994    cx.update_editor(|editor, _, _| {
13995        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13996        {
13997            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13998        } else {
13999            panic!("expected completion menu to be open");
14000        }
14001    });
14002
14003    // When filtering completions, consider the character after the '-' to
14004    // be the start of a subword.
14005    cx.set_state(r#"<p class="yelˇ" />"#);
14006    cx.simulate_keystroke("l");
14007    cx.executor().run_until_parked();
14008    cx.update_editor(|editor, _, _| {
14009        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
14010        {
14011            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
14012        } else {
14013            panic!("expected completion menu to be open");
14014        }
14015    });
14016}
14017
14018fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
14019    let entries = menu.entries.borrow();
14020    entries.iter().map(|mat| mat.string.clone()).collect()
14021}
14022
14023#[gpui::test]
14024async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
14025    init_test(cx, |settings| {
14026        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
14027            FormatterList(vec![Formatter::Prettier].into()),
14028        ))
14029    });
14030
14031    let fs = FakeFs::new(cx.executor());
14032    fs.insert_file(path!("/file.ts"), Default::default()).await;
14033
14034    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
14035    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
14036
14037    language_registry.add(Arc::new(Language::new(
14038        LanguageConfig {
14039            name: "TypeScript".into(),
14040            matcher: LanguageMatcher {
14041                path_suffixes: vec!["ts".to_string()],
14042                ..Default::default()
14043            },
14044            ..Default::default()
14045        },
14046        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
14047    )));
14048    update_test_language_settings(cx, |settings| {
14049        settings.defaults.prettier = Some(PrettierSettings {
14050            allowed: true,
14051            ..PrettierSettings::default()
14052        });
14053    });
14054
14055    let test_plugin = "test_plugin";
14056    let _ = language_registry.register_fake_lsp(
14057        "TypeScript",
14058        FakeLspAdapter {
14059            prettier_plugins: vec![test_plugin],
14060            ..Default::default()
14061        },
14062    );
14063
14064    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
14065    let buffer = project
14066        .update(cx, |project, cx| {
14067            project.open_local_buffer(path!("/file.ts"), cx)
14068        })
14069        .await
14070        .unwrap();
14071
14072    let buffer_text = "one\ntwo\nthree\n";
14073    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
14074    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
14075    editor.update_in(cx, |editor, window, cx| {
14076        editor.set_text(buffer_text, window, cx)
14077    });
14078
14079    editor
14080        .update_in(cx, |editor, window, cx| {
14081            editor.perform_format(
14082                project.clone(),
14083                FormatTrigger::Manual,
14084                FormatTarget::Buffers,
14085                window,
14086                cx,
14087            )
14088        })
14089        .unwrap()
14090        .await;
14091    assert_eq!(
14092        editor.update(cx, |editor, cx| editor.text(cx)),
14093        buffer_text.to_string() + prettier_format_suffix,
14094        "Test prettier formatting was not applied to the original buffer text",
14095    );
14096
14097    update_test_language_settings(cx, |settings| {
14098        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
14099    });
14100    let format = editor.update_in(cx, |editor, window, cx| {
14101        editor.perform_format(
14102            project.clone(),
14103            FormatTrigger::Manual,
14104            FormatTarget::Buffers,
14105            window,
14106            cx,
14107        )
14108    });
14109    format.await.unwrap();
14110    assert_eq!(
14111        editor.update(cx, |editor, cx| editor.text(cx)),
14112        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14113        "Autoformatting (via test prettier) was not applied to the original buffer text",
14114    );
14115}
14116
14117#[gpui::test]
14118async fn test_addition_reverts(cx: &mut TestAppContext) {
14119    init_test(cx, |_| {});
14120    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14121    let base_text = indoc! {r#"
14122        struct Row;
14123        struct Row1;
14124        struct Row2;
14125
14126        struct Row4;
14127        struct Row5;
14128        struct Row6;
14129
14130        struct Row8;
14131        struct Row9;
14132        struct Row10;"#};
14133
14134    // When addition hunks are not adjacent to carets, no hunk revert is performed
14135    assert_hunk_revert(
14136        indoc! {r#"struct Row;
14137                   struct Row1;
14138                   struct Row1.1;
14139                   struct Row1.2;
14140                   struct Row2;ˇ
14141
14142                   struct Row4;
14143                   struct Row5;
14144                   struct Row6;
14145
14146                   struct Row8;
14147                   ˇstruct Row9;
14148                   struct Row9.1;
14149                   struct Row9.2;
14150                   struct Row9.3;
14151                   struct Row10;"#},
14152        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14153        indoc! {r#"struct Row;
14154                   struct Row1;
14155                   struct Row1.1;
14156                   struct Row1.2;
14157                   struct Row2;ˇ
14158
14159                   struct Row4;
14160                   struct Row5;
14161                   struct Row6;
14162
14163                   struct Row8;
14164                   ˇstruct Row9;
14165                   struct Row9.1;
14166                   struct Row9.2;
14167                   struct Row9.3;
14168                   struct Row10;"#},
14169        base_text,
14170        &mut cx,
14171    );
14172    // Same for selections
14173    assert_hunk_revert(
14174        indoc! {r#"struct Row;
14175                   struct Row1;
14176                   struct Row2;
14177                   struct Row2.1;
14178                   struct Row2.2;
14179                   «ˇ
14180                   struct Row4;
14181                   struct» Row5;
14182                   «struct Row6;
14183                   ˇ»
14184                   struct Row9.1;
14185                   struct Row9.2;
14186                   struct Row9.3;
14187                   struct Row8;
14188                   struct Row9;
14189                   struct Row10;"#},
14190        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14191        indoc! {r#"struct Row;
14192                   struct Row1;
14193                   struct Row2;
14194                   struct Row2.1;
14195                   struct Row2.2;
14196                   «ˇ
14197                   struct Row4;
14198                   struct» Row5;
14199                   «struct Row6;
14200                   ˇ»
14201                   struct Row9.1;
14202                   struct Row9.2;
14203                   struct Row9.3;
14204                   struct Row8;
14205                   struct Row9;
14206                   struct Row10;"#},
14207        base_text,
14208        &mut cx,
14209    );
14210
14211    // When carets and selections intersect the addition hunks, those are reverted.
14212    // Adjacent carets got merged.
14213    assert_hunk_revert(
14214        indoc! {r#"struct Row;
14215                   ˇ// something on the top
14216                   struct Row1;
14217                   struct Row2;
14218                   struct Roˇw3.1;
14219                   struct Row2.2;
14220                   struct Row2.3;ˇ
14221
14222                   struct Row4;
14223                   struct ˇRow5.1;
14224                   struct Row5.2;
14225                   struct «Rowˇ»5.3;
14226                   struct Row5;
14227                   struct Row6;
14228                   ˇ
14229                   struct Row9.1;
14230                   struct «Rowˇ»9.2;
14231                   struct «ˇRow»9.3;
14232                   struct Row8;
14233                   struct Row9;
14234                   «ˇ// something on bottom»
14235                   struct Row10;"#},
14236        vec![
14237            DiffHunkStatusKind::Added,
14238            DiffHunkStatusKind::Added,
14239            DiffHunkStatusKind::Added,
14240            DiffHunkStatusKind::Added,
14241            DiffHunkStatusKind::Added,
14242        ],
14243        indoc! {r#"struct Row;
14244                   ˇstruct Row1;
14245                   struct Row2;
14246                   ˇ
14247                   struct Row4;
14248                   ˇstruct Row5;
14249                   struct Row6;
14250                   ˇ
14251                   ˇstruct Row8;
14252                   struct Row9;
14253                   ˇstruct Row10;"#},
14254        base_text,
14255        &mut cx,
14256    );
14257}
14258
14259#[gpui::test]
14260async fn test_modification_reverts(cx: &mut TestAppContext) {
14261    init_test(cx, |_| {});
14262    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14263    let base_text = indoc! {r#"
14264        struct Row;
14265        struct Row1;
14266        struct Row2;
14267
14268        struct Row4;
14269        struct Row5;
14270        struct Row6;
14271
14272        struct Row8;
14273        struct Row9;
14274        struct Row10;"#};
14275
14276    // Modification hunks behave the same as the addition ones.
14277    assert_hunk_revert(
14278        indoc! {r#"struct Row;
14279                   struct Row1;
14280                   struct Row33;
14281                   ˇ
14282                   struct Row4;
14283                   struct Row5;
14284                   struct Row6;
14285                   ˇ
14286                   struct Row99;
14287                   struct Row9;
14288                   struct Row10;"#},
14289        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14290        indoc! {r#"struct Row;
14291                   struct Row1;
14292                   struct Row33;
14293                   ˇ
14294                   struct Row4;
14295                   struct Row5;
14296                   struct Row6;
14297                   ˇ
14298                   struct Row99;
14299                   struct Row9;
14300                   struct Row10;"#},
14301        base_text,
14302        &mut cx,
14303    );
14304    assert_hunk_revert(
14305        indoc! {r#"struct Row;
14306                   struct Row1;
14307                   struct Row33;
14308                   «ˇ
14309                   struct Row4;
14310                   struct» Row5;
14311                   «struct Row6;
14312                   ˇ»
14313                   struct Row99;
14314                   struct Row9;
14315                   struct Row10;"#},
14316        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14317        indoc! {r#"struct Row;
14318                   struct Row1;
14319                   struct Row33;
14320                   «ˇ
14321                   struct Row4;
14322                   struct» Row5;
14323                   «struct Row6;
14324                   ˇ»
14325                   struct Row99;
14326                   struct Row9;
14327                   struct Row10;"#},
14328        base_text,
14329        &mut cx,
14330    );
14331
14332    assert_hunk_revert(
14333        indoc! {r#"ˇstruct Row1.1;
14334                   struct Row1;
14335                   «ˇstr»uct Row22;
14336
14337                   struct ˇRow44;
14338                   struct Row5;
14339                   struct «Rˇ»ow66;ˇ
14340
14341                   «struˇ»ct Row88;
14342                   struct Row9;
14343                   struct Row1011;ˇ"#},
14344        vec![
14345            DiffHunkStatusKind::Modified,
14346            DiffHunkStatusKind::Modified,
14347            DiffHunkStatusKind::Modified,
14348            DiffHunkStatusKind::Modified,
14349            DiffHunkStatusKind::Modified,
14350            DiffHunkStatusKind::Modified,
14351        ],
14352        indoc! {r#"struct Row;
14353                   ˇstruct Row1;
14354                   struct Row2;
14355                   ˇ
14356                   struct Row4;
14357                   ˇstruct Row5;
14358                   struct Row6;
14359                   ˇ
14360                   struct Row8;
14361                   ˇstruct Row9;
14362                   struct Row10;ˇ"#},
14363        base_text,
14364        &mut cx,
14365    );
14366}
14367
14368#[gpui::test]
14369async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14370    init_test(cx, |_| {});
14371    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14372    let base_text = indoc! {r#"
14373        one
14374
14375        two
14376        three
14377        "#};
14378
14379    cx.set_head_text(base_text);
14380    cx.set_state("\nˇ\n");
14381    cx.executor().run_until_parked();
14382    cx.update_editor(|editor, _window, cx| {
14383        editor.expand_selected_diff_hunks(cx);
14384    });
14385    cx.executor().run_until_parked();
14386    cx.update_editor(|editor, window, cx| {
14387        editor.backspace(&Default::default(), window, cx);
14388    });
14389    cx.run_until_parked();
14390    cx.assert_state_with_diff(
14391        indoc! {r#"
14392
14393        - two
14394        - threeˇ
14395        +
14396        "#}
14397        .to_string(),
14398    );
14399}
14400
14401#[gpui::test]
14402async fn test_deletion_reverts(cx: &mut TestAppContext) {
14403    init_test(cx, |_| {});
14404    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14405    let base_text = indoc! {r#"struct Row;
14406struct Row1;
14407struct Row2;
14408
14409struct Row4;
14410struct Row5;
14411struct Row6;
14412
14413struct Row8;
14414struct Row9;
14415struct Row10;"#};
14416
14417    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14418    assert_hunk_revert(
14419        indoc! {r#"struct Row;
14420                   struct Row2;
14421
14422                   ˇstruct Row4;
14423                   struct Row5;
14424                   struct Row6;
14425                   ˇ
14426                   struct Row8;
14427                   struct Row10;"#},
14428        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14429        indoc! {r#"struct Row;
14430                   struct Row2;
14431
14432                   ˇstruct Row4;
14433                   struct Row5;
14434                   struct Row6;
14435                   ˇ
14436                   struct Row8;
14437                   struct Row10;"#},
14438        base_text,
14439        &mut cx,
14440    );
14441    assert_hunk_revert(
14442        indoc! {r#"struct Row;
14443                   struct Row2;
14444
14445                   «ˇstruct Row4;
14446                   struct» Row5;
14447                   «struct Row6;
14448                   ˇ»
14449                   struct Row8;
14450                   struct Row10;"#},
14451        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14452        indoc! {r#"struct Row;
14453                   struct Row2;
14454
14455                   «ˇstruct Row4;
14456                   struct» Row5;
14457                   «struct Row6;
14458                   ˇ»
14459                   struct Row8;
14460                   struct Row10;"#},
14461        base_text,
14462        &mut cx,
14463    );
14464
14465    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14466    assert_hunk_revert(
14467        indoc! {r#"struct Row;
14468                   ˇstruct Row2;
14469
14470                   struct Row4;
14471                   struct Row5;
14472                   struct Row6;
14473
14474                   struct Row8;ˇ
14475                   struct Row10;"#},
14476        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14477        indoc! {r#"struct Row;
14478                   struct Row1;
14479                   ˇstruct Row2;
14480
14481                   struct Row4;
14482                   struct Row5;
14483                   struct Row6;
14484
14485                   struct Row8;ˇ
14486                   struct Row9;
14487                   struct Row10;"#},
14488        base_text,
14489        &mut cx,
14490    );
14491    assert_hunk_revert(
14492        indoc! {r#"struct Row;
14493                   struct Row2«ˇ;
14494                   struct Row4;
14495                   struct» Row5;
14496                   «struct Row6;
14497
14498                   struct Row8;ˇ»
14499                   struct Row10;"#},
14500        vec![
14501            DiffHunkStatusKind::Deleted,
14502            DiffHunkStatusKind::Deleted,
14503            DiffHunkStatusKind::Deleted,
14504        ],
14505        indoc! {r#"struct Row;
14506                   struct Row1;
14507                   struct Row2«ˇ;
14508
14509                   struct Row4;
14510                   struct» Row5;
14511                   «struct Row6;
14512
14513                   struct Row8;ˇ»
14514                   struct Row9;
14515                   struct Row10;"#},
14516        base_text,
14517        &mut cx,
14518    );
14519}
14520
14521#[gpui::test]
14522async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14523    init_test(cx, |_| {});
14524
14525    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14526    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14527    let base_text_3 =
14528        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14529
14530    let text_1 = edit_first_char_of_every_line(base_text_1);
14531    let text_2 = edit_first_char_of_every_line(base_text_2);
14532    let text_3 = edit_first_char_of_every_line(base_text_3);
14533
14534    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14535    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14536    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14537
14538    let multibuffer = cx.new(|cx| {
14539        let mut multibuffer = MultiBuffer::new(ReadWrite);
14540        multibuffer.push_excerpts(
14541            buffer_1.clone(),
14542            [
14543                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14544                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14545                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14546            ],
14547            cx,
14548        );
14549        multibuffer.push_excerpts(
14550            buffer_2.clone(),
14551            [
14552                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14553                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14554                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14555            ],
14556            cx,
14557        );
14558        multibuffer.push_excerpts(
14559            buffer_3.clone(),
14560            [
14561                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14562                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14563                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14564            ],
14565            cx,
14566        );
14567        multibuffer
14568    });
14569
14570    let fs = FakeFs::new(cx.executor());
14571    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14572    let (editor, cx) = cx
14573        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14574    editor.update_in(cx, |editor, _window, cx| {
14575        for (buffer, diff_base) in [
14576            (buffer_1.clone(), base_text_1),
14577            (buffer_2.clone(), base_text_2),
14578            (buffer_3.clone(), base_text_3),
14579        ] {
14580            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14581            editor
14582                .buffer
14583                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14584        }
14585    });
14586    cx.executor().run_until_parked();
14587
14588    editor.update_in(cx, |editor, window, cx| {
14589        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}");
14590        editor.select_all(&SelectAll, window, cx);
14591        editor.git_restore(&Default::default(), window, cx);
14592    });
14593    cx.executor().run_until_parked();
14594
14595    // When all ranges are selected, all buffer hunks are reverted.
14596    editor.update(cx, |editor, cx| {
14597        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");
14598    });
14599    buffer_1.update(cx, |buffer, _| {
14600        assert_eq!(buffer.text(), base_text_1);
14601    });
14602    buffer_2.update(cx, |buffer, _| {
14603        assert_eq!(buffer.text(), base_text_2);
14604    });
14605    buffer_3.update(cx, |buffer, _| {
14606        assert_eq!(buffer.text(), base_text_3);
14607    });
14608
14609    editor.update_in(cx, |editor, window, cx| {
14610        editor.undo(&Default::default(), window, cx);
14611    });
14612
14613    editor.update_in(cx, |editor, window, cx| {
14614        editor.change_selections(None, window, cx, |s| {
14615            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14616        });
14617        editor.git_restore(&Default::default(), window, cx);
14618    });
14619
14620    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14621    // but not affect buffer_2 and its related excerpts.
14622    editor.update(cx, |editor, cx| {
14623        assert_eq!(
14624            editor.text(cx),
14625            "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}"
14626        );
14627    });
14628    buffer_1.update(cx, |buffer, _| {
14629        assert_eq!(buffer.text(), base_text_1);
14630    });
14631    buffer_2.update(cx, |buffer, _| {
14632        assert_eq!(
14633            buffer.text(),
14634            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14635        );
14636    });
14637    buffer_3.update(cx, |buffer, _| {
14638        assert_eq!(
14639            buffer.text(),
14640            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14641        );
14642    });
14643
14644    fn edit_first_char_of_every_line(text: &str) -> String {
14645        text.split('\n')
14646            .map(|line| format!("X{}", &line[1..]))
14647            .collect::<Vec<_>>()
14648            .join("\n")
14649    }
14650}
14651
14652#[gpui::test]
14653async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14654    init_test(cx, |_| {});
14655
14656    let cols = 4;
14657    let rows = 10;
14658    let sample_text_1 = sample_text(rows, cols, 'a');
14659    assert_eq!(
14660        sample_text_1,
14661        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14662    );
14663    let sample_text_2 = sample_text(rows, cols, 'l');
14664    assert_eq!(
14665        sample_text_2,
14666        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14667    );
14668    let sample_text_3 = sample_text(rows, cols, 'v');
14669    assert_eq!(
14670        sample_text_3,
14671        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14672    );
14673
14674    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14675    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14676    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14677
14678    let multi_buffer = cx.new(|cx| {
14679        let mut multibuffer = MultiBuffer::new(ReadWrite);
14680        multibuffer.push_excerpts(
14681            buffer_1.clone(),
14682            [
14683                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14684                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14685                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14686            ],
14687            cx,
14688        );
14689        multibuffer.push_excerpts(
14690            buffer_2.clone(),
14691            [
14692                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14693                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14694                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14695            ],
14696            cx,
14697        );
14698        multibuffer.push_excerpts(
14699            buffer_3.clone(),
14700            [
14701                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14702                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14703                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14704            ],
14705            cx,
14706        );
14707        multibuffer
14708    });
14709
14710    let fs = FakeFs::new(cx.executor());
14711    fs.insert_tree(
14712        "/a",
14713        json!({
14714            "main.rs": sample_text_1,
14715            "other.rs": sample_text_2,
14716            "lib.rs": sample_text_3,
14717        }),
14718    )
14719    .await;
14720    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14721    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14722    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14723    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14724        Editor::new(
14725            EditorMode::full(),
14726            multi_buffer,
14727            Some(project.clone()),
14728            window,
14729            cx,
14730        )
14731    });
14732    let multibuffer_item_id = workspace
14733        .update(cx, |workspace, window, cx| {
14734            assert!(
14735                workspace.active_item(cx).is_none(),
14736                "active item should be None before the first item is added"
14737            );
14738            workspace.add_item_to_active_pane(
14739                Box::new(multi_buffer_editor.clone()),
14740                None,
14741                true,
14742                window,
14743                cx,
14744            );
14745            let active_item = workspace
14746                .active_item(cx)
14747                .expect("should have an active item after adding the multi buffer");
14748            assert!(
14749                !active_item.is_singleton(cx),
14750                "A multi buffer was expected to active after adding"
14751            );
14752            active_item.item_id()
14753        })
14754        .unwrap();
14755    cx.executor().run_until_parked();
14756
14757    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14758        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14759            s.select_ranges(Some(1..2))
14760        });
14761        editor.open_excerpts(&OpenExcerpts, window, cx);
14762    });
14763    cx.executor().run_until_parked();
14764    let first_item_id = workspace
14765        .update(cx, |workspace, window, cx| {
14766            let active_item = workspace
14767                .active_item(cx)
14768                .expect("should have an active item after navigating into the 1st buffer");
14769            let first_item_id = active_item.item_id();
14770            assert_ne!(
14771                first_item_id, multibuffer_item_id,
14772                "Should navigate into the 1st buffer and activate it"
14773            );
14774            assert!(
14775                active_item.is_singleton(cx),
14776                "New active item should be a singleton buffer"
14777            );
14778            assert_eq!(
14779                active_item
14780                    .act_as::<Editor>(cx)
14781                    .expect("should have navigated into an editor for the 1st buffer")
14782                    .read(cx)
14783                    .text(cx),
14784                sample_text_1
14785            );
14786
14787            workspace
14788                .go_back(workspace.active_pane().downgrade(), window, cx)
14789                .detach_and_log_err(cx);
14790
14791            first_item_id
14792        })
14793        .unwrap();
14794    cx.executor().run_until_parked();
14795    workspace
14796        .update(cx, |workspace, _, cx| {
14797            let active_item = workspace
14798                .active_item(cx)
14799                .expect("should have an active item after navigating back");
14800            assert_eq!(
14801                active_item.item_id(),
14802                multibuffer_item_id,
14803                "Should navigate back to the multi buffer"
14804            );
14805            assert!(!active_item.is_singleton(cx));
14806        })
14807        .unwrap();
14808
14809    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14810        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14811            s.select_ranges(Some(39..40))
14812        });
14813        editor.open_excerpts(&OpenExcerpts, window, cx);
14814    });
14815    cx.executor().run_until_parked();
14816    let second_item_id = workspace
14817        .update(cx, |workspace, window, cx| {
14818            let active_item = workspace
14819                .active_item(cx)
14820                .expect("should have an active item after navigating into the 2nd buffer");
14821            let second_item_id = active_item.item_id();
14822            assert_ne!(
14823                second_item_id, multibuffer_item_id,
14824                "Should navigate away from the multibuffer"
14825            );
14826            assert_ne!(
14827                second_item_id, first_item_id,
14828                "Should navigate into the 2nd buffer and activate it"
14829            );
14830            assert!(
14831                active_item.is_singleton(cx),
14832                "New active item should be a singleton buffer"
14833            );
14834            assert_eq!(
14835                active_item
14836                    .act_as::<Editor>(cx)
14837                    .expect("should have navigated into an editor")
14838                    .read(cx)
14839                    .text(cx),
14840                sample_text_2
14841            );
14842
14843            workspace
14844                .go_back(workspace.active_pane().downgrade(), window, cx)
14845                .detach_and_log_err(cx);
14846
14847            second_item_id
14848        })
14849        .unwrap();
14850    cx.executor().run_until_parked();
14851    workspace
14852        .update(cx, |workspace, _, cx| {
14853            let active_item = workspace
14854                .active_item(cx)
14855                .expect("should have an active item after navigating back from the 2nd buffer");
14856            assert_eq!(
14857                active_item.item_id(),
14858                multibuffer_item_id,
14859                "Should navigate back from the 2nd buffer to the multi buffer"
14860            );
14861            assert!(!active_item.is_singleton(cx));
14862        })
14863        .unwrap();
14864
14865    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14866        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14867            s.select_ranges(Some(70..70))
14868        });
14869        editor.open_excerpts(&OpenExcerpts, window, cx);
14870    });
14871    cx.executor().run_until_parked();
14872    workspace
14873        .update(cx, |workspace, window, cx| {
14874            let active_item = workspace
14875                .active_item(cx)
14876                .expect("should have an active item after navigating into the 3rd buffer");
14877            let third_item_id = active_item.item_id();
14878            assert_ne!(
14879                third_item_id, multibuffer_item_id,
14880                "Should navigate into the 3rd buffer and activate it"
14881            );
14882            assert_ne!(third_item_id, first_item_id);
14883            assert_ne!(third_item_id, second_item_id);
14884            assert!(
14885                active_item.is_singleton(cx),
14886                "New active item should be a singleton buffer"
14887            );
14888            assert_eq!(
14889                active_item
14890                    .act_as::<Editor>(cx)
14891                    .expect("should have navigated into an editor")
14892                    .read(cx)
14893                    .text(cx),
14894                sample_text_3
14895            );
14896
14897            workspace
14898                .go_back(workspace.active_pane().downgrade(), window, cx)
14899                .detach_and_log_err(cx);
14900        })
14901        .unwrap();
14902    cx.executor().run_until_parked();
14903    workspace
14904        .update(cx, |workspace, _, cx| {
14905            let active_item = workspace
14906                .active_item(cx)
14907                .expect("should have an active item after navigating back from the 3rd buffer");
14908            assert_eq!(
14909                active_item.item_id(),
14910                multibuffer_item_id,
14911                "Should navigate back from the 3rd buffer to the multi buffer"
14912            );
14913            assert!(!active_item.is_singleton(cx));
14914        })
14915        .unwrap();
14916}
14917
14918#[gpui::test]
14919async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14920    init_test(cx, |_| {});
14921
14922    let mut cx = EditorTestContext::new(cx).await;
14923
14924    let diff_base = r#"
14925        use some::mod;
14926
14927        const A: u32 = 42;
14928
14929        fn main() {
14930            println!("hello");
14931
14932            println!("world");
14933        }
14934        "#
14935    .unindent();
14936
14937    cx.set_state(
14938        &r#"
14939        use some::modified;
14940
14941        ˇ
14942        fn main() {
14943            println!("hello there");
14944
14945            println!("around the");
14946            println!("world");
14947        }
14948        "#
14949        .unindent(),
14950    );
14951
14952    cx.set_head_text(&diff_base);
14953    executor.run_until_parked();
14954
14955    cx.update_editor(|editor, window, cx| {
14956        editor.go_to_next_hunk(&GoToHunk, window, cx);
14957        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14958    });
14959    executor.run_until_parked();
14960    cx.assert_state_with_diff(
14961        r#"
14962          use some::modified;
14963
14964
14965          fn main() {
14966        -     println!("hello");
14967        + ˇ    println!("hello there");
14968
14969              println!("around the");
14970              println!("world");
14971          }
14972        "#
14973        .unindent(),
14974    );
14975
14976    cx.update_editor(|editor, window, cx| {
14977        for _ in 0..2 {
14978            editor.go_to_next_hunk(&GoToHunk, window, cx);
14979            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14980        }
14981    });
14982    executor.run_until_parked();
14983    cx.assert_state_with_diff(
14984        r#"
14985        - use some::mod;
14986        + ˇuse some::modified;
14987
14988
14989          fn main() {
14990        -     println!("hello");
14991        +     println!("hello there");
14992
14993        +     println!("around the");
14994              println!("world");
14995          }
14996        "#
14997        .unindent(),
14998    );
14999
15000    cx.update_editor(|editor, window, cx| {
15001        editor.go_to_next_hunk(&GoToHunk, window, cx);
15002        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15003    });
15004    executor.run_until_parked();
15005    cx.assert_state_with_diff(
15006        r#"
15007        - use some::mod;
15008        + use some::modified;
15009
15010        - const A: u32 = 42;
15011          ˇ
15012          fn main() {
15013        -     println!("hello");
15014        +     println!("hello there");
15015
15016        +     println!("around the");
15017              println!("world");
15018          }
15019        "#
15020        .unindent(),
15021    );
15022
15023    cx.update_editor(|editor, window, cx| {
15024        editor.cancel(&Cancel, window, cx);
15025    });
15026
15027    cx.assert_state_with_diff(
15028        r#"
15029          use some::modified;
15030
15031          ˇ
15032          fn main() {
15033              println!("hello there");
15034
15035              println!("around the");
15036              println!("world");
15037          }
15038        "#
15039        .unindent(),
15040    );
15041}
15042
15043#[gpui::test]
15044async fn test_diff_base_change_with_expanded_diff_hunks(
15045    executor: BackgroundExecutor,
15046    cx: &mut TestAppContext,
15047) {
15048    init_test(cx, |_| {});
15049
15050    let mut cx = EditorTestContext::new(cx).await;
15051
15052    let diff_base = r#"
15053        use some::mod1;
15054        use some::mod2;
15055
15056        const A: u32 = 42;
15057        const B: u32 = 42;
15058        const C: u32 = 42;
15059
15060        fn main() {
15061            println!("hello");
15062
15063            println!("world");
15064        }
15065        "#
15066    .unindent();
15067
15068    cx.set_state(
15069        &r#"
15070        use some::mod2;
15071
15072        const A: u32 = 42;
15073        const C: u32 = 42;
15074
15075        fn main(ˇ) {
15076            //println!("hello");
15077
15078            println!("world");
15079            //
15080            //
15081        }
15082        "#
15083        .unindent(),
15084    );
15085
15086    cx.set_head_text(&diff_base);
15087    executor.run_until_parked();
15088
15089    cx.update_editor(|editor, window, cx| {
15090        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15091    });
15092    executor.run_until_parked();
15093    cx.assert_state_with_diff(
15094        r#"
15095        - use some::mod1;
15096          use some::mod2;
15097
15098          const A: u32 = 42;
15099        - const B: u32 = 42;
15100          const C: u32 = 42;
15101
15102          fn main(ˇ) {
15103        -     println!("hello");
15104        +     //println!("hello");
15105
15106              println!("world");
15107        +     //
15108        +     //
15109          }
15110        "#
15111        .unindent(),
15112    );
15113
15114    cx.set_head_text("new diff base!");
15115    executor.run_until_parked();
15116    cx.assert_state_with_diff(
15117        r#"
15118        - new diff base!
15119        + use some::mod2;
15120        +
15121        + const A: u32 = 42;
15122        + const C: u32 = 42;
15123        +
15124        + fn main(ˇ) {
15125        +     //println!("hello");
15126        +
15127        +     println!("world");
15128        +     //
15129        +     //
15130        + }
15131        "#
15132        .unindent(),
15133    );
15134}
15135
15136#[gpui::test]
15137async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15138    init_test(cx, |_| {});
15139
15140    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15141    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15142    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15143    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15144    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15145    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15146
15147    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15148    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15149    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15150
15151    let multi_buffer = cx.new(|cx| {
15152        let mut multibuffer = MultiBuffer::new(ReadWrite);
15153        multibuffer.push_excerpts(
15154            buffer_1.clone(),
15155            [
15156                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15157                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15158                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15159            ],
15160            cx,
15161        );
15162        multibuffer.push_excerpts(
15163            buffer_2.clone(),
15164            [
15165                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15166                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15167                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15168            ],
15169            cx,
15170        );
15171        multibuffer.push_excerpts(
15172            buffer_3.clone(),
15173            [
15174                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15175                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15176                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15177            ],
15178            cx,
15179        );
15180        multibuffer
15181    });
15182
15183    let editor =
15184        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15185    editor
15186        .update(cx, |editor, _window, cx| {
15187            for (buffer, diff_base) in [
15188                (buffer_1.clone(), file_1_old),
15189                (buffer_2.clone(), file_2_old),
15190                (buffer_3.clone(), file_3_old),
15191            ] {
15192                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15193                editor
15194                    .buffer
15195                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15196            }
15197        })
15198        .unwrap();
15199
15200    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15201    cx.run_until_parked();
15202
15203    cx.assert_editor_state(
15204        &"
15205            ˇaaa
15206            ccc
15207            ddd
15208
15209            ggg
15210            hhh
15211
15212
15213            lll
15214            mmm
15215            NNN
15216
15217            qqq
15218            rrr
15219
15220            uuu
15221            111
15222            222
15223            333
15224
15225            666
15226            777
15227
15228            000
15229            !!!"
15230        .unindent(),
15231    );
15232
15233    cx.update_editor(|editor, window, cx| {
15234        editor.select_all(&SelectAll, window, cx);
15235        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15236    });
15237    cx.executor().run_until_parked();
15238
15239    cx.assert_state_with_diff(
15240        "
15241            «aaa
15242          - bbb
15243            ccc
15244            ddd
15245
15246            ggg
15247            hhh
15248
15249
15250            lll
15251            mmm
15252          - nnn
15253          + NNN
15254
15255            qqq
15256            rrr
15257
15258            uuu
15259            111
15260            222
15261            333
15262
15263          + 666
15264            777
15265
15266            000
15267            !!!ˇ»"
15268            .unindent(),
15269    );
15270}
15271
15272#[gpui::test]
15273async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15274    init_test(cx, |_| {});
15275
15276    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15277    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15278
15279    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15280    let multi_buffer = cx.new(|cx| {
15281        let mut multibuffer = MultiBuffer::new(ReadWrite);
15282        multibuffer.push_excerpts(
15283            buffer.clone(),
15284            [
15285                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15286                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15287                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15288            ],
15289            cx,
15290        );
15291        multibuffer
15292    });
15293
15294    let editor =
15295        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15296    editor
15297        .update(cx, |editor, _window, cx| {
15298            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15299            editor
15300                .buffer
15301                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15302        })
15303        .unwrap();
15304
15305    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15306    cx.run_until_parked();
15307
15308    cx.update_editor(|editor, window, cx| {
15309        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15310    });
15311    cx.executor().run_until_parked();
15312
15313    // When the start of a hunk coincides with the start of its excerpt,
15314    // the hunk is expanded. When the start of a a hunk is earlier than
15315    // the start of its excerpt, the hunk is not expanded.
15316    cx.assert_state_with_diff(
15317        "
15318            ˇaaa
15319          - bbb
15320          + BBB
15321
15322          - ddd
15323          - eee
15324          + DDD
15325          + EEE
15326            fff
15327
15328            iii
15329        "
15330        .unindent(),
15331    );
15332}
15333
15334#[gpui::test]
15335async fn test_edits_around_expanded_insertion_hunks(
15336    executor: BackgroundExecutor,
15337    cx: &mut TestAppContext,
15338) {
15339    init_test(cx, |_| {});
15340
15341    let mut cx = EditorTestContext::new(cx).await;
15342
15343    let diff_base = r#"
15344        use some::mod1;
15345        use some::mod2;
15346
15347        const A: u32 = 42;
15348
15349        fn main() {
15350            println!("hello");
15351
15352            println!("world");
15353        }
15354        "#
15355    .unindent();
15356    executor.run_until_parked();
15357    cx.set_state(
15358        &r#"
15359        use some::mod1;
15360        use some::mod2;
15361
15362        const A: u32 = 42;
15363        const B: u32 = 42;
15364        const C: u32 = 42;
15365        ˇ
15366
15367        fn main() {
15368            println!("hello");
15369
15370            println!("world");
15371        }
15372        "#
15373        .unindent(),
15374    );
15375
15376    cx.set_head_text(&diff_base);
15377    executor.run_until_parked();
15378
15379    cx.update_editor(|editor, window, cx| {
15380        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15381    });
15382    executor.run_until_parked();
15383
15384    cx.assert_state_with_diff(
15385        r#"
15386        use some::mod1;
15387        use some::mod2;
15388
15389        const A: u32 = 42;
15390      + const B: u32 = 42;
15391      + const C: u32 = 42;
15392      + ˇ
15393
15394        fn main() {
15395            println!("hello");
15396
15397            println!("world");
15398        }
15399      "#
15400        .unindent(),
15401    );
15402
15403    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15404    executor.run_until_parked();
15405
15406    cx.assert_state_with_diff(
15407        r#"
15408        use some::mod1;
15409        use some::mod2;
15410
15411        const A: u32 = 42;
15412      + const B: u32 = 42;
15413      + const C: u32 = 42;
15414      + const D: u32 = 42;
15415      + ˇ
15416
15417        fn main() {
15418            println!("hello");
15419
15420            println!("world");
15421        }
15422      "#
15423        .unindent(),
15424    );
15425
15426    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15427    executor.run_until_parked();
15428
15429    cx.assert_state_with_diff(
15430        r#"
15431        use some::mod1;
15432        use some::mod2;
15433
15434        const A: u32 = 42;
15435      + const B: u32 = 42;
15436      + const C: u32 = 42;
15437      + const D: u32 = 42;
15438      + const E: u32 = 42;
15439      + ˇ
15440
15441        fn main() {
15442            println!("hello");
15443
15444            println!("world");
15445        }
15446      "#
15447        .unindent(),
15448    );
15449
15450    cx.update_editor(|editor, window, cx| {
15451        editor.delete_line(&DeleteLine, window, cx);
15452    });
15453    executor.run_until_parked();
15454
15455    cx.assert_state_with_diff(
15456        r#"
15457        use some::mod1;
15458        use some::mod2;
15459
15460        const A: u32 = 42;
15461      + const B: u32 = 42;
15462      + const C: u32 = 42;
15463      + const D: u32 = 42;
15464      + const E: u32 = 42;
15465        ˇ
15466        fn main() {
15467            println!("hello");
15468
15469            println!("world");
15470        }
15471      "#
15472        .unindent(),
15473    );
15474
15475    cx.update_editor(|editor, window, cx| {
15476        editor.move_up(&MoveUp, window, cx);
15477        editor.delete_line(&DeleteLine, window, cx);
15478        editor.move_up(&MoveUp, window, cx);
15479        editor.delete_line(&DeleteLine, window, cx);
15480        editor.move_up(&MoveUp, window, cx);
15481        editor.delete_line(&DeleteLine, window, cx);
15482    });
15483    executor.run_until_parked();
15484    cx.assert_state_with_diff(
15485        r#"
15486        use some::mod1;
15487        use some::mod2;
15488
15489        const A: u32 = 42;
15490      + const B: u32 = 42;
15491        ˇ
15492        fn main() {
15493            println!("hello");
15494
15495            println!("world");
15496        }
15497      "#
15498        .unindent(),
15499    );
15500
15501    cx.update_editor(|editor, window, cx| {
15502        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15503        editor.delete_line(&DeleteLine, window, cx);
15504    });
15505    executor.run_until_parked();
15506    cx.assert_state_with_diff(
15507        r#"
15508        ˇ
15509        fn main() {
15510            println!("hello");
15511
15512            println!("world");
15513        }
15514      "#
15515        .unindent(),
15516    );
15517}
15518
15519#[gpui::test]
15520async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15521    init_test(cx, |_| {});
15522
15523    let mut cx = EditorTestContext::new(cx).await;
15524    cx.set_head_text(indoc! { "
15525        one
15526        two
15527        three
15528        four
15529        five
15530        "
15531    });
15532    cx.set_state(indoc! { "
15533        one
15534        ˇthree
15535        five
15536    "});
15537    cx.run_until_parked();
15538    cx.update_editor(|editor, window, cx| {
15539        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15540    });
15541    cx.assert_state_with_diff(
15542        indoc! { "
15543        one
15544      - two
15545        ˇthree
15546      - four
15547        five
15548    "}
15549        .to_string(),
15550    );
15551    cx.update_editor(|editor, window, cx| {
15552        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15553    });
15554
15555    cx.assert_state_with_diff(
15556        indoc! { "
15557        one
15558        ˇthree
15559        five
15560    "}
15561        .to_string(),
15562    );
15563
15564    cx.set_state(indoc! { "
15565        one
15566        ˇTWO
15567        three
15568        four
15569        five
15570    "});
15571    cx.run_until_parked();
15572    cx.update_editor(|editor, window, cx| {
15573        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15574    });
15575
15576    cx.assert_state_with_diff(
15577        indoc! { "
15578            one
15579          - two
15580          + ˇTWO
15581            three
15582            four
15583            five
15584        "}
15585        .to_string(),
15586    );
15587    cx.update_editor(|editor, window, cx| {
15588        editor.move_up(&Default::default(), window, cx);
15589        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15590    });
15591    cx.assert_state_with_diff(
15592        indoc! { "
15593            one
15594            ˇTWO
15595            three
15596            four
15597            five
15598        "}
15599        .to_string(),
15600    );
15601}
15602
15603#[gpui::test]
15604async fn test_edits_around_expanded_deletion_hunks(
15605    executor: BackgroundExecutor,
15606    cx: &mut TestAppContext,
15607) {
15608    init_test(cx, |_| {});
15609
15610    let mut cx = EditorTestContext::new(cx).await;
15611
15612    let diff_base = r#"
15613        use some::mod1;
15614        use some::mod2;
15615
15616        const A: u32 = 42;
15617        const B: u32 = 42;
15618        const C: u32 = 42;
15619
15620
15621        fn main() {
15622            println!("hello");
15623
15624            println!("world");
15625        }
15626    "#
15627    .unindent();
15628    executor.run_until_parked();
15629    cx.set_state(
15630        &r#"
15631        use some::mod1;
15632        use some::mod2;
15633
15634        ˇconst B: u32 = 42;
15635        const C: u32 = 42;
15636
15637
15638        fn main() {
15639            println!("hello");
15640
15641            println!("world");
15642        }
15643        "#
15644        .unindent(),
15645    );
15646
15647    cx.set_head_text(&diff_base);
15648    executor.run_until_parked();
15649
15650    cx.update_editor(|editor, window, cx| {
15651        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15652    });
15653    executor.run_until_parked();
15654
15655    cx.assert_state_with_diff(
15656        r#"
15657        use some::mod1;
15658        use some::mod2;
15659
15660      - const A: u32 = 42;
15661        ˇconst B: u32 = 42;
15662        const C: u32 = 42;
15663
15664
15665        fn main() {
15666            println!("hello");
15667
15668            println!("world");
15669        }
15670      "#
15671        .unindent(),
15672    );
15673
15674    cx.update_editor(|editor, window, cx| {
15675        editor.delete_line(&DeleteLine, window, cx);
15676    });
15677    executor.run_until_parked();
15678    cx.assert_state_with_diff(
15679        r#"
15680        use some::mod1;
15681        use some::mod2;
15682
15683      - const A: u32 = 42;
15684      - const B: u32 = 42;
15685        ˇconst C: u32 = 42;
15686
15687
15688        fn main() {
15689            println!("hello");
15690
15691            println!("world");
15692        }
15693      "#
15694        .unindent(),
15695    );
15696
15697    cx.update_editor(|editor, window, cx| {
15698        editor.delete_line(&DeleteLine, window, cx);
15699    });
15700    executor.run_until_parked();
15701    cx.assert_state_with_diff(
15702        r#"
15703        use some::mod1;
15704        use some::mod2;
15705
15706      - const A: u32 = 42;
15707      - const B: u32 = 42;
15708      - const C: u32 = 42;
15709        ˇ
15710
15711        fn main() {
15712            println!("hello");
15713
15714            println!("world");
15715        }
15716      "#
15717        .unindent(),
15718    );
15719
15720    cx.update_editor(|editor, window, cx| {
15721        editor.handle_input("replacement", window, cx);
15722    });
15723    executor.run_until_parked();
15724    cx.assert_state_with_diff(
15725        r#"
15726        use some::mod1;
15727        use some::mod2;
15728
15729      - const A: u32 = 42;
15730      - const B: u32 = 42;
15731      - const C: u32 = 42;
15732      -
15733      + replacementˇ
15734
15735        fn main() {
15736            println!("hello");
15737
15738            println!("world");
15739        }
15740      "#
15741        .unindent(),
15742    );
15743}
15744
15745#[gpui::test]
15746async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15747    init_test(cx, |_| {});
15748
15749    let mut cx = EditorTestContext::new(cx).await;
15750
15751    let base_text = r#"
15752        one
15753        two
15754        three
15755        four
15756        five
15757    "#
15758    .unindent();
15759    executor.run_until_parked();
15760    cx.set_state(
15761        &r#"
15762        one
15763        two
15764        fˇour
15765        five
15766        "#
15767        .unindent(),
15768    );
15769
15770    cx.set_head_text(&base_text);
15771    executor.run_until_parked();
15772
15773    cx.update_editor(|editor, window, cx| {
15774        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15775    });
15776    executor.run_until_parked();
15777
15778    cx.assert_state_with_diff(
15779        r#"
15780          one
15781          two
15782        - three
15783          fˇour
15784          five
15785        "#
15786        .unindent(),
15787    );
15788
15789    cx.update_editor(|editor, window, cx| {
15790        editor.backspace(&Backspace, window, cx);
15791        editor.backspace(&Backspace, window, cx);
15792    });
15793    executor.run_until_parked();
15794    cx.assert_state_with_diff(
15795        r#"
15796          one
15797          two
15798        - threeˇ
15799        - four
15800        + our
15801          five
15802        "#
15803        .unindent(),
15804    );
15805}
15806
15807#[gpui::test]
15808async fn test_edit_after_expanded_modification_hunk(
15809    executor: BackgroundExecutor,
15810    cx: &mut TestAppContext,
15811) {
15812    init_test(cx, |_| {});
15813
15814    let mut cx = EditorTestContext::new(cx).await;
15815
15816    let diff_base = r#"
15817        use some::mod1;
15818        use some::mod2;
15819
15820        const A: u32 = 42;
15821        const B: u32 = 42;
15822        const C: u32 = 42;
15823        const D: u32 = 42;
15824
15825
15826        fn main() {
15827            println!("hello");
15828
15829            println!("world");
15830        }"#
15831    .unindent();
15832
15833    cx.set_state(
15834        &r#"
15835        use some::mod1;
15836        use some::mod2;
15837
15838        const A: u32 = 42;
15839        const B: u32 = 42;
15840        const C: u32 = 43ˇ
15841        const D: u32 = 42;
15842
15843
15844        fn main() {
15845            println!("hello");
15846
15847            println!("world");
15848        }"#
15849        .unindent(),
15850    );
15851
15852    cx.set_head_text(&diff_base);
15853    executor.run_until_parked();
15854    cx.update_editor(|editor, window, cx| {
15855        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15856    });
15857    executor.run_until_parked();
15858
15859    cx.assert_state_with_diff(
15860        r#"
15861        use some::mod1;
15862        use some::mod2;
15863
15864        const A: u32 = 42;
15865        const B: u32 = 42;
15866      - const C: u32 = 42;
15867      + const C: u32 = 43ˇ
15868        const D: u32 = 42;
15869
15870
15871        fn main() {
15872            println!("hello");
15873
15874            println!("world");
15875        }"#
15876        .unindent(),
15877    );
15878
15879    cx.update_editor(|editor, window, cx| {
15880        editor.handle_input("\nnew_line\n", window, cx);
15881    });
15882    executor.run_until_parked();
15883
15884    cx.assert_state_with_diff(
15885        r#"
15886        use some::mod1;
15887        use some::mod2;
15888
15889        const A: u32 = 42;
15890        const B: u32 = 42;
15891      - const C: u32 = 42;
15892      + const C: u32 = 43
15893      + new_line
15894      + ˇ
15895        const D: u32 = 42;
15896
15897
15898        fn main() {
15899            println!("hello");
15900
15901            println!("world");
15902        }"#
15903        .unindent(),
15904    );
15905}
15906
15907#[gpui::test]
15908async fn test_stage_and_unstage_added_file_hunk(
15909    executor: BackgroundExecutor,
15910    cx: &mut TestAppContext,
15911) {
15912    init_test(cx, |_| {});
15913
15914    let mut cx = EditorTestContext::new(cx).await;
15915    cx.update_editor(|editor, _, cx| {
15916        editor.set_expand_all_diff_hunks(cx);
15917    });
15918
15919    let working_copy = r#"
15920            ˇfn main() {
15921                println!("hello, world!");
15922            }
15923        "#
15924    .unindent();
15925
15926    cx.set_state(&working_copy);
15927    executor.run_until_parked();
15928
15929    cx.assert_state_with_diff(
15930        r#"
15931            + ˇfn main() {
15932            +     println!("hello, world!");
15933            + }
15934        "#
15935        .unindent(),
15936    );
15937    cx.assert_index_text(None);
15938
15939    cx.update_editor(|editor, window, cx| {
15940        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15941    });
15942    executor.run_until_parked();
15943    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15944    cx.assert_state_with_diff(
15945        r#"
15946            + ˇfn main() {
15947            +     println!("hello, world!");
15948            + }
15949        "#
15950        .unindent(),
15951    );
15952
15953    cx.update_editor(|editor, window, cx| {
15954        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15955    });
15956    executor.run_until_parked();
15957    cx.assert_index_text(None);
15958}
15959
15960async fn setup_indent_guides_editor(
15961    text: &str,
15962    cx: &mut TestAppContext,
15963) -> (BufferId, EditorTestContext) {
15964    init_test(cx, |_| {});
15965
15966    let mut cx = EditorTestContext::new(cx).await;
15967
15968    let buffer_id = cx.update_editor(|editor, window, cx| {
15969        editor.set_text(text, window, cx);
15970        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15971
15972        buffer_ids[0]
15973    });
15974
15975    (buffer_id, cx)
15976}
15977
15978fn assert_indent_guides(
15979    range: Range<u32>,
15980    expected: Vec<IndentGuide>,
15981    active_indices: Option<Vec<usize>>,
15982    cx: &mut EditorTestContext,
15983) {
15984    let indent_guides = cx.update_editor(|editor, window, cx| {
15985        let snapshot = editor.snapshot(window, cx).display_snapshot;
15986        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15987            editor,
15988            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15989            true,
15990            &snapshot,
15991            cx,
15992        );
15993
15994        indent_guides.sort_by(|a, b| {
15995            a.depth.cmp(&b.depth).then(
15996                a.start_row
15997                    .cmp(&b.start_row)
15998                    .then(a.end_row.cmp(&b.end_row)),
15999            )
16000        });
16001        indent_guides
16002    });
16003
16004    if let Some(expected) = active_indices {
16005        let active_indices = cx.update_editor(|editor, window, cx| {
16006            let snapshot = editor.snapshot(window, cx).display_snapshot;
16007            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
16008        });
16009
16010        assert_eq!(
16011            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
16012            expected,
16013            "Active indent guide indices do not match"
16014        );
16015    }
16016
16017    assert_eq!(indent_guides, expected, "Indent guides do not match");
16018}
16019
16020fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
16021    IndentGuide {
16022        buffer_id,
16023        start_row: MultiBufferRow(start_row),
16024        end_row: MultiBufferRow(end_row),
16025        depth,
16026        tab_size: 4,
16027        settings: IndentGuideSettings {
16028            enabled: true,
16029            line_width: 1,
16030            active_line_width: 1,
16031            ..Default::default()
16032        },
16033    }
16034}
16035
16036#[gpui::test]
16037async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
16038    let (buffer_id, mut cx) = setup_indent_guides_editor(
16039        &"
16040    fn main() {
16041        let a = 1;
16042    }"
16043        .unindent(),
16044        cx,
16045    )
16046    .await;
16047
16048    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16049}
16050
16051#[gpui::test]
16052async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
16053    let (buffer_id, mut cx) = setup_indent_guides_editor(
16054        &"
16055    fn main() {
16056        let a = 1;
16057        let b = 2;
16058    }"
16059        .unindent(),
16060        cx,
16061    )
16062    .await;
16063
16064    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
16065}
16066
16067#[gpui::test]
16068async fn test_indent_guide_nested(cx: &mut TestAppContext) {
16069    let (buffer_id, mut cx) = setup_indent_guides_editor(
16070        &"
16071    fn main() {
16072        let a = 1;
16073        if a == 3 {
16074            let b = 2;
16075        } else {
16076            let c = 3;
16077        }
16078    }"
16079        .unindent(),
16080        cx,
16081    )
16082    .await;
16083
16084    assert_indent_guides(
16085        0..8,
16086        vec![
16087            indent_guide(buffer_id, 1, 6, 0),
16088            indent_guide(buffer_id, 3, 3, 1),
16089            indent_guide(buffer_id, 5, 5, 1),
16090        ],
16091        None,
16092        &mut cx,
16093    );
16094}
16095
16096#[gpui::test]
16097async fn test_indent_guide_tab(cx: &mut TestAppContext) {
16098    let (buffer_id, mut cx) = setup_indent_guides_editor(
16099        &"
16100    fn main() {
16101        let a = 1;
16102            let b = 2;
16103        let c = 3;
16104    }"
16105        .unindent(),
16106        cx,
16107    )
16108    .await;
16109
16110    assert_indent_guides(
16111        0..5,
16112        vec![
16113            indent_guide(buffer_id, 1, 3, 0),
16114            indent_guide(buffer_id, 2, 2, 1),
16115        ],
16116        None,
16117        &mut cx,
16118    );
16119}
16120
16121#[gpui::test]
16122async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16123    let (buffer_id, mut cx) = setup_indent_guides_editor(
16124        &"
16125        fn main() {
16126            let a = 1;
16127
16128            let c = 3;
16129        }"
16130        .unindent(),
16131        cx,
16132    )
16133    .await;
16134
16135    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16136}
16137
16138#[gpui::test]
16139async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16140    let (buffer_id, mut cx) = setup_indent_guides_editor(
16141        &"
16142        fn main() {
16143            let a = 1;
16144
16145            let c = 3;
16146
16147            if a == 3 {
16148                let b = 2;
16149            } else {
16150                let c = 3;
16151            }
16152        }"
16153        .unindent(),
16154        cx,
16155    )
16156    .await;
16157
16158    assert_indent_guides(
16159        0..11,
16160        vec![
16161            indent_guide(buffer_id, 1, 9, 0),
16162            indent_guide(buffer_id, 6, 6, 1),
16163            indent_guide(buffer_id, 8, 8, 1),
16164        ],
16165        None,
16166        &mut cx,
16167    );
16168}
16169
16170#[gpui::test]
16171async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16172    let (buffer_id, mut cx) = setup_indent_guides_editor(
16173        &"
16174        fn main() {
16175            let a = 1;
16176
16177            let c = 3;
16178
16179            if a == 3 {
16180                let b = 2;
16181            } else {
16182                let c = 3;
16183            }
16184        }"
16185        .unindent(),
16186        cx,
16187    )
16188    .await;
16189
16190    assert_indent_guides(
16191        1..11,
16192        vec![
16193            indent_guide(buffer_id, 1, 9, 0),
16194            indent_guide(buffer_id, 6, 6, 1),
16195            indent_guide(buffer_id, 8, 8, 1),
16196        ],
16197        None,
16198        &mut cx,
16199    );
16200}
16201
16202#[gpui::test]
16203async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16204    let (buffer_id, mut cx) = setup_indent_guides_editor(
16205        &"
16206        fn main() {
16207            let a = 1;
16208
16209            let c = 3;
16210
16211            if a == 3 {
16212                let b = 2;
16213            } else {
16214                let c = 3;
16215            }
16216        }"
16217        .unindent(),
16218        cx,
16219    )
16220    .await;
16221
16222    assert_indent_guides(
16223        1..10,
16224        vec![
16225            indent_guide(buffer_id, 1, 9, 0),
16226            indent_guide(buffer_id, 6, 6, 1),
16227            indent_guide(buffer_id, 8, 8, 1),
16228        ],
16229        None,
16230        &mut cx,
16231    );
16232}
16233
16234#[gpui::test]
16235async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16236    let (buffer_id, mut cx) = setup_indent_guides_editor(
16237        &"
16238        block1
16239            block2
16240                block3
16241                    block4
16242            block2
16243        block1
16244        block1"
16245            .unindent(),
16246        cx,
16247    )
16248    .await;
16249
16250    assert_indent_guides(
16251        1..10,
16252        vec![
16253            indent_guide(buffer_id, 1, 4, 0),
16254            indent_guide(buffer_id, 2, 3, 1),
16255            indent_guide(buffer_id, 3, 3, 2),
16256        ],
16257        None,
16258        &mut cx,
16259    );
16260}
16261
16262#[gpui::test]
16263async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16264    let (buffer_id, mut cx) = setup_indent_guides_editor(
16265        &"
16266        block1
16267            block2
16268                block3
16269
16270        block1
16271        block1"
16272            .unindent(),
16273        cx,
16274    )
16275    .await;
16276
16277    assert_indent_guides(
16278        0..6,
16279        vec![
16280            indent_guide(buffer_id, 1, 2, 0),
16281            indent_guide(buffer_id, 2, 2, 1),
16282        ],
16283        None,
16284        &mut cx,
16285    );
16286}
16287
16288#[gpui::test]
16289async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16290    let (buffer_id, mut cx) = setup_indent_guides_editor(
16291        &"
16292        block1
16293
16294
16295
16296            block2
16297        "
16298        .unindent(),
16299        cx,
16300    )
16301    .await;
16302
16303    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16304}
16305
16306#[gpui::test]
16307async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16308    let (buffer_id, mut cx) = setup_indent_guides_editor(
16309        &"
16310        def a:
16311        \tb = 3
16312        \tif True:
16313        \t\tc = 4
16314        \t\td = 5
16315        \tprint(b)
16316        "
16317        .unindent(),
16318        cx,
16319    )
16320    .await;
16321
16322    assert_indent_guides(
16323        0..6,
16324        vec![
16325            indent_guide(buffer_id, 1, 6, 0),
16326            indent_guide(buffer_id, 3, 4, 1),
16327        ],
16328        None,
16329        &mut cx,
16330    );
16331}
16332
16333#[gpui::test]
16334async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16335    let (buffer_id, mut cx) = setup_indent_guides_editor(
16336        &"
16337    fn main() {
16338        let a = 1;
16339    }"
16340        .unindent(),
16341        cx,
16342    )
16343    .await;
16344
16345    cx.update_editor(|editor, window, cx| {
16346        editor.change_selections(None, window, cx, |s| {
16347            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16348        });
16349    });
16350
16351    assert_indent_guides(
16352        0..3,
16353        vec![indent_guide(buffer_id, 1, 1, 0)],
16354        Some(vec![0]),
16355        &mut cx,
16356    );
16357}
16358
16359#[gpui::test]
16360async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16361    let (buffer_id, mut cx) = setup_indent_guides_editor(
16362        &"
16363    fn main() {
16364        if 1 == 2 {
16365            let a = 1;
16366        }
16367    }"
16368        .unindent(),
16369        cx,
16370    )
16371    .await;
16372
16373    cx.update_editor(|editor, window, cx| {
16374        editor.change_selections(None, window, cx, |s| {
16375            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16376        });
16377    });
16378
16379    assert_indent_guides(
16380        0..4,
16381        vec![
16382            indent_guide(buffer_id, 1, 3, 0),
16383            indent_guide(buffer_id, 2, 2, 1),
16384        ],
16385        Some(vec![1]),
16386        &mut cx,
16387    );
16388
16389    cx.update_editor(|editor, window, cx| {
16390        editor.change_selections(None, window, cx, |s| {
16391            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16392        });
16393    });
16394
16395    assert_indent_guides(
16396        0..4,
16397        vec![
16398            indent_guide(buffer_id, 1, 3, 0),
16399            indent_guide(buffer_id, 2, 2, 1),
16400        ],
16401        Some(vec![1]),
16402        &mut cx,
16403    );
16404
16405    cx.update_editor(|editor, window, cx| {
16406        editor.change_selections(None, window, cx, |s| {
16407            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16408        });
16409    });
16410
16411    assert_indent_guides(
16412        0..4,
16413        vec![
16414            indent_guide(buffer_id, 1, 3, 0),
16415            indent_guide(buffer_id, 2, 2, 1),
16416        ],
16417        Some(vec![0]),
16418        &mut cx,
16419    );
16420}
16421
16422#[gpui::test]
16423async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16424    let (buffer_id, mut cx) = setup_indent_guides_editor(
16425        &"
16426    fn main() {
16427        let a = 1;
16428
16429        let b = 2;
16430    }"
16431        .unindent(),
16432        cx,
16433    )
16434    .await;
16435
16436    cx.update_editor(|editor, window, cx| {
16437        editor.change_selections(None, window, cx, |s| {
16438            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16439        });
16440    });
16441
16442    assert_indent_guides(
16443        0..5,
16444        vec![indent_guide(buffer_id, 1, 3, 0)],
16445        Some(vec![0]),
16446        &mut cx,
16447    );
16448}
16449
16450#[gpui::test]
16451async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16452    let (buffer_id, mut cx) = setup_indent_guides_editor(
16453        &"
16454    def m:
16455        a = 1
16456        pass"
16457            .unindent(),
16458        cx,
16459    )
16460    .await;
16461
16462    cx.update_editor(|editor, window, cx| {
16463        editor.change_selections(None, window, cx, |s| {
16464            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16465        });
16466    });
16467
16468    assert_indent_guides(
16469        0..3,
16470        vec![indent_guide(buffer_id, 1, 2, 0)],
16471        Some(vec![0]),
16472        &mut cx,
16473    );
16474}
16475
16476#[gpui::test]
16477async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16478    init_test(cx, |_| {});
16479    let mut cx = EditorTestContext::new(cx).await;
16480    let text = indoc! {
16481        "
16482        impl A {
16483            fn b() {
16484                0;
16485                3;
16486                5;
16487                6;
16488                7;
16489            }
16490        }
16491        "
16492    };
16493    let base_text = indoc! {
16494        "
16495        impl A {
16496            fn b() {
16497                0;
16498                1;
16499                2;
16500                3;
16501                4;
16502            }
16503            fn c() {
16504                5;
16505                6;
16506                7;
16507            }
16508        }
16509        "
16510    };
16511
16512    cx.update_editor(|editor, window, cx| {
16513        editor.set_text(text, window, cx);
16514
16515        editor.buffer().update(cx, |multibuffer, cx| {
16516            let buffer = multibuffer.as_singleton().unwrap();
16517            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16518
16519            multibuffer.set_all_diff_hunks_expanded(cx);
16520            multibuffer.add_diff(diff, cx);
16521
16522            buffer.read(cx).remote_id()
16523        })
16524    });
16525    cx.run_until_parked();
16526
16527    cx.assert_state_with_diff(
16528        indoc! { "
16529          impl A {
16530              fn b() {
16531                  0;
16532        -         1;
16533        -         2;
16534                  3;
16535        -         4;
16536        -     }
16537        -     fn c() {
16538                  5;
16539                  6;
16540                  7;
16541              }
16542          }
16543          ˇ"
16544        }
16545        .to_string(),
16546    );
16547
16548    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16549        editor
16550            .snapshot(window, cx)
16551            .buffer_snapshot
16552            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16553            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16554            .collect::<Vec<_>>()
16555    });
16556    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16557    assert_eq!(
16558        actual_guides,
16559        vec![
16560            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16561            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16562            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16563        ]
16564    );
16565}
16566
16567#[gpui::test]
16568async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16569    init_test(cx, |_| {});
16570    let mut cx = EditorTestContext::new(cx).await;
16571
16572    let diff_base = r#"
16573        a
16574        b
16575        c
16576        "#
16577    .unindent();
16578
16579    cx.set_state(
16580        &r#"
16581        ˇA
16582        b
16583        C
16584        "#
16585        .unindent(),
16586    );
16587    cx.set_head_text(&diff_base);
16588    cx.update_editor(|editor, window, cx| {
16589        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16590    });
16591    executor.run_until_parked();
16592
16593    let both_hunks_expanded = r#"
16594        - a
16595        + ˇA
16596          b
16597        - c
16598        + C
16599        "#
16600    .unindent();
16601
16602    cx.assert_state_with_diff(both_hunks_expanded.clone());
16603
16604    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16605        let snapshot = editor.snapshot(window, cx);
16606        let hunks = editor
16607            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16608            .collect::<Vec<_>>();
16609        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16610        let buffer_id = hunks[0].buffer_id;
16611        hunks
16612            .into_iter()
16613            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16614            .collect::<Vec<_>>()
16615    });
16616    assert_eq!(hunk_ranges.len(), 2);
16617
16618    cx.update_editor(|editor, _, cx| {
16619        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16620    });
16621    executor.run_until_parked();
16622
16623    let second_hunk_expanded = r#"
16624          ˇA
16625          b
16626        - c
16627        + C
16628        "#
16629    .unindent();
16630
16631    cx.assert_state_with_diff(second_hunk_expanded);
16632
16633    cx.update_editor(|editor, _, cx| {
16634        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16635    });
16636    executor.run_until_parked();
16637
16638    cx.assert_state_with_diff(both_hunks_expanded.clone());
16639
16640    cx.update_editor(|editor, _, cx| {
16641        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16642    });
16643    executor.run_until_parked();
16644
16645    let first_hunk_expanded = r#"
16646        - a
16647        + ˇA
16648          b
16649          C
16650        "#
16651    .unindent();
16652
16653    cx.assert_state_with_diff(first_hunk_expanded);
16654
16655    cx.update_editor(|editor, _, cx| {
16656        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16657    });
16658    executor.run_until_parked();
16659
16660    cx.assert_state_with_diff(both_hunks_expanded);
16661
16662    cx.set_state(
16663        &r#"
16664        ˇA
16665        b
16666        "#
16667        .unindent(),
16668    );
16669    cx.run_until_parked();
16670
16671    // TODO this cursor position seems bad
16672    cx.assert_state_with_diff(
16673        r#"
16674        - ˇa
16675        + A
16676          b
16677        "#
16678        .unindent(),
16679    );
16680
16681    cx.update_editor(|editor, window, cx| {
16682        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16683    });
16684
16685    cx.assert_state_with_diff(
16686        r#"
16687            - ˇa
16688            + A
16689              b
16690            - c
16691            "#
16692        .unindent(),
16693    );
16694
16695    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16696        let snapshot = editor.snapshot(window, cx);
16697        let hunks = editor
16698            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16699            .collect::<Vec<_>>();
16700        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16701        let buffer_id = hunks[0].buffer_id;
16702        hunks
16703            .into_iter()
16704            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16705            .collect::<Vec<_>>()
16706    });
16707    assert_eq!(hunk_ranges.len(), 2);
16708
16709    cx.update_editor(|editor, _, cx| {
16710        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16711    });
16712    executor.run_until_parked();
16713
16714    cx.assert_state_with_diff(
16715        r#"
16716        - ˇa
16717        + A
16718          b
16719        "#
16720        .unindent(),
16721    );
16722}
16723
16724#[gpui::test]
16725async fn test_toggle_deletion_hunk_at_start_of_file(
16726    executor: BackgroundExecutor,
16727    cx: &mut TestAppContext,
16728) {
16729    init_test(cx, |_| {});
16730    let mut cx = EditorTestContext::new(cx).await;
16731
16732    let diff_base = r#"
16733        a
16734        b
16735        c
16736        "#
16737    .unindent();
16738
16739    cx.set_state(
16740        &r#"
16741        ˇb
16742        c
16743        "#
16744        .unindent(),
16745    );
16746    cx.set_head_text(&diff_base);
16747    cx.update_editor(|editor, window, cx| {
16748        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16749    });
16750    executor.run_until_parked();
16751
16752    let hunk_expanded = r#"
16753        - a
16754          ˇb
16755          c
16756        "#
16757    .unindent();
16758
16759    cx.assert_state_with_diff(hunk_expanded.clone());
16760
16761    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16762        let snapshot = editor.snapshot(window, cx);
16763        let hunks = editor
16764            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16765            .collect::<Vec<_>>();
16766        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16767        let buffer_id = hunks[0].buffer_id;
16768        hunks
16769            .into_iter()
16770            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16771            .collect::<Vec<_>>()
16772    });
16773    assert_eq!(hunk_ranges.len(), 1);
16774
16775    cx.update_editor(|editor, _, cx| {
16776        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16777    });
16778    executor.run_until_parked();
16779
16780    let hunk_collapsed = r#"
16781          ˇb
16782          c
16783        "#
16784    .unindent();
16785
16786    cx.assert_state_with_diff(hunk_collapsed);
16787
16788    cx.update_editor(|editor, _, cx| {
16789        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16790    });
16791    executor.run_until_parked();
16792
16793    cx.assert_state_with_diff(hunk_expanded.clone());
16794}
16795
16796#[gpui::test]
16797async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16798    init_test(cx, |_| {});
16799
16800    let fs = FakeFs::new(cx.executor());
16801    fs.insert_tree(
16802        path!("/test"),
16803        json!({
16804            ".git": {},
16805            "file-1": "ONE\n",
16806            "file-2": "TWO\n",
16807            "file-3": "THREE\n",
16808        }),
16809    )
16810    .await;
16811
16812    fs.set_head_for_repo(
16813        path!("/test/.git").as_ref(),
16814        &[
16815            ("file-1".into(), "one\n".into()),
16816            ("file-2".into(), "two\n".into()),
16817            ("file-3".into(), "three\n".into()),
16818        ],
16819    );
16820
16821    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16822    let mut buffers = vec![];
16823    for i in 1..=3 {
16824        let buffer = project
16825            .update(cx, |project, cx| {
16826                let path = format!(path!("/test/file-{}"), i);
16827                project.open_local_buffer(path, cx)
16828            })
16829            .await
16830            .unwrap();
16831        buffers.push(buffer);
16832    }
16833
16834    let multibuffer = cx.new(|cx| {
16835        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16836        multibuffer.set_all_diff_hunks_expanded(cx);
16837        for buffer in &buffers {
16838            let snapshot = buffer.read(cx).snapshot();
16839            multibuffer.set_excerpts_for_path(
16840                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16841                buffer.clone(),
16842                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16843                DEFAULT_MULTIBUFFER_CONTEXT,
16844                cx,
16845            );
16846        }
16847        multibuffer
16848    });
16849
16850    let editor = cx.add_window(|window, cx| {
16851        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16852    });
16853    cx.run_until_parked();
16854
16855    let snapshot = editor
16856        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16857        .unwrap();
16858    let hunks = snapshot
16859        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16860        .map(|hunk| match hunk {
16861            DisplayDiffHunk::Unfolded {
16862                display_row_range, ..
16863            } => display_row_range,
16864            DisplayDiffHunk::Folded { .. } => unreachable!(),
16865        })
16866        .collect::<Vec<_>>();
16867    assert_eq!(
16868        hunks,
16869        [
16870            DisplayRow(2)..DisplayRow(4),
16871            DisplayRow(7)..DisplayRow(9),
16872            DisplayRow(12)..DisplayRow(14),
16873        ]
16874    );
16875}
16876
16877#[gpui::test]
16878async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16879    init_test(cx, |_| {});
16880
16881    let mut cx = EditorTestContext::new(cx).await;
16882    cx.set_head_text(indoc! { "
16883        one
16884        two
16885        three
16886        four
16887        five
16888        "
16889    });
16890    cx.set_index_text(indoc! { "
16891        one
16892        two
16893        three
16894        four
16895        five
16896        "
16897    });
16898    cx.set_state(indoc! {"
16899        one
16900        TWO
16901        ˇTHREE
16902        FOUR
16903        five
16904    "});
16905    cx.run_until_parked();
16906    cx.update_editor(|editor, window, cx| {
16907        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16908    });
16909    cx.run_until_parked();
16910    cx.assert_index_text(Some(indoc! {"
16911        one
16912        TWO
16913        THREE
16914        FOUR
16915        five
16916    "}));
16917    cx.set_state(indoc! { "
16918        one
16919        TWO
16920        ˇTHREE-HUNDRED
16921        FOUR
16922        five
16923    "});
16924    cx.run_until_parked();
16925    cx.update_editor(|editor, window, cx| {
16926        let snapshot = editor.snapshot(window, cx);
16927        let hunks = editor
16928            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16929            .collect::<Vec<_>>();
16930        assert_eq!(hunks.len(), 1);
16931        assert_eq!(
16932            hunks[0].status(),
16933            DiffHunkStatus {
16934                kind: DiffHunkStatusKind::Modified,
16935                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16936            }
16937        );
16938
16939        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16940    });
16941    cx.run_until_parked();
16942    cx.assert_index_text(Some(indoc! {"
16943        one
16944        TWO
16945        THREE-HUNDRED
16946        FOUR
16947        five
16948    "}));
16949}
16950
16951#[gpui::test]
16952fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16953    init_test(cx, |_| {});
16954
16955    let editor = cx.add_window(|window, cx| {
16956        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16957        build_editor(buffer, window, cx)
16958    });
16959
16960    let render_args = Arc::new(Mutex::new(None));
16961    let snapshot = editor
16962        .update(cx, |editor, window, cx| {
16963            let snapshot = editor.buffer().read(cx).snapshot(cx);
16964            let range =
16965                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16966
16967            struct RenderArgs {
16968                row: MultiBufferRow,
16969                folded: bool,
16970                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16971            }
16972
16973            let crease = Crease::inline(
16974                range,
16975                FoldPlaceholder::test(),
16976                {
16977                    let toggle_callback = render_args.clone();
16978                    move |row, folded, callback, _window, _cx| {
16979                        *toggle_callback.lock() = Some(RenderArgs {
16980                            row,
16981                            folded,
16982                            callback,
16983                        });
16984                        div()
16985                    }
16986                },
16987                |_row, _folded, _window, _cx| div(),
16988            );
16989
16990            editor.insert_creases(Some(crease), cx);
16991            let snapshot = editor.snapshot(window, cx);
16992            let _div = snapshot.render_crease_toggle(
16993                MultiBufferRow(1),
16994                false,
16995                cx.entity().clone(),
16996                window,
16997                cx,
16998            );
16999            snapshot
17000        })
17001        .unwrap();
17002
17003    let render_args = render_args.lock().take().unwrap();
17004    assert_eq!(render_args.row, MultiBufferRow(1));
17005    assert!(!render_args.folded);
17006    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17007
17008    cx.update_window(*editor, |_, window, cx| {
17009        (render_args.callback)(true, window, cx)
17010    })
17011    .unwrap();
17012    let snapshot = editor
17013        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17014        .unwrap();
17015    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
17016
17017    cx.update_window(*editor, |_, window, cx| {
17018        (render_args.callback)(false, window, cx)
17019    })
17020    .unwrap();
17021    let snapshot = editor
17022        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
17023        .unwrap();
17024    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
17025}
17026
17027#[gpui::test]
17028async fn test_input_text(cx: &mut TestAppContext) {
17029    init_test(cx, |_| {});
17030    let mut cx = EditorTestContext::new(cx).await;
17031
17032    cx.set_state(
17033        &r#"ˇone
17034        two
17035
17036        three
17037        fourˇ
17038        five
17039
17040        siˇx"#
17041            .unindent(),
17042    );
17043
17044    cx.dispatch_action(HandleInput(String::new()));
17045    cx.assert_editor_state(
17046        &r#"ˇone
17047        two
17048
17049        three
17050        fourˇ
17051        five
17052
17053        siˇx"#
17054            .unindent(),
17055    );
17056
17057    cx.dispatch_action(HandleInput("AAAA".to_string()));
17058    cx.assert_editor_state(
17059        &r#"AAAAˇone
17060        two
17061
17062        three
17063        fourAAAAˇ
17064        five
17065
17066        siAAAAˇx"#
17067            .unindent(),
17068    );
17069}
17070
17071#[gpui::test]
17072async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
17073    init_test(cx, |_| {});
17074
17075    let mut cx = EditorTestContext::new(cx).await;
17076    cx.set_state(
17077        r#"let foo = 1;
17078let foo = 2;
17079let foo = 3;
17080let fooˇ = 4;
17081let foo = 5;
17082let foo = 6;
17083let foo = 7;
17084let foo = 8;
17085let foo = 9;
17086let foo = 10;
17087let foo = 11;
17088let foo = 12;
17089let foo = 13;
17090let foo = 14;
17091let foo = 15;"#,
17092    );
17093
17094    cx.update_editor(|e, window, cx| {
17095        assert_eq!(
17096            e.next_scroll_position,
17097            NextScrollCursorCenterTopBottom::Center,
17098            "Default next scroll direction is center",
17099        );
17100
17101        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17102        assert_eq!(
17103            e.next_scroll_position,
17104            NextScrollCursorCenterTopBottom::Top,
17105            "After center, next scroll direction should be top",
17106        );
17107
17108        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17109        assert_eq!(
17110            e.next_scroll_position,
17111            NextScrollCursorCenterTopBottom::Bottom,
17112            "After top, next scroll direction should be bottom",
17113        );
17114
17115        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17116        assert_eq!(
17117            e.next_scroll_position,
17118            NextScrollCursorCenterTopBottom::Center,
17119            "After bottom, scrolling should start over",
17120        );
17121
17122        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17123        assert_eq!(
17124            e.next_scroll_position,
17125            NextScrollCursorCenterTopBottom::Top,
17126            "Scrolling continues if retriggered fast enough"
17127        );
17128    });
17129
17130    cx.executor()
17131        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17132    cx.executor().run_until_parked();
17133    cx.update_editor(|e, _, _| {
17134        assert_eq!(
17135            e.next_scroll_position,
17136            NextScrollCursorCenterTopBottom::Center,
17137            "If scrolling is not triggered fast enough, it should reset"
17138        );
17139    });
17140}
17141
17142#[gpui::test]
17143async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17144    init_test(cx, |_| {});
17145    let mut cx = EditorLspTestContext::new_rust(
17146        lsp::ServerCapabilities {
17147            definition_provider: Some(lsp::OneOf::Left(true)),
17148            references_provider: Some(lsp::OneOf::Left(true)),
17149            ..lsp::ServerCapabilities::default()
17150        },
17151        cx,
17152    )
17153    .await;
17154
17155    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17156        let go_to_definition = cx
17157            .lsp
17158            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17159                move |params, _| async move {
17160                    if empty_go_to_definition {
17161                        Ok(None)
17162                    } else {
17163                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17164                            uri: params.text_document_position_params.text_document.uri,
17165                            range: lsp::Range::new(
17166                                lsp::Position::new(4, 3),
17167                                lsp::Position::new(4, 6),
17168                            ),
17169                        })))
17170                    }
17171                },
17172            );
17173        let references = cx
17174            .lsp
17175            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17176                Ok(Some(vec![lsp::Location {
17177                    uri: params.text_document_position.text_document.uri,
17178                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17179                }]))
17180            });
17181        (go_to_definition, references)
17182    };
17183
17184    cx.set_state(
17185        &r#"fn one() {
17186            let mut a = ˇtwo();
17187        }
17188
17189        fn two() {}"#
17190            .unindent(),
17191    );
17192    set_up_lsp_handlers(false, &mut cx);
17193    let navigated = cx
17194        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17195        .await
17196        .expect("Failed to navigate to definition");
17197    assert_eq!(
17198        navigated,
17199        Navigated::Yes,
17200        "Should have navigated to definition from the GetDefinition response"
17201    );
17202    cx.assert_editor_state(
17203        &r#"fn one() {
17204            let mut a = two();
17205        }
17206
17207        fn «twoˇ»() {}"#
17208            .unindent(),
17209    );
17210
17211    let editors = cx.update_workspace(|workspace, _, cx| {
17212        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17213    });
17214    cx.update_editor(|_, _, test_editor_cx| {
17215        assert_eq!(
17216            editors.len(),
17217            1,
17218            "Initially, only one, test, editor should be open in the workspace"
17219        );
17220        assert_eq!(
17221            test_editor_cx.entity(),
17222            editors.last().expect("Asserted len is 1").clone()
17223        );
17224    });
17225
17226    set_up_lsp_handlers(true, &mut cx);
17227    let navigated = cx
17228        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17229        .await
17230        .expect("Failed to navigate to lookup references");
17231    assert_eq!(
17232        navigated,
17233        Navigated::Yes,
17234        "Should have navigated to references as a fallback after empty GoToDefinition response"
17235    );
17236    // We should not change the selections in the existing file,
17237    // if opening another milti buffer with the references
17238    cx.assert_editor_state(
17239        &r#"fn one() {
17240            let mut a = two();
17241        }
17242
17243        fn «twoˇ»() {}"#
17244            .unindent(),
17245    );
17246    let editors = cx.update_workspace(|workspace, _, cx| {
17247        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17248    });
17249    cx.update_editor(|_, _, test_editor_cx| {
17250        assert_eq!(
17251            editors.len(),
17252            2,
17253            "After falling back to references search, we open a new editor with the results"
17254        );
17255        let references_fallback_text = editors
17256            .into_iter()
17257            .find(|new_editor| *new_editor != test_editor_cx.entity())
17258            .expect("Should have one non-test editor now")
17259            .read(test_editor_cx)
17260            .text(test_editor_cx);
17261        assert_eq!(
17262            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17263            "Should use the range from the references response and not the GoToDefinition one"
17264        );
17265    });
17266}
17267
17268#[gpui::test]
17269async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17270    init_test(cx, |_| {});
17271    cx.update(|cx| {
17272        let mut editor_settings = EditorSettings::get_global(cx).clone();
17273        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17274        EditorSettings::override_global(editor_settings, cx);
17275    });
17276    let mut cx = EditorLspTestContext::new_rust(
17277        lsp::ServerCapabilities {
17278            definition_provider: Some(lsp::OneOf::Left(true)),
17279            references_provider: Some(lsp::OneOf::Left(true)),
17280            ..lsp::ServerCapabilities::default()
17281        },
17282        cx,
17283    )
17284    .await;
17285    let original_state = r#"fn one() {
17286        let mut a = ˇtwo();
17287    }
17288
17289    fn two() {}"#
17290        .unindent();
17291    cx.set_state(&original_state);
17292
17293    let mut go_to_definition = cx
17294        .lsp
17295        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17296            move |_, _| async move { Ok(None) },
17297        );
17298    let _references = cx
17299        .lsp
17300        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17301            panic!("Should not call for references with no go to definition fallback")
17302        });
17303
17304    let navigated = cx
17305        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17306        .await
17307        .expect("Failed to navigate to lookup references");
17308    go_to_definition
17309        .next()
17310        .await
17311        .expect("Should have called the go_to_definition handler");
17312
17313    assert_eq!(
17314        navigated,
17315        Navigated::No,
17316        "Should have navigated to references as a fallback after empty GoToDefinition response"
17317    );
17318    cx.assert_editor_state(&original_state);
17319    let editors = cx.update_workspace(|workspace, _, cx| {
17320        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17321    });
17322    cx.update_editor(|_, _, _| {
17323        assert_eq!(
17324            editors.len(),
17325            1,
17326            "After unsuccessful fallback, no other editor should have been opened"
17327        );
17328    });
17329}
17330
17331#[gpui::test]
17332async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17333    init_test(cx, |_| {});
17334
17335    let language = Arc::new(Language::new(
17336        LanguageConfig::default(),
17337        Some(tree_sitter_rust::LANGUAGE.into()),
17338    ));
17339
17340    let text = r#"
17341        #[cfg(test)]
17342        mod tests() {
17343            #[test]
17344            fn runnable_1() {
17345                let a = 1;
17346            }
17347
17348            #[test]
17349            fn runnable_2() {
17350                let a = 1;
17351                let b = 2;
17352            }
17353        }
17354    "#
17355    .unindent();
17356
17357    let fs = FakeFs::new(cx.executor());
17358    fs.insert_file("/file.rs", Default::default()).await;
17359
17360    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17361    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17362    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17363    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17364    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17365
17366    let editor = cx.new_window_entity(|window, cx| {
17367        Editor::new(
17368            EditorMode::full(),
17369            multi_buffer,
17370            Some(project.clone()),
17371            window,
17372            cx,
17373        )
17374    });
17375
17376    editor.update_in(cx, |editor, window, cx| {
17377        let snapshot = editor.buffer().read(cx).snapshot(cx);
17378        editor.tasks.insert(
17379            (buffer.read(cx).remote_id(), 3),
17380            RunnableTasks {
17381                templates: vec![],
17382                offset: snapshot.anchor_before(43),
17383                column: 0,
17384                extra_variables: HashMap::default(),
17385                context_range: BufferOffset(43)..BufferOffset(85),
17386            },
17387        );
17388        editor.tasks.insert(
17389            (buffer.read(cx).remote_id(), 8),
17390            RunnableTasks {
17391                templates: vec![],
17392                offset: snapshot.anchor_before(86),
17393                column: 0,
17394                extra_variables: HashMap::default(),
17395                context_range: BufferOffset(86)..BufferOffset(191),
17396            },
17397        );
17398
17399        // Test finding task when cursor is inside function body
17400        editor.change_selections(None, window, cx, |s| {
17401            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17402        });
17403        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17404        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17405
17406        // Test finding task when cursor is on function name
17407        editor.change_selections(None, window, cx, |s| {
17408            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17409        });
17410        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17411        assert_eq!(row, 8, "Should find task when cursor is on function name");
17412    });
17413}
17414
17415#[gpui::test]
17416async fn test_folding_buffers(cx: &mut TestAppContext) {
17417    init_test(cx, |_| {});
17418
17419    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17420    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17421    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17422
17423    let fs = FakeFs::new(cx.executor());
17424    fs.insert_tree(
17425        path!("/a"),
17426        json!({
17427            "first.rs": sample_text_1,
17428            "second.rs": sample_text_2,
17429            "third.rs": sample_text_3,
17430        }),
17431    )
17432    .await;
17433    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17434    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17435    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17436    let worktree = project.update(cx, |project, cx| {
17437        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17438        assert_eq!(worktrees.len(), 1);
17439        worktrees.pop().unwrap()
17440    });
17441    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17442
17443    let buffer_1 = project
17444        .update(cx, |project, cx| {
17445            project.open_buffer((worktree_id, "first.rs"), cx)
17446        })
17447        .await
17448        .unwrap();
17449    let buffer_2 = project
17450        .update(cx, |project, cx| {
17451            project.open_buffer((worktree_id, "second.rs"), cx)
17452        })
17453        .await
17454        .unwrap();
17455    let buffer_3 = project
17456        .update(cx, |project, cx| {
17457            project.open_buffer((worktree_id, "third.rs"), cx)
17458        })
17459        .await
17460        .unwrap();
17461
17462    let multi_buffer = cx.new(|cx| {
17463        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17464        multi_buffer.push_excerpts(
17465            buffer_1.clone(),
17466            [
17467                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17468                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17469                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17470            ],
17471            cx,
17472        );
17473        multi_buffer.push_excerpts(
17474            buffer_2.clone(),
17475            [
17476                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17477                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17478                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17479            ],
17480            cx,
17481        );
17482        multi_buffer.push_excerpts(
17483            buffer_3.clone(),
17484            [
17485                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17486                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17487                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17488            ],
17489            cx,
17490        );
17491        multi_buffer
17492    });
17493    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17494        Editor::new(
17495            EditorMode::full(),
17496            multi_buffer.clone(),
17497            Some(project.clone()),
17498            window,
17499            cx,
17500        )
17501    });
17502
17503    assert_eq!(
17504        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17505        "\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",
17506    );
17507
17508    multi_buffer_editor.update(cx, |editor, cx| {
17509        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17510    });
17511    assert_eq!(
17512        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17513        "\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",
17514        "After folding the first buffer, its text should not be displayed"
17515    );
17516
17517    multi_buffer_editor.update(cx, |editor, cx| {
17518        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17519    });
17520    assert_eq!(
17521        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17522        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17523        "After folding the second buffer, its text should not be displayed"
17524    );
17525
17526    multi_buffer_editor.update(cx, |editor, cx| {
17527        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17528    });
17529    assert_eq!(
17530        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17531        "\n\n\n\n\n",
17532        "After folding the third buffer, its text should not be displayed"
17533    );
17534
17535    // Emulate selection inside the fold logic, that should work
17536    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17537        editor
17538            .snapshot(window, cx)
17539            .next_line_boundary(Point::new(0, 4));
17540    });
17541
17542    multi_buffer_editor.update(cx, |editor, cx| {
17543        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17544    });
17545    assert_eq!(
17546        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17547        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17548        "After unfolding the second buffer, its text should be displayed"
17549    );
17550
17551    // Typing inside of buffer 1 causes that buffer to be unfolded.
17552    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17553        assert_eq!(
17554            multi_buffer
17555                .read(cx)
17556                .snapshot(cx)
17557                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17558                .collect::<String>(),
17559            "bbbb"
17560        );
17561        editor.change_selections(None, window, cx, |selections| {
17562            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17563        });
17564        editor.handle_input("B", window, cx);
17565    });
17566
17567    assert_eq!(
17568        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17569        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17570        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17571    );
17572
17573    multi_buffer_editor.update(cx, |editor, cx| {
17574        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17575    });
17576    assert_eq!(
17577        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17578        "\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",
17579        "After unfolding the all buffers, all original text should be displayed"
17580    );
17581}
17582
17583#[gpui::test]
17584async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17585    init_test(cx, |_| {});
17586
17587    let sample_text_1 = "1111\n2222\n3333".to_string();
17588    let sample_text_2 = "4444\n5555\n6666".to_string();
17589    let sample_text_3 = "7777\n8888\n9999".to_string();
17590
17591    let fs = FakeFs::new(cx.executor());
17592    fs.insert_tree(
17593        path!("/a"),
17594        json!({
17595            "first.rs": sample_text_1,
17596            "second.rs": sample_text_2,
17597            "third.rs": sample_text_3,
17598        }),
17599    )
17600    .await;
17601    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17602    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17603    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17604    let worktree = project.update(cx, |project, cx| {
17605        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17606        assert_eq!(worktrees.len(), 1);
17607        worktrees.pop().unwrap()
17608    });
17609    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17610
17611    let buffer_1 = project
17612        .update(cx, |project, cx| {
17613            project.open_buffer((worktree_id, "first.rs"), cx)
17614        })
17615        .await
17616        .unwrap();
17617    let buffer_2 = project
17618        .update(cx, |project, cx| {
17619            project.open_buffer((worktree_id, "second.rs"), cx)
17620        })
17621        .await
17622        .unwrap();
17623    let buffer_3 = project
17624        .update(cx, |project, cx| {
17625            project.open_buffer((worktree_id, "third.rs"), cx)
17626        })
17627        .await
17628        .unwrap();
17629
17630    let multi_buffer = cx.new(|cx| {
17631        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17632        multi_buffer.push_excerpts(
17633            buffer_1.clone(),
17634            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17635            cx,
17636        );
17637        multi_buffer.push_excerpts(
17638            buffer_2.clone(),
17639            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17640            cx,
17641        );
17642        multi_buffer.push_excerpts(
17643            buffer_3.clone(),
17644            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17645            cx,
17646        );
17647        multi_buffer
17648    });
17649
17650    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17651        Editor::new(
17652            EditorMode::full(),
17653            multi_buffer,
17654            Some(project.clone()),
17655            window,
17656            cx,
17657        )
17658    });
17659
17660    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17661    assert_eq!(
17662        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17663        full_text,
17664    );
17665
17666    multi_buffer_editor.update(cx, |editor, cx| {
17667        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17668    });
17669    assert_eq!(
17670        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17671        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17672        "After folding the first buffer, its text should not be displayed"
17673    );
17674
17675    multi_buffer_editor.update(cx, |editor, cx| {
17676        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17677    });
17678
17679    assert_eq!(
17680        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17681        "\n\n\n\n\n\n7777\n8888\n9999",
17682        "After folding the second buffer, its text should not be displayed"
17683    );
17684
17685    multi_buffer_editor.update(cx, |editor, cx| {
17686        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17687    });
17688    assert_eq!(
17689        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17690        "\n\n\n\n\n",
17691        "After folding the third buffer, its text should not be displayed"
17692    );
17693
17694    multi_buffer_editor.update(cx, |editor, cx| {
17695        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17696    });
17697    assert_eq!(
17698        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17699        "\n\n\n\n4444\n5555\n6666\n\n",
17700        "After unfolding the second buffer, its text should be displayed"
17701    );
17702
17703    multi_buffer_editor.update(cx, |editor, cx| {
17704        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17705    });
17706    assert_eq!(
17707        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17708        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17709        "After unfolding the first buffer, its text should be displayed"
17710    );
17711
17712    multi_buffer_editor.update(cx, |editor, cx| {
17713        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17714    });
17715    assert_eq!(
17716        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17717        full_text,
17718        "After unfolding all buffers, all original text should be displayed"
17719    );
17720}
17721
17722#[gpui::test]
17723async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17724    init_test(cx, |_| {});
17725
17726    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17727
17728    let fs = FakeFs::new(cx.executor());
17729    fs.insert_tree(
17730        path!("/a"),
17731        json!({
17732            "main.rs": sample_text,
17733        }),
17734    )
17735    .await;
17736    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17737    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17738    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17739    let worktree = project.update(cx, |project, cx| {
17740        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17741        assert_eq!(worktrees.len(), 1);
17742        worktrees.pop().unwrap()
17743    });
17744    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17745
17746    let buffer_1 = project
17747        .update(cx, |project, cx| {
17748            project.open_buffer((worktree_id, "main.rs"), cx)
17749        })
17750        .await
17751        .unwrap();
17752
17753    let multi_buffer = cx.new(|cx| {
17754        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17755        multi_buffer.push_excerpts(
17756            buffer_1.clone(),
17757            [ExcerptRange::new(
17758                Point::new(0, 0)
17759                    ..Point::new(
17760                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17761                        0,
17762                    ),
17763            )],
17764            cx,
17765        );
17766        multi_buffer
17767    });
17768    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17769        Editor::new(
17770            EditorMode::full(),
17771            multi_buffer,
17772            Some(project.clone()),
17773            window,
17774            cx,
17775        )
17776    });
17777
17778    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17779    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17780        enum TestHighlight {}
17781        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17782        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17783        editor.highlight_text::<TestHighlight>(
17784            vec![highlight_range.clone()],
17785            HighlightStyle::color(Hsla::green()),
17786            cx,
17787        );
17788        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17789    });
17790
17791    let full_text = format!("\n\n{sample_text}");
17792    assert_eq!(
17793        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17794        full_text,
17795    );
17796}
17797
17798#[gpui::test]
17799async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17800    init_test(cx, |_| {});
17801    cx.update(|cx| {
17802        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17803            "keymaps/default-linux.json",
17804            cx,
17805        )
17806        .unwrap();
17807        cx.bind_keys(default_key_bindings);
17808    });
17809
17810    let (editor, cx) = cx.add_window_view(|window, cx| {
17811        let multi_buffer = MultiBuffer::build_multi(
17812            [
17813                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17814                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17815                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17816                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17817            ],
17818            cx,
17819        );
17820        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17821
17822        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17823        // fold all but the second buffer, so that we test navigating between two
17824        // adjacent folded buffers, as well as folded buffers at the start and
17825        // end the multibuffer
17826        editor.fold_buffer(buffer_ids[0], cx);
17827        editor.fold_buffer(buffer_ids[2], cx);
17828        editor.fold_buffer(buffer_ids[3], cx);
17829
17830        editor
17831    });
17832    cx.simulate_resize(size(px(1000.), px(1000.)));
17833
17834    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17835    cx.assert_excerpts_with_selections(indoc! {"
17836        [EXCERPT]
17837        ˇ[FOLDED]
17838        [EXCERPT]
17839        a1
17840        b1
17841        [EXCERPT]
17842        [FOLDED]
17843        [EXCERPT]
17844        [FOLDED]
17845        "
17846    });
17847    cx.simulate_keystroke("down");
17848    cx.assert_excerpts_with_selections(indoc! {"
17849        [EXCERPT]
17850        [FOLDED]
17851        [EXCERPT]
17852        ˇa1
17853        b1
17854        [EXCERPT]
17855        [FOLDED]
17856        [EXCERPT]
17857        [FOLDED]
17858        "
17859    });
17860    cx.simulate_keystroke("down");
17861    cx.assert_excerpts_with_selections(indoc! {"
17862        [EXCERPT]
17863        [FOLDED]
17864        [EXCERPT]
17865        a1
17866        ˇb1
17867        [EXCERPT]
17868        [FOLDED]
17869        [EXCERPT]
17870        [FOLDED]
17871        "
17872    });
17873    cx.simulate_keystroke("down");
17874    cx.assert_excerpts_with_selections(indoc! {"
17875        [EXCERPT]
17876        [FOLDED]
17877        [EXCERPT]
17878        a1
17879        b1
17880        ˇ[EXCERPT]
17881        [FOLDED]
17882        [EXCERPT]
17883        [FOLDED]
17884        "
17885    });
17886    cx.simulate_keystroke("down");
17887    cx.assert_excerpts_with_selections(indoc! {"
17888        [EXCERPT]
17889        [FOLDED]
17890        [EXCERPT]
17891        a1
17892        b1
17893        [EXCERPT]
17894        ˇ[FOLDED]
17895        [EXCERPT]
17896        [FOLDED]
17897        "
17898    });
17899    for _ in 0..5 {
17900        cx.simulate_keystroke("down");
17901        cx.assert_excerpts_with_selections(indoc! {"
17902            [EXCERPT]
17903            [FOLDED]
17904            [EXCERPT]
17905            a1
17906            b1
17907            [EXCERPT]
17908            [FOLDED]
17909            [EXCERPT]
17910            ˇ[FOLDED]
17911            "
17912        });
17913    }
17914
17915    cx.simulate_keystroke("up");
17916    cx.assert_excerpts_with_selections(indoc! {"
17917        [EXCERPT]
17918        [FOLDED]
17919        [EXCERPT]
17920        a1
17921        b1
17922        [EXCERPT]
17923        ˇ[FOLDED]
17924        [EXCERPT]
17925        [FOLDED]
17926        "
17927    });
17928    cx.simulate_keystroke("up");
17929    cx.assert_excerpts_with_selections(indoc! {"
17930        [EXCERPT]
17931        [FOLDED]
17932        [EXCERPT]
17933        a1
17934        b1
17935        ˇ[EXCERPT]
17936        [FOLDED]
17937        [EXCERPT]
17938        [FOLDED]
17939        "
17940    });
17941    cx.simulate_keystroke("up");
17942    cx.assert_excerpts_with_selections(indoc! {"
17943        [EXCERPT]
17944        [FOLDED]
17945        [EXCERPT]
17946        a1
17947        ˇb1
17948        [EXCERPT]
17949        [FOLDED]
17950        [EXCERPT]
17951        [FOLDED]
17952        "
17953    });
17954    cx.simulate_keystroke("up");
17955    cx.assert_excerpts_with_selections(indoc! {"
17956        [EXCERPT]
17957        [FOLDED]
17958        [EXCERPT]
17959        ˇa1
17960        b1
17961        [EXCERPT]
17962        [FOLDED]
17963        [EXCERPT]
17964        [FOLDED]
17965        "
17966    });
17967    for _ in 0..5 {
17968        cx.simulate_keystroke("up");
17969        cx.assert_excerpts_with_selections(indoc! {"
17970            [EXCERPT]
17971            ˇ[FOLDED]
17972            [EXCERPT]
17973            a1
17974            b1
17975            [EXCERPT]
17976            [FOLDED]
17977            [EXCERPT]
17978            [FOLDED]
17979            "
17980        });
17981    }
17982}
17983
17984#[gpui::test]
17985async fn test_inline_completion_text(cx: &mut TestAppContext) {
17986    init_test(cx, |_| {});
17987
17988    // Simple insertion
17989    assert_highlighted_edits(
17990        "Hello, world!",
17991        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17992        true,
17993        cx,
17994        |highlighted_edits, cx| {
17995            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17996            assert_eq!(highlighted_edits.highlights.len(), 1);
17997            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17998            assert_eq!(
17999                highlighted_edits.highlights[0].1.background_color,
18000                Some(cx.theme().status().created_background)
18001            );
18002        },
18003    )
18004    .await;
18005
18006    // Replacement
18007    assert_highlighted_edits(
18008        "This is a test.",
18009        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
18010        false,
18011        cx,
18012        |highlighted_edits, cx| {
18013            assert_eq!(highlighted_edits.text, "That is a test.");
18014            assert_eq!(highlighted_edits.highlights.len(), 1);
18015            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
18016            assert_eq!(
18017                highlighted_edits.highlights[0].1.background_color,
18018                Some(cx.theme().status().created_background)
18019            );
18020        },
18021    )
18022    .await;
18023
18024    // Multiple edits
18025    assert_highlighted_edits(
18026        "Hello, world!",
18027        vec![
18028            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
18029            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
18030        ],
18031        false,
18032        cx,
18033        |highlighted_edits, cx| {
18034            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
18035            assert_eq!(highlighted_edits.highlights.len(), 2);
18036            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
18037            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
18038            assert_eq!(
18039                highlighted_edits.highlights[0].1.background_color,
18040                Some(cx.theme().status().created_background)
18041            );
18042            assert_eq!(
18043                highlighted_edits.highlights[1].1.background_color,
18044                Some(cx.theme().status().created_background)
18045            );
18046        },
18047    )
18048    .await;
18049
18050    // Multiple lines with edits
18051    assert_highlighted_edits(
18052        "First line\nSecond line\nThird line\nFourth line",
18053        vec![
18054            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
18055            (
18056                Point::new(2, 0)..Point::new(2, 10),
18057                "New third line".to_string(),
18058            ),
18059            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
18060        ],
18061        false,
18062        cx,
18063        |highlighted_edits, cx| {
18064            assert_eq!(
18065                highlighted_edits.text,
18066                "Second modified\nNew third line\nFourth updated line"
18067            );
18068            assert_eq!(highlighted_edits.highlights.len(), 3);
18069            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
18070            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
18071            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
18072            for highlight in &highlighted_edits.highlights {
18073                assert_eq!(
18074                    highlight.1.background_color,
18075                    Some(cx.theme().status().created_background)
18076                );
18077            }
18078        },
18079    )
18080    .await;
18081}
18082
18083#[gpui::test]
18084async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
18085    init_test(cx, |_| {});
18086
18087    // Deletion
18088    assert_highlighted_edits(
18089        "Hello, world!",
18090        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
18091        true,
18092        cx,
18093        |highlighted_edits, cx| {
18094            assert_eq!(highlighted_edits.text, "Hello, world!");
18095            assert_eq!(highlighted_edits.highlights.len(), 1);
18096            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
18097            assert_eq!(
18098                highlighted_edits.highlights[0].1.background_color,
18099                Some(cx.theme().status().deleted_background)
18100            );
18101        },
18102    )
18103    .await;
18104
18105    // Insertion
18106    assert_highlighted_edits(
18107        "Hello, world!",
18108        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18109        true,
18110        cx,
18111        |highlighted_edits, cx| {
18112            assert_eq!(highlighted_edits.highlights.len(), 1);
18113            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18114            assert_eq!(
18115                highlighted_edits.highlights[0].1.background_color,
18116                Some(cx.theme().status().created_background)
18117            );
18118        },
18119    )
18120    .await;
18121}
18122
18123async fn assert_highlighted_edits(
18124    text: &str,
18125    edits: Vec<(Range<Point>, String)>,
18126    include_deletions: bool,
18127    cx: &mut TestAppContext,
18128    assertion_fn: impl Fn(HighlightedText, &App),
18129) {
18130    let window = cx.add_window(|window, cx| {
18131        let buffer = MultiBuffer::build_simple(text, cx);
18132        Editor::new(EditorMode::full(), buffer, None, window, cx)
18133    });
18134    let cx = &mut VisualTestContext::from_window(*window, cx);
18135
18136    let (buffer, snapshot) = window
18137        .update(cx, |editor, _window, cx| {
18138            (
18139                editor.buffer().clone(),
18140                editor.buffer().read(cx).snapshot(cx),
18141            )
18142        })
18143        .unwrap();
18144
18145    let edits = edits
18146        .into_iter()
18147        .map(|(range, edit)| {
18148            (
18149                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18150                edit,
18151            )
18152        })
18153        .collect::<Vec<_>>();
18154
18155    let text_anchor_edits = edits
18156        .clone()
18157        .into_iter()
18158        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18159        .collect::<Vec<_>>();
18160
18161    let edit_preview = window
18162        .update(cx, |_, _window, cx| {
18163            buffer
18164                .read(cx)
18165                .as_singleton()
18166                .unwrap()
18167                .read(cx)
18168                .preview_edits(text_anchor_edits.into(), cx)
18169        })
18170        .unwrap()
18171        .await;
18172
18173    cx.update(|_window, cx| {
18174        let highlighted_edits = inline_completion_edit_text(
18175            &snapshot.as_singleton().unwrap().2,
18176            &edits,
18177            &edit_preview,
18178            include_deletions,
18179            cx,
18180        );
18181        assertion_fn(highlighted_edits, cx)
18182    });
18183}
18184
18185#[track_caller]
18186fn assert_breakpoint(
18187    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18188    path: &Arc<Path>,
18189    expected: Vec<(u32, Breakpoint)>,
18190) {
18191    if expected.len() == 0usize {
18192        assert!(!breakpoints.contains_key(path), "{}", path.display());
18193    } else {
18194        let mut breakpoint = breakpoints
18195            .get(path)
18196            .unwrap()
18197            .into_iter()
18198            .map(|breakpoint| {
18199                (
18200                    breakpoint.row,
18201                    Breakpoint {
18202                        message: breakpoint.message.clone(),
18203                        state: breakpoint.state,
18204                        condition: breakpoint.condition.clone(),
18205                        hit_condition: breakpoint.hit_condition.clone(),
18206                    },
18207                )
18208            })
18209            .collect::<Vec<_>>();
18210
18211        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18212
18213        assert_eq!(expected, breakpoint);
18214    }
18215}
18216
18217fn add_log_breakpoint_at_cursor(
18218    editor: &mut Editor,
18219    log_message: &str,
18220    window: &mut Window,
18221    cx: &mut Context<Editor>,
18222) {
18223    let (anchor, bp) = editor
18224        .breakpoints_at_cursors(window, cx)
18225        .first()
18226        .and_then(|(anchor, bp)| {
18227            if let Some(bp) = bp {
18228                Some((*anchor, bp.clone()))
18229            } else {
18230                None
18231            }
18232        })
18233        .unwrap_or_else(|| {
18234            let cursor_position: Point = editor.selections.newest(cx).head();
18235
18236            let breakpoint_position = editor
18237                .snapshot(window, cx)
18238                .display_snapshot
18239                .buffer_snapshot
18240                .anchor_before(Point::new(cursor_position.row, 0));
18241
18242            (breakpoint_position, Breakpoint::new_log(&log_message))
18243        });
18244
18245    editor.edit_breakpoint_at_anchor(
18246        anchor,
18247        bp,
18248        BreakpointEditAction::EditLogMessage(log_message.into()),
18249        cx,
18250    );
18251}
18252
18253#[gpui::test]
18254async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18255    init_test(cx, |_| {});
18256
18257    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18258    let fs = FakeFs::new(cx.executor());
18259    fs.insert_tree(
18260        path!("/a"),
18261        json!({
18262            "main.rs": sample_text,
18263        }),
18264    )
18265    .await;
18266    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18267    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18268    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18269
18270    let fs = FakeFs::new(cx.executor());
18271    fs.insert_tree(
18272        path!("/a"),
18273        json!({
18274            "main.rs": sample_text,
18275        }),
18276    )
18277    .await;
18278    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18279    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18280    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18281    let worktree_id = workspace
18282        .update(cx, |workspace, _window, cx| {
18283            workspace.project().update(cx, |project, cx| {
18284                project.worktrees(cx).next().unwrap().read(cx).id()
18285            })
18286        })
18287        .unwrap();
18288
18289    let buffer = project
18290        .update(cx, |project, cx| {
18291            project.open_buffer((worktree_id, "main.rs"), cx)
18292        })
18293        .await
18294        .unwrap();
18295
18296    let (editor, cx) = cx.add_window_view(|window, cx| {
18297        Editor::new(
18298            EditorMode::full(),
18299            MultiBuffer::build_from_buffer(buffer, cx),
18300            Some(project.clone()),
18301            window,
18302            cx,
18303        )
18304    });
18305
18306    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18307    let abs_path = project.read_with(cx, |project, cx| {
18308        project
18309            .absolute_path(&project_path, cx)
18310            .map(|path_buf| Arc::from(path_buf.to_owned()))
18311            .unwrap()
18312    });
18313
18314    // assert we can add breakpoint on the first line
18315    editor.update_in(cx, |editor, window, cx| {
18316        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18317        editor.move_to_end(&MoveToEnd, window, cx);
18318        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18319    });
18320
18321    let breakpoints = editor.update(cx, |editor, cx| {
18322        editor
18323            .breakpoint_store()
18324            .as_ref()
18325            .unwrap()
18326            .read(cx)
18327            .all_breakpoints(cx)
18328            .clone()
18329    });
18330
18331    assert_eq!(1, breakpoints.len());
18332    assert_breakpoint(
18333        &breakpoints,
18334        &abs_path,
18335        vec![
18336            (0, Breakpoint::new_standard()),
18337            (3, Breakpoint::new_standard()),
18338        ],
18339    );
18340
18341    editor.update_in(cx, |editor, window, cx| {
18342        editor.move_to_beginning(&MoveToBeginning, window, cx);
18343        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18344    });
18345
18346    let breakpoints = editor.update(cx, |editor, cx| {
18347        editor
18348            .breakpoint_store()
18349            .as_ref()
18350            .unwrap()
18351            .read(cx)
18352            .all_breakpoints(cx)
18353            .clone()
18354    });
18355
18356    assert_eq!(1, breakpoints.len());
18357    assert_breakpoint(
18358        &breakpoints,
18359        &abs_path,
18360        vec![(3, Breakpoint::new_standard())],
18361    );
18362
18363    editor.update_in(cx, |editor, window, cx| {
18364        editor.move_to_end(&MoveToEnd, window, cx);
18365        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18366    });
18367
18368    let breakpoints = editor.update(cx, |editor, cx| {
18369        editor
18370            .breakpoint_store()
18371            .as_ref()
18372            .unwrap()
18373            .read(cx)
18374            .all_breakpoints(cx)
18375            .clone()
18376    });
18377
18378    assert_eq!(0, breakpoints.len());
18379    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18380}
18381
18382#[gpui::test]
18383async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18384    init_test(cx, |_| {});
18385
18386    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18387
18388    let fs = FakeFs::new(cx.executor());
18389    fs.insert_tree(
18390        path!("/a"),
18391        json!({
18392            "main.rs": sample_text,
18393        }),
18394    )
18395    .await;
18396    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18397    let (workspace, cx) =
18398        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18399
18400    let worktree_id = workspace.update(cx, |workspace, cx| {
18401        workspace.project().update(cx, |project, cx| {
18402            project.worktrees(cx).next().unwrap().read(cx).id()
18403        })
18404    });
18405
18406    let buffer = project
18407        .update(cx, |project, cx| {
18408            project.open_buffer((worktree_id, "main.rs"), cx)
18409        })
18410        .await
18411        .unwrap();
18412
18413    let (editor, cx) = cx.add_window_view(|window, cx| {
18414        Editor::new(
18415            EditorMode::full(),
18416            MultiBuffer::build_from_buffer(buffer, cx),
18417            Some(project.clone()),
18418            window,
18419            cx,
18420        )
18421    });
18422
18423    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18424    let abs_path = project.read_with(cx, |project, cx| {
18425        project
18426            .absolute_path(&project_path, cx)
18427            .map(|path_buf| Arc::from(path_buf.to_owned()))
18428            .unwrap()
18429    });
18430
18431    editor.update_in(cx, |editor, window, cx| {
18432        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18433    });
18434
18435    let breakpoints = editor.update(cx, |editor, cx| {
18436        editor
18437            .breakpoint_store()
18438            .as_ref()
18439            .unwrap()
18440            .read(cx)
18441            .all_breakpoints(cx)
18442            .clone()
18443    });
18444
18445    assert_breakpoint(
18446        &breakpoints,
18447        &abs_path,
18448        vec![(0, Breakpoint::new_log("hello world"))],
18449    );
18450
18451    // Removing a log message from a log breakpoint should remove it
18452    editor.update_in(cx, |editor, window, cx| {
18453        add_log_breakpoint_at_cursor(editor, "", window, cx);
18454    });
18455
18456    let breakpoints = editor.update(cx, |editor, cx| {
18457        editor
18458            .breakpoint_store()
18459            .as_ref()
18460            .unwrap()
18461            .read(cx)
18462            .all_breakpoints(cx)
18463            .clone()
18464    });
18465
18466    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18467
18468    editor.update_in(cx, |editor, window, cx| {
18469        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18470        editor.move_to_end(&MoveToEnd, window, cx);
18471        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18472        // Not adding a log message to a standard breakpoint shouldn't remove it
18473        add_log_breakpoint_at_cursor(editor, "", window, cx);
18474    });
18475
18476    let breakpoints = editor.update(cx, |editor, cx| {
18477        editor
18478            .breakpoint_store()
18479            .as_ref()
18480            .unwrap()
18481            .read(cx)
18482            .all_breakpoints(cx)
18483            .clone()
18484    });
18485
18486    assert_breakpoint(
18487        &breakpoints,
18488        &abs_path,
18489        vec![
18490            (0, Breakpoint::new_standard()),
18491            (3, Breakpoint::new_standard()),
18492        ],
18493    );
18494
18495    editor.update_in(cx, |editor, window, cx| {
18496        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18497    });
18498
18499    let breakpoints = editor.update(cx, |editor, cx| {
18500        editor
18501            .breakpoint_store()
18502            .as_ref()
18503            .unwrap()
18504            .read(cx)
18505            .all_breakpoints(cx)
18506            .clone()
18507    });
18508
18509    assert_breakpoint(
18510        &breakpoints,
18511        &abs_path,
18512        vec![
18513            (0, Breakpoint::new_standard()),
18514            (3, Breakpoint::new_log("hello world")),
18515        ],
18516    );
18517
18518    editor.update_in(cx, |editor, window, cx| {
18519        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18520    });
18521
18522    let breakpoints = editor.update(cx, |editor, cx| {
18523        editor
18524            .breakpoint_store()
18525            .as_ref()
18526            .unwrap()
18527            .read(cx)
18528            .all_breakpoints(cx)
18529            .clone()
18530    });
18531
18532    assert_breakpoint(
18533        &breakpoints,
18534        &abs_path,
18535        vec![
18536            (0, Breakpoint::new_standard()),
18537            (3, Breakpoint::new_log("hello Earth!!")),
18538        ],
18539    );
18540}
18541
18542/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18543/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18544/// or when breakpoints were placed out of order. This tests for a regression too
18545#[gpui::test]
18546async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18547    init_test(cx, |_| {});
18548
18549    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18550    let fs = FakeFs::new(cx.executor());
18551    fs.insert_tree(
18552        path!("/a"),
18553        json!({
18554            "main.rs": sample_text,
18555        }),
18556    )
18557    .await;
18558    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18559    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18560    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18561
18562    let fs = FakeFs::new(cx.executor());
18563    fs.insert_tree(
18564        path!("/a"),
18565        json!({
18566            "main.rs": sample_text,
18567        }),
18568    )
18569    .await;
18570    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18571    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18572    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18573    let worktree_id = workspace
18574        .update(cx, |workspace, _window, cx| {
18575            workspace.project().update(cx, |project, cx| {
18576                project.worktrees(cx).next().unwrap().read(cx).id()
18577            })
18578        })
18579        .unwrap();
18580
18581    let buffer = project
18582        .update(cx, |project, cx| {
18583            project.open_buffer((worktree_id, "main.rs"), cx)
18584        })
18585        .await
18586        .unwrap();
18587
18588    let (editor, cx) = cx.add_window_view(|window, cx| {
18589        Editor::new(
18590            EditorMode::full(),
18591            MultiBuffer::build_from_buffer(buffer, cx),
18592            Some(project.clone()),
18593            window,
18594            cx,
18595        )
18596    });
18597
18598    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18599    let abs_path = project.read_with(cx, |project, cx| {
18600        project
18601            .absolute_path(&project_path, cx)
18602            .map(|path_buf| Arc::from(path_buf.to_owned()))
18603            .unwrap()
18604    });
18605
18606    // assert we can add breakpoint on the first line
18607    editor.update_in(cx, |editor, window, cx| {
18608        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18609        editor.move_to_end(&MoveToEnd, window, cx);
18610        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18611        editor.move_up(&MoveUp, window, cx);
18612        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18613    });
18614
18615    let breakpoints = editor.update(cx, |editor, cx| {
18616        editor
18617            .breakpoint_store()
18618            .as_ref()
18619            .unwrap()
18620            .read(cx)
18621            .all_breakpoints(cx)
18622            .clone()
18623    });
18624
18625    assert_eq!(1, breakpoints.len());
18626    assert_breakpoint(
18627        &breakpoints,
18628        &abs_path,
18629        vec![
18630            (0, Breakpoint::new_standard()),
18631            (2, Breakpoint::new_standard()),
18632            (3, Breakpoint::new_standard()),
18633        ],
18634    );
18635
18636    editor.update_in(cx, |editor, window, cx| {
18637        editor.move_to_beginning(&MoveToBeginning, window, cx);
18638        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18639        editor.move_to_end(&MoveToEnd, window, cx);
18640        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18641        // Disabling a breakpoint that doesn't exist should do nothing
18642        editor.move_up(&MoveUp, window, cx);
18643        editor.move_up(&MoveUp, window, cx);
18644        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18645    });
18646
18647    let breakpoints = editor.update(cx, |editor, cx| {
18648        editor
18649            .breakpoint_store()
18650            .as_ref()
18651            .unwrap()
18652            .read(cx)
18653            .all_breakpoints(cx)
18654            .clone()
18655    });
18656
18657    let disable_breakpoint = {
18658        let mut bp = Breakpoint::new_standard();
18659        bp.state = BreakpointState::Disabled;
18660        bp
18661    };
18662
18663    assert_eq!(1, breakpoints.len());
18664    assert_breakpoint(
18665        &breakpoints,
18666        &abs_path,
18667        vec![
18668            (0, disable_breakpoint.clone()),
18669            (2, Breakpoint::new_standard()),
18670            (3, disable_breakpoint.clone()),
18671        ],
18672    );
18673
18674    editor.update_in(cx, |editor, window, cx| {
18675        editor.move_to_beginning(&MoveToBeginning, window, cx);
18676        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18677        editor.move_to_end(&MoveToEnd, window, cx);
18678        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18679        editor.move_up(&MoveUp, window, cx);
18680        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18681    });
18682
18683    let breakpoints = editor.update(cx, |editor, cx| {
18684        editor
18685            .breakpoint_store()
18686            .as_ref()
18687            .unwrap()
18688            .read(cx)
18689            .all_breakpoints(cx)
18690            .clone()
18691    });
18692
18693    assert_eq!(1, breakpoints.len());
18694    assert_breakpoint(
18695        &breakpoints,
18696        &abs_path,
18697        vec![
18698            (0, Breakpoint::new_standard()),
18699            (2, disable_breakpoint),
18700            (3, Breakpoint::new_standard()),
18701        ],
18702    );
18703}
18704
18705#[gpui::test]
18706async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18707    init_test(cx, |_| {});
18708    let capabilities = lsp::ServerCapabilities {
18709        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18710            prepare_provider: Some(true),
18711            work_done_progress_options: Default::default(),
18712        })),
18713        ..Default::default()
18714    };
18715    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18716
18717    cx.set_state(indoc! {"
18718        struct Fˇoo {}
18719    "});
18720
18721    cx.update_editor(|editor, _, cx| {
18722        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18723        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18724        editor.highlight_background::<DocumentHighlightRead>(
18725            &[highlight_range],
18726            |c| c.editor_document_highlight_read_background,
18727            cx,
18728        );
18729    });
18730
18731    let mut prepare_rename_handler = cx
18732        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18733            move |_, _, _| async move {
18734                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18735                    start: lsp::Position {
18736                        line: 0,
18737                        character: 7,
18738                    },
18739                    end: lsp::Position {
18740                        line: 0,
18741                        character: 10,
18742                    },
18743                })))
18744            },
18745        );
18746    let prepare_rename_task = cx
18747        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18748        .expect("Prepare rename was not started");
18749    prepare_rename_handler.next().await.unwrap();
18750    prepare_rename_task.await.expect("Prepare rename failed");
18751
18752    let mut rename_handler =
18753        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18754            let edit = lsp::TextEdit {
18755                range: lsp::Range {
18756                    start: lsp::Position {
18757                        line: 0,
18758                        character: 7,
18759                    },
18760                    end: lsp::Position {
18761                        line: 0,
18762                        character: 10,
18763                    },
18764                },
18765                new_text: "FooRenamed".to_string(),
18766            };
18767            Ok(Some(lsp::WorkspaceEdit::new(
18768                // Specify the same edit twice
18769                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18770            )))
18771        });
18772    let rename_task = cx
18773        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18774        .expect("Confirm rename was not started");
18775    rename_handler.next().await.unwrap();
18776    rename_task.await.expect("Confirm rename failed");
18777    cx.run_until_parked();
18778
18779    // Despite two edits, only one is actually applied as those are identical
18780    cx.assert_editor_state(indoc! {"
18781        struct FooRenamedˇ {}
18782    "});
18783}
18784
18785#[gpui::test]
18786async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18787    init_test(cx, |_| {});
18788    // These capabilities indicate that the server does not support prepare rename.
18789    let capabilities = lsp::ServerCapabilities {
18790        rename_provider: Some(lsp::OneOf::Left(true)),
18791        ..Default::default()
18792    };
18793    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18794
18795    cx.set_state(indoc! {"
18796        struct Fˇoo {}
18797    "});
18798
18799    cx.update_editor(|editor, _window, cx| {
18800        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18801        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18802        editor.highlight_background::<DocumentHighlightRead>(
18803            &[highlight_range],
18804            |c| c.editor_document_highlight_read_background,
18805            cx,
18806        );
18807    });
18808
18809    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18810        .expect("Prepare rename was not started")
18811        .await
18812        .expect("Prepare rename failed");
18813
18814    let mut rename_handler =
18815        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18816            let edit = lsp::TextEdit {
18817                range: lsp::Range {
18818                    start: lsp::Position {
18819                        line: 0,
18820                        character: 7,
18821                    },
18822                    end: lsp::Position {
18823                        line: 0,
18824                        character: 10,
18825                    },
18826                },
18827                new_text: "FooRenamed".to_string(),
18828            };
18829            Ok(Some(lsp::WorkspaceEdit::new(
18830                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18831            )))
18832        });
18833    let rename_task = cx
18834        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18835        .expect("Confirm rename was not started");
18836    rename_handler.next().await.unwrap();
18837    rename_task.await.expect("Confirm rename failed");
18838    cx.run_until_parked();
18839
18840    // Correct range is renamed, as `surrounding_word` is used to find it.
18841    cx.assert_editor_state(indoc! {"
18842        struct FooRenamedˇ {}
18843    "});
18844}
18845
18846#[gpui::test]
18847async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18848    init_test(cx, |_| {});
18849    let mut cx = EditorTestContext::new(cx).await;
18850
18851    let language = Arc::new(
18852        Language::new(
18853            LanguageConfig::default(),
18854            Some(tree_sitter_html::LANGUAGE.into()),
18855        )
18856        .with_brackets_query(
18857            r#"
18858            ("<" @open "/>" @close)
18859            ("</" @open ">" @close)
18860            ("<" @open ">" @close)
18861            ("\"" @open "\"" @close)
18862            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18863        "#,
18864        )
18865        .unwrap(),
18866    );
18867    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18868
18869    cx.set_state(indoc! {"
18870        <span>ˇ</span>
18871    "});
18872    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18873    cx.assert_editor_state(indoc! {"
18874        <span>
18875        ˇ
18876        </span>
18877    "});
18878
18879    cx.set_state(indoc! {"
18880        <span><span></span>ˇ</span>
18881    "});
18882    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18883    cx.assert_editor_state(indoc! {"
18884        <span><span></span>
18885        ˇ</span>
18886    "});
18887
18888    cx.set_state(indoc! {"
18889        <span>ˇ
18890        </span>
18891    "});
18892    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18893    cx.assert_editor_state(indoc! {"
18894        <span>
18895        ˇ
18896        </span>
18897    "});
18898}
18899
18900#[gpui::test(iterations = 10)]
18901async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18902    init_test(cx, |_| {});
18903
18904    let fs = FakeFs::new(cx.executor());
18905    fs.insert_tree(
18906        path!("/dir"),
18907        json!({
18908            "a.ts": "a",
18909        }),
18910    )
18911    .await;
18912
18913    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18914    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18915    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18916
18917    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18918    language_registry.add(Arc::new(Language::new(
18919        LanguageConfig {
18920            name: "TypeScript".into(),
18921            matcher: LanguageMatcher {
18922                path_suffixes: vec!["ts".to_string()],
18923                ..Default::default()
18924            },
18925            ..Default::default()
18926        },
18927        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18928    )));
18929    let mut fake_language_servers = language_registry.register_fake_lsp(
18930        "TypeScript",
18931        FakeLspAdapter {
18932            capabilities: lsp::ServerCapabilities {
18933                code_lens_provider: Some(lsp::CodeLensOptions {
18934                    resolve_provider: Some(true),
18935                }),
18936                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18937                    commands: vec!["_the/command".to_string()],
18938                    ..lsp::ExecuteCommandOptions::default()
18939                }),
18940                ..lsp::ServerCapabilities::default()
18941            },
18942            ..FakeLspAdapter::default()
18943        },
18944    );
18945
18946    let (buffer, _handle) = project
18947        .update(cx, |p, cx| {
18948            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18949        })
18950        .await
18951        .unwrap();
18952    cx.executor().run_until_parked();
18953
18954    let fake_server = fake_language_servers.next().await.unwrap();
18955
18956    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18957    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18958    drop(buffer_snapshot);
18959    let actions = cx
18960        .update_window(*workspace, |_, window, cx| {
18961            project.code_actions(&buffer, anchor..anchor, window, cx)
18962        })
18963        .unwrap();
18964
18965    fake_server
18966        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18967            Ok(Some(vec![
18968                lsp::CodeLens {
18969                    range: lsp::Range::default(),
18970                    command: Some(lsp::Command {
18971                        title: "Code lens command".to_owned(),
18972                        command: "_the/command".to_owned(),
18973                        arguments: None,
18974                    }),
18975                    data: None,
18976                },
18977                lsp::CodeLens {
18978                    range: lsp::Range::default(),
18979                    command: Some(lsp::Command {
18980                        title: "Command not in capabilities".to_owned(),
18981                        command: "not in capabilities".to_owned(),
18982                        arguments: None,
18983                    }),
18984                    data: None,
18985                },
18986                lsp::CodeLens {
18987                    range: lsp::Range {
18988                        start: lsp::Position {
18989                            line: 1,
18990                            character: 1,
18991                        },
18992                        end: lsp::Position {
18993                            line: 1,
18994                            character: 1,
18995                        },
18996                    },
18997                    command: Some(lsp::Command {
18998                        title: "Command not in range".to_owned(),
18999                        command: "_the/command".to_owned(),
19000                        arguments: None,
19001                    }),
19002                    data: None,
19003                },
19004            ]))
19005        })
19006        .next()
19007        .await;
19008
19009    let actions = actions.await.unwrap();
19010    assert_eq!(
19011        actions.len(),
19012        1,
19013        "Should have only one valid action for the 0..0 range"
19014    );
19015    let action = actions[0].clone();
19016    let apply = project.update(cx, |project, cx| {
19017        project.apply_code_action(buffer.clone(), action, true, cx)
19018    });
19019
19020    // Resolving the code action does not populate its edits. In absence of
19021    // edits, we must execute the given command.
19022    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
19023        |mut lens, _| async move {
19024            let lens_command = lens.command.as_mut().expect("should have a command");
19025            assert_eq!(lens_command.title, "Code lens command");
19026            lens_command.arguments = Some(vec![json!("the-argument")]);
19027            Ok(lens)
19028        },
19029    );
19030
19031    // While executing the command, the language server sends the editor
19032    // a `workspaceEdit` request.
19033    fake_server
19034        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
19035            let fake = fake_server.clone();
19036            move |params, _| {
19037                assert_eq!(params.command, "_the/command");
19038                let fake = fake.clone();
19039                async move {
19040                    fake.server
19041                        .request::<lsp::request::ApplyWorkspaceEdit>(
19042                            lsp::ApplyWorkspaceEditParams {
19043                                label: None,
19044                                edit: lsp::WorkspaceEdit {
19045                                    changes: Some(
19046                                        [(
19047                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
19048                                            vec![lsp::TextEdit {
19049                                                range: lsp::Range::new(
19050                                                    lsp::Position::new(0, 0),
19051                                                    lsp::Position::new(0, 0),
19052                                                ),
19053                                                new_text: "X".into(),
19054                                            }],
19055                                        )]
19056                                        .into_iter()
19057                                        .collect(),
19058                                    ),
19059                                    ..Default::default()
19060                                },
19061                            },
19062                        )
19063                        .await
19064                        .unwrap();
19065                    Ok(Some(json!(null)))
19066                }
19067            }
19068        })
19069        .next()
19070        .await;
19071
19072    // Applying the code lens command returns a project transaction containing the edits
19073    // sent by the language server in its `workspaceEdit` request.
19074    let transaction = apply.await.unwrap();
19075    assert!(transaction.0.contains_key(&buffer));
19076    buffer.update(cx, |buffer, cx| {
19077        assert_eq!(buffer.text(), "Xa");
19078        buffer.undo(cx);
19079        assert_eq!(buffer.text(), "a");
19080    });
19081}
19082
19083#[gpui::test]
19084async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
19085    init_test(cx, |_| {});
19086
19087    let fs = FakeFs::new(cx.executor());
19088    let main_text = r#"fn main() {
19089println!("1");
19090println!("2");
19091println!("3");
19092println!("4");
19093println!("5");
19094}"#;
19095    let lib_text = "mod foo {}";
19096    fs.insert_tree(
19097        path!("/a"),
19098        json!({
19099            "lib.rs": lib_text,
19100            "main.rs": main_text,
19101        }),
19102    )
19103    .await;
19104
19105    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19106    let (workspace, cx) =
19107        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19108    let worktree_id = workspace.update(cx, |workspace, cx| {
19109        workspace.project().update(cx, |project, cx| {
19110            project.worktrees(cx).next().unwrap().read(cx).id()
19111        })
19112    });
19113
19114    let expected_ranges = vec![
19115        Point::new(0, 0)..Point::new(0, 0),
19116        Point::new(1, 0)..Point::new(1, 1),
19117        Point::new(2, 0)..Point::new(2, 2),
19118        Point::new(3, 0)..Point::new(3, 3),
19119    ];
19120
19121    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19122    let editor_1 = workspace
19123        .update_in(cx, |workspace, window, cx| {
19124            workspace.open_path(
19125                (worktree_id, "main.rs"),
19126                Some(pane_1.downgrade()),
19127                true,
19128                window,
19129                cx,
19130            )
19131        })
19132        .unwrap()
19133        .await
19134        .downcast::<Editor>()
19135        .unwrap();
19136    pane_1.update(cx, |pane, cx| {
19137        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19138        open_editor.update(cx, |editor, cx| {
19139            assert_eq!(
19140                editor.display_text(cx),
19141                main_text,
19142                "Original main.rs text on initial open",
19143            );
19144            assert_eq!(
19145                editor
19146                    .selections
19147                    .all::<Point>(cx)
19148                    .into_iter()
19149                    .map(|s| s.range())
19150                    .collect::<Vec<_>>(),
19151                vec![Point::zero()..Point::zero()],
19152                "Default selections on initial open",
19153            );
19154        })
19155    });
19156    editor_1.update_in(cx, |editor, window, cx| {
19157        editor.change_selections(None, window, cx, |s| {
19158            s.select_ranges(expected_ranges.clone());
19159        });
19160    });
19161
19162    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19163        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19164    });
19165    let editor_2 = workspace
19166        .update_in(cx, |workspace, window, cx| {
19167            workspace.open_path(
19168                (worktree_id, "main.rs"),
19169                Some(pane_2.downgrade()),
19170                true,
19171                window,
19172                cx,
19173            )
19174        })
19175        .unwrap()
19176        .await
19177        .downcast::<Editor>()
19178        .unwrap();
19179    pane_2.update(cx, |pane, cx| {
19180        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19181        open_editor.update(cx, |editor, cx| {
19182            assert_eq!(
19183                editor.display_text(cx),
19184                main_text,
19185                "Original main.rs text on initial open in another panel",
19186            );
19187            assert_eq!(
19188                editor
19189                    .selections
19190                    .all::<Point>(cx)
19191                    .into_iter()
19192                    .map(|s| s.range())
19193                    .collect::<Vec<_>>(),
19194                vec![Point::zero()..Point::zero()],
19195                "Default selections on initial open in another panel",
19196            );
19197        })
19198    });
19199
19200    editor_2.update_in(cx, |editor, window, cx| {
19201        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19202    });
19203
19204    let _other_editor_1 = workspace
19205        .update_in(cx, |workspace, window, cx| {
19206            workspace.open_path(
19207                (worktree_id, "lib.rs"),
19208                Some(pane_1.downgrade()),
19209                true,
19210                window,
19211                cx,
19212            )
19213        })
19214        .unwrap()
19215        .await
19216        .downcast::<Editor>()
19217        .unwrap();
19218    pane_1
19219        .update_in(cx, |pane, window, cx| {
19220            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19221                .unwrap()
19222        })
19223        .await
19224        .unwrap();
19225    drop(editor_1);
19226    pane_1.update(cx, |pane, cx| {
19227        pane.active_item()
19228            .unwrap()
19229            .downcast::<Editor>()
19230            .unwrap()
19231            .update(cx, |editor, cx| {
19232                assert_eq!(
19233                    editor.display_text(cx),
19234                    lib_text,
19235                    "Other file should be open and active",
19236                );
19237            });
19238        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19239    });
19240
19241    let _other_editor_2 = workspace
19242        .update_in(cx, |workspace, window, cx| {
19243            workspace.open_path(
19244                (worktree_id, "lib.rs"),
19245                Some(pane_2.downgrade()),
19246                true,
19247                window,
19248                cx,
19249            )
19250        })
19251        .unwrap()
19252        .await
19253        .downcast::<Editor>()
19254        .unwrap();
19255    pane_2
19256        .update_in(cx, |pane, window, cx| {
19257            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19258                .unwrap()
19259        })
19260        .await
19261        .unwrap();
19262    drop(editor_2);
19263    pane_2.update(cx, |pane, cx| {
19264        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19265        open_editor.update(cx, |editor, cx| {
19266            assert_eq!(
19267                editor.display_text(cx),
19268                lib_text,
19269                "Other file should be open and active in another panel too",
19270            );
19271        });
19272        assert_eq!(
19273            pane.items().count(),
19274            1,
19275            "No other editors should be open in another pane",
19276        );
19277    });
19278
19279    let _editor_1_reopened = workspace
19280        .update_in(cx, |workspace, window, cx| {
19281            workspace.open_path(
19282                (worktree_id, "main.rs"),
19283                Some(pane_1.downgrade()),
19284                true,
19285                window,
19286                cx,
19287            )
19288        })
19289        .unwrap()
19290        .await
19291        .downcast::<Editor>()
19292        .unwrap();
19293    let _editor_2_reopened = workspace
19294        .update_in(cx, |workspace, window, cx| {
19295            workspace.open_path(
19296                (worktree_id, "main.rs"),
19297                Some(pane_2.downgrade()),
19298                true,
19299                window,
19300                cx,
19301            )
19302        })
19303        .unwrap()
19304        .await
19305        .downcast::<Editor>()
19306        .unwrap();
19307    pane_1.update(cx, |pane, cx| {
19308        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19309        open_editor.update(cx, |editor, cx| {
19310            assert_eq!(
19311                editor.display_text(cx),
19312                main_text,
19313                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19314            );
19315            assert_eq!(
19316                editor
19317                    .selections
19318                    .all::<Point>(cx)
19319                    .into_iter()
19320                    .map(|s| s.range())
19321                    .collect::<Vec<_>>(),
19322                expected_ranges,
19323                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19324            );
19325        })
19326    });
19327    pane_2.update(cx, |pane, cx| {
19328        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19329        open_editor.update(cx, |editor, cx| {
19330            assert_eq!(
19331                editor.display_text(cx),
19332                r#"fn main() {
19333⋯rintln!("1");
19334⋯intln!("2");
19335⋯ntln!("3");
19336println!("4");
19337println!("5");
19338}"#,
19339                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19340            );
19341            assert_eq!(
19342                editor
19343                    .selections
19344                    .all::<Point>(cx)
19345                    .into_iter()
19346                    .map(|s| s.range())
19347                    .collect::<Vec<_>>(),
19348                vec![Point::zero()..Point::zero()],
19349                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19350            );
19351        })
19352    });
19353}
19354
19355#[gpui::test]
19356async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19357    init_test(cx, |_| {});
19358
19359    let fs = FakeFs::new(cx.executor());
19360    let main_text = r#"fn main() {
19361println!("1");
19362println!("2");
19363println!("3");
19364println!("4");
19365println!("5");
19366}"#;
19367    let lib_text = "mod foo {}";
19368    fs.insert_tree(
19369        path!("/a"),
19370        json!({
19371            "lib.rs": lib_text,
19372            "main.rs": main_text,
19373        }),
19374    )
19375    .await;
19376
19377    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19378    let (workspace, cx) =
19379        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19380    let worktree_id = workspace.update(cx, |workspace, cx| {
19381        workspace.project().update(cx, |project, cx| {
19382            project.worktrees(cx).next().unwrap().read(cx).id()
19383        })
19384    });
19385
19386    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19387    let editor = workspace
19388        .update_in(cx, |workspace, window, cx| {
19389            workspace.open_path(
19390                (worktree_id, "main.rs"),
19391                Some(pane.downgrade()),
19392                true,
19393                window,
19394                cx,
19395            )
19396        })
19397        .unwrap()
19398        .await
19399        .downcast::<Editor>()
19400        .unwrap();
19401    pane.update(cx, |pane, cx| {
19402        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19403        open_editor.update(cx, |editor, cx| {
19404            assert_eq!(
19405                editor.display_text(cx),
19406                main_text,
19407                "Original main.rs text on initial open",
19408            );
19409        })
19410    });
19411    editor.update_in(cx, |editor, window, cx| {
19412        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19413    });
19414
19415    cx.update_global(|store: &mut SettingsStore, cx| {
19416        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19417            s.restore_on_file_reopen = Some(false);
19418        });
19419    });
19420    editor.update_in(cx, |editor, window, cx| {
19421        editor.fold_ranges(
19422            vec![
19423                Point::new(1, 0)..Point::new(1, 1),
19424                Point::new(2, 0)..Point::new(2, 2),
19425                Point::new(3, 0)..Point::new(3, 3),
19426            ],
19427            false,
19428            window,
19429            cx,
19430        );
19431    });
19432    pane.update_in(cx, |pane, window, cx| {
19433        pane.close_all_items(&CloseAllItems::default(), window, cx)
19434            .unwrap()
19435    })
19436    .await
19437    .unwrap();
19438    pane.update(cx, |pane, _| {
19439        assert!(pane.active_item().is_none());
19440    });
19441    cx.update_global(|store: &mut SettingsStore, cx| {
19442        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19443            s.restore_on_file_reopen = Some(true);
19444        });
19445    });
19446
19447    let _editor_reopened = workspace
19448        .update_in(cx, |workspace, window, cx| {
19449            workspace.open_path(
19450                (worktree_id, "main.rs"),
19451                Some(pane.downgrade()),
19452                true,
19453                window,
19454                cx,
19455            )
19456        })
19457        .unwrap()
19458        .await
19459        .downcast::<Editor>()
19460        .unwrap();
19461    pane.update(cx, |pane, cx| {
19462        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19463        open_editor.update(cx, |editor, cx| {
19464            assert_eq!(
19465                editor.display_text(cx),
19466                main_text,
19467                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19468            );
19469        })
19470    });
19471}
19472
19473#[gpui::test]
19474async fn test_hide_mouse_context_menu_on_modal_opened(cx: &mut TestAppContext) {
19475    struct EmptyModalView {
19476        focus_handle: gpui::FocusHandle,
19477    }
19478    impl EventEmitter<DismissEvent> for EmptyModalView {}
19479    impl Render for EmptyModalView {
19480        fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement {
19481            div()
19482        }
19483    }
19484    impl Focusable for EmptyModalView {
19485        fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
19486            self.focus_handle.clone()
19487        }
19488    }
19489    impl workspace::ModalView for EmptyModalView {}
19490    fn new_empty_modal_view(cx: &App) -> EmptyModalView {
19491        EmptyModalView {
19492            focus_handle: cx.focus_handle(),
19493        }
19494    }
19495
19496    init_test(cx, |_| {});
19497
19498    let fs = FakeFs::new(cx.executor());
19499    let project = Project::test(fs, [], cx).await;
19500    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
19501    let buffer = cx.update(|cx| MultiBuffer::build_simple("hello world!", cx));
19502    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
19503    let editor = cx.new_window_entity(|window, cx| {
19504        Editor::new(
19505            EditorMode::full(),
19506            buffer,
19507            Some(project.clone()),
19508            window,
19509            cx,
19510        )
19511    });
19512    workspace
19513        .update(cx, |workspace, window, cx| {
19514            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
19515        })
19516        .unwrap();
19517    editor.update_in(cx, |editor, window, cx| {
19518        editor.open_context_menu(&OpenContextMenu, window, cx);
19519        assert!(editor.mouse_context_menu.is_some());
19520    });
19521    workspace
19522        .update(cx, |workspace, window, cx| {
19523            workspace.toggle_modal(window, cx, |_, cx| new_empty_modal_view(cx));
19524        })
19525        .unwrap();
19526    cx.read(|cx| {
19527        assert!(editor.read(cx).mouse_context_menu.is_none());
19528    });
19529}
19530
19531fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19532    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19533    point..point
19534}
19535
19536fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19537    let (text, ranges) = marked_text_ranges(marked_text, true);
19538    assert_eq!(editor.text(cx), text);
19539    assert_eq!(
19540        editor.selections.ranges(cx),
19541        ranges,
19542        "Assert selections are {}",
19543        marked_text
19544    );
19545}
19546
19547pub fn handle_signature_help_request(
19548    cx: &mut EditorLspTestContext,
19549    mocked_response: lsp::SignatureHelp,
19550) -> impl Future<Output = ()> + use<> {
19551    let mut request =
19552        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19553            let mocked_response = mocked_response.clone();
19554            async move { Ok(Some(mocked_response)) }
19555        });
19556
19557    async move {
19558        request.next().await;
19559    }
19560}
19561
19562/// Handle completion request passing a marked string specifying where the completion
19563/// should be triggered from using '|' character, what range should be replaced, and what completions
19564/// should be returned using '<' and '>' to delimit the range.
19565///
19566/// Also see `handle_completion_request_with_insert_and_replace`.
19567#[track_caller]
19568pub fn handle_completion_request(
19569    cx: &mut EditorLspTestContext,
19570    marked_string: &str,
19571    completions: Vec<&'static str>,
19572    counter: Arc<AtomicUsize>,
19573) -> impl Future<Output = ()> {
19574    let complete_from_marker: TextRangeMarker = '|'.into();
19575    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19576    let (_, mut marked_ranges) = marked_text_ranges_by(
19577        marked_string,
19578        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19579    );
19580
19581    let complete_from_position =
19582        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19583    let replace_range =
19584        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19585
19586    let mut request =
19587        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19588            let completions = completions.clone();
19589            counter.fetch_add(1, atomic::Ordering::Release);
19590            async move {
19591                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19592                assert_eq!(
19593                    params.text_document_position.position,
19594                    complete_from_position
19595                );
19596                Ok(Some(lsp::CompletionResponse::Array(
19597                    completions
19598                        .iter()
19599                        .map(|completion_text| lsp::CompletionItem {
19600                            label: completion_text.to_string(),
19601                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19602                                range: replace_range,
19603                                new_text: completion_text.to_string(),
19604                            })),
19605                            ..Default::default()
19606                        })
19607                        .collect(),
19608                )))
19609            }
19610        });
19611
19612    async move {
19613        request.next().await;
19614    }
19615}
19616
19617/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19618/// given instead, which also contains an `insert` range.
19619///
19620/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19621/// that is, `replace_range.start..cursor_pos`.
19622pub fn handle_completion_request_with_insert_and_replace(
19623    cx: &mut EditorLspTestContext,
19624    marked_string: &str,
19625    completions: Vec<&'static str>,
19626    counter: Arc<AtomicUsize>,
19627) -> impl Future<Output = ()> {
19628    let complete_from_marker: TextRangeMarker = '|'.into();
19629    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19630    let (_, mut marked_ranges) = marked_text_ranges_by(
19631        marked_string,
19632        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19633    );
19634
19635    let complete_from_position =
19636        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19637    let replace_range =
19638        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19639
19640    let mut request =
19641        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19642            let completions = completions.clone();
19643            counter.fetch_add(1, atomic::Ordering::Release);
19644            async move {
19645                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19646                assert_eq!(
19647                    params.text_document_position.position, complete_from_position,
19648                    "marker `|` position doesn't match",
19649                );
19650                Ok(Some(lsp::CompletionResponse::Array(
19651                    completions
19652                        .iter()
19653                        .map(|completion_text| lsp::CompletionItem {
19654                            label: completion_text.to_string(),
19655                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19656                                lsp::InsertReplaceEdit {
19657                                    insert: lsp::Range {
19658                                        start: replace_range.start,
19659                                        end: complete_from_position,
19660                                    },
19661                                    replace: replace_range,
19662                                    new_text: completion_text.to_string(),
19663                                },
19664                            )),
19665                            ..Default::default()
19666                        })
19667                        .collect(),
19668                )))
19669            }
19670        });
19671
19672    async move {
19673        request.next().await;
19674    }
19675}
19676
19677fn handle_resolve_completion_request(
19678    cx: &mut EditorLspTestContext,
19679    edits: Option<Vec<(&'static str, &'static str)>>,
19680) -> impl Future<Output = ()> {
19681    let edits = edits.map(|edits| {
19682        edits
19683            .iter()
19684            .map(|(marked_string, new_text)| {
19685                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19686                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19687                lsp::TextEdit::new(replace_range, new_text.to_string())
19688            })
19689            .collect::<Vec<_>>()
19690    });
19691
19692    let mut request =
19693        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19694            let edits = edits.clone();
19695            async move {
19696                Ok(lsp::CompletionItem {
19697                    additional_text_edits: edits,
19698                    ..Default::default()
19699                })
19700            }
19701        });
19702
19703    async move {
19704        request.next().await;
19705    }
19706}
19707
19708pub(crate) fn update_test_language_settings(
19709    cx: &mut TestAppContext,
19710    f: impl Fn(&mut AllLanguageSettingsContent),
19711) {
19712    cx.update(|cx| {
19713        SettingsStore::update_global(cx, |store, cx| {
19714            store.update_user_settings::<AllLanguageSettings>(cx, f);
19715        });
19716    });
19717}
19718
19719pub(crate) fn update_test_project_settings(
19720    cx: &mut TestAppContext,
19721    f: impl Fn(&mut ProjectSettings),
19722) {
19723    cx.update(|cx| {
19724        SettingsStore::update_global(cx, |store, cx| {
19725            store.update_user_settings::<ProjectSettings>(cx, f);
19726        });
19727    });
19728}
19729
19730pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19731    cx.update(|cx| {
19732        assets::Assets.load_test_fonts(cx);
19733        let store = SettingsStore::test(cx);
19734        cx.set_global(store);
19735        theme::init(theme::LoadThemes::JustBase, cx);
19736        release_channel::init(SemanticVersion::default(), cx);
19737        client::init_settings(cx);
19738        language::init(cx);
19739        Project::init_settings(cx);
19740        workspace::init_settings(cx);
19741        crate::init(cx);
19742    });
19743
19744    update_test_language_settings(cx, f);
19745}
19746
19747#[track_caller]
19748fn assert_hunk_revert(
19749    not_reverted_text_with_selections: &str,
19750    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19751    expected_reverted_text_with_selections: &str,
19752    base_text: &str,
19753    cx: &mut EditorLspTestContext,
19754) {
19755    cx.set_state(not_reverted_text_with_selections);
19756    cx.set_head_text(base_text);
19757    cx.executor().run_until_parked();
19758
19759    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19760        let snapshot = editor.snapshot(window, cx);
19761        let reverted_hunk_statuses = snapshot
19762            .buffer_snapshot
19763            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19764            .map(|hunk| hunk.status().kind)
19765            .collect::<Vec<_>>();
19766
19767        editor.git_restore(&Default::default(), window, cx);
19768        reverted_hunk_statuses
19769    });
19770    cx.executor().run_until_parked();
19771    cx.assert_editor_state(expected_reverted_text_with_selections);
19772    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19773}