editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319#[gpui::test]
 1320fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1321    init_test(cx, |_| {});
 1322
 1323    let editor = cx.add_window(|window, cx| {
 1324        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1325        build_editor(buffer.clone(), window, cx)
 1326    });
 1327
 1328    assert_eq!('🟥'.len_utf8(), 4);
 1329    assert_eq!('α'.len_utf8(), 2);
 1330
 1331    _ = editor.update(cx, |editor, window, cx| {
 1332        editor.fold_creases(
 1333            vec![
 1334                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1337            ],
 1338            true,
 1339            window,
 1340            cx,
 1341        );
 1342        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1343
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧".len())]
 1353        );
 1354        editor.move_right(&MoveRight, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(0, "🟥🟧⋯".len())]
 1358        );
 1359
 1360        editor.move_down(&MoveDown, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯e".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "ab".len())]
 1374        );
 1375        editor.move_left(&MoveLeft, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(1, "a".len())]
 1379        );
 1380
 1381        editor.move_down(&MoveDown, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "α".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯".len())]
 1395        );
 1396        editor.move_right(&MoveRight, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(2, "αβ⋯ε".len())]
 1400        );
 1401
 1402        editor.move_up(&MoveUp, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(1, "ab⋯e".len())]
 1406        );
 1407        editor.move_down(&MoveDown, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβ⋯ε".len())]
 1411        );
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(1, "ab⋯e".len())]
 1416        );
 1417
 1418        editor.move_up(&MoveUp, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥🟧".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "🟥".len())]
 1427        );
 1428        editor.move_left(&MoveLeft, window, cx);
 1429        assert_eq!(
 1430            editor.selections.display_ranges(cx),
 1431            &[empty_range(0, "".len())]
 1432        );
 1433    });
 1434}
 1435
 1436#[gpui::test]
 1437fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1438    init_test(cx, |_| {});
 1439
 1440    let editor = cx.add_window(|window, cx| {
 1441        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1442        build_editor(buffer.clone(), window, cx)
 1443    });
 1444    _ = editor.update(cx, |editor, window, cx| {
 1445        editor.change_selections(None, window, cx, |s| {
 1446            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1447        });
 1448
 1449        // moving above start of document should move selection to start of document,
 1450        // but the next move down should still be at the original goal_x
 1451        editor.move_up(&MoveUp, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(0, "".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(1, "abcd".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(3, "abcd".len())]
 1473        );
 1474
 1475        editor.move_down(&MoveDown, window, cx);
 1476        assert_eq!(
 1477            editor.selections.display_ranges(cx),
 1478            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1479        );
 1480
 1481        // moving past end of document should not change goal_x
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_down(&MoveDown, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(5, "".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(3, "abcd".len())]
 1504        );
 1505
 1506        editor.move_up(&MoveUp, window, cx);
 1507        assert_eq!(
 1508            editor.selections.display_ranges(cx),
 1509            &[empty_range(2, "αβγ".len())]
 1510        );
 1511    });
 1512}
 1513
 1514#[gpui::test]
 1515fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1516    init_test(cx, |_| {});
 1517    let move_to_beg = MoveToBeginningOfLine {
 1518        stop_at_soft_wraps: true,
 1519        stop_at_indent: true,
 1520    };
 1521
 1522    let delete_to_beg = DeleteToBeginningOfLine {
 1523        stop_at_indent: false,
 1524    };
 1525
 1526    let move_to_end = MoveToEndOfLine {
 1527        stop_at_soft_wraps: true,
 1528    };
 1529
 1530    let editor = cx.add_window(|window, cx| {
 1531        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1532        build_editor(buffer, window, cx)
 1533    });
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.change_selections(None, window, cx, |s| {
 1536            s.select_display_ranges([
 1537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1538                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1539            ]);
 1540        });
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1571                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1572            ]
 1573        );
 1574    });
 1575
 1576    _ = editor.update(cx, |editor, window, cx| {
 1577        editor.move_to_end_of_line(&move_to_end, window, cx);
 1578        assert_eq!(
 1579            editor.selections.display_ranges(cx),
 1580            &[
 1581                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1582                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1583            ]
 1584        );
 1585    });
 1586
 1587    // Moving to the end of line again is a no-op.
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_to_end_of_line(&move_to_end, window, cx);
 1590        assert_eq!(
 1591            editor.selections.display_ranges(cx),
 1592            &[
 1593                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1594                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1595            ]
 1596        );
 1597    });
 1598
 1599    _ = editor.update(cx, |editor, window, cx| {
 1600        editor.move_left(&MoveLeft, window, cx);
 1601        editor.select_to_beginning_of_line(
 1602            &SelectToBeginningOfLine {
 1603                stop_at_soft_wraps: true,
 1604                stop_at_indent: true,
 1605            },
 1606            window,
 1607            cx,
 1608        );
 1609        assert_eq!(
 1610            editor.selections.display_ranges(cx),
 1611            &[
 1612                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1613                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1614            ]
 1615        );
 1616    });
 1617
 1618    _ = editor.update(cx, |editor, window, cx| {
 1619        editor.select_to_beginning_of_line(
 1620            &SelectToBeginningOfLine {
 1621                stop_at_soft_wraps: true,
 1622                stop_at_indent: true,
 1623            },
 1624            window,
 1625            cx,
 1626        );
 1627        assert_eq!(
 1628            editor.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635
 1636    _ = editor.update(cx, |editor, window, cx| {
 1637        editor.select_to_beginning_of_line(
 1638            &SelectToBeginningOfLine {
 1639                stop_at_soft_wraps: true,
 1640                stop_at_indent: true,
 1641            },
 1642            window,
 1643            cx,
 1644        );
 1645        assert_eq!(
 1646            editor.selections.display_ranges(cx),
 1647            &[
 1648                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1649                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1650            ]
 1651        );
 1652    });
 1653
 1654    _ = editor.update(cx, |editor, window, cx| {
 1655        editor.select_to_end_of_line(
 1656            &SelectToEndOfLine {
 1657                stop_at_soft_wraps: true,
 1658            },
 1659            window,
 1660            cx,
 1661        );
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "ab\n  de");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1678                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1679            ]
 1680        );
 1681    });
 1682
 1683    _ = editor.update(cx, |editor, window, cx| {
 1684        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1685        assert_eq!(editor.display_text(cx), "\n");
 1686        assert_eq!(
 1687            editor.selections.display_ranges(cx),
 1688            &[
 1689                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1691            ]
 1692        );
 1693    });
 1694}
 1695
 1696#[gpui::test]
 1697fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1698    init_test(cx, |_| {});
 1699    let move_to_beg = MoveToBeginningOfLine {
 1700        stop_at_soft_wraps: false,
 1701        stop_at_indent: false,
 1702    };
 1703
 1704    let move_to_end = MoveToEndOfLine {
 1705        stop_at_soft_wraps: false,
 1706    };
 1707
 1708    let editor = cx.add_window(|window, cx| {
 1709        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1710        build_editor(buffer, window, cx)
 1711    });
 1712
 1713    _ = editor.update(cx, |editor, window, cx| {
 1714        editor.set_wrap_width(Some(140.0.into()), cx);
 1715
 1716        // We expect the following lines after wrapping
 1717        // ```
 1718        // thequickbrownfox
 1719        // jumpedoverthelazydo
 1720        // gs
 1721        // ```
 1722        // The final `gs` was soft-wrapped onto a new line.
 1723        assert_eq!(
 1724            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1725            editor.display_text(cx),
 1726        );
 1727
 1728        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1729        // Start the cursor at the `k` on the first line
 1730        editor.change_selections(None, window, cx, |s| {
 1731            s.select_display_ranges([
 1732                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1733            ]);
 1734        });
 1735
 1736        // Moving to the beginning of the line should put us at the beginning of the line.
 1737        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1738        assert_eq!(
 1739            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1740            editor.selections.display_ranges(cx)
 1741        );
 1742
 1743        // Moving to the end of the line should put us at the end of the line.
 1744        editor.move_to_end_of_line(&move_to_end, window, cx);
 1745        assert_eq!(
 1746            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1747            editor.selections.display_ranges(cx)
 1748        );
 1749
 1750        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1751        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1752        editor.change_selections(None, window, cx, |s| {
 1753            s.select_display_ranges([
 1754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1755            ]);
 1756        });
 1757
 1758        // Moving to the beginning of the line should put us at the start of the second line of
 1759        // display text, i.e., the `j`.
 1760        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the beginning of the line again should be a no-op.
 1767        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772
 1773        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1774        // next display line.
 1775        editor.move_to_end_of_line(&move_to_end, window, cx);
 1776        assert_eq!(
 1777            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1778            editor.selections.display_ranges(cx)
 1779        );
 1780
 1781        // Moving to the end of the line again should be a no-op.
 1782        editor.move_to_end_of_line(&move_to_end, window, cx);
 1783        assert_eq!(
 1784            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1785            editor.selections.display_ranges(cx)
 1786        );
 1787    });
 1788}
 1789
 1790#[gpui::test]
 1791fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1792    init_test(cx, |_| {});
 1793
 1794    let move_to_beg = MoveToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let select_to_beg = SelectToBeginningOfLine {
 1800        stop_at_soft_wraps: true,
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let delete_to_beg = DeleteToBeginningOfLine {
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let move_to_end = MoveToEndOfLine {
 1809        stop_at_soft_wraps: false,
 1810    };
 1811
 1812    let editor = cx.add_window(|window, cx| {
 1813        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1814        build_editor(buffer, window, cx)
 1815    });
 1816
 1817    _ = editor.update(cx, |editor, window, cx| {
 1818        editor.change_selections(None, window, cx, |s| {
 1819            s.select_display_ranges([
 1820                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1821                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1822            ]);
 1823        });
 1824
 1825        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1826        // and the second cursor at the first non-whitespace character in the line.
 1827        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1828        assert_eq!(
 1829            editor.selections.display_ranges(cx),
 1830            &[
 1831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1833            ]
 1834        );
 1835
 1836        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1837        // and should move the second cursor to the beginning of the line.
 1838        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1839        assert_eq!(
 1840            editor.selections.display_ranges(cx),
 1841            &[
 1842                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1843                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1844            ]
 1845        );
 1846
 1847        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1848        // and should move the second cursor back to the first non-whitespace character in the line.
 1849        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1850        assert_eq!(
 1851            editor.selections.display_ranges(cx),
 1852            &[
 1853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1855            ]
 1856        );
 1857
 1858        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1859        // and to the first non-whitespace character in the line for the second cursor.
 1860        editor.move_to_end_of_line(&move_to_end, window, cx);
 1861        editor.move_left(&MoveLeft, window, cx);
 1862        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[
 1866                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1867                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1868            ]
 1869        );
 1870
 1871        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1872        // and should select to the beginning of the line for the second cursor.
 1873        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[
 1877                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1878                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1879            ]
 1880        );
 1881
 1882        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1883        // and should delete to the first non-whitespace character in the line for the second cursor.
 1884        editor.move_to_end_of_line(&move_to_end, window, cx);
 1885        editor.move_left(&MoveLeft, window, cx);
 1886        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1887        assert_eq!(editor.text(cx), "c\n  f");
 1888    });
 1889}
 1890
 1891#[gpui::test]
 1892fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1893    init_test(cx, |_| {});
 1894
 1895    let editor = cx.add_window(|window, cx| {
 1896        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1897        build_editor(buffer, window, cx)
 1898    });
 1899    _ = editor.update(cx, |editor, window, cx| {
 1900        editor.change_selections(None, window, cx, |s| {
 1901            s.select_display_ranges([
 1902                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1903                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1904            ])
 1905        });
 1906
 1907        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1908        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1923        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1930
 1931        editor.move_right(&MoveRight, window, cx);
 1932        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1933        assert_selection_ranges(
 1934            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1935            editor,
 1936            cx,
 1937        );
 1938
 1939        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1940        assert_selection_ranges(
 1941            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1942            editor,
 1943            cx,
 1944        );
 1945
 1946        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1947        assert_selection_ranges(
 1948            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1949            editor,
 1950            cx,
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1957    init_test(cx, |_| {});
 1958
 1959    let editor = cx.add_window(|window, cx| {
 1960        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1961        build_editor(buffer, window, cx)
 1962    });
 1963
 1964    _ = editor.update(cx, |editor, window, cx| {
 1965        editor.set_wrap_width(Some(140.0.into()), cx);
 1966        assert_eq!(
 1967            editor.display_text(cx),
 1968            "use one::{\n    two::three::\n    four::five\n};"
 1969        );
 1970
 1971        editor.change_selections(None, window, cx, |s| {
 1972            s.select_display_ranges([
 1973                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1974            ]);
 1975        });
 1976
 1977        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1978        assert_eq!(
 1979            editor.selections.display_ranges(cx),
 1980            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1981        );
 1982
 1983        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1984        assert_eq!(
 1985            editor.selections.display_ranges(cx),
 1986            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1987        );
 1988
 1989        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1990        assert_eq!(
 1991            editor.selections.display_ranges(cx),
 1992            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1993        );
 1994
 1995        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1996        assert_eq!(
 1997            editor.selections.display_ranges(cx),
 1998            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1999        );
 2000
 2001        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2002        assert_eq!(
 2003            editor.selections.display_ranges(cx),
 2004            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2005        );
 2006
 2007        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2008        assert_eq!(
 2009            editor.selections.display_ranges(cx),
 2010            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2011        );
 2012    });
 2013}
 2014
 2015#[gpui::test]
 2016async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2017    init_test(cx, |_| {});
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2028
 2029    cx.set_state(
 2030        &r#"ˇone
 2031        two
 2032
 2033        three
 2034        fourˇ
 2035        five
 2036
 2037        six"#
 2038            .unindent(),
 2039    );
 2040
 2041    cx.update_editor(|editor, window, cx| {
 2042        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2043    });
 2044    cx.assert_editor_state(
 2045        &r#"one
 2046        two
 2047        ˇ
 2048        three
 2049        four
 2050        five
 2051        ˇ
 2052        six"#
 2053            .unindent(),
 2054    );
 2055
 2056    cx.update_editor(|editor, window, cx| {
 2057        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"one
 2061        two
 2062
 2063        three
 2064        four
 2065        five
 2066        ˇ
 2067        sixˇ"#
 2068            .unindent(),
 2069    );
 2070
 2071    cx.update_editor(|editor, window, cx| {
 2072        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2073    });
 2074    cx.assert_editor_state(
 2075        &r#"one
 2076        two
 2077
 2078        three
 2079        four
 2080        five
 2081
 2082        sixˇ"#
 2083            .unindent(),
 2084    );
 2085
 2086    cx.update_editor(|editor, window, cx| {
 2087        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2088    });
 2089    cx.assert_editor_state(
 2090        &r#"one
 2091        two
 2092
 2093        three
 2094        four
 2095        five
 2096        ˇ
 2097        six"#
 2098            .unindent(),
 2099    );
 2100
 2101    cx.update_editor(|editor, window, cx| {
 2102        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2103    });
 2104    cx.assert_editor_state(
 2105        &r#"one
 2106        two
 2107        ˇ
 2108        three
 2109        four
 2110        five
 2111
 2112        six"#
 2113            .unindent(),
 2114    );
 2115
 2116    cx.update_editor(|editor, window, cx| {
 2117        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2118    });
 2119    cx.assert_editor_state(
 2120        &r#"ˇone
 2121        two
 2122
 2123        three
 2124        four
 2125        five
 2126
 2127        six"#
 2128            .unindent(),
 2129    );
 2130}
 2131
 2132#[gpui::test]
 2133async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135    let mut cx = EditorTestContext::new(cx).await;
 2136    let line_height = cx.editor(|editor, window, _| {
 2137        editor
 2138            .style()
 2139            .unwrap()
 2140            .text
 2141            .line_height_in_pixels(window.rem_size())
 2142    });
 2143    let window = cx.window;
 2144    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2145
 2146    cx.set_state(
 2147        r#"ˇone
 2148        two
 2149        three
 2150        four
 2151        five
 2152        six
 2153        seven
 2154        eight
 2155        nine
 2156        ten
 2157        "#,
 2158    );
 2159
 2160    cx.update_editor(|editor, window, cx| {
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 0.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 3.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 6.)
 2174        );
 2175        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2176        assert_eq!(
 2177            editor.snapshot(window, cx).scroll_position(),
 2178            gpui::Point::new(0., 3.)
 2179        );
 2180
 2181        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 1.)
 2185        );
 2186        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2187        assert_eq!(
 2188            editor.snapshot(window, cx).scroll_position(),
 2189            gpui::Point::new(0., 3.)
 2190        );
 2191    });
 2192}
 2193
 2194#[gpui::test]
 2195async fn test_autoscroll(cx: &mut TestAppContext) {
 2196    init_test(cx, |_| {});
 2197    let mut cx = EditorTestContext::new(cx).await;
 2198
 2199    let line_height = cx.update_editor(|editor, window, cx| {
 2200        editor.set_vertical_scroll_margin(2, cx);
 2201        editor
 2202            .style()
 2203            .unwrap()
 2204            .text
 2205            .line_height_in_pixels(window.rem_size())
 2206    });
 2207    let window = cx.window;
 2208    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2209
 2210    cx.set_state(
 2211        r#"ˇone
 2212            two
 2213            three
 2214            four
 2215            five
 2216            six
 2217            seven
 2218            eight
 2219            nine
 2220            ten
 2221        "#,
 2222    );
 2223    cx.update_editor(|editor, window, cx| {
 2224        assert_eq!(
 2225            editor.snapshot(window, cx).scroll_position(),
 2226            gpui::Point::new(0., 0.0)
 2227        );
 2228    });
 2229
 2230    // Add a cursor below the visible area. Since both cursors cannot fit
 2231    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2232    // allows the vertical scroll margin below that cursor.
 2233    cx.update_editor(|editor, window, cx| {
 2234        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2235            selections.select_ranges([
 2236                Point::new(0, 0)..Point::new(0, 0),
 2237                Point::new(6, 0)..Point::new(6, 0),
 2238            ]);
 2239        })
 2240    });
 2241    cx.update_editor(|editor, window, cx| {
 2242        assert_eq!(
 2243            editor.snapshot(window, cx).scroll_position(),
 2244            gpui::Point::new(0., 3.0)
 2245        );
 2246    });
 2247
 2248    // Move down. The editor cursor scrolls down to track the newest cursor.
 2249    cx.update_editor(|editor, window, cx| {
 2250        editor.move_down(&Default::default(), window, cx);
 2251    });
 2252    cx.update_editor(|editor, window, cx| {
 2253        assert_eq!(
 2254            editor.snapshot(window, cx).scroll_position(),
 2255            gpui::Point::new(0., 4.0)
 2256        );
 2257    });
 2258
 2259    // Add a cursor above the visible area. Since both cursors fit on screen,
 2260    // the editor scrolls to show both.
 2261    cx.update_editor(|editor, window, cx| {
 2262        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2263            selections.select_ranges([
 2264                Point::new(1, 0)..Point::new(1, 0),
 2265                Point::new(6, 0)..Point::new(6, 0),
 2266            ]);
 2267        })
 2268    });
 2269    cx.update_editor(|editor, window, cx| {
 2270        assert_eq!(
 2271            editor.snapshot(window, cx).scroll_position(),
 2272            gpui::Point::new(0., 1.0)
 2273        );
 2274    });
 2275}
 2276
 2277#[gpui::test]
 2278async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2279    init_test(cx, |_| {});
 2280    let mut cx = EditorTestContext::new(cx).await;
 2281
 2282    let line_height = cx.editor(|editor, window, _cx| {
 2283        editor
 2284            .style()
 2285            .unwrap()
 2286            .text
 2287            .line_height_in_pixels(window.rem_size())
 2288    });
 2289    let window = cx.window;
 2290    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2291    cx.set_state(
 2292        &r#"
 2293        ˇone
 2294        two
 2295        threeˇ
 2296        four
 2297        five
 2298        six
 2299        seven
 2300        eight
 2301        nine
 2302        ten
 2303        "#
 2304        .unindent(),
 2305    );
 2306
 2307    cx.update_editor(|editor, window, cx| {
 2308        editor.move_page_down(&MovePageDown::default(), window, cx)
 2309    });
 2310    cx.assert_editor_state(
 2311        &r#"
 2312        one
 2313        two
 2314        three
 2315        ˇfour
 2316        five
 2317        sixˇ
 2318        seven
 2319        eight
 2320        nine
 2321        ten
 2322        "#
 2323        .unindent(),
 2324    );
 2325
 2326    cx.update_editor(|editor, window, cx| {
 2327        editor.move_page_down(&MovePageDown::default(), window, cx)
 2328    });
 2329    cx.assert_editor_state(
 2330        &r#"
 2331        one
 2332        two
 2333        three
 2334        four
 2335        five
 2336        six
 2337        ˇseven
 2338        eight
 2339        nineˇ
 2340        ten
 2341        "#
 2342        .unindent(),
 2343    );
 2344
 2345    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2346    cx.assert_editor_state(
 2347        &r#"
 2348        one
 2349        two
 2350        three
 2351        ˇfour
 2352        five
 2353        sixˇ
 2354        seven
 2355        eight
 2356        nine
 2357        ten
 2358        "#
 2359        .unindent(),
 2360    );
 2361
 2362    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2363    cx.assert_editor_state(
 2364        &r#"
 2365        ˇone
 2366        two
 2367        threeˇ
 2368        four
 2369        five
 2370        six
 2371        seven
 2372        eight
 2373        nine
 2374        ten
 2375        "#
 2376        .unindent(),
 2377    );
 2378
 2379    // Test select collapsing
 2380    cx.update_editor(|editor, window, cx| {
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384    });
 2385    cx.assert_editor_state(
 2386        &r#"
 2387        one
 2388        two
 2389        three
 2390        four
 2391        five
 2392        six
 2393        seven
 2394        eight
 2395        nine
 2396        ˇten
 2397        ˇ"#
 2398        .unindent(),
 2399    );
 2400}
 2401
 2402#[gpui::test]
 2403async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2404    init_test(cx, |_| {});
 2405    let mut cx = EditorTestContext::new(cx).await;
 2406    cx.set_state("one «two threeˇ» four");
 2407    cx.update_editor(|editor, window, cx| {
 2408        editor.delete_to_beginning_of_line(
 2409            &DeleteToBeginningOfLine {
 2410                stop_at_indent: false,
 2411            },
 2412            window,
 2413            cx,
 2414        );
 2415        assert_eq!(editor.text(cx), " four");
 2416    });
 2417}
 2418
 2419#[gpui::test]
 2420fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2421    init_test(cx, |_| {});
 2422
 2423    let editor = cx.add_window(|window, cx| {
 2424        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2425        build_editor(buffer.clone(), window, cx)
 2426    });
 2427
 2428    _ = editor.update(cx, |editor, window, cx| {
 2429        editor.change_selections(None, window, cx, |s| {
 2430            s.select_display_ranges([
 2431                // an empty selection - the preceding word fragment is deleted
 2432                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2433                // characters selected - they are deleted
 2434                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2435            ])
 2436        });
 2437        editor.delete_to_previous_word_start(
 2438            &DeleteToPreviousWordStart {
 2439                ignore_newlines: false,
 2440            },
 2441            window,
 2442            cx,
 2443        );
 2444        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2445    });
 2446
 2447    _ = editor.update(cx, |editor, window, cx| {
 2448        editor.change_selections(None, window, cx, |s| {
 2449            s.select_display_ranges([
 2450                // an empty selection - the following word fragment is deleted
 2451                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2452                // characters selected - they are deleted
 2453                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2454            ])
 2455        });
 2456        editor.delete_to_next_word_end(
 2457            &DeleteToNextWordEnd {
 2458                ignore_newlines: false,
 2459            },
 2460            window,
 2461            cx,
 2462        );
 2463        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2464    });
 2465}
 2466
 2467#[gpui::test]
 2468fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2469    init_test(cx, |_| {});
 2470
 2471    let editor = cx.add_window(|window, cx| {
 2472        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2473        build_editor(buffer.clone(), window, cx)
 2474    });
 2475    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2476        ignore_newlines: false,
 2477    };
 2478    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2479        ignore_newlines: true,
 2480    };
 2481
 2482    _ = editor.update(cx, |editor, window, cx| {
 2483        editor.change_selections(None, window, cx, |s| {
 2484            s.select_display_ranges([
 2485                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2486            ])
 2487        });
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2500    });
 2501}
 2502
 2503#[gpui::test]
 2504fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2505    init_test(cx, |_| {});
 2506
 2507    let editor = cx.add_window(|window, cx| {
 2508        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2509        build_editor(buffer.clone(), window, cx)
 2510    });
 2511    let del_to_next_word_end = DeleteToNextWordEnd {
 2512        ignore_newlines: false,
 2513    };
 2514    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2515        ignore_newlines: true,
 2516    };
 2517
 2518    _ = editor.update(cx, |editor, window, cx| {
 2519        editor.change_selections(None, window, cx, |s| {
 2520            s.select_display_ranges([
 2521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2522            ])
 2523        });
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "one\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "\n   two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(
 2536            editor.buffer.read(cx).read(cx).text(),
 2537            "two\nthree\n   four"
 2538        );
 2539        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2541        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2545    });
 2546}
 2547
 2548#[gpui::test]
 2549fn test_newline(cx: &mut TestAppContext) {
 2550    init_test(cx, |_| {});
 2551
 2552    let editor = cx.add_window(|window, cx| {
 2553        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2554        build_editor(buffer.clone(), window, cx)
 2555    });
 2556
 2557    _ = editor.update(cx, |editor, window, cx| {
 2558        editor.change_selections(None, window, cx, |s| {
 2559            s.select_display_ranges([
 2560                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2562                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2563            ])
 2564        });
 2565
 2566        editor.newline(&Newline, window, cx);
 2567        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2568    });
 2569}
 2570
 2571#[gpui::test]
 2572fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|window, cx| {
 2576        let buffer = MultiBuffer::build_simple(
 2577            "
 2578                a
 2579                b(
 2580                    X
 2581                )
 2582                c(
 2583                    X
 2584                )
 2585            "
 2586            .unindent()
 2587            .as_str(),
 2588            cx,
 2589        );
 2590        let mut editor = build_editor(buffer.clone(), window, cx);
 2591        editor.change_selections(None, window, cx, |s| {
 2592            s.select_ranges([
 2593                Point::new(2, 4)..Point::new(2, 5),
 2594                Point::new(5, 4)..Point::new(5, 5),
 2595            ])
 2596        });
 2597        editor
 2598    });
 2599
 2600    _ = editor.update(cx, |editor, window, cx| {
 2601        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2602        editor.buffer.update(cx, |buffer, cx| {
 2603            buffer.edit(
 2604                [
 2605                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2606                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2607                ],
 2608                None,
 2609                cx,
 2610            );
 2611            assert_eq!(
 2612                buffer.read(cx).text(),
 2613                "
 2614                    a
 2615                    b()
 2616                    c()
 2617                "
 2618                .unindent()
 2619            );
 2620        });
 2621        assert_eq!(
 2622            editor.selections.ranges(cx),
 2623            &[
 2624                Point::new(1, 2)..Point::new(1, 2),
 2625                Point::new(2, 2)..Point::new(2, 2),
 2626            ],
 2627        );
 2628
 2629        editor.newline(&Newline, window, cx);
 2630        assert_eq!(
 2631            editor.text(cx),
 2632            "
 2633                a
 2634                b(
 2635                )
 2636                c(
 2637                )
 2638            "
 2639            .unindent()
 2640        );
 2641
 2642        // The selections are moved after the inserted newlines
 2643        assert_eq!(
 2644            editor.selections.ranges(cx),
 2645            &[
 2646                Point::new(2, 0)..Point::new(2, 0),
 2647                Point::new(4, 0)..Point::new(4, 0),
 2648            ],
 2649        );
 2650    });
 2651}
 2652
 2653#[gpui::test]
 2654async fn test_newline_above(cx: &mut TestAppContext) {
 2655    init_test(cx, |settings| {
 2656        settings.defaults.tab_size = NonZeroU32::new(4)
 2657    });
 2658
 2659    let language = Arc::new(
 2660        Language::new(
 2661            LanguageConfig::default(),
 2662            Some(tree_sitter_rust::LANGUAGE.into()),
 2663        )
 2664        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2665        .unwrap(),
 2666    );
 2667
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670    cx.set_state(indoc! {"
 2671        const a: ˇA = (
 2672 2673                «const_functionˇ»(ˇ),
 2674                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2675 2676        ˇ);ˇ
 2677    "});
 2678
 2679    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        ˇ
 2682        const a: A = (
 2683            ˇ
 2684            (
 2685                ˇ
 2686                ˇ
 2687                const_function(),
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                something_else,
 2693                ˇ
 2694            )
 2695            ˇ
 2696            ˇ
 2697        );
 2698    "});
 2699}
 2700
 2701#[gpui::test]
 2702async fn test_newline_below(cx: &mut TestAppContext) {
 2703    init_test(cx, |settings| {
 2704        settings.defaults.tab_size = NonZeroU32::new(4)
 2705    });
 2706
 2707    let language = Arc::new(
 2708        Language::new(
 2709            LanguageConfig::default(),
 2710            Some(tree_sitter_rust::LANGUAGE.into()),
 2711        )
 2712        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2713        .unwrap(),
 2714    );
 2715
 2716    let mut cx = EditorTestContext::new(cx).await;
 2717    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2718    cx.set_state(indoc! {"
 2719        const a: ˇA = (
 2720 2721                «const_functionˇ»(ˇ),
 2722                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2723 2724        ˇ);ˇ
 2725    "});
 2726
 2727    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2728    cx.assert_editor_state(indoc! {"
 2729        const a: A = (
 2730            ˇ
 2731            (
 2732                ˇ
 2733                const_function(),
 2734                ˇ
 2735                ˇ
 2736                something_else,
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741            )
 2742            ˇ
 2743        );
 2744        ˇ
 2745        ˇ
 2746    "});
 2747}
 2748
 2749#[gpui::test]
 2750async fn test_newline_comments(cx: &mut TestAppContext) {
 2751    init_test(cx, |settings| {
 2752        settings.defaults.tab_size = NonZeroU32::new(4)
 2753    });
 2754
 2755    let language = Arc::new(Language::new(
 2756        LanguageConfig {
 2757            line_comments: vec!["//".into()],
 2758            ..LanguageConfig::default()
 2759        },
 2760        None,
 2761    ));
 2762    {
 2763        let mut cx = EditorTestContext::new(cx).await;
 2764        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2765        cx.set_state(indoc! {"
 2766        // Fooˇ
 2767    "});
 2768
 2769        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2770        cx.assert_editor_state(indoc! {"
 2771        // Foo
 2772        //ˇ
 2773    "});
 2774        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2775        cx.set_state(indoc! {"
 2776        ˇ// Foo
 2777    "});
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(indoc! {"
 2780
 2781        ˇ// Foo
 2782    "});
 2783    }
 2784    // Ensure that comment continuations can be disabled.
 2785    update_test_language_settings(cx, |settings| {
 2786        settings.defaults.extend_comment_on_newline = Some(false);
 2787    });
 2788    let mut cx = EditorTestContext::new(cx).await;
 2789    cx.set_state(indoc! {"
 2790        // Fooˇ
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        ˇ
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2801    init_test(cx, |_| {});
 2802
 2803    let editor = cx.add_window(|window, cx| {
 2804        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2805        let mut editor = build_editor(buffer.clone(), window, cx);
 2806        editor.change_selections(None, window, cx, |s| {
 2807            s.select_ranges([3..4, 11..12, 19..20])
 2808        });
 2809        editor
 2810    });
 2811
 2812    _ = editor.update(cx, |editor, window, cx| {
 2813        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2814        editor.buffer.update(cx, |buffer, cx| {
 2815            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2816            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2817        });
 2818        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2819
 2820        editor.insert("Z", window, cx);
 2821        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2822
 2823        // The selections are moved after the inserted characters
 2824        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2825    });
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_tab(cx: &mut TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.tab_size = NonZeroU32::new(3)
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835    cx.set_state(indoc! {"
 2836        ˇabˇc
 2837        ˇ🏀ˇ🏀ˇefg
 2838 2839    "});
 2840    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2841    cx.assert_editor_state(indoc! {"
 2842           ˇab ˇc
 2843           ˇ🏀  ˇ🏀  ˇefg
 2844        d  ˇ
 2845    "});
 2846
 2847    cx.set_state(indoc! {"
 2848        a
 2849        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        a
 2854           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2855    "});
 2856}
 2857
 2858#[gpui::test]
 2859async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2860    init_test(cx, |_| {});
 2861
 2862    let mut cx = EditorTestContext::new(cx).await;
 2863    let language = Arc::new(
 2864        Language::new(
 2865            LanguageConfig::default(),
 2866            Some(tree_sitter_rust::LANGUAGE.into()),
 2867        )
 2868        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2869        .unwrap(),
 2870    );
 2871    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2872
 2873    // cursors that are already at the suggested indent level insert
 2874    // a soft tab. cursors that are to the left of the suggested indent
 2875    // auto-indent their line.
 2876    cx.set_state(indoc! {"
 2877        ˇ
 2878        const a: B = (
 2879            c(
 2880                d(
 2881        ˇ
 2882                )
 2883        ˇ
 2884        ˇ    )
 2885        );
 2886    "});
 2887    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889            ˇ
 2890        const a: B = (
 2891            c(
 2892                d(
 2893                    ˇ
 2894                )
 2895                ˇ
 2896            ˇ)
 2897        );
 2898    "});
 2899
 2900    // handle auto-indent when there are multiple cursors on the same line
 2901    cx.set_state(indoc! {"
 2902        const a: B = (
 2903            c(
 2904        ˇ    ˇ
 2905        ˇ    )
 2906        );
 2907    "});
 2908    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        const a: B = (
 2911            c(
 2912                ˇ
 2913            ˇ)
 2914        );
 2915    "});
 2916}
 2917
 2918#[gpui::test]
 2919async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2920    init_test(cx, |settings| {
 2921        settings.defaults.tab_size = NonZeroU32::new(3)
 2922    });
 2923
 2924    let mut cx = EditorTestContext::new(cx).await;
 2925    cx.set_state(indoc! {"
 2926         ˇ
 2927        \t ˇ
 2928        \t  ˇ
 2929        \t   ˇ
 2930         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2931    "});
 2932
 2933    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2934    cx.assert_editor_state(indoc! {"
 2935           ˇ
 2936        \t   ˇ
 2937        \t   ˇ
 2938        \t      ˇ
 2939         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2940    "});
 2941}
 2942
 2943#[gpui::test]
 2944async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2945    init_test(cx, |settings| {
 2946        settings.defaults.tab_size = NonZeroU32::new(4)
 2947    });
 2948
 2949    let language = Arc::new(
 2950        Language::new(
 2951            LanguageConfig::default(),
 2952            Some(tree_sitter_rust::LANGUAGE.into()),
 2953        )
 2954        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2955        .unwrap(),
 2956    );
 2957
 2958    let mut cx = EditorTestContext::new(cx).await;
 2959    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2960    cx.set_state(indoc! {"
 2961        fn a() {
 2962            if b {
 2963        \t ˇc
 2964            }
 2965        }
 2966    "});
 2967
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        fn a() {
 2971            if b {
 2972                ˇc
 2973            }
 2974        }
 2975    "});
 2976}
 2977
 2978#[gpui::test]
 2979async fn test_indent_outdent(cx: &mut TestAppContext) {
 2980    init_test(cx, |settings| {
 2981        settings.defaults.tab_size = NonZeroU32::new(4);
 2982    });
 2983
 2984    let mut cx = EditorTestContext::new(cx).await;
 2985
 2986    cx.set_state(indoc! {"
 2987          «oneˇ» «twoˇ»
 2988        three
 2989         four
 2990    "});
 2991    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2992    cx.assert_editor_state(indoc! {"
 2993            «oneˇ» «twoˇ»
 2994        three
 2995         four
 2996    "});
 2997
 2998    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        «oneˇ» «twoˇ»
 3001        three
 3002         four
 3003    "});
 3004
 3005    // select across line ending
 3006    cx.set_state(indoc! {"
 3007        one two
 3008        t«hree
 3009        ˇ» four
 3010    "});
 3011    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3012    cx.assert_editor_state(indoc! {"
 3013        one two
 3014            t«hree
 3015        ˇ» four
 3016    "});
 3017
 3018    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        one two
 3021        t«hree
 3022        ˇ» four
 3023    "});
 3024
 3025    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3026    cx.set_state(indoc! {"
 3027        one two
 3028        ˇthree
 3029            four
 3030    "});
 3031    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3032    cx.assert_editor_state(indoc! {"
 3033        one two
 3034            ˇthree
 3035            four
 3036    "});
 3037
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        ˇ    three
 3041            four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046        ˇthree
 3047            four
 3048    "});
 3049}
 3050
 3051#[gpui::test]
 3052async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3053    init_test(cx, |settings| {
 3054        settings.defaults.hard_tabs = Some(true);
 3055    });
 3056
 3057    let mut cx = EditorTestContext::new(cx).await;
 3058
 3059    // select two ranges on one line
 3060    cx.set_state(indoc! {"
 3061        «oneˇ» «twoˇ»
 3062        three
 3063        four
 3064    "});
 3065    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        \t«oneˇ» «twoˇ»
 3068        three
 3069        four
 3070    "});
 3071    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3072    cx.assert_editor_state(indoc! {"
 3073        \t\t«oneˇ» «twoˇ»
 3074        three
 3075        four
 3076    "});
 3077    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        \t«oneˇ» «twoˇ»
 3080        three
 3081        four
 3082    "});
 3083    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3084    cx.assert_editor_state(indoc! {"
 3085        «oneˇ» «twoˇ»
 3086        three
 3087        four
 3088    "});
 3089
 3090    // select across a line ending
 3091    cx.set_state(indoc! {"
 3092        one two
 3093        t«hree
 3094        ˇ»four
 3095    "});
 3096    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3097    cx.assert_editor_state(indoc! {"
 3098        one two
 3099        \tt«hree
 3100        ˇ»four
 3101    "});
 3102    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3103    cx.assert_editor_state(indoc! {"
 3104        one two
 3105        \t\tt«hree
 3106        ˇ»four
 3107    "});
 3108    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        one two
 3111        \tt«hree
 3112        ˇ»four
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        one two
 3117        t«hree
 3118        ˇ»four
 3119    "});
 3120
 3121    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3122    cx.set_state(indoc! {"
 3123        one two
 3124        ˇthree
 3125        four
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        one two
 3130        ˇthree
 3131        four
 3132    "});
 3133    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3134    cx.assert_editor_state(indoc! {"
 3135        one two
 3136        \tˇthree
 3137        four
 3138    "});
 3139    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3140    cx.assert_editor_state(indoc! {"
 3141        one two
 3142        ˇthree
 3143        four
 3144    "});
 3145}
 3146
 3147#[gpui::test]
 3148fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3149    init_test(cx, |settings| {
 3150        settings.languages.extend([
 3151            (
 3152                "TOML".into(),
 3153                LanguageSettingsContent {
 3154                    tab_size: NonZeroU32::new(2),
 3155                    ..Default::default()
 3156                },
 3157            ),
 3158            (
 3159                "Rust".into(),
 3160                LanguageSettingsContent {
 3161                    tab_size: NonZeroU32::new(4),
 3162                    ..Default::default()
 3163                },
 3164            ),
 3165        ]);
 3166    });
 3167
 3168    let toml_language = Arc::new(Language::new(
 3169        LanguageConfig {
 3170            name: "TOML".into(),
 3171            ..Default::default()
 3172        },
 3173        None,
 3174    ));
 3175    let rust_language = Arc::new(Language::new(
 3176        LanguageConfig {
 3177            name: "Rust".into(),
 3178            ..Default::default()
 3179        },
 3180        None,
 3181    ));
 3182
 3183    let toml_buffer =
 3184        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3185    let rust_buffer =
 3186        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3187    let multibuffer = cx.new(|cx| {
 3188        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3189        multibuffer.push_excerpts(
 3190            toml_buffer.clone(),
 3191            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3192            cx,
 3193        );
 3194        multibuffer.push_excerpts(
 3195            rust_buffer.clone(),
 3196            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3197            cx,
 3198        );
 3199        multibuffer
 3200    });
 3201
 3202    cx.add_window(|window, cx| {
 3203        let mut editor = build_editor(multibuffer, window, cx);
 3204
 3205        assert_eq!(
 3206            editor.text(cx),
 3207            indoc! {"
 3208                a = 1
 3209                b = 2
 3210
 3211                const c: usize = 3;
 3212            "}
 3213        );
 3214
 3215        select_ranges(
 3216            &mut editor,
 3217            indoc! {"
 3218                «aˇ» = 1
 3219                b = 2
 3220
 3221                «const c:ˇ» usize = 3;
 3222            "},
 3223            window,
 3224            cx,
 3225        );
 3226
 3227        editor.tab(&Tab, window, cx);
 3228        assert_text_with_selections(
 3229            &mut editor,
 3230            indoc! {"
 3231                  «aˇ» = 1
 3232                b = 2
 3233
 3234                    «const c:ˇ» usize = 3;
 3235            "},
 3236            cx,
 3237        );
 3238        editor.backtab(&Backtab, window, cx);
 3239        assert_text_with_selections(
 3240            &mut editor,
 3241            indoc! {"
 3242                «aˇ» = 1
 3243                b = 2
 3244
 3245                «const c:ˇ» usize = 3;
 3246            "},
 3247            cx,
 3248        );
 3249
 3250        editor
 3251    });
 3252}
 3253
 3254#[gpui::test]
 3255async fn test_backspace(cx: &mut TestAppContext) {
 3256    init_test(cx, |_| {});
 3257
 3258    let mut cx = EditorTestContext::new(cx).await;
 3259
 3260    // Basic backspace
 3261    cx.set_state(indoc! {"
 3262        onˇe two three
 3263        fou«rˇ» five six
 3264        seven «ˇeight nine
 3265        »ten
 3266    "});
 3267    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        oˇe two three
 3270        fouˇ five six
 3271        seven ˇten
 3272    "});
 3273
 3274    // Test backspace inside and around indents
 3275    cx.set_state(indoc! {"
 3276        zero
 3277            ˇone
 3278                ˇtwo
 3279            ˇ ˇ ˇ  three
 3280        ˇ  ˇ  four
 3281    "});
 3282    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3283    cx.assert_editor_state(indoc! {"
 3284        zero
 3285        ˇone
 3286            ˇtwo
 3287        ˇ  threeˇ  four
 3288    "});
 3289}
 3290
 3291#[gpui::test]
 3292async fn test_delete(cx: &mut TestAppContext) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296    cx.set_state(indoc! {"
 3297        onˇe two three
 3298        fou«rˇ» five six
 3299        seven «ˇeight nine
 3300        »ten
 3301    "});
 3302    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3303    cx.assert_editor_state(indoc! {"
 3304        onˇ two three
 3305        fouˇ five six
 3306        seven ˇten
 3307    "});
 3308}
 3309
 3310#[gpui::test]
 3311fn test_delete_line(cx: &mut TestAppContext) {
 3312    init_test(cx, |_| {});
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3322                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3323                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3324            ])
 3325        });
 3326        editor.delete_line(&DeleteLine, window, cx);
 3327        assert_eq!(editor.display_text(cx), "ghi");
 3328        assert_eq!(
 3329            editor.selections.display_ranges(cx),
 3330            vec![
 3331                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3332                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3333            ]
 3334        );
 3335    });
 3336
 3337    let editor = cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3339        build_editor(buffer, window, cx)
 3340    });
 3341    _ = editor.update(cx, |editor, window, cx| {
 3342        editor.change_selections(None, window, cx, |s| {
 3343            s.select_display_ranges([
 3344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3345            ])
 3346        });
 3347        editor.delete_line(&DeleteLine, window, cx);
 3348        assert_eq!(editor.display_text(cx), "ghi\n");
 3349        assert_eq!(
 3350            editor.selections.display_ranges(cx),
 3351            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3352        );
 3353    });
 3354}
 3355
 3356#[gpui::test]
 3357fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3358    init_test(cx, |_| {});
 3359
 3360    cx.add_window(|window, cx| {
 3361        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3362        let mut editor = build_editor(buffer.clone(), window, cx);
 3363        let buffer = buffer.read(cx).as_singleton().unwrap();
 3364
 3365        assert_eq!(
 3366            editor.selections.ranges::<Point>(cx),
 3367            &[Point::new(0, 0)..Point::new(0, 0)]
 3368        );
 3369
 3370        // When on single line, replace newline at end by space
 3371        editor.join_lines(&JoinLines, window, cx);
 3372        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3373        assert_eq!(
 3374            editor.selections.ranges::<Point>(cx),
 3375            &[Point::new(0, 3)..Point::new(0, 3)]
 3376        );
 3377
 3378        // When multiple lines are selected, remove newlines that are spanned by the selection
 3379        editor.change_selections(None, window, cx, |s| {
 3380            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3381        });
 3382        editor.join_lines(&JoinLines, window, cx);
 3383        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3384        assert_eq!(
 3385            editor.selections.ranges::<Point>(cx),
 3386            &[Point::new(0, 11)..Point::new(0, 11)]
 3387        );
 3388
 3389        // Undo should be transactional
 3390        editor.undo(&Undo, window, cx);
 3391        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3392        assert_eq!(
 3393            editor.selections.ranges::<Point>(cx),
 3394            &[Point::new(0, 5)..Point::new(2, 2)]
 3395        );
 3396
 3397        // When joining an empty line don't insert a space
 3398        editor.change_selections(None, window, cx, |s| {
 3399            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3400        });
 3401        editor.join_lines(&JoinLines, window, cx);
 3402        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3403        assert_eq!(
 3404            editor.selections.ranges::<Point>(cx),
 3405            [Point::new(2, 3)..Point::new(2, 3)]
 3406        );
 3407
 3408        // We can remove trailing newlines
 3409        editor.join_lines(&JoinLines, window, cx);
 3410        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3411        assert_eq!(
 3412            editor.selections.ranges::<Point>(cx),
 3413            [Point::new(2, 3)..Point::new(2, 3)]
 3414        );
 3415
 3416        // We don't blow up on the last line
 3417        editor.join_lines(&JoinLines, window, cx);
 3418        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3419        assert_eq!(
 3420            editor.selections.ranges::<Point>(cx),
 3421            [Point::new(2, 3)..Point::new(2, 3)]
 3422        );
 3423
 3424        // reset to test indentation
 3425        editor.buffer.update(cx, |buffer, cx| {
 3426            buffer.edit(
 3427                [
 3428                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3429                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3430                ],
 3431                None,
 3432                cx,
 3433            )
 3434        });
 3435
 3436        // We remove any leading spaces
 3437        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3438        editor.change_selections(None, window, cx, |s| {
 3439            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3440        });
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3443
 3444        // We don't insert a space for a line containing only spaces
 3445        editor.join_lines(&JoinLines, window, cx);
 3446        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3447
 3448        // We ignore any leading tabs
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3451
 3452        editor
 3453    });
 3454}
 3455
 3456#[gpui::test]
 3457fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    cx.add_window(|window, cx| {
 3461        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3462        let mut editor = build_editor(buffer.clone(), window, cx);
 3463        let buffer = buffer.read(cx).as_singleton().unwrap();
 3464
 3465        editor.change_selections(None, window, cx, |s| {
 3466            s.select_ranges([
 3467                Point::new(0, 2)..Point::new(1, 1),
 3468                Point::new(1, 2)..Point::new(1, 2),
 3469                Point::new(3, 1)..Point::new(3, 2),
 3470            ])
 3471        });
 3472
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3475
 3476        assert_eq!(
 3477            editor.selections.ranges::<Point>(cx),
 3478            [
 3479                Point::new(0, 7)..Point::new(0, 7),
 3480                Point::new(1, 3)..Point::new(1, 3)
 3481            ]
 3482        );
 3483        editor
 3484    });
 3485}
 3486
 3487#[gpui::test]
 3488async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3489    init_test(cx, |_| {});
 3490
 3491    let mut cx = EditorTestContext::new(cx).await;
 3492
 3493    let diff_base = r#"
 3494        Line 0
 3495        Line 1
 3496        Line 2
 3497        Line 3
 3498        "#
 3499    .unindent();
 3500
 3501    cx.set_state(
 3502        &r#"
 3503        ˇLine 0
 3504        Line 1
 3505        Line 2
 3506        Line 3
 3507        "#
 3508        .unindent(),
 3509    );
 3510
 3511    cx.set_head_text(&diff_base);
 3512    executor.run_until_parked();
 3513
 3514    // Join lines
 3515    cx.update_editor(|editor, window, cx| {
 3516        editor.join_lines(&JoinLines, window, cx);
 3517    });
 3518    executor.run_until_parked();
 3519
 3520    cx.assert_editor_state(
 3521        &r#"
 3522        Line 0ˇ Line 1
 3523        Line 2
 3524        Line 3
 3525        "#
 3526        .unindent(),
 3527    );
 3528    // Join again
 3529    cx.update_editor(|editor, window, cx| {
 3530        editor.join_lines(&JoinLines, window, cx);
 3531    });
 3532    executor.run_until_parked();
 3533
 3534    cx.assert_editor_state(
 3535        &r#"
 3536        Line 0 Line 1ˇ Line 2
 3537        Line 3
 3538        "#
 3539        .unindent(),
 3540    );
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_custom_newlines_cause_no_false_positive_diffs(
 3545    executor: BackgroundExecutor,
 3546    cx: &mut TestAppContext,
 3547) {
 3548    init_test(cx, |_| {});
 3549    let mut cx = EditorTestContext::new(cx).await;
 3550    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3551    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3552    executor.run_until_parked();
 3553
 3554    cx.update_editor(|editor, window, cx| {
 3555        let snapshot = editor.snapshot(window, cx);
 3556        assert_eq!(
 3557            snapshot
 3558                .buffer_snapshot
 3559                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3560                .collect::<Vec<_>>(),
 3561            Vec::new(),
 3562            "Should not have any diffs for files with custom newlines"
 3563        );
 3564    });
 3565}
 3566
 3567#[gpui::test]
 3568async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3569    init_test(cx, |_| {});
 3570
 3571    let mut cx = EditorTestContext::new(cx).await;
 3572
 3573    // Test sort_lines_case_insensitive()
 3574    cx.set_state(indoc! {"
 3575        «z
 3576        y
 3577        x
 3578        Z
 3579        Y
 3580        Xˇ»
 3581    "});
 3582    cx.update_editor(|e, window, cx| {
 3583        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «x
 3587        X
 3588        y
 3589        Y
 3590        z
 3591        Zˇ»
 3592    "});
 3593
 3594    // Test reverse_lines()
 3595    cx.set_state(indoc! {"
 3596        «5
 3597        4
 3598        3
 3599        2
 3600        1ˇ»
 3601    "});
 3602    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «1
 3605        2
 3606        3
 3607        4
 3608        5ˇ»
 3609    "});
 3610
 3611    // Skip testing shuffle_line()
 3612
 3613    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3614    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3615
 3616    // Don't manipulate when cursor is on single line, but expand the selection
 3617    cx.set_state(indoc! {"
 3618        ddˇdd
 3619        ccc
 3620        bb
 3621        a
 3622    "});
 3623    cx.update_editor(|e, window, cx| {
 3624        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3625    });
 3626    cx.assert_editor_state(indoc! {"
 3627        «ddddˇ»
 3628        ccc
 3629        bb
 3630        a
 3631    "});
 3632
 3633    // Basic manipulate case
 3634    // Start selection moves to column 0
 3635    // End of selection shrinks to fit shorter line
 3636    cx.set_state(indoc! {"
 3637        dd«d
 3638        ccc
 3639        bb
 3640        aaaaaˇ»
 3641    "});
 3642    cx.update_editor(|e, window, cx| {
 3643        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «aaaaa
 3647        bb
 3648        ccc
 3649        dddˇ»
 3650    "});
 3651
 3652    // Manipulate case with newlines
 3653    cx.set_state(indoc! {"
 3654        dd«d
 3655        ccc
 3656
 3657        bb
 3658        aaaaa
 3659
 3660        ˇ»
 3661    "});
 3662    cx.update_editor(|e, window, cx| {
 3663        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3664    });
 3665    cx.assert_editor_state(indoc! {"
 3666        «
 3667
 3668        aaaaa
 3669        bb
 3670        ccc
 3671        dddˇ»
 3672
 3673    "});
 3674
 3675    // Adding new line
 3676    cx.set_state(indoc! {"
 3677        aa«a
 3678        bbˇ»b
 3679    "});
 3680    cx.update_editor(|e, window, cx| {
 3681        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3682    });
 3683    cx.assert_editor_state(indoc! {"
 3684        «aaa
 3685        bbb
 3686        added_lineˇ»
 3687    "});
 3688
 3689    // Removing line
 3690    cx.set_state(indoc! {"
 3691        aa«a
 3692        bbbˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.manipulate_lines(window, cx, |lines| {
 3696            lines.pop();
 3697        })
 3698    });
 3699    cx.assert_editor_state(indoc! {"
 3700        «aaaˇ»
 3701    "});
 3702
 3703    // Removing all lines
 3704    cx.set_state(indoc! {"
 3705        aa«a
 3706        bbbˇ»
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.manipulate_lines(window, cx, |lines| {
 3710            lines.drain(..);
 3711        })
 3712    });
 3713    cx.assert_editor_state(indoc! {"
 3714        ˇ
 3715    "});
 3716}
 3717
 3718#[gpui::test]
 3719async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3720    init_test(cx, |_| {});
 3721
 3722    let mut cx = EditorTestContext::new(cx).await;
 3723
 3724    // Consider continuous selection as single selection
 3725    cx.set_state(indoc! {"
 3726        Aaa«aa
 3727        cˇ»c«c
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «Aaaaa
 3736        ccc
 3737        bb
 3738        aaaaaˇ»
 3739    "});
 3740
 3741    cx.set_state(indoc! {"
 3742        Aaa«aa
 3743        cˇ»c«c
 3744        bb
 3745        aaaˇ»aa
 3746    "});
 3747    cx.update_editor(|e, window, cx| {
 3748        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3749    });
 3750    cx.assert_editor_state(indoc! {"
 3751        «Aaaaa
 3752        ccc
 3753        bbˇ»
 3754    "});
 3755
 3756    // Consider non continuous selection as distinct dedup operations
 3757    cx.set_state(indoc! {"
 3758        «aaaaa
 3759        bb
 3760        aaaaa
 3761        aaaaaˇ»
 3762
 3763        aaa«aaˇ»
 3764    "});
 3765    cx.update_editor(|e, window, cx| {
 3766        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3767    });
 3768    cx.assert_editor_state(indoc! {"
 3769        «aaaaa
 3770        bbˇ»
 3771
 3772        «aaaaaˇ»
 3773    "});
 3774}
 3775
 3776#[gpui::test]
 3777async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3778    init_test(cx, |_| {});
 3779
 3780    let mut cx = EditorTestContext::new(cx).await;
 3781
 3782    cx.set_state(indoc! {"
 3783        «Aaa
 3784        aAa
 3785        Aaaˇ»
 3786    "});
 3787    cx.update_editor(|e, window, cx| {
 3788        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3789    });
 3790    cx.assert_editor_state(indoc! {"
 3791        «Aaa
 3792        aAaˇ»
 3793    "});
 3794
 3795    cx.set_state(indoc! {"
 3796        «Aaa
 3797        aAa
 3798        aaAˇ»
 3799    "});
 3800    cx.update_editor(|e, window, cx| {
 3801        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3802    });
 3803    cx.assert_editor_state(indoc! {"
 3804        «Aaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    // Manipulate with multiple selections on a single line
 3815    cx.set_state(indoc! {"
 3816        dd«dd
 3817        cˇ»c«c
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «aaaaa
 3826        bb
 3827        ccc
 3828        ddddˇ»
 3829    "});
 3830
 3831    // Manipulate with multiple disjoin selections
 3832    cx.set_state(indoc! {"
 3833 3834        4
 3835        3
 3836        2
 3837        1ˇ»
 3838
 3839        dd«dd
 3840        ccc
 3841        bb
 3842        aaaˇ»aa
 3843    "});
 3844    cx.update_editor(|e, window, cx| {
 3845        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3846    });
 3847    cx.assert_editor_state(indoc! {"
 3848        «1
 3849        2
 3850        3
 3851        4
 3852        5ˇ»
 3853
 3854        «aaaaa
 3855        bb
 3856        ccc
 3857        ddddˇ»
 3858    "});
 3859
 3860    // Adding lines on each selection
 3861    cx.set_state(indoc! {"
 3862 3863        1ˇ»
 3864
 3865        bb«bb
 3866        aaaˇ»aa
 3867    "});
 3868    cx.update_editor(|e, window, cx| {
 3869        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2
 3873        1
 3874        added lineˇ»
 3875
 3876        «bbbb
 3877        aaaaa
 3878        added lineˇ»
 3879    "});
 3880
 3881    // Removing lines on each selection
 3882    cx.set_state(indoc! {"
 3883 3884        1ˇ»
 3885
 3886        bb«bb
 3887        aaaˇ»aa
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.manipulate_lines(window, cx, |lines| {
 3891            lines.pop();
 3892        })
 3893    });
 3894    cx.assert_editor_state(indoc! {"
 3895        «2ˇ»
 3896
 3897        «bbbbˇ»
 3898    "});
 3899}
 3900
 3901#[gpui::test]
 3902async fn test_toggle_case(cx: &mut TestAppContext) {
 3903    init_test(cx, |_| {});
 3904
 3905    let mut cx = EditorTestContext::new(cx).await;
 3906
 3907    // If all lower case -> upper case
 3908    cx.set_state(indoc! {"
 3909        «hello worldˇ»
 3910    "});
 3911    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3912    cx.assert_editor_state(indoc! {"
 3913        «HELLO WORLDˇ»
 3914    "});
 3915
 3916    // If all upper case -> lower case
 3917    cx.set_state(indoc! {"
 3918        «HELLO WORLDˇ»
 3919    "});
 3920    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3921    cx.assert_editor_state(indoc! {"
 3922        «hello worldˇ»
 3923    "});
 3924
 3925    // If any upper case characters are identified -> lower case
 3926    // This matches JetBrains IDEs
 3927    cx.set_state(indoc! {"
 3928        «hEllo worldˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «hello worldˇ»
 3933    "});
 3934}
 3935
 3936#[gpui::test]
 3937async fn test_manipulate_text(cx: &mut TestAppContext) {
 3938    init_test(cx, |_| {});
 3939
 3940    let mut cx = EditorTestContext::new(cx).await;
 3941
 3942    // Test convert_to_upper_case()
 3943    cx.set_state(indoc! {"
 3944        «hello worldˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3947    cx.assert_editor_state(indoc! {"
 3948        «HELLO WORLDˇ»
 3949    "});
 3950
 3951    // Test convert_to_lower_case()
 3952    cx.set_state(indoc! {"
 3953        «HELLO WORLDˇ»
 3954    "});
 3955    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3956    cx.assert_editor_state(indoc! {"
 3957        «hello worldˇ»
 3958    "});
 3959
 3960    // Test multiple line, single selection case
 3961    cx.set_state(indoc! {"
 3962        «The quick brown
 3963        fox jumps over
 3964        the lazy dogˇ»
 3965    "});
 3966    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3967    cx.assert_editor_state(indoc! {"
 3968        «The Quick Brown
 3969        Fox Jumps Over
 3970        The Lazy Dogˇ»
 3971    "});
 3972
 3973    // Test multiple line, single selection case
 3974    cx.set_state(indoc! {"
 3975        «The quick brown
 3976        fox jumps over
 3977        the lazy dogˇ»
 3978    "});
 3979    cx.update_editor(|e, window, cx| {
 3980        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3981    });
 3982    cx.assert_editor_state(indoc! {"
 3983        «TheQuickBrown
 3984        FoxJumpsOver
 3985        TheLazyDogˇ»
 3986    "});
 3987
 3988    // From here on out, test more complex cases of manipulate_text()
 3989
 3990    // Test no selection case - should affect words cursors are in
 3991    // Cursor at beginning, middle, and end of word
 3992    cx.set_state(indoc! {"
 3993        ˇhello big beauˇtiful worldˇ
 3994    "});
 3995    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3996    cx.assert_editor_state(indoc! {"
 3997        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3998    "});
 3999
 4000    // Test multiple selections on a single line and across multiple lines
 4001    cx.set_state(indoc! {"
 4002        «Theˇ» quick «brown
 4003        foxˇ» jumps «overˇ»
 4004        the «lazyˇ» dog
 4005    "});
 4006    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4007    cx.assert_editor_state(indoc! {"
 4008        «THEˇ» quick «BROWN
 4009        FOXˇ» jumps «OVERˇ»
 4010        the «LAZYˇ» dog
 4011    "});
 4012
 4013    // Test case where text length grows
 4014    cx.set_state(indoc! {"
 4015        «tschüߡ»
 4016    "});
 4017    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4018    cx.assert_editor_state(indoc! {"
 4019        «TSCHÜSSˇ»
 4020    "});
 4021
 4022    // Test to make sure we don't crash when text shrinks
 4023    cx.set_state(indoc! {"
 4024        aaa_bbbˇ
 4025    "});
 4026    cx.update_editor(|e, window, cx| {
 4027        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4028    });
 4029    cx.assert_editor_state(indoc! {"
 4030        «aaaBbbˇ»
 4031    "});
 4032
 4033    // Test to make sure we all aware of the fact that each word can grow and shrink
 4034    // Final selections should be aware of this fact
 4035    cx.set_state(indoc! {"
 4036        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4037    "});
 4038    cx.update_editor(|e, window, cx| {
 4039        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4040    });
 4041    cx.assert_editor_state(indoc! {"
 4042        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4043    "});
 4044
 4045    cx.set_state(indoc! {"
 4046        «hElLo, WoRld!ˇ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| {
 4049        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4050    });
 4051    cx.assert_editor_state(indoc! {"
 4052        «HeLlO, wOrLD!ˇ»
 4053    "});
 4054}
 4055
 4056#[gpui::test]
 4057fn test_duplicate_line(cx: &mut TestAppContext) {
 4058    init_test(cx, |_| {});
 4059
 4060    let editor = cx.add_window(|window, cx| {
 4061        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4062        build_editor(buffer, window, cx)
 4063    });
 4064    _ = editor.update(cx, |editor, window, cx| {
 4065        editor.change_selections(None, window, cx, |s| {
 4066            s.select_display_ranges([
 4067                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4068                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4069                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4070                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4071            ])
 4072        });
 4073        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4074        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4075        assert_eq!(
 4076            editor.selections.display_ranges(cx),
 4077            vec![
 4078                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4079                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4080                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4081                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4082            ]
 4083        );
 4084    });
 4085
 4086    let editor = cx.add_window(|window, cx| {
 4087        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4088        build_editor(buffer, window, cx)
 4089    });
 4090    _ = editor.update(cx, |editor, window, cx| {
 4091        editor.change_selections(None, window, cx, |s| {
 4092            s.select_display_ranges([
 4093                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4094                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4095            ])
 4096        });
 4097        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4098        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4099        assert_eq!(
 4100            editor.selections.display_ranges(cx),
 4101            vec![
 4102                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4103                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4104            ]
 4105        );
 4106    });
 4107
 4108    // With `move_upwards` the selections stay in place, except for
 4109    // the lines inserted above them
 4110    let editor = cx.add_window(|window, cx| {
 4111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4112        build_editor(buffer, window, cx)
 4113    });
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.change_selections(None, window, cx, |s| {
 4116            s.select_display_ranges([
 4117                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4118                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4119                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4120                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4121            ])
 4122        });
 4123        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4124        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4125        assert_eq!(
 4126            editor.selections.display_ranges(cx),
 4127            vec![
 4128                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4129                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4130                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        editor.change_selections(None, window, cx, |s| {
 4142            s.select_display_ranges([
 4143                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4144                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4145            ])
 4146        });
 4147        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4148        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4149        assert_eq!(
 4150            editor.selections.display_ranges(cx),
 4151            vec![
 4152                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4153                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4154            ]
 4155        );
 4156    });
 4157
 4158    let editor = cx.add_window(|window, cx| {
 4159        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4160        build_editor(buffer, window, cx)
 4161    });
 4162    _ = editor.update(cx, |editor, window, cx| {
 4163        editor.change_selections(None, window, cx, |s| {
 4164            s.select_display_ranges([
 4165                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4166                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4167            ])
 4168        });
 4169        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4170        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4171        assert_eq!(
 4172            editor.selections.display_ranges(cx),
 4173            vec![
 4174                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4175                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4176            ]
 4177        );
 4178    });
 4179}
 4180
 4181#[gpui::test]
 4182fn test_move_line_up_down(cx: &mut TestAppContext) {
 4183    init_test(cx, |_| {});
 4184
 4185    let editor = cx.add_window(|window, cx| {
 4186        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4187        build_editor(buffer, window, cx)
 4188    });
 4189    _ = editor.update(cx, |editor, window, cx| {
 4190        editor.fold_creases(
 4191            vec![
 4192                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4193                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4194                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4195            ],
 4196            true,
 4197            window,
 4198            cx,
 4199        );
 4200        editor.change_selections(None, window, cx, |s| {
 4201            s.select_display_ranges([
 4202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4206            ])
 4207        });
 4208        assert_eq!(
 4209            editor.display_text(cx),
 4210            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4211        );
 4212
 4213        editor.move_line_up(&MoveLineUp, window, cx);
 4214        assert_eq!(
 4215            editor.display_text(cx),
 4216            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4217        );
 4218        assert_eq!(
 4219            editor.selections.display_ranges(cx),
 4220            vec![
 4221                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4222                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4223                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4224                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4225            ]
 4226        );
 4227    });
 4228
 4229    _ = editor.update(cx, |editor, window, cx| {
 4230        editor.move_line_down(&MoveLineDown, window, cx);
 4231        assert_eq!(
 4232            editor.display_text(cx),
 4233            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4234        );
 4235        assert_eq!(
 4236            editor.selections.display_ranges(cx),
 4237            vec![
 4238                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4239                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4240                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4241                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4242            ]
 4243        );
 4244    });
 4245
 4246    _ = editor.update(cx, |editor, window, cx| {
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248        assert_eq!(
 4249            editor.display_text(cx),
 4250            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4251        );
 4252        assert_eq!(
 4253            editor.selections.display_ranges(cx),
 4254            vec![
 4255                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4257                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4258                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4259            ]
 4260        );
 4261    });
 4262
 4263    _ = editor.update(cx, |editor, window, cx| {
 4264        editor.move_line_up(&MoveLineUp, window, cx);
 4265        assert_eq!(
 4266            editor.display_text(cx),
 4267            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4268        );
 4269        assert_eq!(
 4270            editor.selections.display_ranges(cx),
 4271            vec![
 4272                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4273                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4274                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4275                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4276            ]
 4277        );
 4278    });
 4279}
 4280
 4281#[gpui::test]
 4282fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4283    init_test(cx, |_| {});
 4284
 4285    let editor = cx.add_window(|window, cx| {
 4286        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4287        build_editor(buffer, window, cx)
 4288    });
 4289    _ = editor.update(cx, |editor, window, cx| {
 4290        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4291        editor.insert_blocks(
 4292            [BlockProperties {
 4293                style: BlockStyle::Fixed,
 4294                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4295                height: Some(1),
 4296                render: Arc::new(|_| div().into_any()),
 4297                priority: 0,
 4298            }],
 4299            Some(Autoscroll::fit()),
 4300            cx,
 4301        );
 4302        editor.change_selections(None, window, cx, |s| {
 4303            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4304        });
 4305        editor.move_line_down(&MoveLineDown, window, cx);
 4306    });
 4307}
 4308
 4309#[gpui::test]
 4310async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4311    init_test(cx, |_| {});
 4312
 4313    let mut cx = EditorTestContext::new(cx).await;
 4314    cx.set_state(
 4315        &"
 4316            ˇzero
 4317            one
 4318            two
 4319            three
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Create a four-line block that replaces three lines of text.
 4327    cx.update_editor(|editor, window, cx| {
 4328        let snapshot = editor.snapshot(window, cx);
 4329        let snapshot = &snapshot.buffer_snapshot;
 4330        let placement = BlockPlacement::Replace(
 4331            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4332        );
 4333        editor.insert_blocks(
 4334            [BlockProperties {
 4335                placement,
 4336                height: Some(4),
 4337                style: BlockStyle::Sticky,
 4338                render: Arc::new(|_| gpui::div().into_any_element()),
 4339                priority: 0,
 4340            }],
 4341            None,
 4342            cx,
 4343        );
 4344    });
 4345
 4346    // Move down so that the cursor touches the block.
 4347    cx.update_editor(|editor, window, cx| {
 4348        editor.move_down(&Default::default(), window, cx);
 4349    });
 4350    cx.assert_editor_state(
 4351        &"
 4352            zero
 4353            «one
 4354            two
 4355            threeˇ»
 4356            four
 4357            five
 4358        "
 4359        .unindent(),
 4360    );
 4361
 4362    // Move down past the block.
 4363    cx.update_editor(|editor, window, cx| {
 4364        editor.move_down(&Default::default(), window, cx);
 4365    });
 4366    cx.assert_editor_state(
 4367        &"
 4368            zero
 4369            one
 4370            two
 4371            three
 4372            ˇfour
 4373            five
 4374        "
 4375        .unindent(),
 4376    );
 4377}
 4378
 4379#[gpui::test]
 4380fn test_transpose(cx: &mut TestAppContext) {
 4381    init_test(cx, |_| {});
 4382
 4383    _ = cx.add_window(|window, cx| {
 4384        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4385        editor.set_style(EditorStyle::default(), window, cx);
 4386        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4387        editor.transpose(&Default::default(), window, cx);
 4388        assert_eq!(editor.text(cx), "bac");
 4389        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4390
 4391        editor.transpose(&Default::default(), window, cx);
 4392        assert_eq!(editor.text(cx), "bca");
 4393        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4394
 4395        editor.transpose(&Default::default(), window, cx);
 4396        assert_eq!(editor.text(cx), "bac");
 4397        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4398
 4399        editor
 4400    });
 4401
 4402    _ = cx.add_window(|window, cx| {
 4403        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4404        editor.set_style(EditorStyle::default(), window, cx);
 4405        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "acb\nde");
 4408        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4409
 4410        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "acbd\ne");
 4413        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "acbde\n");
 4417        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4418
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "acbd\ne");
 4421        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4422
 4423        editor
 4424    });
 4425
 4426    _ = cx.add_window(|window, cx| {
 4427        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4428        editor.set_style(EditorStyle::default(), window, cx);
 4429        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4430        editor.transpose(&Default::default(), window, cx);
 4431        assert_eq!(editor.text(cx), "bacd\ne");
 4432        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4433
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "bcade\n");
 4436        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4437
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "bcda\ne");
 4440        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4441
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "bcade\n");
 4444        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4445
 4446        editor.transpose(&Default::default(), window, cx);
 4447        assert_eq!(editor.text(cx), "bcaed\n");
 4448        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4449
 4450        editor
 4451    });
 4452
 4453    _ = cx.add_window(|window, cx| {
 4454        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4455        editor.set_style(EditorStyle::default(), window, cx);
 4456        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4457        editor.transpose(&Default::default(), window, cx);
 4458        assert_eq!(editor.text(cx), "🏀🍐✋");
 4459        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4460
 4461        editor.transpose(&Default::default(), window, cx);
 4462        assert_eq!(editor.text(cx), "🏀✋🍐");
 4463        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4464
 4465        editor.transpose(&Default::default(), window, cx);
 4466        assert_eq!(editor.text(cx), "🏀🍐✋");
 4467        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4468
 4469        editor
 4470    });
 4471}
 4472
 4473#[gpui::test]
 4474async fn test_rewrap(cx: &mut TestAppContext) {
 4475    init_test(cx, |settings| {
 4476        settings.languages.extend([
 4477            (
 4478                "Markdown".into(),
 4479                LanguageSettingsContent {
 4480                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4481                    ..Default::default()
 4482                },
 4483            ),
 4484            (
 4485                "Plain Text".into(),
 4486                LanguageSettingsContent {
 4487                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4488                    ..Default::default()
 4489                },
 4490            ),
 4491        ])
 4492    });
 4493
 4494    let mut cx = EditorTestContext::new(cx).await;
 4495
 4496    let language_with_c_comments = Arc::new(Language::new(
 4497        LanguageConfig {
 4498            line_comments: vec!["// ".into()],
 4499            ..LanguageConfig::default()
 4500        },
 4501        None,
 4502    ));
 4503    let language_with_pound_comments = Arc::new(Language::new(
 4504        LanguageConfig {
 4505            line_comments: vec!["# ".into()],
 4506            ..LanguageConfig::default()
 4507        },
 4508        None,
 4509    ));
 4510    let markdown_language = Arc::new(Language::new(
 4511        LanguageConfig {
 4512            name: "Markdown".into(),
 4513            ..LanguageConfig::default()
 4514        },
 4515        None,
 4516    ));
 4517    let language_with_doc_comments = Arc::new(Language::new(
 4518        LanguageConfig {
 4519            line_comments: vec!["// ".into(), "/// ".into()],
 4520            ..LanguageConfig::default()
 4521        },
 4522        Some(tree_sitter_rust::LANGUAGE.into()),
 4523    ));
 4524
 4525    let plaintext_language = Arc::new(Language::new(
 4526        LanguageConfig {
 4527            name: "Plain Text".into(),
 4528            ..LanguageConfig::default()
 4529        },
 4530        None,
 4531    ));
 4532
 4533    assert_rewrap(
 4534        indoc! {"
 4535            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4536        "},
 4537        indoc! {"
 4538            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4539            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4540            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4541            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4542            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4543            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4544            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4545            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4546            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4547            // porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        language_with_c_comments.clone(),
 4550        &mut cx,
 4551    );
 4552
 4553    // Test that rewrapping works inside of a selection
 4554    assert_rewrap(
 4555        indoc! {"
 4556            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4557        "},
 4558        indoc! {"
 4559            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4560            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4561            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4562            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4563            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4564            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4565            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4566            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4567            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4568            // porttitor id. Aliquam id accumsan eros.ˇ»
 4569        "},
 4570        language_with_c_comments.clone(),
 4571        &mut cx,
 4572    );
 4573
 4574    // Test that cursors that expand to the same region are collapsed.
 4575    assert_rewrap(
 4576        indoc! {"
 4577            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4578            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4579            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4580            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4581        "},
 4582        indoc! {"
 4583            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4584            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4585            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4586            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4587            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4588            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4589            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4590            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4591            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4592            // porttitor id. Aliquam id accumsan eros.
 4593        "},
 4594        language_with_c_comments.clone(),
 4595        &mut cx,
 4596    );
 4597
 4598    // Test that non-contiguous selections are treated separately.
 4599    assert_rewrap(
 4600        indoc! {"
 4601            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4602            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4603            //
 4604            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4605            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4606        "},
 4607        indoc! {"
 4608            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4609            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4610            // auctor, eu lacinia sapien scelerisque.
 4611            //
 4612            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4613            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4614            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4615            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4616            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4617            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4618            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4619        "},
 4620        language_with_c_comments.clone(),
 4621        &mut cx,
 4622    );
 4623
 4624    // Test that different comment prefixes are supported.
 4625    assert_rewrap(
 4626        indoc! {"
 4627            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4628        "},
 4629        indoc! {"
 4630            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4631            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4632            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4633            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4634            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4635            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4636            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4637            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4638            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4639            # accumsan eros.
 4640        "},
 4641        language_with_pound_comments.clone(),
 4642        &mut cx,
 4643    );
 4644
 4645    // Test that rewrapping is ignored outside of comments in most languages.
 4646    assert_rewrap(
 4647        indoc! {"
 4648            /// Adds two numbers.
 4649            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4650            fn add(a: u32, b: u32) -> u32 {
 4651                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4652            }
 4653        "},
 4654        indoc! {"
 4655            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4656            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4657            fn add(a: u32, b: u32) -> u32 {
 4658                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4659            }
 4660        "},
 4661        language_with_doc_comments.clone(),
 4662        &mut cx,
 4663    );
 4664
 4665    // Test that rewrapping works in Markdown and Plain Text languages.
 4666    assert_rewrap(
 4667        indoc! {"
 4668            # Hello
 4669
 4670            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4671        "},
 4672        indoc! {"
 4673            # Hello
 4674
 4675            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4676            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4677            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4678            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4679            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4680            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4681            Integer sit amet scelerisque nisi.
 4682        "},
 4683        markdown_language,
 4684        &mut cx,
 4685    );
 4686
 4687    assert_rewrap(
 4688        indoc! {"
 4689            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4690        "},
 4691        indoc! {"
 4692            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4693            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4694            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4695            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4696            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4697            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4698            Integer sit amet scelerisque nisi.
 4699        "},
 4700        plaintext_language,
 4701        &mut cx,
 4702    );
 4703
 4704    // Test rewrapping unaligned comments in a selection.
 4705    assert_rewrap(
 4706        indoc! {"
 4707            fn foo() {
 4708                if true {
 4709            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4710            // Praesent semper egestas tellus id dignissim.ˇ»
 4711                    do_something();
 4712                } else {
 4713                    //
 4714                }
 4715            }
 4716        "},
 4717        indoc! {"
 4718            fn foo() {
 4719                if true {
 4720            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4721                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4722                    // egestas tellus id dignissim.ˇ»
 4723                    do_something();
 4724                } else {
 4725                    //
 4726                }
 4727            }
 4728        "},
 4729        language_with_doc_comments.clone(),
 4730        &mut cx,
 4731    );
 4732
 4733    assert_rewrap(
 4734        indoc! {"
 4735            fn foo() {
 4736                if true {
 4737            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4738            // Praesent semper egestas tellus id dignissim.»
 4739                    do_something();
 4740                } else {
 4741                    //
 4742                }
 4743
 4744            }
 4745        "},
 4746        indoc! {"
 4747            fn foo() {
 4748                if true {
 4749            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4750                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4751                    // egestas tellus id dignissim.»
 4752                    do_something();
 4753                } else {
 4754                    //
 4755                }
 4756
 4757            }
 4758        "},
 4759        language_with_doc_comments.clone(),
 4760        &mut cx,
 4761    );
 4762
 4763    #[track_caller]
 4764    fn assert_rewrap(
 4765        unwrapped_text: &str,
 4766        wrapped_text: &str,
 4767        language: Arc<Language>,
 4768        cx: &mut EditorTestContext,
 4769    ) {
 4770        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4771        cx.set_state(unwrapped_text);
 4772        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4773        cx.assert_editor_state(wrapped_text);
 4774    }
 4775}
 4776
 4777#[gpui::test]
 4778async fn test_hard_wrap(cx: &mut TestAppContext) {
 4779    init_test(cx, |_| {});
 4780    let mut cx = EditorTestContext::new(cx).await;
 4781
 4782    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4783    cx.update_editor(|editor, _, cx| {
 4784        editor.set_hard_wrap(Some(14), cx);
 4785    });
 4786
 4787    cx.set_state(indoc!(
 4788        "
 4789        one two three ˇ
 4790        "
 4791    ));
 4792    cx.simulate_input("four");
 4793    cx.run_until_parked();
 4794
 4795    cx.assert_editor_state(indoc!(
 4796        "
 4797        one two three
 4798        fourˇ
 4799        "
 4800    ));
 4801
 4802    cx.update_editor(|editor, window, cx| {
 4803        editor.newline(&Default::default(), window, cx);
 4804    });
 4805    cx.run_until_parked();
 4806    cx.assert_editor_state(indoc!(
 4807        "
 4808        one two three
 4809        four
 4810        ˇ
 4811        "
 4812    ));
 4813
 4814    cx.simulate_input("five");
 4815    cx.run_until_parked();
 4816    cx.assert_editor_state(indoc!(
 4817        "
 4818        one two three
 4819        four
 4820        fiveˇ
 4821        "
 4822    ));
 4823
 4824    cx.update_editor(|editor, window, cx| {
 4825        editor.newline(&Default::default(), window, cx);
 4826    });
 4827    cx.run_until_parked();
 4828    cx.simulate_input("# ");
 4829    cx.run_until_parked();
 4830    cx.assert_editor_state(indoc!(
 4831        "
 4832        one two three
 4833        four
 4834        five
 4835        # ˇ
 4836        "
 4837    ));
 4838
 4839    cx.update_editor(|editor, window, cx| {
 4840        editor.newline(&Default::default(), window, cx);
 4841    });
 4842    cx.run_until_parked();
 4843    cx.assert_editor_state(indoc!(
 4844        "
 4845        one two three
 4846        four
 4847        five
 4848        #\x20
 4849 4850        "
 4851    ));
 4852
 4853    cx.simulate_input(" 6");
 4854    cx.run_until_parked();
 4855    cx.assert_editor_state(indoc!(
 4856        "
 4857        one two three
 4858        four
 4859        five
 4860        #
 4861        # 6ˇ
 4862        "
 4863    ));
 4864}
 4865
 4866#[gpui::test]
 4867async fn test_clipboard(cx: &mut TestAppContext) {
 4868    init_test(cx, |_| {});
 4869
 4870    let mut cx = EditorTestContext::new(cx).await;
 4871
 4872    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4873    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4874    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4875
 4876    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4877    cx.set_state("two ˇfour ˇsix ˇ");
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4880
 4881    // Paste again but with only two cursors. Since the number of cursors doesn't
 4882    // match the number of slices in the clipboard, the entire clipboard text
 4883    // is pasted at each cursor.
 4884    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4885    cx.update_editor(|e, window, cx| {
 4886        e.handle_input("( ", window, cx);
 4887        e.paste(&Paste, window, cx);
 4888        e.handle_input(") ", window, cx);
 4889    });
 4890    cx.assert_editor_state(
 4891        &([
 4892            "( one✅ ",
 4893            "three ",
 4894            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4895            "three ",
 4896            "five ) ˇ",
 4897        ]
 4898        .join("\n")),
 4899    );
 4900
 4901    // Cut with three selections, one of which is full-line.
 4902    cx.set_state(indoc! {"
 4903        1«2ˇ»3
 4904        4ˇ567
 4905        «8ˇ»9"});
 4906    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4907    cx.assert_editor_state(indoc! {"
 4908        1ˇ3
 4909        ˇ9"});
 4910
 4911    // Paste with three selections, noticing how the copied selection that was full-line
 4912    // gets inserted before the second cursor.
 4913    cx.set_state(indoc! {"
 4914        1ˇ3
 4915 4916        «oˇ»ne"});
 4917    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        12ˇ3
 4920        4567
 4921 4922        8ˇne"});
 4923
 4924    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4925    cx.set_state(indoc! {"
 4926        The quick brown
 4927        fox juˇmps over
 4928        the lazy dog"});
 4929    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4930    assert_eq!(
 4931        cx.read_from_clipboard()
 4932            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4933        Some("fox jumps over\n".to_string())
 4934    );
 4935
 4936    // Paste with three selections, noticing how the copied full-line selection is inserted
 4937    // before the empty selections but replaces the selection that is non-empty.
 4938    cx.set_state(indoc! {"
 4939        Tˇhe quick brown
 4940        «foˇ»x jumps over
 4941        tˇhe lazy dog"});
 4942    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4943    cx.assert_editor_state(indoc! {"
 4944        fox jumps over
 4945        Tˇhe quick brown
 4946        fox jumps over
 4947        ˇx jumps over
 4948        fox jumps over
 4949        tˇhe lazy dog"});
 4950}
 4951
 4952#[gpui::test]
 4953async fn test_copy_trim(cx: &mut TestAppContext) {
 4954    init_test(cx, |_| {});
 4955
 4956    let mut cx = EditorTestContext::new(cx).await;
 4957    cx.set_state(
 4958        r#"            «for selection in selections.iter() {
 4959            let mut start = selection.start;
 4960            let mut end = selection.end;
 4961            let is_entire_line = selection.is_empty();
 4962            if is_entire_line {
 4963                start = Point::new(start.row, 0);ˇ»
 4964                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4965            }
 4966        "#,
 4967    );
 4968    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4969    assert_eq!(
 4970        cx.read_from_clipboard()
 4971            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4972        Some(
 4973            "for selection in selections.iter() {
 4974            let mut start = selection.start;
 4975            let mut end = selection.end;
 4976            let is_entire_line = selection.is_empty();
 4977            if is_entire_line {
 4978                start = Point::new(start.row, 0);"
 4979                .to_string()
 4980        ),
 4981        "Regular copying preserves all indentation selected",
 4982    );
 4983    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4984    assert_eq!(
 4985        cx.read_from_clipboard()
 4986            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4987        Some(
 4988            "for selection in selections.iter() {
 4989let mut start = selection.start;
 4990let mut end = selection.end;
 4991let is_entire_line = selection.is_empty();
 4992if is_entire_line {
 4993    start = Point::new(start.row, 0);"
 4994                .to_string()
 4995        ),
 4996        "Copying with stripping should strip all leading whitespaces"
 4997    );
 4998
 4999    cx.set_state(
 5000        r#"       «     for selection in selections.iter() {
 5001            let mut start = selection.start;
 5002            let mut end = selection.end;
 5003            let is_entire_line = selection.is_empty();
 5004            if is_entire_line {
 5005                start = Point::new(start.row, 0);ˇ»
 5006                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5007            }
 5008        "#,
 5009    );
 5010    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5011    assert_eq!(
 5012        cx.read_from_clipboard()
 5013            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5014        Some(
 5015            "     for selection in selections.iter() {
 5016            let mut start = selection.start;
 5017            let mut end = selection.end;
 5018            let is_entire_line = selection.is_empty();
 5019            if is_entire_line {
 5020                start = Point::new(start.row, 0);"
 5021                .to_string()
 5022        ),
 5023        "Regular copying preserves all indentation selected",
 5024    );
 5025    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5026    assert_eq!(
 5027        cx.read_from_clipboard()
 5028            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5029        Some(
 5030            "for selection in selections.iter() {
 5031let mut start = selection.start;
 5032let mut end = selection.end;
 5033let is_entire_line = selection.is_empty();
 5034if is_entire_line {
 5035    start = Point::new(start.row, 0);"
 5036                .to_string()
 5037        ),
 5038        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5039    );
 5040
 5041    cx.set_state(
 5042        r#"       «ˇ     for selection in selections.iter() {
 5043            let mut start = selection.start;
 5044            let mut end = selection.end;
 5045            let is_entire_line = selection.is_empty();
 5046            if is_entire_line {
 5047                start = Point::new(start.row, 0);»
 5048                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5049            }
 5050        "#,
 5051    );
 5052    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5053    assert_eq!(
 5054        cx.read_from_clipboard()
 5055            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5056        Some(
 5057            "     for selection in selections.iter() {
 5058            let mut start = selection.start;
 5059            let mut end = selection.end;
 5060            let is_entire_line = selection.is_empty();
 5061            if is_entire_line {
 5062                start = Point::new(start.row, 0);"
 5063                .to_string()
 5064        ),
 5065        "Regular copying for reverse selection works the same",
 5066    );
 5067    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5068    assert_eq!(
 5069        cx.read_from_clipboard()
 5070            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5071        Some(
 5072            "for selection in selections.iter() {
 5073let mut start = selection.start;
 5074let mut end = selection.end;
 5075let is_entire_line = selection.is_empty();
 5076if is_entire_line {
 5077    start = Point::new(start.row, 0);"
 5078                .to_string()
 5079        ),
 5080        "Copying with stripping for reverse selection works the same"
 5081    );
 5082
 5083    cx.set_state(
 5084        r#"            for selection «in selections.iter() {
 5085            let mut start = selection.start;
 5086            let mut end = selection.end;
 5087            let is_entire_line = selection.is_empty();
 5088            if is_entire_line {
 5089                start = Point::new(start.row, 0);ˇ»
 5090                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5091            }
 5092        "#,
 5093    );
 5094    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5095    assert_eq!(
 5096        cx.read_from_clipboard()
 5097            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5098        Some(
 5099            "in selections.iter() {
 5100            let mut start = selection.start;
 5101            let mut end = selection.end;
 5102            let is_entire_line = selection.is_empty();
 5103            if is_entire_line {
 5104                start = Point::new(start.row, 0);"
 5105                .to_string()
 5106        ),
 5107        "When selecting past the indent, the copying works as usual",
 5108    );
 5109    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5110    assert_eq!(
 5111        cx.read_from_clipboard()
 5112            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5113        Some(
 5114            "in selections.iter() {
 5115            let mut start = selection.start;
 5116            let mut end = selection.end;
 5117            let is_entire_line = selection.is_empty();
 5118            if is_entire_line {
 5119                start = Point::new(start.row, 0);"
 5120                .to_string()
 5121        ),
 5122        "When selecting past the indent, nothing is trimmed"
 5123    );
 5124
 5125    cx.set_state(
 5126        r#"            «for selection in selections.iter() {
 5127            let mut start = selection.start;
 5128
 5129            let mut end = selection.end;
 5130            let is_entire_line = selection.is_empty();
 5131            if is_entire_line {
 5132                start = Point::new(start.row, 0);
 5133ˇ»                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5134            }
 5135        "#,
 5136    );
 5137    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5138    assert_eq!(
 5139        cx.read_from_clipboard()
 5140            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5141        Some(
 5142            "for selection in selections.iter() {
 5143let mut start = selection.start;
 5144
 5145let mut end = selection.end;
 5146let is_entire_line = selection.is_empty();
 5147if is_entire_line {
 5148    start = Point::new(start.row, 0);
 5149"
 5150            .to_string()
 5151        ),
 5152        "Copying with stripping should ignore empty lines"
 5153    );
 5154}
 5155
 5156#[gpui::test]
 5157async fn test_paste_multiline(cx: &mut TestAppContext) {
 5158    init_test(cx, |_| {});
 5159
 5160    let mut cx = EditorTestContext::new(cx).await;
 5161    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5162
 5163    // Cut an indented block, without the leading whitespace.
 5164    cx.set_state(indoc! {"
 5165        const a: B = (
 5166            c(),
 5167            «d(
 5168                e,
 5169                f
 5170            )ˇ»
 5171        );
 5172    "});
 5173    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5174    cx.assert_editor_state(indoc! {"
 5175        const a: B = (
 5176            c(),
 5177            ˇ
 5178        );
 5179    "});
 5180
 5181    // Paste it at the same position.
 5182    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5183    cx.assert_editor_state(indoc! {"
 5184        const a: B = (
 5185            c(),
 5186            d(
 5187                e,
 5188                f
 5189 5190        );
 5191    "});
 5192
 5193    // Paste it at a line with a lower indent level.
 5194    cx.set_state(indoc! {"
 5195        ˇ
 5196        const a: B = (
 5197            c(),
 5198        );
 5199    "});
 5200    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5201    cx.assert_editor_state(indoc! {"
 5202        d(
 5203            e,
 5204            f
 5205 5206        const a: B = (
 5207            c(),
 5208        );
 5209    "});
 5210
 5211    // Cut an indented block, with the leading whitespace.
 5212    cx.set_state(indoc! {"
 5213        const a: B = (
 5214            c(),
 5215        «    d(
 5216                e,
 5217                f
 5218            )
 5219        ˇ»);
 5220    "});
 5221    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5222    cx.assert_editor_state(indoc! {"
 5223        const a: B = (
 5224            c(),
 5225        ˇ);
 5226    "});
 5227
 5228    // Paste it at the same position.
 5229    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5230    cx.assert_editor_state(indoc! {"
 5231        const a: B = (
 5232            c(),
 5233            d(
 5234                e,
 5235                f
 5236            )
 5237        ˇ);
 5238    "});
 5239
 5240    // Paste it at a line with a higher indent level.
 5241    cx.set_state(indoc! {"
 5242        const a: B = (
 5243            c(),
 5244            d(
 5245                e,
 5246 5247            )
 5248        );
 5249    "});
 5250    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5251    cx.assert_editor_state(indoc! {"
 5252        const a: B = (
 5253            c(),
 5254            d(
 5255                e,
 5256                f    d(
 5257                    e,
 5258                    f
 5259                )
 5260        ˇ
 5261            )
 5262        );
 5263    "});
 5264
 5265    // Copy an indented block, starting mid-line
 5266    cx.set_state(indoc! {"
 5267        const a: B = (
 5268            c(),
 5269            somethin«g(
 5270                e,
 5271                f
 5272            )ˇ»
 5273        );
 5274    "});
 5275    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5276
 5277    // Paste it on a line with a lower indent level
 5278    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5279    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5280    cx.assert_editor_state(indoc! {"
 5281        const a: B = (
 5282            c(),
 5283            something(
 5284                e,
 5285                f
 5286            )
 5287        );
 5288        g(
 5289            e,
 5290            f
 5291"});
 5292}
 5293
 5294#[gpui::test]
 5295async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5296    init_test(cx, |_| {});
 5297
 5298    cx.write_to_clipboard(ClipboardItem::new_string(
 5299        "    d(\n        e\n    );\n".into(),
 5300    ));
 5301
 5302    let mut cx = EditorTestContext::new(cx).await;
 5303    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5304
 5305    cx.set_state(indoc! {"
 5306        fn a() {
 5307            b();
 5308            if c() {
 5309                ˇ
 5310            }
 5311        }
 5312    "});
 5313
 5314    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5315    cx.assert_editor_state(indoc! {"
 5316        fn a() {
 5317            b();
 5318            if c() {
 5319                d(
 5320                    e
 5321                );
 5322        ˇ
 5323            }
 5324        }
 5325    "});
 5326
 5327    cx.set_state(indoc! {"
 5328        fn a() {
 5329            b();
 5330            ˇ
 5331        }
 5332    "});
 5333
 5334    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5335    cx.assert_editor_state(indoc! {"
 5336        fn a() {
 5337            b();
 5338            d(
 5339                e
 5340            );
 5341        ˇ
 5342        }
 5343    "});
 5344}
 5345
 5346#[gpui::test]
 5347fn test_select_all(cx: &mut TestAppContext) {
 5348    init_test(cx, |_| {});
 5349
 5350    let editor = cx.add_window(|window, cx| {
 5351        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5352        build_editor(buffer, window, cx)
 5353    });
 5354    _ = editor.update(cx, |editor, window, cx| {
 5355        editor.select_all(&SelectAll, window, cx);
 5356        assert_eq!(
 5357            editor.selections.display_ranges(cx),
 5358            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5359        );
 5360    });
 5361}
 5362
 5363#[gpui::test]
 5364fn test_select_line(cx: &mut TestAppContext) {
 5365    init_test(cx, |_| {});
 5366
 5367    let editor = cx.add_window(|window, cx| {
 5368        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5369        build_editor(buffer, window, cx)
 5370    });
 5371    _ = editor.update(cx, |editor, window, cx| {
 5372        editor.change_selections(None, window, cx, |s| {
 5373            s.select_display_ranges([
 5374                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5375                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5376                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5377                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5378            ])
 5379        });
 5380        editor.select_line(&SelectLine, window, cx);
 5381        assert_eq!(
 5382            editor.selections.display_ranges(cx),
 5383            vec![
 5384                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5385                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5386            ]
 5387        );
 5388    });
 5389
 5390    _ = editor.update(cx, |editor, window, cx| {
 5391        editor.select_line(&SelectLine, window, cx);
 5392        assert_eq!(
 5393            editor.selections.display_ranges(cx),
 5394            vec![
 5395                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5396                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5397            ]
 5398        );
 5399    });
 5400
 5401    _ = editor.update(cx, |editor, window, cx| {
 5402        editor.select_line(&SelectLine, window, cx);
 5403        assert_eq!(
 5404            editor.selections.display_ranges(cx),
 5405            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5406        );
 5407    });
 5408}
 5409
 5410#[gpui::test]
 5411async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5412    init_test(cx, |_| {});
 5413    let mut cx = EditorTestContext::new(cx).await;
 5414
 5415    #[track_caller]
 5416    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5417        cx.set_state(initial_state);
 5418        cx.update_editor(|e, window, cx| {
 5419            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5420        });
 5421        cx.assert_editor_state(expected_state);
 5422    }
 5423
 5424    // Selection starts and ends at the middle of lines, left-to-right
 5425    test(
 5426        &mut cx,
 5427        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5428        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5429    );
 5430    // Same thing, right-to-left
 5431    test(
 5432        &mut cx,
 5433        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5434        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5435    );
 5436
 5437    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5438    test(
 5439        &mut cx,
 5440        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5441        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5442    );
 5443    // Same thing, right-to-left
 5444    test(
 5445        &mut cx,
 5446        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5447        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5448    );
 5449
 5450    // Whole buffer, left-to-right, last line ends with newline
 5451    test(
 5452        &mut cx,
 5453        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5454        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5455    );
 5456    // Same thing, right-to-left
 5457    test(
 5458        &mut cx,
 5459        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5460        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5461    );
 5462
 5463    // Starts at the end of a line, ends at the start of another
 5464    test(
 5465        &mut cx,
 5466        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5467        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5468    );
 5469}
 5470
 5471#[gpui::test]
 5472async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5473    init_test(cx, |_| {});
 5474
 5475    let editor = cx.add_window(|window, cx| {
 5476        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5477        build_editor(buffer, window, cx)
 5478    });
 5479
 5480    // setup
 5481    _ = editor.update(cx, |editor, window, cx| {
 5482        editor.fold_creases(
 5483            vec![
 5484                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5485                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5486                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5487            ],
 5488            true,
 5489            window,
 5490            cx,
 5491        );
 5492        assert_eq!(
 5493            editor.display_text(cx),
 5494            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5495        );
 5496    });
 5497
 5498    _ = editor.update(cx, |editor, window, cx| {
 5499        editor.change_selections(None, window, cx, |s| {
 5500            s.select_display_ranges([
 5501                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5502                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5503                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5504                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5505            ])
 5506        });
 5507        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5508        assert_eq!(
 5509            editor.display_text(cx),
 5510            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5511        );
 5512    });
 5513    EditorTestContext::for_editor(editor, cx)
 5514        .await
 5515        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5516
 5517    _ = editor.update(cx, |editor, window, cx| {
 5518        editor.change_selections(None, window, cx, |s| {
 5519            s.select_display_ranges([
 5520                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5521            ])
 5522        });
 5523        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5524        assert_eq!(
 5525            editor.display_text(cx),
 5526            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5527        );
 5528        assert_eq!(
 5529            editor.selections.display_ranges(cx),
 5530            [
 5531                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5532                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5533                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5534                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5535                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5536                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5537                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5538            ]
 5539        );
 5540    });
 5541    EditorTestContext::for_editor(editor, cx)
 5542        .await
 5543        .assert_editor_state(
 5544            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5545        );
 5546}
 5547
 5548#[gpui::test]
 5549async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5550    init_test(cx, |_| {});
 5551
 5552    let mut cx = EditorTestContext::new(cx).await;
 5553
 5554    cx.set_state(indoc!(
 5555        r#"abc
 5556           defˇghi
 5557
 5558           jk
 5559           nlmo
 5560           "#
 5561    ));
 5562
 5563    cx.update_editor(|editor, window, cx| {
 5564        editor.add_selection_above(&Default::default(), window, cx);
 5565    });
 5566
 5567    cx.assert_editor_state(indoc!(
 5568        r#"abcˇ
 5569           defˇghi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_above(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abcˇ
 5582            defˇghi
 5583
 5584            jk
 5585            nlmo
 5586            "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           defˇghi
 5596
 5597           jk
 5598           nlmo
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.undo_selection(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abcˇ
 5608           defˇghi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.redo_selection(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           defˇghi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    cx.update_editor(|editor, window, cx| {
 5629        editor.add_selection_below(&Default::default(), window, cx);
 5630    });
 5631
 5632    cx.assert_editor_state(indoc!(
 5633        r#"abc
 5634           defˇghi
 5635
 5636           jk
 5637           nlmˇo
 5638           "#
 5639    ));
 5640
 5641    cx.update_editor(|editor, window, cx| {
 5642        editor.add_selection_below(&Default::default(), window, cx);
 5643    });
 5644
 5645    cx.assert_editor_state(indoc!(
 5646        r#"abc
 5647           defˇghi
 5648
 5649           jk
 5650           nlmˇo
 5651           "#
 5652    ));
 5653
 5654    // change selections
 5655    cx.set_state(indoc!(
 5656        r#"abc
 5657           def«ˇg»hi
 5658
 5659           jk
 5660           nlmo
 5661           "#
 5662    ));
 5663
 5664    cx.update_editor(|editor, window, cx| {
 5665        editor.add_selection_below(&Default::default(), window, cx);
 5666    });
 5667
 5668    cx.assert_editor_state(indoc!(
 5669        r#"abc
 5670           def«ˇg»hi
 5671
 5672           jk
 5673           nlm«ˇo»
 5674           "#
 5675    ));
 5676
 5677    cx.update_editor(|editor, window, cx| {
 5678        editor.add_selection_below(&Default::default(), window, cx);
 5679    });
 5680
 5681    cx.assert_editor_state(indoc!(
 5682        r#"abc
 5683           def«ˇg»hi
 5684
 5685           jk
 5686           nlm«ˇo»
 5687           "#
 5688    ));
 5689
 5690    cx.update_editor(|editor, window, cx| {
 5691        editor.add_selection_above(&Default::default(), window, cx);
 5692    });
 5693
 5694    cx.assert_editor_state(indoc!(
 5695        r#"abc
 5696           def«ˇg»hi
 5697
 5698           jk
 5699           nlmo
 5700           "#
 5701    ));
 5702
 5703    cx.update_editor(|editor, window, cx| {
 5704        editor.add_selection_above(&Default::default(), window, cx);
 5705    });
 5706
 5707    cx.assert_editor_state(indoc!(
 5708        r#"abc
 5709           def«ˇg»hi
 5710
 5711           jk
 5712           nlmo
 5713           "#
 5714    ));
 5715
 5716    // Change selections again
 5717    cx.set_state(indoc!(
 5718        r#"a«bc
 5719           defgˇ»hi
 5720
 5721           jk
 5722           nlmo
 5723           "#
 5724    ));
 5725
 5726    cx.update_editor(|editor, window, cx| {
 5727        editor.add_selection_below(&Default::default(), window, cx);
 5728    });
 5729
 5730    cx.assert_editor_state(indoc!(
 5731        r#"a«bcˇ»
 5732           d«efgˇ»hi
 5733
 5734           j«kˇ»
 5735           nlmo
 5736           "#
 5737    ));
 5738
 5739    cx.update_editor(|editor, window, cx| {
 5740        editor.add_selection_below(&Default::default(), window, cx);
 5741    });
 5742    cx.assert_editor_state(indoc!(
 5743        r#"a«bcˇ»
 5744           d«efgˇ»hi
 5745
 5746           j«kˇ»
 5747           n«lmoˇ»
 5748           "#
 5749    ));
 5750    cx.update_editor(|editor, window, cx| {
 5751        editor.add_selection_above(&Default::default(), window, cx);
 5752    });
 5753
 5754    cx.assert_editor_state(indoc!(
 5755        r#"a«bcˇ»
 5756           d«efgˇ»hi
 5757
 5758           j«kˇ»
 5759           nlmo
 5760           "#
 5761    ));
 5762
 5763    // Change selections again
 5764    cx.set_state(indoc!(
 5765        r#"abc
 5766           d«ˇefghi
 5767
 5768           jk
 5769           nlm»o
 5770           "#
 5771    ));
 5772
 5773    cx.update_editor(|editor, window, cx| {
 5774        editor.add_selection_above(&Default::default(), window, cx);
 5775    });
 5776
 5777    cx.assert_editor_state(indoc!(
 5778        r#"a«ˇbc»
 5779           d«ˇef»ghi
 5780
 5781           j«ˇk»
 5782           n«ˇlm»o
 5783           "#
 5784    ));
 5785
 5786    cx.update_editor(|editor, window, cx| {
 5787        editor.add_selection_below(&Default::default(), window, cx);
 5788    });
 5789
 5790    cx.assert_editor_state(indoc!(
 5791        r#"abc
 5792           d«ˇef»ghi
 5793
 5794           j«ˇk»
 5795           n«ˇlm»o
 5796           "#
 5797    ));
 5798}
 5799
 5800#[gpui::test]
 5801async fn test_select_next(cx: &mut TestAppContext) {
 5802    init_test(cx, |_| {});
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5806
 5807    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5808        .unwrap();
 5809    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5810
 5811    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5812        .unwrap();
 5813    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5814
 5815    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5816    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5817
 5818    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5819    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5820
 5821    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5822        .unwrap();
 5823    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5824
 5825    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5826        .unwrap();
 5827    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5828}
 5829
 5830#[gpui::test]
 5831async fn test_select_all_matches(cx: &mut TestAppContext) {
 5832    init_test(cx, |_| {});
 5833
 5834    let mut cx = EditorTestContext::new(cx).await;
 5835
 5836    // Test caret-only selections
 5837    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5838    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5839        .unwrap();
 5840    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5841
 5842    // Test left-to-right selections
 5843    cx.set_state("abc\n«abcˇ»\nabc");
 5844    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5845        .unwrap();
 5846    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5847
 5848    // Test right-to-left selections
 5849    cx.set_state("abc\n«ˇabc»\nabc");
 5850    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5851        .unwrap();
 5852    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5853
 5854    // Test selecting whitespace with caret selection
 5855    cx.set_state("abc\nˇ   abc\nabc");
 5856    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5857        .unwrap();
 5858    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5859
 5860    // Test selecting whitespace with left-to-right selection
 5861    cx.set_state("abc\n«ˇ  »abc\nabc");
 5862    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5863        .unwrap();
 5864    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5865
 5866    // Test no matches with right-to-left selection
 5867    cx.set_state("abc\n«  ˇ»abc\nabc");
 5868    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5869        .unwrap();
 5870    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5871}
 5872
 5873#[gpui::test]
 5874async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5875    init_test(cx, |_| {});
 5876
 5877    let mut cx = EditorTestContext::new(cx).await;
 5878
 5879    let large_body_1 = "\nd".repeat(200);
 5880    let large_body_2 = "\ne".repeat(200);
 5881
 5882    cx.set_state(&format!(
 5883        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5884    ));
 5885    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5886        let scroll_position = editor.scroll_position(cx);
 5887        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5888        scroll_position
 5889    });
 5890
 5891    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5892        .unwrap();
 5893    cx.assert_editor_state(&format!(
 5894        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5895    ));
 5896    let scroll_position_after_selection =
 5897        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5898    assert_eq!(
 5899        initial_scroll_position, scroll_position_after_selection,
 5900        "Scroll position should not change after selecting all matches"
 5901    );
 5902}
 5903
 5904#[gpui::test]
 5905async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5906    init_test(cx, |_| {});
 5907
 5908    let mut cx = EditorLspTestContext::new_rust(
 5909        lsp::ServerCapabilities {
 5910            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5911            ..Default::default()
 5912        },
 5913        cx,
 5914    )
 5915    .await;
 5916
 5917    cx.set_state(indoc! {"
 5918        line 1
 5919        line 2
 5920        linˇe 3
 5921        line 4
 5922        line 5
 5923    "});
 5924
 5925    // Make an edit
 5926    cx.update_editor(|editor, window, cx| {
 5927        editor.handle_input("X", window, cx);
 5928    });
 5929
 5930    // Move cursor to a different position
 5931    cx.update_editor(|editor, window, cx| {
 5932        editor.change_selections(None, window, cx, |s| {
 5933            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5934        });
 5935    });
 5936
 5937    cx.assert_editor_state(indoc! {"
 5938        line 1
 5939        line 2
 5940        linXe 3
 5941        line 4
 5942        liˇne 5
 5943    "});
 5944
 5945    cx.lsp
 5946        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5947            Ok(Some(vec![lsp::TextEdit::new(
 5948                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5949                "PREFIX ".to_string(),
 5950            )]))
 5951        });
 5952
 5953    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5954        .unwrap()
 5955        .await
 5956        .unwrap();
 5957
 5958    cx.assert_editor_state(indoc! {"
 5959        PREFIX line 1
 5960        line 2
 5961        linXe 3
 5962        line 4
 5963        liˇne 5
 5964    "});
 5965
 5966    // Undo formatting
 5967    cx.update_editor(|editor, window, cx| {
 5968        editor.undo(&Default::default(), window, cx);
 5969    });
 5970
 5971    // Verify cursor moved back to position after edit
 5972    cx.assert_editor_state(indoc! {"
 5973        line 1
 5974        line 2
 5975        linXˇe 3
 5976        line 4
 5977        line 5
 5978    "});
 5979}
 5980
 5981#[gpui::test]
 5982async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5983    init_test(cx, |_| {});
 5984
 5985    let mut cx = EditorTestContext::new(cx).await;
 5986    cx.set_state(
 5987        r#"let foo = 2;
 5988lˇet foo = 2;
 5989let fooˇ = 2;
 5990let foo = 2;
 5991let foo = ˇ2;"#,
 5992    );
 5993
 5994    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5995        .unwrap();
 5996    cx.assert_editor_state(
 5997        r#"let foo = 2;
 5998«letˇ» foo = 2;
 5999let «fooˇ» = 2;
 6000let foo = 2;
 6001let foo = «2ˇ»;"#,
 6002    );
 6003
 6004    // noop for multiple selections with different contents
 6005    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 6006        .unwrap();
 6007    cx.assert_editor_state(
 6008        r#"let foo = 2;
 6009«letˇ» foo = 2;
 6010let «fooˇ» = 2;
 6011let foo = 2;
 6012let foo = «2ˇ»;"#,
 6013    );
 6014}
 6015
 6016#[gpui::test]
 6017async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 6018    init_test(cx, |_| {});
 6019
 6020    let mut cx =
 6021        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 6022
 6023    cx.assert_editor_state(indoc! {"
 6024        ˇbbb
 6025        ccc
 6026
 6027        bbb
 6028        ccc
 6029        "});
 6030    cx.dispatch_action(SelectPrevious::default());
 6031    cx.assert_editor_state(indoc! {"
 6032                «bbbˇ»
 6033                ccc
 6034
 6035                bbb
 6036                ccc
 6037                "});
 6038    cx.dispatch_action(SelectPrevious::default());
 6039    cx.assert_editor_state(indoc! {"
 6040                «bbbˇ»
 6041                ccc
 6042
 6043                «bbbˇ»
 6044                ccc
 6045                "});
 6046}
 6047
 6048#[gpui::test]
 6049async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6050    init_test(cx, |_| {});
 6051
 6052    let mut cx = EditorTestContext::new(cx).await;
 6053    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6054
 6055    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6056        .unwrap();
 6057    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6058
 6059    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6060        .unwrap();
 6061    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6062
 6063    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6064    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6065
 6066    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6067    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6068
 6069    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6070        .unwrap();
 6071    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6072
 6073    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6074        .unwrap();
 6075    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 6076
 6077    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6078        .unwrap();
 6079    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6080}
 6081
 6082#[gpui::test]
 6083async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6084    init_test(cx, |_| {});
 6085
 6086    let mut cx = EditorTestContext::new(cx).await;
 6087    cx.set_state("");
 6088
 6089    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6090        .unwrap();
 6091    cx.assert_editor_state("«aˇ»");
 6092    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6093        .unwrap();
 6094    cx.assert_editor_state("«aˇ»");
 6095}
 6096
 6097#[gpui::test]
 6098async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6099    init_test(cx, |_| {});
 6100
 6101    let mut cx = EditorTestContext::new(cx).await;
 6102    cx.set_state(
 6103        r#"let foo = 2;
 6104lˇet foo = 2;
 6105let fooˇ = 2;
 6106let foo = 2;
 6107let foo = ˇ2;"#,
 6108    );
 6109
 6110    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6111        .unwrap();
 6112    cx.assert_editor_state(
 6113        r#"let foo = 2;
 6114«letˇ» foo = 2;
 6115let «fooˇ» = 2;
 6116let foo = 2;
 6117let foo = «2ˇ»;"#,
 6118    );
 6119
 6120    // noop for multiple selections with different contents
 6121    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6122        .unwrap();
 6123    cx.assert_editor_state(
 6124        r#"let foo = 2;
 6125«letˇ» foo = 2;
 6126let «fooˇ» = 2;
 6127let foo = 2;
 6128let foo = «2ˇ»;"#,
 6129    );
 6130}
 6131
 6132#[gpui::test]
 6133async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6134    init_test(cx, |_| {});
 6135
 6136    let mut cx = EditorTestContext::new(cx).await;
 6137    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6138
 6139    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6140        .unwrap();
 6141    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6142
 6143    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6144        .unwrap();
 6145    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6146
 6147    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6148    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6149
 6150    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6151    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6152
 6153    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6154        .unwrap();
 6155    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 6156
 6157    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6158        .unwrap();
 6159    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6160}
 6161
 6162#[gpui::test]
 6163async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6164    init_test(cx, |_| {});
 6165
 6166    let language = Arc::new(Language::new(
 6167        LanguageConfig::default(),
 6168        Some(tree_sitter_rust::LANGUAGE.into()),
 6169    ));
 6170
 6171    let text = r#"
 6172        use mod1::mod2::{mod3, mod4};
 6173
 6174        fn fn_1(param1: bool, param2: &str) {
 6175            let var1 = "text";
 6176        }
 6177    "#
 6178    .unindent();
 6179
 6180    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6181    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6182    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6183
 6184    editor
 6185        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6186        .await;
 6187
 6188    editor.update_in(cx, |editor, window, cx| {
 6189        editor.change_selections(None, window, cx, |s| {
 6190            s.select_display_ranges([
 6191                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6192                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6193                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6194            ]);
 6195        });
 6196        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6197    });
 6198    editor.update(cx, |editor, cx| {
 6199        assert_text_with_selections(
 6200            editor,
 6201            indoc! {r#"
 6202                use mod1::mod2::{mod3, «mod4ˇ»};
 6203
 6204                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6205                    let var1 = "«ˇtext»";
 6206                }
 6207            "#},
 6208            cx,
 6209        );
 6210    });
 6211
 6212    editor.update_in(cx, |editor, window, cx| {
 6213        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6214    });
 6215    editor.update(cx, |editor, cx| {
 6216        assert_text_with_selections(
 6217            editor,
 6218            indoc! {r#"
 6219                use mod1::mod2::«{mod3, mod4}ˇ»;
 6220
 6221                «ˇfn fn_1(param1: bool, param2: &str) {
 6222                    let var1 = "text";
 6223 6224            "#},
 6225            cx,
 6226        );
 6227    });
 6228
 6229    editor.update_in(cx, |editor, window, cx| {
 6230        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6231    });
 6232    assert_eq!(
 6233        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6234        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6235    );
 6236
 6237    // Trying to expand the selected syntax node one more time has no effect.
 6238    editor.update_in(cx, |editor, window, cx| {
 6239        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6240    });
 6241    assert_eq!(
 6242        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6243        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6244    );
 6245
 6246    editor.update_in(cx, |editor, window, cx| {
 6247        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6248    });
 6249    editor.update(cx, |editor, cx| {
 6250        assert_text_with_selections(
 6251            editor,
 6252            indoc! {r#"
 6253                use mod1::mod2::«{mod3, mod4}ˇ»;
 6254
 6255                «ˇfn fn_1(param1: bool, param2: &str) {
 6256                    let var1 = "text";
 6257 6258            "#},
 6259            cx,
 6260        );
 6261    });
 6262
 6263    editor.update_in(cx, |editor, window, cx| {
 6264        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6265    });
 6266    editor.update(cx, |editor, cx| {
 6267        assert_text_with_selections(
 6268            editor,
 6269            indoc! {r#"
 6270                use mod1::mod2::{mod3, «mod4ˇ»};
 6271
 6272                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6273                    let var1 = "«ˇtext»";
 6274                }
 6275            "#},
 6276            cx,
 6277        );
 6278    });
 6279
 6280    editor.update_in(cx, |editor, window, cx| {
 6281        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6282    });
 6283    editor.update(cx, |editor, cx| {
 6284        assert_text_with_selections(
 6285            editor,
 6286            indoc! {r#"
 6287                use mod1::mod2::{mod3, mo«ˇ»d4};
 6288
 6289                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6290                    let var1 = "te«ˇ»xt";
 6291                }
 6292            "#},
 6293            cx,
 6294        );
 6295    });
 6296
 6297    // Trying to shrink the selected syntax node one more time has no effect.
 6298    editor.update_in(cx, |editor, window, cx| {
 6299        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6300    });
 6301    editor.update_in(cx, |editor, _, cx| {
 6302        assert_text_with_selections(
 6303            editor,
 6304            indoc! {r#"
 6305                use mod1::mod2::{mod3, mo«ˇ»d4};
 6306
 6307                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6308                    let var1 = "te«ˇ»xt";
 6309                }
 6310            "#},
 6311            cx,
 6312        );
 6313    });
 6314
 6315    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6316    // a fold.
 6317    editor.update_in(cx, |editor, window, cx| {
 6318        editor.fold_creases(
 6319            vec![
 6320                Crease::simple(
 6321                    Point::new(0, 21)..Point::new(0, 24),
 6322                    FoldPlaceholder::test(),
 6323                ),
 6324                Crease::simple(
 6325                    Point::new(3, 20)..Point::new(3, 22),
 6326                    FoldPlaceholder::test(),
 6327                ),
 6328            ],
 6329            true,
 6330            window,
 6331            cx,
 6332        );
 6333        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6334    });
 6335    editor.update(cx, |editor, cx| {
 6336        assert_text_with_selections(
 6337            editor,
 6338            indoc! {r#"
 6339                use mod1::mod2::«{mod3, mod4}ˇ»;
 6340
 6341                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6342                    «ˇlet var1 = "text";»
 6343                }
 6344            "#},
 6345            cx,
 6346        );
 6347    });
 6348}
 6349
 6350#[gpui::test]
 6351async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6352    init_test(cx, |_| {});
 6353
 6354    let base_text = r#"
 6355        impl A {
 6356            // this is an uncommitted comment
 6357
 6358            fn b() {
 6359                c();
 6360            }
 6361
 6362            // this is another uncommitted comment
 6363
 6364            fn d() {
 6365                // e
 6366                // f
 6367            }
 6368        }
 6369
 6370        fn g() {
 6371            // h
 6372        }
 6373    "#
 6374    .unindent();
 6375
 6376    let text = r#"
 6377        ˇimpl A {
 6378
 6379            fn b() {
 6380                c();
 6381            }
 6382
 6383            fn d() {
 6384                // e
 6385                // f
 6386            }
 6387        }
 6388
 6389        fn g() {
 6390            // h
 6391        }
 6392    "#
 6393    .unindent();
 6394
 6395    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6396    cx.set_state(&text);
 6397    cx.set_head_text(&base_text);
 6398    cx.update_editor(|editor, window, cx| {
 6399        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6400    });
 6401
 6402    cx.assert_state_with_diff(
 6403        "
 6404        ˇimpl A {
 6405      -     // this is an uncommitted comment
 6406
 6407            fn b() {
 6408                c();
 6409            }
 6410
 6411      -     // this is another uncommitted comment
 6412      -
 6413            fn d() {
 6414                // e
 6415                // f
 6416            }
 6417        }
 6418
 6419        fn g() {
 6420            // h
 6421        }
 6422    "
 6423        .unindent(),
 6424    );
 6425
 6426    let expected_display_text = "
 6427        impl A {
 6428            // this is an uncommitted comment
 6429
 6430            fn b() {
 6431 6432            }
 6433
 6434            // this is another uncommitted comment
 6435
 6436            fn d() {
 6437 6438            }
 6439        }
 6440
 6441        fn g() {
 6442 6443        }
 6444        "
 6445    .unindent();
 6446
 6447    cx.update_editor(|editor, window, cx| {
 6448        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6449        assert_eq!(editor.display_text(cx), expected_display_text);
 6450    });
 6451}
 6452
 6453#[gpui::test]
 6454async fn test_autoindent(cx: &mut TestAppContext) {
 6455    init_test(cx, |_| {});
 6456
 6457    let language = Arc::new(
 6458        Language::new(
 6459            LanguageConfig {
 6460                brackets: BracketPairConfig {
 6461                    pairs: vec![
 6462                        BracketPair {
 6463                            start: "{".to_string(),
 6464                            end: "}".to_string(),
 6465                            close: false,
 6466                            surround: false,
 6467                            newline: true,
 6468                        },
 6469                        BracketPair {
 6470                            start: "(".to_string(),
 6471                            end: ")".to_string(),
 6472                            close: false,
 6473                            surround: false,
 6474                            newline: true,
 6475                        },
 6476                    ],
 6477                    ..Default::default()
 6478                },
 6479                ..Default::default()
 6480            },
 6481            Some(tree_sitter_rust::LANGUAGE.into()),
 6482        )
 6483        .with_indents_query(
 6484            r#"
 6485                (_ "(" ")" @end) @indent
 6486                (_ "{" "}" @end) @indent
 6487            "#,
 6488        )
 6489        .unwrap(),
 6490    );
 6491
 6492    let text = "fn a() {}";
 6493
 6494    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6495    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6496    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6497    editor
 6498        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6499        .await;
 6500
 6501    editor.update_in(cx, |editor, window, cx| {
 6502        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6503        editor.newline(&Newline, window, cx);
 6504        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6505        assert_eq!(
 6506            editor.selections.ranges(cx),
 6507            &[
 6508                Point::new(1, 4)..Point::new(1, 4),
 6509                Point::new(3, 4)..Point::new(3, 4),
 6510                Point::new(5, 0)..Point::new(5, 0)
 6511            ]
 6512        );
 6513    });
 6514}
 6515
 6516#[gpui::test]
 6517async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6518    init_test(cx, |_| {});
 6519
 6520    {
 6521        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6522        cx.set_state(indoc! {"
 6523            impl A {
 6524
 6525                fn b() {}
 6526
 6527            «fn c() {
 6528
 6529            }ˇ»
 6530            }
 6531        "});
 6532
 6533        cx.update_editor(|editor, window, cx| {
 6534            editor.autoindent(&Default::default(), window, cx);
 6535        });
 6536
 6537        cx.assert_editor_state(indoc! {"
 6538            impl A {
 6539
 6540                fn b() {}
 6541
 6542                «fn c() {
 6543
 6544                }ˇ»
 6545            }
 6546        "});
 6547    }
 6548
 6549    {
 6550        let mut cx = EditorTestContext::new_multibuffer(
 6551            cx,
 6552            [indoc! { "
 6553                impl A {
 6554                «
 6555                // a
 6556                fn b(){}
 6557                »
 6558                «
 6559                    }
 6560                    fn c(){}
 6561                »
 6562            "}],
 6563        );
 6564
 6565        let buffer = cx.update_editor(|editor, _, cx| {
 6566            let buffer = editor.buffer().update(cx, |buffer, _| {
 6567                buffer.all_buffers().iter().next().unwrap().clone()
 6568            });
 6569            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6570            buffer
 6571        });
 6572
 6573        cx.run_until_parked();
 6574        cx.update_editor(|editor, window, cx| {
 6575            editor.select_all(&Default::default(), window, cx);
 6576            editor.autoindent(&Default::default(), window, cx)
 6577        });
 6578        cx.run_until_parked();
 6579
 6580        cx.update(|_, cx| {
 6581            assert_eq!(
 6582                buffer.read(cx).text(),
 6583                indoc! { "
 6584                    impl A {
 6585
 6586                        // a
 6587                        fn b(){}
 6588
 6589
 6590                    }
 6591                    fn c(){}
 6592
 6593                " }
 6594            )
 6595        });
 6596    }
 6597}
 6598
 6599#[gpui::test]
 6600async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6601    init_test(cx, |_| {});
 6602
 6603    let mut cx = EditorTestContext::new(cx).await;
 6604
 6605    let language = Arc::new(Language::new(
 6606        LanguageConfig {
 6607            brackets: BracketPairConfig {
 6608                pairs: vec![
 6609                    BracketPair {
 6610                        start: "{".to_string(),
 6611                        end: "}".to_string(),
 6612                        close: true,
 6613                        surround: true,
 6614                        newline: true,
 6615                    },
 6616                    BracketPair {
 6617                        start: "(".to_string(),
 6618                        end: ")".to_string(),
 6619                        close: true,
 6620                        surround: true,
 6621                        newline: true,
 6622                    },
 6623                    BracketPair {
 6624                        start: "/*".to_string(),
 6625                        end: " */".to_string(),
 6626                        close: true,
 6627                        surround: true,
 6628                        newline: true,
 6629                    },
 6630                    BracketPair {
 6631                        start: "[".to_string(),
 6632                        end: "]".to_string(),
 6633                        close: false,
 6634                        surround: false,
 6635                        newline: true,
 6636                    },
 6637                    BracketPair {
 6638                        start: "\"".to_string(),
 6639                        end: "\"".to_string(),
 6640                        close: true,
 6641                        surround: true,
 6642                        newline: false,
 6643                    },
 6644                    BracketPair {
 6645                        start: "<".to_string(),
 6646                        end: ">".to_string(),
 6647                        close: false,
 6648                        surround: true,
 6649                        newline: true,
 6650                    },
 6651                ],
 6652                ..Default::default()
 6653            },
 6654            autoclose_before: "})]".to_string(),
 6655            ..Default::default()
 6656        },
 6657        Some(tree_sitter_rust::LANGUAGE.into()),
 6658    ));
 6659
 6660    cx.language_registry().add(language.clone());
 6661    cx.update_buffer(|buffer, cx| {
 6662        buffer.set_language(Some(language), cx);
 6663    });
 6664
 6665    cx.set_state(
 6666        &r#"
 6667            🏀ˇ
 6668            εˇ
 6669            ❤️ˇ
 6670        "#
 6671        .unindent(),
 6672    );
 6673
 6674    // autoclose multiple nested brackets at multiple cursors
 6675    cx.update_editor(|editor, window, cx| {
 6676        editor.handle_input("{", window, cx);
 6677        editor.handle_input("{", window, cx);
 6678        editor.handle_input("{", window, cx);
 6679    });
 6680    cx.assert_editor_state(
 6681        &"
 6682            🏀{{{ˇ}}}
 6683            ε{{{ˇ}}}
 6684            ❤️{{{ˇ}}}
 6685        "
 6686        .unindent(),
 6687    );
 6688
 6689    // insert a different closing bracket
 6690    cx.update_editor(|editor, window, cx| {
 6691        editor.handle_input(")", window, cx);
 6692    });
 6693    cx.assert_editor_state(
 6694        &"
 6695            🏀{{{)ˇ}}}
 6696            ε{{{)ˇ}}}
 6697            ❤️{{{)ˇ}}}
 6698        "
 6699        .unindent(),
 6700    );
 6701
 6702    // skip over the auto-closed brackets when typing a closing bracket
 6703    cx.update_editor(|editor, window, cx| {
 6704        editor.move_right(&MoveRight, window, cx);
 6705        editor.handle_input("}", window, cx);
 6706        editor.handle_input("}", window, cx);
 6707        editor.handle_input("}", window, cx);
 6708    });
 6709    cx.assert_editor_state(
 6710        &"
 6711            🏀{{{)}}}}ˇ
 6712            ε{{{)}}}}ˇ
 6713            ❤️{{{)}}}}ˇ
 6714        "
 6715        .unindent(),
 6716    );
 6717
 6718    // autoclose multi-character pairs
 6719    cx.set_state(
 6720        &"
 6721            ˇ
 6722            ˇ
 6723        "
 6724        .unindent(),
 6725    );
 6726    cx.update_editor(|editor, window, cx| {
 6727        editor.handle_input("/", window, cx);
 6728        editor.handle_input("*", window, cx);
 6729    });
 6730    cx.assert_editor_state(
 6731        &"
 6732            /*ˇ */
 6733            /*ˇ */
 6734        "
 6735        .unindent(),
 6736    );
 6737
 6738    // one cursor autocloses a multi-character pair, one cursor
 6739    // does not autoclose.
 6740    cx.set_state(
 6741        &"
 6742 6743            ˇ
 6744        "
 6745        .unindent(),
 6746    );
 6747    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6748    cx.assert_editor_state(
 6749        &"
 6750            /*ˇ */
 6751 6752        "
 6753        .unindent(),
 6754    );
 6755
 6756    // Don't autoclose if the next character isn't whitespace and isn't
 6757    // listed in the language's "autoclose_before" section.
 6758    cx.set_state("ˇa b");
 6759    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6760    cx.assert_editor_state("{ˇa b");
 6761
 6762    // Don't autoclose if `close` is false for the bracket pair
 6763    cx.set_state("ˇ");
 6764    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6765    cx.assert_editor_state("");
 6766
 6767    // Surround with brackets if text is selected
 6768    cx.set_state("«aˇ» b");
 6769    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6770    cx.assert_editor_state("{«aˇ»} b");
 6771
 6772    // Autoclose when not immediately after a word character
 6773    cx.set_state("a ˇ");
 6774    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6775    cx.assert_editor_state("a \"ˇ\"");
 6776
 6777    // Autoclose pair where the start and end characters are the same
 6778    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6779    cx.assert_editor_state("a \"\"ˇ");
 6780
 6781    // Don't autoclose when immediately after a word character
 6782    cx.set_state("");
 6783    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6784    cx.assert_editor_state("a\"ˇ");
 6785
 6786    // Do autoclose when after a non-word character
 6787    cx.set_state("");
 6788    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6789    cx.assert_editor_state("{\"ˇ\"");
 6790
 6791    // Non identical pairs autoclose regardless of preceding character
 6792    cx.set_state("");
 6793    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6794    cx.assert_editor_state("a{ˇ}");
 6795
 6796    // Don't autoclose pair if autoclose is disabled
 6797    cx.set_state("ˇ");
 6798    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6799    cx.assert_editor_state("");
 6800
 6801    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6802    cx.set_state("«aˇ» b");
 6803    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6804    cx.assert_editor_state("<«aˇ»> b");
 6805}
 6806
 6807#[gpui::test]
 6808async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6809    init_test(cx, |settings| {
 6810        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6811    });
 6812
 6813    let mut cx = EditorTestContext::new(cx).await;
 6814
 6815    let language = Arc::new(Language::new(
 6816        LanguageConfig {
 6817            brackets: BracketPairConfig {
 6818                pairs: vec![
 6819                    BracketPair {
 6820                        start: "{".to_string(),
 6821                        end: "}".to_string(),
 6822                        close: true,
 6823                        surround: true,
 6824                        newline: true,
 6825                    },
 6826                    BracketPair {
 6827                        start: "(".to_string(),
 6828                        end: ")".to_string(),
 6829                        close: true,
 6830                        surround: true,
 6831                        newline: true,
 6832                    },
 6833                    BracketPair {
 6834                        start: "[".to_string(),
 6835                        end: "]".to_string(),
 6836                        close: false,
 6837                        surround: false,
 6838                        newline: true,
 6839                    },
 6840                ],
 6841                ..Default::default()
 6842            },
 6843            autoclose_before: "})]".to_string(),
 6844            ..Default::default()
 6845        },
 6846        Some(tree_sitter_rust::LANGUAGE.into()),
 6847    ));
 6848
 6849    cx.language_registry().add(language.clone());
 6850    cx.update_buffer(|buffer, cx| {
 6851        buffer.set_language(Some(language), cx);
 6852    });
 6853
 6854    cx.set_state(
 6855        &"
 6856            ˇ
 6857            ˇ
 6858            ˇ
 6859        "
 6860        .unindent(),
 6861    );
 6862
 6863    // ensure only matching closing brackets are skipped over
 6864    cx.update_editor(|editor, window, cx| {
 6865        editor.handle_input("}", window, cx);
 6866        editor.move_left(&MoveLeft, window, cx);
 6867        editor.handle_input(")", window, cx);
 6868        editor.move_left(&MoveLeft, window, cx);
 6869    });
 6870    cx.assert_editor_state(
 6871        &"
 6872            ˇ)}
 6873            ˇ)}
 6874            ˇ)}
 6875        "
 6876        .unindent(),
 6877    );
 6878
 6879    // skip-over closing brackets at multiple cursors
 6880    cx.update_editor(|editor, window, cx| {
 6881        editor.handle_input(")", window, cx);
 6882        editor.handle_input("}", window, cx);
 6883    });
 6884    cx.assert_editor_state(
 6885        &"
 6886            )}ˇ
 6887            )}ˇ
 6888            )}ˇ
 6889        "
 6890        .unindent(),
 6891    );
 6892
 6893    // ignore non-close brackets
 6894    cx.update_editor(|editor, window, cx| {
 6895        editor.handle_input("]", window, cx);
 6896        editor.move_left(&MoveLeft, window, cx);
 6897        editor.handle_input("]", window, cx);
 6898    });
 6899    cx.assert_editor_state(
 6900        &"
 6901            )}]ˇ]
 6902            )}]ˇ]
 6903            )}]ˇ]
 6904        "
 6905        .unindent(),
 6906    );
 6907}
 6908
 6909#[gpui::test]
 6910async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6911    init_test(cx, |_| {});
 6912
 6913    let mut cx = EditorTestContext::new(cx).await;
 6914
 6915    let html_language = Arc::new(
 6916        Language::new(
 6917            LanguageConfig {
 6918                name: "HTML".into(),
 6919                brackets: BracketPairConfig {
 6920                    pairs: vec![
 6921                        BracketPair {
 6922                            start: "<".into(),
 6923                            end: ">".into(),
 6924                            close: true,
 6925                            ..Default::default()
 6926                        },
 6927                        BracketPair {
 6928                            start: "{".into(),
 6929                            end: "}".into(),
 6930                            close: true,
 6931                            ..Default::default()
 6932                        },
 6933                        BracketPair {
 6934                            start: "(".into(),
 6935                            end: ")".into(),
 6936                            close: true,
 6937                            ..Default::default()
 6938                        },
 6939                    ],
 6940                    ..Default::default()
 6941                },
 6942                autoclose_before: "})]>".into(),
 6943                ..Default::default()
 6944            },
 6945            Some(tree_sitter_html::LANGUAGE.into()),
 6946        )
 6947        .with_injection_query(
 6948            r#"
 6949            (script_element
 6950                (raw_text) @injection.content
 6951                (#set! injection.language "javascript"))
 6952            "#,
 6953        )
 6954        .unwrap(),
 6955    );
 6956
 6957    let javascript_language = Arc::new(Language::new(
 6958        LanguageConfig {
 6959            name: "JavaScript".into(),
 6960            brackets: BracketPairConfig {
 6961                pairs: vec![
 6962                    BracketPair {
 6963                        start: "/*".into(),
 6964                        end: " */".into(),
 6965                        close: true,
 6966                        ..Default::default()
 6967                    },
 6968                    BracketPair {
 6969                        start: "{".into(),
 6970                        end: "}".into(),
 6971                        close: true,
 6972                        ..Default::default()
 6973                    },
 6974                    BracketPair {
 6975                        start: "(".into(),
 6976                        end: ")".into(),
 6977                        close: true,
 6978                        ..Default::default()
 6979                    },
 6980                ],
 6981                ..Default::default()
 6982            },
 6983            autoclose_before: "})]>".into(),
 6984            ..Default::default()
 6985        },
 6986        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6987    ));
 6988
 6989    cx.language_registry().add(html_language.clone());
 6990    cx.language_registry().add(javascript_language.clone());
 6991
 6992    cx.update_buffer(|buffer, cx| {
 6993        buffer.set_language(Some(html_language), cx);
 6994    });
 6995
 6996    cx.set_state(
 6997        &r#"
 6998            <body>ˇ
 6999                <script>
 7000                    var x = 1;ˇ
 7001                </script>
 7002            </body>ˇ
 7003        "#
 7004        .unindent(),
 7005    );
 7006
 7007    // Precondition: different languages are active at different locations.
 7008    cx.update_editor(|editor, window, cx| {
 7009        let snapshot = editor.snapshot(window, cx);
 7010        let cursors = editor.selections.ranges::<usize>(cx);
 7011        let languages = cursors
 7012            .iter()
 7013            .map(|c| snapshot.language_at(c.start).unwrap().name())
 7014            .collect::<Vec<_>>();
 7015        assert_eq!(
 7016            languages,
 7017            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 7018        );
 7019    });
 7020
 7021    // Angle brackets autoclose in HTML, but not JavaScript.
 7022    cx.update_editor(|editor, window, cx| {
 7023        editor.handle_input("<", window, cx);
 7024        editor.handle_input("a", window, cx);
 7025    });
 7026    cx.assert_editor_state(
 7027        &r#"
 7028            <body><aˇ>
 7029                <script>
 7030                    var x = 1;<aˇ
 7031                </script>
 7032            </body><aˇ>
 7033        "#
 7034        .unindent(),
 7035    );
 7036
 7037    // Curly braces and parens autoclose in both HTML and JavaScript.
 7038    cx.update_editor(|editor, window, cx| {
 7039        editor.handle_input(" b=", window, cx);
 7040        editor.handle_input("{", window, cx);
 7041        editor.handle_input("c", window, cx);
 7042        editor.handle_input("(", window, cx);
 7043    });
 7044    cx.assert_editor_state(
 7045        &r#"
 7046            <body><a b={c(ˇ)}>
 7047                <script>
 7048                    var x = 1;<a b={c(ˇ)}
 7049                </script>
 7050            </body><a b={c(ˇ)}>
 7051        "#
 7052        .unindent(),
 7053    );
 7054
 7055    // Brackets that were already autoclosed are skipped.
 7056    cx.update_editor(|editor, window, cx| {
 7057        editor.handle_input(")", window, cx);
 7058        editor.handle_input("d", window, cx);
 7059        editor.handle_input("}", window, cx);
 7060    });
 7061    cx.assert_editor_state(
 7062        &r#"
 7063            <body><a b={c()d}ˇ>
 7064                <script>
 7065                    var x = 1;<a b={c()d}ˇ
 7066                </script>
 7067            </body><a b={c()d}ˇ>
 7068        "#
 7069        .unindent(),
 7070    );
 7071    cx.update_editor(|editor, window, cx| {
 7072        editor.handle_input(">", window, cx);
 7073    });
 7074    cx.assert_editor_state(
 7075        &r#"
 7076            <body><a b={c()d}>ˇ
 7077                <script>
 7078                    var x = 1;<a b={c()d}>ˇ
 7079                </script>
 7080            </body><a b={c()d}>ˇ
 7081        "#
 7082        .unindent(),
 7083    );
 7084
 7085    // Reset
 7086    cx.set_state(
 7087        &r#"
 7088            <body>ˇ
 7089                <script>
 7090                    var x = 1;ˇ
 7091                </script>
 7092            </body>ˇ
 7093        "#
 7094        .unindent(),
 7095    );
 7096
 7097    cx.update_editor(|editor, window, cx| {
 7098        editor.handle_input("<", window, cx);
 7099    });
 7100    cx.assert_editor_state(
 7101        &r#"
 7102            <body><ˇ>
 7103                <script>
 7104                    var x = 1;<ˇ
 7105                </script>
 7106            </body><ˇ>
 7107        "#
 7108        .unindent(),
 7109    );
 7110
 7111    // When backspacing, the closing angle brackets are removed.
 7112    cx.update_editor(|editor, window, cx| {
 7113        editor.backspace(&Backspace, window, cx);
 7114    });
 7115    cx.assert_editor_state(
 7116        &r#"
 7117            <body>ˇ
 7118                <script>
 7119                    var x = 1;ˇ
 7120                </script>
 7121            </body>ˇ
 7122        "#
 7123        .unindent(),
 7124    );
 7125
 7126    // Block comments autoclose in JavaScript, but not HTML.
 7127    cx.update_editor(|editor, window, cx| {
 7128        editor.handle_input("/", window, cx);
 7129        editor.handle_input("*", window, cx);
 7130    });
 7131    cx.assert_editor_state(
 7132        &r#"
 7133            <body>/*ˇ
 7134                <script>
 7135                    var x = 1;/*ˇ */
 7136                </script>
 7137            </body>/*ˇ
 7138        "#
 7139        .unindent(),
 7140    );
 7141}
 7142
 7143#[gpui::test]
 7144async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7145    init_test(cx, |_| {});
 7146
 7147    let mut cx = EditorTestContext::new(cx).await;
 7148
 7149    let rust_language = Arc::new(
 7150        Language::new(
 7151            LanguageConfig {
 7152                name: "Rust".into(),
 7153                brackets: serde_json::from_value(json!([
 7154                    { "start": "{", "end": "}", "close": true, "newline": true },
 7155                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7156                ]))
 7157                .unwrap(),
 7158                autoclose_before: "})]>".into(),
 7159                ..Default::default()
 7160            },
 7161            Some(tree_sitter_rust::LANGUAGE.into()),
 7162        )
 7163        .with_override_query("(string_literal) @string")
 7164        .unwrap(),
 7165    );
 7166
 7167    cx.language_registry().add(rust_language.clone());
 7168    cx.update_buffer(|buffer, cx| {
 7169        buffer.set_language(Some(rust_language), cx);
 7170    });
 7171
 7172    cx.set_state(
 7173        &r#"
 7174            let x = ˇ
 7175        "#
 7176        .unindent(),
 7177    );
 7178
 7179    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7180    cx.update_editor(|editor, window, cx| {
 7181        editor.handle_input("\"", window, cx);
 7182    });
 7183    cx.assert_editor_state(
 7184        &r#"
 7185            let x = "ˇ"
 7186        "#
 7187        .unindent(),
 7188    );
 7189
 7190    // Inserting another quotation mark. The cursor moves across the existing
 7191    // automatically-inserted quotation mark.
 7192    cx.update_editor(|editor, window, cx| {
 7193        editor.handle_input("\"", window, cx);
 7194    });
 7195    cx.assert_editor_state(
 7196        &r#"
 7197            let x = ""ˇ
 7198        "#
 7199        .unindent(),
 7200    );
 7201
 7202    // Reset
 7203    cx.set_state(
 7204        &r#"
 7205            let x = ˇ
 7206        "#
 7207        .unindent(),
 7208    );
 7209
 7210    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7211    cx.update_editor(|editor, window, cx| {
 7212        editor.handle_input("\"", window, cx);
 7213        editor.handle_input(" ", window, cx);
 7214        editor.move_left(&Default::default(), window, cx);
 7215        editor.handle_input("\\", window, cx);
 7216        editor.handle_input("\"", window, cx);
 7217    });
 7218    cx.assert_editor_state(
 7219        &r#"
 7220            let x = "\"ˇ "
 7221        "#
 7222        .unindent(),
 7223    );
 7224
 7225    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7226    // mark. Nothing is inserted.
 7227    cx.update_editor(|editor, window, cx| {
 7228        editor.move_right(&Default::default(), window, cx);
 7229        editor.handle_input("\"", window, cx);
 7230    });
 7231    cx.assert_editor_state(
 7232        &r#"
 7233            let x = "\" "ˇ
 7234        "#
 7235        .unindent(),
 7236    );
 7237}
 7238
 7239#[gpui::test]
 7240async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7241    init_test(cx, |_| {});
 7242
 7243    let language = Arc::new(Language::new(
 7244        LanguageConfig {
 7245            brackets: BracketPairConfig {
 7246                pairs: vec![
 7247                    BracketPair {
 7248                        start: "{".to_string(),
 7249                        end: "}".to_string(),
 7250                        close: true,
 7251                        surround: true,
 7252                        newline: true,
 7253                    },
 7254                    BracketPair {
 7255                        start: "/* ".to_string(),
 7256                        end: "*/".to_string(),
 7257                        close: true,
 7258                        surround: true,
 7259                        ..Default::default()
 7260                    },
 7261                ],
 7262                ..Default::default()
 7263            },
 7264            ..Default::default()
 7265        },
 7266        Some(tree_sitter_rust::LANGUAGE.into()),
 7267    ));
 7268
 7269    let text = r#"
 7270        a
 7271        b
 7272        c
 7273    "#
 7274    .unindent();
 7275
 7276    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7277    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7278    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7279    editor
 7280        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7281        .await;
 7282
 7283    editor.update_in(cx, |editor, window, cx| {
 7284        editor.change_selections(None, window, cx, |s| {
 7285            s.select_display_ranges([
 7286                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7287                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7288                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7289            ])
 7290        });
 7291
 7292        editor.handle_input("{", window, cx);
 7293        editor.handle_input("{", window, cx);
 7294        editor.handle_input("{", window, cx);
 7295        assert_eq!(
 7296            editor.text(cx),
 7297            "
 7298                {{{a}}}
 7299                {{{b}}}
 7300                {{{c}}}
 7301            "
 7302            .unindent()
 7303        );
 7304        assert_eq!(
 7305            editor.selections.display_ranges(cx),
 7306            [
 7307                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7308                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7309                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7310            ]
 7311        );
 7312
 7313        editor.undo(&Undo, window, cx);
 7314        editor.undo(&Undo, window, cx);
 7315        editor.undo(&Undo, window, cx);
 7316        assert_eq!(
 7317            editor.text(cx),
 7318            "
 7319                a
 7320                b
 7321                c
 7322            "
 7323            .unindent()
 7324        );
 7325        assert_eq!(
 7326            editor.selections.display_ranges(cx),
 7327            [
 7328                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7329                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7330                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7331            ]
 7332        );
 7333
 7334        // Ensure inserting the first character of a multi-byte bracket pair
 7335        // doesn't surround the selections with the bracket.
 7336        editor.handle_input("/", window, cx);
 7337        assert_eq!(
 7338            editor.text(cx),
 7339            "
 7340                /
 7341                /
 7342                /
 7343            "
 7344            .unindent()
 7345        );
 7346        assert_eq!(
 7347            editor.selections.display_ranges(cx),
 7348            [
 7349                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7350                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7351                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7352            ]
 7353        );
 7354
 7355        editor.undo(&Undo, window, cx);
 7356        assert_eq!(
 7357            editor.text(cx),
 7358            "
 7359                a
 7360                b
 7361                c
 7362            "
 7363            .unindent()
 7364        );
 7365        assert_eq!(
 7366            editor.selections.display_ranges(cx),
 7367            [
 7368                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7369                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7370                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7371            ]
 7372        );
 7373
 7374        // Ensure inserting the last character of a multi-byte bracket pair
 7375        // doesn't surround the selections with the bracket.
 7376        editor.handle_input("*", window, cx);
 7377        assert_eq!(
 7378            editor.text(cx),
 7379            "
 7380                *
 7381                *
 7382                *
 7383            "
 7384            .unindent()
 7385        );
 7386        assert_eq!(
 7387            editor.selections.display_ranges(cx),
 7388            [
 7389                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7390                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7391                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7392            ]
 7393        );
 7394    });
 7395}
 7396
 7397#[gpui::test]
 7398async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7399    init_test(cx, |_| {});
 7400
 7401    let language = Arc::new(Language::new(
 7402        LanguageConfig {
 7403            brackets: BracketPairConfig {
 7404                pairs: vec![BracketPair {
 7405                    start: "{".to_string(),
 7406                    end: "}".to_string(),
 7407                    close: true,
 7408                    surround: true,
 7409                    newline: true,
 7410                }],
 7411                ..Default::default()
 7412            },
 7413            autoclose_before: "}".to_string(),
 7414            ..Default::default()
 7415        },
 7416        Some(tree_sitter_rust::LANGUAGE.into()),
 7417    ));
 7418
 7419    let text = r#"
 7420        a
 7421        b
 7422        c
 7423    "#
 7424    .unindent();
 7425
 7426    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7427    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7428    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7429    editor
 7430        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7431        .await;
 7432
 7433    editor.update_in(cx, |editor, window, cx| {
 7434        editor.change_selections(None, window, cx, |s| {
 7435            s.select_ranges([
 7436                Point::new(0, 1)..Point::new(0, 1),
 7437                Point::new(1, 1)..Point::new(1, 1),
 7438                Point::new(2, 1)..Point::new(2, 1),
 7439            ])
 7440        });
 7441
 7442        editor.handle_input("{", window, cx);
 7443        editor.handle_input("{", window, cx);
 7444        editor.handle_input("_", window, cx);
 7445        assert_eq!(
 7446            editor.text(cx),
 7447            "
 7448                a{{_}}
 7449                b{{_}}
 7450                c{{_}}
 7451            "
 7452            .unindent()
 7453        );
 7454        assert_eq!(
 7455            editor.selections.ranges::<Point>(cx),
 7456            [
 7457                Point::new(0, 4)..Point::new(0, 4),
 7458                Point::new(1, 4)..Point::new(1, 4),
 7459                Point::new(2, 4)..Point::new(2, 4)
 7460            ]
 7461        );
 7462
 7463        editor.backspace(&Default::default(), window, cx);
 7464        editor.backspace(&Default::default(), window, cx);
 7465        assert_eq!(
 7466            editor.text(cx),
 7467            "
 7468                a{}
 7469                b{}
 7470                c{}
 7471            "
 7472            .unindent()
 7473        );
 7474        assert_eq!(
 7475            editor.selections.ranges::<Point>(cx),
 7476            [
 7477                Point::new(0, 2)..Point::new(0, 2),
 7478                Point::new(1, 2)..Point::new(1, 2),
 7479                Point::new(2, 2)..Point::new(2, 2)
 7480            ]
 7481        );
 7482
 7483        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7484        assert_eq!(
 7485            editor.text(cx),
 7486            "
 7487                a
 7488                b
 7489                c
 7490            "
 7491            .unindent()
 7492        );
 7493        assert_eq!(
 7494            editor.selections.ranges::<Point>(cx),
 7495            [
 7496                Point::new(0, 1)..Point::new(0, 1),
 7497                Point::new(1, 1)..Point::new(1, 1),
 7498                Point::new(2, 1)..Point::new(2, 1)
 7499            ]
 7500        );
 7501    });
 7502}
 7503
 7504#[gpui::test]
 7505async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7506    init_test(cx, |settings| {
 7507        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7508    });
 7509
 7510    let mut cx = EditorTestContext::new(cx).await;
 7511
 7512    let language = Arc::new(Language::new(
 7513        LanguageConfig {
 7514            brackets: BracketPairConfig {
 7515                pairs: vec![
 7516                    BracketPair {
 7517                        start: "{".to_string(),
 7518                        end: "}".to_string(),
 7519                        close: true,
 7520                        surround: true,
 7521                        newline: true,
 7522                    },
 7523                    BracketPair {
 7524                        start: "(".to_string(),
 7525                        end: ")".to_string(),
 7526                        close: true,
 7527                        surround: true,
 7528                        newline: true,
 7529                    },
 7530                    BracketPair {
 7531                        start: "[".to_string(),
 7532                        end: "]".to_string(),
 7533                        close: false,
 7534                        surround: true,
 7535                        newline: true,
 7536                    },
 7537                ],
 7538                ..Default::default()
 7539            },
 7540            autoclose_before: "})]".to_string(),
 7541            ..Default::default()
 7542        },
 7543        Some(tree_sitter_rust::LANGUAGE.into()),
 7544    ));
 7545
 7546    cx.language_registry().add(language.clone());
 7547    cx.update_buffer(|buffer, cx| {
 7548        buffer.set_language(Some(language), cx);
 7549    });
 7550
 7551    cx.set_state(
 7552        &"
 7553            {(ˇ)}
 7554            [[ˇ]]
 7555            {(ˇ)}
 7556        "
 7557        .unindent(),
 7558    );
 7559
 7560    cx.update_editor(|editor, window, cx| {
 7561        editor.backspace(&Default::default(), window, cx);
 7562        editor.backspace(&Default::default(), window, cx);
 7563    });
 7564
 7565    cx.assert_editor_state(
 7566        &"
 7567            ˇ
 7568            ˇ]]
 7569            ˇ
 7570        "
 7571        .unindent(),
 7572    );
 7573
 7574    cx.update_editor(|editor, window, cx| {
 7575        editor.handle_input("{", window, cx);
 7576        editor.handle_input("{", window, cx);
 7577        editor.move_right(&MoveRight, window, cx);
 7578        editor.move_right(&MoveRight, window, cx);
 7579        editor.move_left(&MoveLeft, window, cx);
 7580        editor.move_left(&MoveLeft, window, cx);
 7581        editor.backspace(&Default::default(), window, cx);
 7582    });
 7583
 7584    cx.assert_editor_state(
 7585        &"
 7586            {ˇ}
 7587            {ˇ}]]
 7588            {ˇ}
 7589        "
 7590        .unindent(),
 7591    );
 7592
 7593    cx.update_editor(|editor, window, cx| {
 7594        editor.backspace(&Default::default(), window, cx);
 7595    });
 7596
 7597    cx.assert_editor_state(
 7598        &"
 7599            ˇ
 7600            ˇ]]
 7601            ˇ
 7602        "
 7603        .unindent(),
 7604    );
 7605}
 7606
 7607#[gpui::test]
 7608async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7609    init_test(cx, |_| {});
 7610
 7611    let language = Arc::new(Language::new(
 7612        LanguageConfig::default(),
 7613        Some(tree_sitter_rust::LANGUAGE.into()),
 7614    ));
 7615
 7616    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7617    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7618    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7619    editor
 7620        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7621        .await;
 7622
 7623    editor.update_in(cx, |editor, window, cx| {
 7624        editor.set_auto_replace_emoji_shortcode(true);
 7625
 7626        editor.handle_input("Hello ", window, cx);
 7627        editor.handle_input(":wave", window, cx);
 7628        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7629
 7630        editor.handle_input(":", window, cx);
 7631        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7632
 7633        editor.handle_input(" :smile", window, cx);
 7634        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7635
 7636        editor.handle_input(":", window, cx);
 7637        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7638
 7639        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7640        editor.handle_input(":wave", window, cx);
 7641        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7642
 7643        editor.handle_input(":", window, cx);
 7644        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7645
 7646        editor.handle_input(":1", window, cx);
 7647        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7648
 7649        editor.handle_input(":", window, cx);
 7650        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7651
 7652        // Ensure shortcode does not get replaced when it is part of a word
 7653        editor.handle_input(" Test:wave", window, cx);
 7654        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7655
 7656        editor.handle_input(":", window, cx);
 7657        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7658
 7659        editor.set_auto_replace_emoji_shortcode(false);
 7660
 7661        // Ensure shortcode does not get replaced when auto replace is off
 7662        editor.handle_input(" :wave", window, cx);
 7663        assert_eq!(
 7664            editor.text(cx),
 7665            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7666        );
 7667
 7668        editor.handle_input(":", window, cx);
 7669        assert_eq!(
 7670            editor.text(cx),
 7671            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7672        );
 7673    });
 7674}
 7675
 7676#[gpui::test]
 7677async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7678    init_test(cx, |_| {});
 7679
 7680    let (text, insertion_ranges) = marked_text_ranges(
 7681        indoc! {"
 7682            ˇ
 7683        "},
 7684        false,
 7685    );
 7686
 7687    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7688    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7689
 7690    _ = editor.update_in(cx, |editor, window, cx| {
 7691        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7692
 7693        editor
 7694            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7695            .unwrap();
 7696
 7697        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7698            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7699            assert_eq!(editor.text(cx), expected_text);
 7700            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7701        }
 7702
 7703        assert(
 7704            editor,
 7705            cx,
 7706            indoc! {"
 7707            type «» =•
 7708            "},
 7709        );
 7710
 7711        assert!(editor.context_menu_visible(), "There should be a matches");
 7712    });
 7713}
 7714
 7715#[gpui::test]
 7716async fn test_snippets(cx: &mut TestAppContext) {
 7717    init_test(cx, |_| {});
 7718
 7719    let (text, insertion_ranges) = marked_text_ranges(
 7720        indoc! {"
 7721            a.ˇ b
 7722            a.ˇ b
 7723            a.ˇ b
 7724        "},
 7725        false,
 7726    );
 7727
 7728    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7729    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7730
 7731    editor.update_in(cx, |editor, window, cx| {
 7732        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7733
 7734        editor
 7735            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7736            .unwrap();
 7737
 7738        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7739            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7740            assert_eq!(editor.text(cx), expected_text);
 7741            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7742        }
 7743
 7744        assert(
 7745            editor,
 7746            cx,
 7747            indoc! {"
 7748                a.f(«one», two, «three») b
 7749                a.f(«one», two, «three») b
 7750                a.f(«one», two, «three») b
 7751            "},
 7752        );
 7753
 7754        // Can't move earlier than the first tab stop
 7755        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7756        assert(
 7757            editor,
 7758            cx,
 7759            indoc! {"
 7760                a.f(«one», two, «three») b
 7761                a.f(«one», two, «three») b
 7762                a.f(«one», two, «three») b
 7763            "},
 7764        );
 7765
 7766        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7767        assert(
 7768            editor,
 7769            cx,
 7770            indoc! {"
 7771                a.f(one, «two», three) b
 7772                a.f(one, «two», three) b
 7773                a.f(one, «two», three) b
 7774            "},
 7775        );
 7776
 7777        editor.move_to_prev_snippet_tabstop(window, cx);
 7778        assert(
 7779            editor,
 7780            cx,
 7781            indoc! {"
 7782                a.f(«one», two, «three») b
 7783                a.f(«one», two, «three») b
 7784                a.f(«one», two, «three») b
 7785            "},
 7786        );
 7787
 7788        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7789        assert(
 7790            editor,
 7791            cx,
 7792            indoc! {"
 7793                a.f(one, «two», three) b
 7794                a.f(one, «two», three) b
 7795                a.f(one, «two», three) b
 7796            "},
 7797        );
 7798        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7799        assert(
 7800            editor,
 7801            cx,
 7802            indoc! {"
 7803                a.f(one, two, three)ˇ b
 7804                a.f(one, two, three)ˇ b
 7805                a.f(one, two, three)ˇ b
 7806            "},
 7807        );
 7808
 7809        // As soon as the last tab stop is reached, snippet state is gone
 7810        editor.move_to_prev_snippet_tabstop(window, cx);
 7811        assert(
 7812            editor,
 7813            cx,
 7814            indoc! {"
 7815                a.f(one, two, three)ˇ b
 7816                a.f(one, two, three)ˇ b
 7817                a.f(one, two, three)ˇ b
 7818            "},
 7819        );
 7820    });
 7821}
 7822
 7823#[gpui::test]
 7824async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7825    init_test(cx, |_| {});
 7826
 7827    let fs = FakeFs::new(cx.executor());
 7828    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7829
 7830    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7831
 7832    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7833    language_registry.add(rust_lang());
 7834    let mut fake_servers = language_registry.register_fake_lsp(
 7835        "Rust",
 7836        FakeLspAdapter {
 7837            capabilities: lsp::ServerCapabilities {
 7838                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7839                ..Default::default()
 7840            },
 7841            ..Default::default()
 7842        },
 7843    );
 7844
 7845    let buffer = project
 7846        .update(cx, |project, cx| {
 7847            project.open_local_buffer(path!("/file.rs"), cx)
 7848        })
 7849        .await
 7850        .unwrap();
 7851
 7852    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7853    let (editor, cx) = cx.add_window_view(|window, cx| {
 7854        build_editor_with_project(project.clone(), buffer, window, cx)
 7855    });
 7856    editor.update_in(cx, |editor, window, cx| {
 7857        editor.set_text("one\ntwo\nthree\n", window, cx)
 7858    });
 7859    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7860
 7861    cx.executor().start_waiting();
 7862    let fake_server = fake_servers.next().await.unwrap();
 7863
 7864    {
 7865        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7866            move |params, _| async move {
 7867                assert_eq!(
 7868                    params.text_document.uri,
 7869                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7870                );
 7871                assert_eq!(params.options.tab_size, 4);
 7872                Ok(Some(vec![lsp::TextEdit::new(
 7873                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7874                    ", ".to_string(),
 7875                )]))
 7876            },
 7877        );
 7878        let save = editor
 7879            .update_in(cx, |editor, window, cx| {
 7880                editor.save(true, project.clone(), window, cx)
 7881            })
 7882            .unwrap();
 7883        cx.executor().start_waiting();
 7884        save.await;
 7885
 7886        assert_eq!(
 7887            editor.update(cx, |editor, cx| editor.text(cx)),
 7888            "one, two\nthree\n"
 7889        );
 7890        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7891    }
 7892
 7893    {
 7894        editor.update_in(cx, |editor, window, cx| {
 7895            editor.set_text("one\ntwo\nthree\n", window, cx)
 7896        });
 7897        assert!(cx.read(|cx| editor.is_dirty(cx)));
 7898
 7899        // Ensure we can still save even if formatting hangs.
 7900        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7901            move |params, _| async move {
 7902                assert_eq!(
 7903                    params.text_document.uri,
 7904                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7905                );
 7906                futures::future::pending::<()>().await;
 7907                unreachable!()
 7908            },
 7909        );
 7910        let save = editor
 7911            .update_in(cx, |editor, window, cx| {
 7912                editor.save(true, project.clone(), window, cx)
 7913            })
 7914            .unwrap();
 7915        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7916        cx.executor().start_waiting();
 7917        save.await;
 7918        assert_eq!(
 7919            editor.update(cx, |editor, cx| editor.text(cx)),
 7920            "one\ntwo\nthree\n"
 7921        );
 7922    }
 7923
 7924    // For non-dirty buffer, no formatting request should be sent
 7925    {
 7926        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7927
 7928        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 7929            panic!("Should not be invoked on non-dirty buffer");
 7930        });
 7931        let save = editor
 7932            .update_in(cx, |editor, window, cx| {
 7933                editor.save(true, project.clone(), window, cx)
 7934            })
 7935            .unwrap();
 7936        cx.executor().start_waiting();
 7937        save.await;
 7938    }
 7939
 7940    // Set rust language override and assert overridden tabsize is sent to language server
 7941    update_test_language_settings(cx, |settings| {
 7942        settings.languages.insert(
 7943            "Rust".into(),
 7944            LanguageSettingsContent {
 7945                tab_size: NonZeroU32::new(8),
 7946                ..Default::default()
 7947            },
 7948        );
 7949    });
 7950
 7951    {
 7952        editor.update_in(cx, |editor, window, cx| {
 7953            editor.set_text("somehting_new\n", window, cx)
 7954        });
 7955        assert!(cx.read(|cx| editor.is_dirty(cx)));
 7956        let _formatting_request_signal = fake_server
 7957            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7958                assert_eq!(
 7959                    params.text_document.uri,
 7960                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7961                );
 7962                assert_eq!(params.options.tab_size, 8);
 7963                Ok(Some(vec![]))
 7964            });
 7965        let save = editor
 7966            .update_in(cx, |editor, window, cx| {
 7967                editor.save(true, project.clone(), window, cx)
 7968            })
 7969            .unwrap();
 7970        cx.executor().start_waiting();
 7971        save.await;
 7972    }
 7973}
 7974
 7975#[gpui::test]
 7976async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7977    init_test(cx, |_| {});
 7978
 7979    let cols = 4;
 7980    let rows = 10;
 7981    let sample_text_1 = sample_text(rows, cols, 'a');
 7982    assert_eq!(
 7983        sample_text_1,
 7984        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7985    );
 7986    let sample_text_2 = sample_text(rows, cols, 'l');
 7987    assert_eq!(
 7988        sample_text_2,
 7989        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7990    );
 7991    let sample_text_3 = sample_text(rows, cols, 'v');
 7992    assert_eq!(
 7993        sample_text_3,
 7994        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7995    );
 7996
 7997    let fs = FakeFs::new(cx.executor());
 7998    fs.insert_tree(
 7999        path!("/a"),
 8000        json!({
 8001            "main.rs": sample_text_1,
 8002            "other.rs": sample_text_2,
 8003            "lib.rs": sample_text_3,
 8004        }),
 8005    )
 8006    .await;
 8007
 8008    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 8009    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 8010    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 8011
 8012    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8013    language_registry.add(rust_lang());
 8014    let mut fake_servers = language_registry.register_fake_lsp(
 8015        "Rust",
 8016        FakeLspAdapter {
 8017            capabilities: lsp::ServerCapabilities {
 8018                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8019                ..Default::default()
 8020            },
 8021            ..Default::default()
 8022        },
 8023    );
 8024
 8025    let worktree = project.update(cx, |project, cx| {
 8026        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 8027        assert_eq!(worktrees.len(), 1);
 8028        worktrees.pop().unwrap()
 8029    });
 8030    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8031
 8032    let buffer_1 = project
 8033        .update(cx, |project, cx| {
 8034            project.open_buffer((worktree_id, "main.rs"), cx)
 8035        })
 8036        .await
 8037        .unwrap();
 8038    let buffer_2 = project
 8039        .update(cx, |project, cx| {
 8040            project.open_buffer((worktree_id, "other.rs"), cx)
 8041        })
 8042        .await
 8043        .unwrap();
 8044    let buffer_3 = project
 8045        .update(cx, |project, cx| {
 8046            project.open_buffer((worktree_id, "lib.rs"), cx)
 8047        })
 8048        .await
 8049        .unwrap();
 8050
 8051    let multi_buffer = cx.new(|cx| {
 8052        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8053        multi_buffer.push_excerpts(
 8054            buffer_1.clone(),
 8055            [
 8056                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8057                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8058                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8059            ],
 8060            cx,
 8061        );
 8062        multi_buffer.push_excerpts(
 8063            buffer_2.clone(),
 8064            [
 8065                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8066                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8067                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8068            ],
 8069            cx,
 8070        );
 8071        multi_buffer.push_excerpts(
 8072            buffer_3.clone(),
 8073            [
 8074                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8075                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8076                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8077            ],
 8078            cx,
 8079        );
 8080        multi_buffer
 8081    });
 8082    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8083        Editor::new(
 8084            EditorMode::full(),
 8085            multi_buffer,
 8086            Some(project.clone()),
 8087            window,
 8088            cx,
 8089        )
 8090    });
 8091
 8092    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8093        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8094            s.select_ranges(Some(1..2))
 8095        });
 8096        editor.insert("|one|two|three|", window, cx);
 8097    });
 8098    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8099    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8100        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8101            s.select_ranges(Some(60..70))
 8102        });
 8103        editor.insert("|four|five|six|", window, cx);
 8104    });
 8105    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8106
 8107    // First two buffers should be edited, but not the third one.
 8108    assert_eq!(
 8109        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8110        "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}",
 8111    );
 8112    buffer_1.update(cx, |buffer, _| {
 8113        assert!(buffer.is_dirty());
 8114        assert_eq!(
 8115            buffer.text(),
 8116            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8117        )
 8118    });
 8119    buffer_2.update(cx, |buffer, _| {
 8120        assert!(buffer.is_dirty());
 8121        assert_eq!(
 8122            buffer.text(),
 8123            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8124        )
 8125    });
 8126    buffer_3.update(cx, |buffer, _| {
 8127        assert!(!buffer.is_dirty());
 8128        assert_eq!(buffer.text(), sample_text_3,)
 8129    });
 8130    cx.executor().run_until_parked();
 8131
 8132    cx.executor().start_waiting();
 8133    let save = multi_buffer_editor
 8134        .update_in(cx, |editor, window, cx| {
 8135            editor.save(true, project.clone(), window, cx)
 8136        })
 8137        .unwrap();
 8138
 8139    let fake_server = fake_servers.next().await.unwrap();
 8140    fake_server
 8141        .server
 8142        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8143            Ok(Some(vec![lsp::TextEdit::new(
 8144                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8145                format!("[{} formatted]", params.text_document.uri),
 8146            )]))
 8147        })
 8148        .detach();
 8149    save.await;
 8150
 8151    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8152    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8153    assert_eq!(
 8154        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8155        uri!(
 8156            "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}"
 8157        ),
 8158    );
 8159    buffer_1.update(cx, |buffer, _| {
 8160        assert!(!buffer.is_dirty());
 8161        assert_eq!(
 8162            buffer.text(),
 8163            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8164        )
 8165    });
 8166    buffer_2.update(cx, |buffer, _| {
 8167        assert!(!buffer.is_dirty());
 8168        assert_eq!(
 8169            buffer.text(),
 8170            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8171        )
 8172    });
 8173    buffer_3.update(cx, |buffer, _| {
 8174        assert!(!buffer.is_dirty());
 8175        assert_eq!(buffer.text(), sample_text_3,)
 8176    });
 8177}
 8178
 8179#[gpui::test]
 8180async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8181    init_test(cx, |_| {});
 8182
 8183    let fs = FakeFs::new(cx.executor());
 8184    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8185
 8186    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8187
 8188    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8189    language_registry.add(rust_lang());
 8190    let mut fake_servers = language_registry.register_fake_lsp(
 8191        "Rust",
 8192        FakeLspAdapter {
 8193            capabilities: lsp::ServerCapabilities {
 8194                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8195                ..Default::default()
 8196            },
 8197            ..Default::default()
 8198        },
 8199    );
 8200
 8201    let buffer = project
 8202        .update(cx, |project, cx| {
 8203            project.open_local_buffer(path!("/file.rs"), cx)
 8204        })
 8205        .await
 8206        .unwrap();
 8207
 8208    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8209    let (editor, cx) = cx.add_window_view(|window, cx| {
 8210        build_editor_with_project(project.clone(), buffer, window, cx)
 8211    });
 8212    editor.update_in(cx, |editor, window, cx| {
 8213        editor.set_text("one\ntwo\nthree\n", window, cx)
 8214    });
 8215    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8216
 8217    cx.executor().start_waiting();
 8218    let fake_server = fake_servers.next().await.unwrap();
 8219
 8220    let save = editor
 8221        .update_in(cx, |editor, window, cx| {
 8222            editor.save(true, project.clone(), window, cx)
 8223        })
 8224        .unwrap();
 8225    fake_server
 8226        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8227            assert_eq!(
 8228                params.text_document.uri,
 8229                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8230            );
 8231            assert_eq!(params.options.tab_size, 4);
 8232            Ok(Some(vec![lsp::TextEdit::new(
 8233                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8234                ", ".to_string(),
 8235            )]))
 8236        })
 8237        .next()
 8238        .await;
 8239    cx.executor().start_waiting();
 8240    save.await;
 8241    assert_eq!(
 8242        editor.update(cx, |editor, cx| editor.text(cx)),
 8243        "one, two\nthree\n"
 8244    );
 8245    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8246
 8247    editor.update_in(cx, |editor, window, cx| {
 8248        editor.set_text("one\ntwo\nthree\n", window, cx)
 8249    });
 8250    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8251
 8252    // Ensure we can still save even if formatting hangs.
 8253    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8254        move |params, _| async move {
 8255            assert_eq!(
 8256                params.text_document.uri,
 8257                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8258            );
 8259            futures::future::pending::<()>().await;
 8260            unreachable!()
 8261        },
 8262    );
 8263    let save = editor
 8264        .update_in(cx, |editor, window, cx| {
 8265            editor.save(true, project.clone(), window, cx)
 8266        })
 8267        .unwrap();
 8268    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8269    cx.executor().start_waiting();
 8270    save.await;
 8271    assert_eq!(
 8272        editor.update(cx, |editor, cx| editor.text(cx)),
 8273        "one\ntwo\nthree\n"
 8274    );
 8275    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8276
 8277    // For non-dirty buffer, no formatting request should be sent
 8278    let save = editor
 8279        .update_in(cx, |editor, window, cx| {
 8280            editor.save(true, project.clone(), window, cx)
 8281        })
 8282        .unwrap();
 8283    let _pending_format_request = fake_server
 8284        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8285            panic!("Should not be invoked on non-dirty buffer");
 8286        })
 8287        .next();
 8288    cx.executor().start_waiting();
 8289    save.await;
 8290
 8291    // Set Rust language override and assert overridden tabsize is sent to language server
 8292    update_test_language_settings(cx, |settings| {
 8293        settings.languages.insert(
 8294            "Rust".into(),
 8295            LanguageSettingsContent {
 8296                tab_size: NonZeroU32::new(8),
 8297                ..Default::default()
 8298            },
 8299        );
 8300    });
 8301
 8302    editor.update_in(cx, |editor, window, cx| {
 8303        editor.set_text("somehting_new\n", window, cx)
 8304    });
 8305    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8306    let save = editor
 8307        .update_in(cx, |editor, window, cx| {
 8308            editor.save(true, project.clone(), window, cx)
 8309        })
 8310        .unwrap();
 8311    fake_server
 8312        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8313            assert_eq!(
 8314                params.text_document.uri,
 8315                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8316            );
 8317            assert_eq!(params.options.tab_size, 8);
 8318            Ok(Some(vec![]))
 8319        })
 8320        .next()
 8321        .await;
 8322    cx.executor().start_waiting();
 8323    save.await;
 8324}
 8325
 8326#[gpui::test]
 8327async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8328    init_test(cx, |settings| {
 8329        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8330            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8331        ))
 8332    });
 8333
 8334    let fs = FakeFs::new(cx.executor());
 8335    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8336
 8337    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8338
 8339    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8340    language_registry.add(Arc::new(Language::new(
 8341        LanguageConfig {
 8342            name: "Rust".into(),
 8343            matcher: LanguageMatcher {
 8344                path_suffixes: vec!["rs".to_string()],
 8345                ..Default::default()
 8346            },
 8347            ..LanguageConfig::default()
 8348        },
 8349        Some(tree_sitter_rust::LANGUAGE.into()),
 8350    )));
 8351    update_test_language_settings(cx, |settings| {
 8352        // Enable Prettier formatting for the same buffer, and ensure
 8353        // LSP is called instead of Prettier.
 8354        settings.defaults.prettier = Some(PrettierSettings {
 8355            allowed: true,
 8356            ..PrettierSettings::default()
 8357        });
 8358    });
 8359    let mut fake_servers = language_registry.register_fake_lsp(
 8360        "Rust",
 8361        FakeLspAdapter {
 8362            capabilities: lsp::ServerCapabilities {
 8363                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8364                ..Default::default()
 8365            },
 8366            ..Default::default()
 8367        },
 8368    );
 8369
 8370    let buffer = project
 8371        .update(cx, |project, cx| {
 8372            project.open_local_buffer(path!("/file.rs"), cx)
 8373        })
 8374        .await
 8375        .unwrap();
 8376
 8377    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8378    let (editor, cx) = cx.add_window_view(|window, cx| {
 8379        build_editor_with_project(project.clone(), buffer, window, cx)
 8380    });
 8381    editor.update_in(cx, |editor, window, cx| {
 8382        editor.set_text("one\ntwo\nthree\n", window, cx)
 8383    });
 8384
 8385    cx.executor().start_waiting();
 8386    let fake_server = fake_servers.next().await.unwrap();
 8387
 8388    let format = editor
 8389        .update_in(cx, |editor, window, cx| {
 8390            editor.perform_format(
 8391                project.clone(),
 8392                FormatTrigger::Manual,
 8393                FormatTarget::Buffers,
 8394                window,
 8395                cx,
 8396            )
 8397        })
 8398        .unwrap();
 8399    fake_server
 8400        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8401            assert_eq!(
 8402                params.text_document.uri,
 8403                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8404            );
 8405            assert_eq!(params.options.tab_size, 4);
 8406            Ok(Some(vec![lsp::TextEdit::new(
 8407                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8408                ", ".to_string(),
 8409            )]))
 8410        })
 8411        .next()
 8412        .await;
 8413    cx.executor().start_waiting();
 8414    format.await;
 8415    assert_eq!(
 8416        editor.update(cx, |editor, cx| editor.text(cx)),
 8417        "one, two\nthree\n"
 8418    );
 8419
 8420    editor.update_in(cx, |editor, window, cx| {
 8421        editor.set_text("one\ntwo\nthree\n", window, cx)
 8422    });
 8423    // Ensure we don't lock if formatting hangs.
 8424    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8425        move |params, _| async move {
 8426            assert_eq!(
 8427                params.text_document.uri,
 8428                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8429            );
 8430            futures::future::pending::<()>().await;
 8431            unreachable!()
 8432        },
 8433    );
 8434    let format = editor
 8435        .update_in(cx, |editor, window, cx| {
 8436            editor.perform_format(
 8437                project,
 8438                FormatTrigger::Manual,
 8439                FormatTarget::Buffers,
 8440                window,
 8441                cx,
 8442            )
 8443        })
 8444        .unwrap();
 8445    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8446    cx.executor().start_waiting();
 8447    format.await;
 8448    assert_eq!(
 8449        editor.update(cx, |editor, cx| editor.text(cx)),
 8450        "one\ntwo\nthree\n"
 8451    );
 8452}
 8453
 8454#[gpui::test]
 8455async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8456    init_test(cx, |settings| {
 8457        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8458        settings.defaults.formatter =
 8459            Some(language_settings::SelectedFormatter::List(FormatterList(
 8460                vec![
 8461                    Formatter::LanguageServer { name: None },
 8462                    Formatter::CodeActions(
 8463                        [
 8464                            ("code-action-1".into(), true),
 8465                            ("code-action-2".into(), true),
 8466                        ]
 8467                        .into_iter()
 8468                        .collect(),
 8469                    ),
 8470                ]
 8471                .into(),
 8472            )))
 8473    });
 8474
 8475    let fs = FakeFs::new(cx.executor());
 8476    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8477        .await;
 8478
 8479    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8480    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8481    language_registry.add(rust_lang());
 8482
 8483    let mut fake_servers = language_registry.register_fake_lsp(
 8484        "Rust",
 8485        FakeLspAdapter {
 8486            capabilities: lsp::ServerCapabilities {
 8487                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8488                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8489                    commands: vec!["the-command-for-code-action-1".into()],
 8490                    ..Default::default()
 8491                }),
 8492                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8493                ..Default::default()
 8494            },
 8495            ..Default::default()
 8496        },
 8497    );
 8498
 8499    let buffer = project
 8500        .update(cx, |project, cx| {
 8501            project.open_local_buffer(path!("/file.rs"), cx)
 8502        })
 8503        .await
 8504        .unwrap();
 8505
 8506    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8507    let (editor, cx) = cx.add_window_view(|window, cx| {
 8508        build_editor_with_project(project.clone(), buffer, window, cx)
 8509    });
 8510
 8511    cx.executor().start_waiting();
 8512
 8513    let fake_server = fake_servers.next().await.unwrap();
 8514    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8515        move |_params, _| async move {
 8516            Ok(Some(vec![lsp::TextEdit::new(
 8517                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8518                "applied-formatting\n".to_string(),
 8519            )]))
 8520        },
 8521    );
 8522    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8523        move |params, _| async move {
 8524            assert_eq!(
 8525                params.context.only,
 8526                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8527            );
 8528            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8529            Ok(Some(vec![
 8530                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8531                    kind: Some("code-action-1".into()),
 8532                    edit: Some(lsp::WorkspaceEdit::new(
 8533                        [(
 8534                            uri.clone(),
 8535                            vec![lsp::TextEdit::new(
 8536                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8537                                "applied-code-action-1-edit\n".to_string(),
 8538                            )],
 8539                        )]
 8540                        .into_iter()
 8541                        .collect(),
 8542                    )),
 8543                    command: Some(lsp::Command {
 8544                        command: "the-command-for-code-action-1".into(),
 8545                        ..Default::default()
 8546                    }),
 8547                    ..Default::default()
 8548                }),
 8549                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8550                    kind: Some("code-action-2".into()),
 8551                    edit: Some(lsp::WorkspaceEdit::new(
 8552                        [(
 8553                            uri.clone(),
 8554                            vec![lsp::TextEdit::new(
 8555                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8556                                "applied-code-action-2-edit\n".to_string(),
 8557                            )],
 8558                        )]
 8559                        .into_iter()
 8560                        .collect(),
 8561                    )),
 8562                    ..Default::default()
 8563                }),
 8564            ]))
 8565        },
 8566    );
 8567
 8568    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8569        move |params, _| async move { Ok(params) }
 8570    });
 8571
 8572    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8573    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8574        let fake = fake_server.clone();
 8575        let lock = command_lock.clone();
 8576        move |params, _| {
 8577            assert_eq!(params.command, "the-command-for-code-action-1");
 8578            let fake = fake.clone();
 8579            let lock = lock.clone();
 8580            async move {
 8581                lock.lock().await;
 8582                fake.server
 8583                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8584                        label: None,
 8585                        edit: lsp::WorkspaceEdit {
 8586                            changes: Some(
 8587                                [(
 8588                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8589                                    vec![lsp::TextEdit {
 8590                                        range: lsp::Range::new(
 8591                                            lsp::Position::new(0, 0),
 8592                                            lsp::Position::new(0, 0),
 8593                                        ),
 8594                                        new_text: "applied-code-action-1-command\n".into(),
 8595                                    }],
 8596                                )]
 8597                                .into_iter()
 8598                                .collect(),
 8599                            ),
 8600                            ..Default::default()
 8601                        },
 8602                    })
 8603                    .await
 8604                    .unwrap();
 8605                Ok(Some(json!(null)))
 8606            }
 8607        }
 8608    });
 8609
 8610    cx.executor().start_waiting();
 8611    editor
 8612        .update_in(cx, |editor, window, cx| {
 8613            editor.perform_format(
 8614                project.clone(),
 8615                FormatTrigger::Manual,
 8616                FormatTarget::Buffers,
 8617                window,
 8618                cx,
 8619            )
 8620        })
 8621        .unwrap()
 8622        .await;
 8623    editor.update(cx, |editor, cx| {
 8624        assert_eq!(
 8625            editor.text(cx),
 8626            r#"
 8627                applied-code-action-2-edit
 8628                applied-code-action-1-command
 8629                applied-code-action-1-edit
 8630                applied-formatting
 8631                one
 8632                two
 8633                three
 8634            "#
 8635            .unindent()
 8636        );
 8637    });
 8638
 8639    editor.update_in(cx, |editor, window, cx| {
 8640        editor.undo(&Default::default(), window, cx);
 8641        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8642    });
 8643
 8644    // Perform a manual edit while waiting for an LSP command
 8645    // that's being run as part of a formatting code action.
 8646    let lock_guard = command_lock.lock().await;
 8647    let format = editor
 8648        .update_in(cx, |editor, window, cx| {
 8649            editor.perform_format(
 8650                project.clone(),
 8651                FormatTrigger::Manual,
 8652                FormatTarget::Buffers,
 8653                window,
 8654                cx,
 8655            )
 8656        })
 8657        .unwrap();
 8658    cx.run_until_parked();
 8659    editor.update(cx, |editor, cx| {
 8660        assert_eq!(
 8661            editor.text(cx),
 8662            r#"
 8663                applied-code-action-1-edit
 8664                applied-formatting
 8665                one
 8666                two
 8667                three
 8668            "#
 8669            .unindent()
 8670        );
 8671
 8672        editor.buffer.update(cx, |buffer, cx| {
 8673            let ix = buffer.len(cx);
 8674            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8675        });
 8676    });
 8677
 8678    // Allow the LSP command to proceed. Because the buffer was edited,
 8679    // the second code action will not be run.
 8680    drop(lock_guard);
 8681    format.await;
 8682    editor.update_in(cx, |editor, window, cx| {
 8683        assert_eq!(
 8684            editor.text(cx),
 8685            r#"
 8686                applied-code-action-1-command
 8687                applied-code-action-1-edit
 8688                applied-formatting
 8689                one
 8690                two
 8691                three
 8692                edited
 8693            "#
 8694            .unindent()
 8695        );
 8696
 8697        // The manual edit is undone first, because it is the last thing the user did
 8698        // (even though the command completed afterwards).
 8699        editor.undo(&Default::default(), window, cx);
 8700        assert_eq!(
 8701            editor.text(cx),
 8702            r#"
 8703                applied-code-action-1-command
 8704                applied-code-action-1-edit
 8705                applied-formatting
 8706                one
 8707                two
 8708                three
 8709            "#
 8710            .unindent()
 8711        );
 8712
 8713        // All the formatting (including the command, which completed after the manual edit)
 8714        // is undone together.
 8715        editor.undo(&Default::default(), window, cx);
 8716        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8717    });
 8718}
 8719
 8720#[gpui::test]
 8721async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8722    init_test(cx, |settings| {
 8723        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8724            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8725        ))
 8726    });
 8727
 8728    let fs = FakeFs::new(cx.executor());
 8729    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8730
 8731    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8732
 8733    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8734    language_registry.add(Arc::new(Language::new(
 8735        LanguageConfig {
 8736            name: "TypeScript".into(),
 8737            matcher: LanguageMatcher {
 8738                path_suffixes: vec!["ts".to_string()],
 8739                ..Default::default()
 8740            },
 8741            ..LanguageConfig::default()
 8742        },
 8743        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8744    )));
 8745    update_test_language_settings(cx, |settings| {
 8746        settings.defaults.prettier = Some(PrettierSettings {
 8747            allowed: true,
 8748            ..PrettierSettings::default()
 8749        });
 8750    });
 8751    let mut fake_servers = language_registry.register_fake_lsp(
 8752        "TypeScript",
 8753        FakeLspAdapter {
 8754            capabilities: lsp::ServerCapabilities {
 8755                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8756                ..Default::default()
 8757            },
 8758            ..Default::default()
 8759        },
 8760    );
 8761
 8762    let buffer = project
 8763        .update(cx, |project, cx| {
 8764            project.open_local_buffer(path!("/file.ts"), cx)
 8765        })
 8766        .await
 8767        .unwrap();
 8768
 8769    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8770    let (editor, cx) = cx.add_window_view(|window, cx| {
 8771        build_editor_with_project(project.clone(), buffer, window, cx)
 8772    });
 8773    editor.update_in(cx, |editor, window, cx| {
 8774        editor.set_text(
 8775            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8776            window,
 8777            cx,
 8778        )
 8779    });
 8780
 8781    cx.executor().start_waiting();
 8782    let fake_server = fake_servers.next().await.unwrap();
 8783
 8784    let format = editor
 8785        .update_in(cx, |editor, window, cx| {
 8786            editor.perform_code_action_kind(
 8787                project.clone(),
 8788                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8789                window,
 8790                cx,
 8791            )
 8792        })
 8793        .unwrap();
 8794    fake_server
 8795        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8796            assert_eq!(
 8797                params.text_document.uri,
 8798                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8799            );
 8800            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8801                lsp::CodeAction {
 8802                    title: "Organize Imports".to_string(),
 8803                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8804                    edit: Some(lsp::WorkspaceEdit {
 8805                        changes: Some(
 8806                            [(
 8807                                params.text_document.uri.clone(),
 8808                                vec![lsp::TextEdit::new(
 8809                                    lsp::Range::new(
 8810                                        lsp::Position::new(1, 0),
 8811                                        lsp::Position::new(2, 0),
 8812                                    ),
 8813                                    "".to_string(),
 8814                                )],
 8815                            )]
 8816                            .into_iter()
 8817                            .collect(),
 8818                        ),
 8819                        ..Default::default()
 8820                    }),
 8821                    ..Default::default()
 8822                },
 8823            )]))
 8824        })
 8825        .next()
 8826        .await;
 8827    cx.executor().start_waiting();
 8828    format.await;
 8829    assert_eq!(
 8830        editor.update(cx, |editor, cx| editor.text(cx)),
 8831        "import { a } from 'module';\n\nconst x = a;\n"
 8832    );
 8833
 8834    editor.update_in(cx, |editor, window, cx| {
 8835        editor.set_text(
 8836            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8837            window,
 8838            cx,
 8839        )
 8840    });
 8841    // Ensure we don't lock if code action hangs.
 8842    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8843        move |params, _| async move {
 8844            assert_eq!(
 8845                params.text_document.uri,
 8846                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8847            );
 8848            futures::future::pending::<()>().await;
 8849            unreachable!()
 8850        },
 8851    );
 8852    let format = editor
 8853        .update_in(cx, |editor, window, cx| {
 8854            editor.perform_code_action_kind(
 8855                project,
 8856                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8857                window,
 8858                cx,
 8859            )
 8860        })
 8861        .unwrap();
 8862    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8863    cx.executor().start_waiting();
 8864    format.await;
 8865    assert_eq!(
 8866        editor.update(cx, |editor, cx| editor.text(cx)),
 8867        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8868    );
 8869}
 8870
 8871#[gpui::test]
 8872async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8873    init_test(cx, |_| {});
 8874
 8875    let mut cx = EditorLspTestContext::new_rust(
 8876        lsp::ServerCapabilities {
 8877            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8878            ..Default::default()
 8879        },
 8880        cx,
 8881    )
 8882    .await;
 8883
 8884    cx.set_state(indoc! {"
 8885        one.twoˇ
 8886    "});
 8887
 8888    // The format request takes a long time. When it completes, it inserts
 8889    // a newline and an indent before the `.`
 8890    cx.lsp
 8891        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8892            let executor = cx.background_executor().clone();
 8893            async move {
 8894                executor.timer(Duration::from_millis(100)).await;
 8895                Ok(Some(vec![lsp::TextEdit {
 8896                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8897                    new_text: "\n    ".into(),
 8898                }]))
 8899            }
 8900        });
 8901
 8902    // Submit a format request.
 8903    let format_1 = cx
 8904        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8905        .unwrap();
 8906    cx.executor().run_until_parked();
 8907
 8908    // Submit a second format request.
 8909    let format_2 = cx
 8910        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8911        .unwrap();
 8912    cx.executor().run_until_parked();
 8913
 8914    // Wait for both format requests to complete
 8915    cx.executor().advance_clock(Duration::from_millis(200));
 8916    cx.executor().start_waiting();
 8917    format_1.await.unwrap();
 8918    cx.executor().start_waiting();
 8919    format_2.await.unwrap();
 8920
 8921    // The formatting edits only happens once.
 8922    cx.assert_editor_state(indoc! {"
 8923        one
 8924            .twoˇ
 8925    "});
 8926}
 8927
 8928#[gpui::test]
 8929async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8930    init_test(cx, |settings| {
 8931        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8932    });
 8933
 8934    let mut cx = EditorLspTestContext::new_rust(
 8935        lsp::ServerCapabilities {
 8936            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8937            ..Default::default()
 8938        },
 8939        cx,
 8940    )
 8941    .await;
 8942
 8943    // Set up a buffer white some trailing whitespace and no trailing newline.
 8944    cx.set_state(
 8945        &[
 8946            "one ",   //
 8947            "twoˇ",   //
 8948            "three ", //
 8949            "four",   //
 8950        ]
 8951        .join("\n"),
 8952    );
 8953
 8954    // Submit a format request.
 8955    let format = cx
 8956        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8957        .unwrap();
 8958
 8959    // Record which buffer changes have been sent to the language server
 8960    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8961    cx.lsp
 8962        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8963            let buffer_changes = buffer_changes.clone();
 8964            move |params, _| {
 8965                buffer_changes.lock().extend(
 8966                    params
 8967                        .content_changes
 8968                        .into_iter()
 8969                        .map(|e| (e.range.unwrap(), e.text)),
 8970                );
 8971            }
 8972        });
 8973
 8974    // Handle formatting requests to the language server.
 8975    cx.lsp
 8976        .set_request_handler::<lsp::request::Formatting, _, _>({
 8977            let buffer_changes = buffer_changes.clone();
 8978            move |_, _| {
 8979                // When formatting is requested, trailing whitespace has already been stripped,
 8980                // and the trailing newline has already been added.
 8981                assert_eq!(
 8982                    &buffer_changes.lock()[1..],
 8983                    &[
 8984                        (
 8985                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8986                            "".into()
 8987                        ),
 8988                        (
 8989                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8990                            "".into()
 8991                        ),
 8992                        (
 8993                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8994                            "\n".into()
 8995                        ),
 8996                    ]
 8997                );
 8998
 8999                // Insert blank lines between each line of the buffer.
 9000                async move {
 9001                    Ok(Some(vec![
 9002                        lsp::TextEdit {
 9003                            range: lsp::Range::new(
 9004                                lsp::Position::new(1, 0),
 9005                                lsp::Position::new(1, 0),
 9006                            ),
 9007                            new_text: "\n".into(),
 9008                        },
 9009                        lsp::TextEdit {
 9010                            range: lsp::Range::new(
 9011                                lsp::Position::new(2, 0),
 9012                                lsp::Position::new(2, 0),
 9013                            ),
 9014                            new_text: "\n".into(),
 9015                        },
 9016                    ]))
 9017                }
 9018            }
 9019        });
 9020
 9021    // After formatting the buffer, the trailing whitespace is stripped,
 9022    // a newline is appended, and the edits provided by the language server
 9023    // have been applied.
 9024    format.await.unwrap();
 9025    cx.assert_editor_state(
 9026        &[
 9027            "one",   //
 9028            "",      //
 9029            "twoˇ",  //
 9030            "",      //
 9031            "three", //
 9032            "four",  //
 9033            "",      //
 9034        ]
 9035        .join("\n"),
 9036    );
 9037
 9038    // Undoing the formatting undoes the trailing whitespace removal, the
 9039    // trailing newline, and the LSP edits.
 9040    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9041    cx.assert_editor_state(
 9042        &[
 9043            "one ",   //
 9044            "twoˇ",   //
 9045            "three ", //
 9046            "four",   //
 9047        ]
 9048        .join("\n"),
 9049    );
 9050}
 9051
 9052#[gpui::test]
 9053async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9054    cx: &mut TestAppContext,
 9055) {
 9056    init_test(cx, |_| {});
 9057
 9058    cx.update(|cx| {
 9059        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9060            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9061                settings.auto_signature_help = Some(true);
 9062            });
 9063        });
 9064    });
 9065
 9066    let mut cx = EditorLspTestContext::new_rust(
 9067        lsp::ServerCapabilities {
 9068            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9069                ..Default::default()
 9070            }),
 9071            ..Default::default()
 9072        },
 9073        cx,
 9074    )
 9075    .await;
 9076
 9077    let language = Language::new(
 9078        LanguageConfig {
 9079            name: "Rust".into(),
 9080            brackets: BracketPairConfig {
 9081                pairs: vec![
 9082                    BracketPair {
 9083                        start: "{".to_string(),
 9084                        end: "}".to_string(),
 9085                        close: true,
 9086                        surround: true,
 9087                        newline: true,
 9088                    },
 9089                    BracketPair {
 9090                        start: "(".to_string(),
 9091                        end: ")".to_string(),
 9092                        close: true,
 9093                        surround: true,
 9094                        newline: true,
 9095                    },
 9096                    BracketPair {
 9097                        start: "/*".to_string(),
 9098                        end: " */".to_string(),
 9099                        close: true,
 9100                        surround: true,
 9101                        newline: true,
 9102                    },
 9103                    BracketPair {
 9104                        start: "[".to_string(),
 9105                        end: "]".to_string(),
 9106                        close: false,
 9107                        surround: false,
 9108                        newline: true,
 9109                    },
 9110                    BracketPair {
 9111                        start: "\"".to_string(),
 9112                        end: "\"".to_string(),
 9113                        close: true,
 9114                        surround: true,
 9115                        newline: false,
 9116                    },
 9117                    BracketPair {
 9118                        start: "<".to_string(),
 9119                        end: ">".to_string(),
 9120                        close: false,
 9121                        surround: true,
 9122                        newline: true,
 9123                    },
 9124                ],
 9125                ..Default::default()
 9126            },
 9127            autoclose_before: "})]".to_string(),
 9128            ..Default::default()
 9129        },
 9130        Some(tree_sitter_rust::LANGUAGE.into()),
 9131    );
 9132    let language = Arc::new(language);
 9133
 9134    cx.language_registry().add(language.clone());
 9135    cx.update_buffer(|buffer, cx| {
 9136        buffer.set_language(Some(language), cx);
 9137    });
 9138
 9139    cx.set_state(
 9140        &r#"
 9141            fn main() {
 9142                sampleˇ
 9143            }
 9144        "#
 9145        .unindent(),
 9146    );
 9147
 9148    cx.update_editor(|editor, window, cx| {
 9149        editor.handle_input("(", window, cx);
 9150    });
 9151    cx.assert_editor_state(
 9152        &"
 9153            fn main() {
 9154                sample(ˇ)
 9155            }
 9156        "
 9157        .unindent(),
 9158    );
 9159
 9160    let mocked_response = lsp::SignatureHelp {
 9161        signatures: vec![lsp::SignatureInformation {
 9162            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9163            documentation: None,
 9164            parameters: Some(vec![
 9165                lsp::ParameterInformation {
 9166                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9167                    documentation: None,
 9168                },
 9169                lsp::ParameterInformation {
 9170                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9171                    documentation: None,
 9172                },
 9173            ]),
 9174            active_parameter: None,
 9175        }],
 9176        active_signature: Some(0),
 9177        active_parameter: Some(0),
 9178    };
 9179    handle_signature_help_request(&mut cx, mocked_response).await;
 9180
 9181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9182        .await;
 9183
 9184    cx.editor(|editor, _, _| {
 9185        let signature_help_state = editor.signature_help_state.popover().cloned();
 9186        assert_eq!(
 9187            signature_help_state.unwrap().label,
 9188            "param1: u8, param2: u8"
 9189        );
 9190    });
 9191}
 9192
 9193#[gpui::test]
 9194async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9195    init_test(cx, |_| {});
 9196
 9197    cx.update(|cx| {
 9198        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9199            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9200                settings.auto_signature_help = Some(false);
 9201                settings.show_signature_help_after_edits = Some(false);
 9202            });
 9203        });
 9204    });
 9205
 9206    let mut cx = EditorLspTestContext::new_rust(
 9207        lsp::ServerCapabilities {
 9208            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9209                ..Default::default()
 9210            }),
 9211            ..Default::default()
 9212        },
 9213        cx,
 9214    )
 9215    .await;
 9216
 9217    let language = Language::new(
 9218        LanguageConfig {
 9219            name: "Rust".into(),
 9220            brackets: BracketPairConfig {
 9221                pairs: vec![
 9222                    BracketPair {
 9223                        start: "{".to_string(),
 9224                        end: "}".to_string(),
 9225                        close: true,
 9226                        surround: true,
 9227                        newline: true,
 9228                    },
 9229                    BracketPair {
 9230                        start: "(".to_string(),
 9231                        end: ")".to_string(),
 9232                        close: true,
 9233                        surround: true,
 9234                        newline: true,
 9235                    },
 9236                    BracketPair {
 9237                        start: "/*".to_string(),
 9238                        end: " */".to_string(),
 9239                        close: true,
 9240                        surround: true,
 9241                        newline: true,
 9242                    },
 9243                    BracketPair {
 9244                        start: "[".to_string(),
 9245                        end: "]".to_string(),
 9246                        close: false,
 9247                        surround: false,
 9248                        newline: true,
 9249                    },
 9250                    BracketPair {
 9251                        start: "\"".to_string(),
 9252                        end: "\"".to_string(),
 9253                        close: true,
 9254                        surround: true,
 9255                        newline: false,
 9256                    },
 9257                    BracketPair {
 9258                        start: "<".to_string(),
 9259                        end: ">".to_string(),
 9260                        close: false,
 9261                        surround: true,
 9262                        newline: true,
 9263                    },
 9264                ],
 9265                ..Default::default()
 9266            },
 9267            autoclose_before: "})]".to_string(),
 9268            ..Default::default()
 9269        },
 9270        Some(tree_sitter_rust::LANGUAGE.into()),
 9271    );
 9272    let language = Arc::new(language);
 9273
 9274    cx.language_registry().add(language.clone());
 9275    cx.update_buffer(|buffer, cx| {
 9276        buffer.set_language(Some(language), cx);
 9277    });
 9278
 9279    // Ensure that signature_help is not called when no signature help is enabled.
 9280    cx.set_state(
 9281        &r#"
 9282            fn main() {
 9283                sampleˇ
 9284            }
 9285        "#
 9286        .unindent(),
 9287    );
 9288    cx.update_editor(|editor, window, cx| {
 9289        editor.handle_input("(", window, cx);
 9290    });
 9291    cx.assert_editor_state(
 9292        &"
 9293            fn main() {
 9294                sample(ˇ)
 9295            }
 9296        "
 9297        .unindent(),
 9298    );
 9299    cx.editor(|editor, _, _| {
 9300        assert!(editor.signature_help_state.task().is_none());
 9301    });
 9302
 9303    let mocked_response = lsp::SignatureHelp {
 9304        signatures: vec![lsp::SignatureInformation {
 9305            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9306            documentation: None,
 9307            parameters: Some(vec![
 9308                lsp::ParameterInformation {
 9309                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9310                    documentation: None,
 9311                },
 9312                lsp::ParameterInformation {
 9313                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9314                    documentation: None,
 9315                },
 9316            ]),
 9317            active_parameter: None,
 9318        }],
 9319        active_signature: Some(0),
 9320        active_parameter: Some(0),
 9321    };
 9322
 9323    // Ensure that signature_help is called when enabled afte edits
 9324    cx.update(|_, cx| {
 9325        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9326            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9327                settings.auto_signature_help = Some(false);
 9328                settings.show_signature_help_after_edits = Some(true);
 9329            });
 9330        });
 9331    });
 9332    cx.set_state(
 9333        &r#"
 9334            fn main() {
 9335                sampleˇ
 9336            }
 9337        "#
 9338        .unindent(),
 9339    );
 9340    cx.update_editor(|editor, window, cx| {
 9341        editor.handle_input("(", window, cx);
 9342    });
 9343    cx.assert_editor_state(
 9344        &"
 9345            fn main() {
 9346                sample(ˇ)
 9347            }
 9348        "
 9349        .unindent(),
 9350    );
 9351    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9352    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9353        .await;
 9354    cx.update_editor(|editor, _, _| {
 9355        let signature_help_state = editor.signature_help_state.popover().cloned();
 9356        assert!(signature_help_state.is_some());
 9357        assert_eq!(
 9358            signature_help_state.unwrap().label,
 9359            "param1: u8, param2: u8"
 9360        );
 9361        editor.signature_help_state = SignatureHelpState::default();
 9362    });
 9363
 9364    // Ensure that signature_help is called when auto signature help override is enabled
 9365    cx.update(|_, cx| {
 9366        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9367            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9368                settings.auto_signature_help = Some(true);
 9369                settings.show_signature_help_after_edits = Some(false);
 9370            });
 9371        });
 9372    });
 9373    cx.set_state(
 9374        &r#"
 9375            fn main() {
 9376                sampleˇ
 9377            }
 9378        "#
 9379        .unindent(),
 9380    );
 9381    cx.update_editor(|editor, window, cx| {
 9382        editor.handle_input("(", window, cx);
 9383    });
 9384    cx.assert_editor_state(
 9385        &"
 9386            fn main() {
 9387                sample(ˇ)
 9388            }
 9389        "
 9390        .unindent(),
 9391    );
 9392    handle_signature_help_request(&mut cx, mocked_response).await;
 9393    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9394        .await;
 9395    cx.editor(|editor, _, _| {
 9396        let signature_help_state = editor.signature_help_state.popover().cloned();
 9397        assert!(signature_help_state.is_some());
 9398        assert_eq!(
 9399            signature_help_state.unwrap().label,
 9400            "param1: u8, param2: u8"
 9401        );
 9402    });
 9403}
 9404
 9405#[gpui::test]
 9406async fn test_signature_help(cx: &mut TestAppContext) {
 9407    init_test(cx, |_| {});
 9408    cx.update(|cx| {
 9409        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9410            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9411                settings.auto_signature_help = Some(true);
 9412            });
 9413        });
 9414    });
 9415
 9416    let mut cx = EditorLspTestContext::new_rust(
 9417        lsp::ServerCapabilities {
 9418            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9419                ..Default::default()
 9420            }),
 9421            ..Default::default()
 9422        },
 9423        cx,
 9424    )
 9425    .await;
 9426
 9427    // A test that directly calls `show_signature_help`
 9428    cx.update_editor(|editor, window, cx| {
 9429        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9430    });
 9431
 9432    let mocked_response = lsp::SignatureHelp {
 9433        signatures: vec![lsp::SignatureInformation {
 9434            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9435            documentation: None,
 9436            parameters: Some(vec![
 9437                lsp::ParameterInformation {
 9438                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9439                    documentation: None,
 9440                },
 9441                lsp::ParameterInformation {
 9442                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9443                    documentation: None,
 9444                },
 9445            ]),
 9446            active_parameter: None,
 9447        }],
 9448        active_signature: Some(0),
 9449        active_parameter: Some(0),
 9450    };
 9451    handle_signature_help_request(&mut cx, mocked_response).await;
 9452
 9453    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9454        .await;
 9455
 9456    cx.editor(|editor, _, _| {
 9457        let signature_help_state = editor.signature_help_state.popover().cloned();
 9458        assert!(signature_help_state.is_some());
 9459        assert_eq!(
 9460            signature_help_state.unwrap().label,
 9461            "param1: u8, param2: u8"
 9462        );
 9463    });
 9464
 9465    // When exiting outside from inside the brackets, `signature_help` is closed.
 9466    cx.set_state(indoc! {"
 9467        fn main() {
 9468            sample(ˇ);
 9469        }
 9470
 9471        fn sample(param1: u8, param2: u8) {}
 9472    "});
 9473
 9474    cx.update_editor(|editor, window, cx| {
 9475        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9476    });
 9477
 9478    let mocked_response = lsp::SignatureHelp {
 9479        signatures: Vec::new(),
 9480        active_signature: None,
 9481        active_parameter: None,
 9482    };
 9483    handle_signature_help_request(&mut cx, mocked_response).await;
 9484
 9485    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9486        .await;
 9487
 9488    cx.editor(|editor, _, _| {
 9489        assert!(!editor.signature_help_state.is_shown());
 9490    });
 9491
 9492    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9493    cx.set_state(indoc! {"
 9494        fn main() {
 9495            sample(ˇ);
 9496        }
 9497
 9498        fn sample(param1: u8, param2: u8) {}
 9499    "});
 9500
 9501    let mocked_response = lsp::SignatureHelp {
 9502        signatures: vec![lsp::SignatureInformation {
 9503            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9504            documentation: None,
 9505            parameters: Some(vec![
 9506                lsp::ParameterInformation {
 9507                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9508                    documentation: None,
 9509                },
 9510                lsp::ParameterInformation {
 9511                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9512                    documentation: None,
 9513                },
 9514            ]),
 9515            active_parameter: None,
 9516        }],
 9517        active_signature: Some(0),
 9518        active_parameter: Some(0),
 9519    };
 9520    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9521    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9522        .await;
 9523    cx.editor(|editor, _, _| {
 9524        assert!(editor.signature_help_state.is_shown());
 9525    });
 9526
 9527    // Restore the popover with more parameter input
 9528    cx.set_state(indoc! {"
 9529        fn main() {
 9530            sample(param1, param2ˇ);
 9531        }
 9532
 9533        fn sample(param1: u8, param2: u8) {}
 9534    "});
 9535
 9536    let mocked_response = lsp::SignatureHelp {
 9537        signatures: vec![lsp::SignatureInformation {
 9538            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9539            documentation: None,
 9540            parameters: Some(vec![
 9541                lsp::ParameterInformation {
 9542                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9543                    documentation: None,
 9544                },
 9545                lsp::ParameterInformation {
 9546                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9547                    documentation: None,
 9548                },
 9549            ]),
 9550            active_parameter: None,
 9551        }],
 9552        active_signature: Some(0),
 9553        active_parameter: Some(1),
 9554    };
 9555    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9556    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9557        .await;
 9558
 9559    // When selecting a range, the popover is gone.
 9560    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9561    cx.update_editor(|editor, window, cx| {
 9562        editor.change_selections(None, window, cx, |s| {
 9563            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9564        })
 9565    });
 9566    cx.assert_editor_state(indoc! {"
 9567        fn main() {
 9568            sample(param1, «ˇparam2»);
 9569        }
 9570
 9571        fn sample(param1: u8, param2: u8) {}
 9572    "});
 9573    cx.editor(|editor, _, _| {
 9574        assert!(!editor.signature_help_state.is_shown());
 9575    });
 9576
 9577    // When unselecting again, the popover is back if within the brackets.
 9578    cx.update_editor(|editor, window, cx| {
 9579        editor.change_selections(None, window, cx, |s| {
 9580            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9581        })
 9582    });
 9583    cx.assert_editor_state(indoc! {"
 9584        fn main() {
 9585            sample(param1, ˇparam2);
 9586        }
 9587
 9588        fn sample(param1: u8, param2: u8) {}
 9589    "});
 9590    handle_signature_help_request(&mut cx, mocked_response).await;
 9591    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9592        .await;
 9593    cx.editor(|editor, _, _| {
 9594        assert!(editor.signature_help_state.is_shown());
 9595    });
 9596
 9597    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9598    cx.update_editor(|editor, window, cx| {
 9599        editor.change_selections(None, window, cx, |s| {
 9600            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9601            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9602        })
 9603    });
 9604    cx.assert_editor_state(indoc! {"
 9605        fn main() {
 9606            sample(param1, ˇparam2);
 9607        }
 9608
 9609        fn sample(param1: u8, param2: u8) {}
 9610    "});
 9611
 9612    let mocked_response = lsp::SignatureHelp {
 9613        signatures: vec![lsp::SignatureInformation {
 9614            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9615            documentation: None,
 9616            parameters: Some(vec![
 9617                lsp::ParameterInformation {
 9618                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9619                    documentation: None,
 9620                },
 9621                lsp::ParameterInformation {
 9622                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9623                    documentation: None,
 9624                },
 9625            ]),
 9626            active_parameter: None,
 9627        }],
 9628        active_signature: Some(0),
 9629        active_parameter: Some(1),
 9630    };
 9631    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9632    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9633        .await;
 9634    cx.update_editor(|editor, _, cx| {
 9635        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9636    });
 9637    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9638        .await;
 9639    cx.update_editor(|editor, window, cx| {
 9640        editor.change_selections(None, window, cx, |s| {
 9641            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9642        })
 9643    });
 9644    cx.assert_editor_state(indoc! {"
 9645        fn main() {
 9646            sample(param1, «ˇparam2»);
 9647        }
 9648
 9649        fn sample(param1: u8, param2: u8) {}
 9650    "});
 9651    cx.update_editor(|editor, window, cx| {
 9652        editor.change_selections(None, window, cx, |s| {
 9653            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9654        })
 9655    });
 9656    cx.assert_editor_state(indoc! {"
 9657        fn main() {
 9658            sample(param1, ˇparam2);
 9659        }
 9660
 9661        fn sample(param1: u8, param2: u8) {}
 9662    "});
 9663    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9664        .await;
 9665}
 9666
 9667#[gpui::test]
 9668async fn test_completion_mode(cx: &mut TestAppContext) {
 9669    init_test(cx, |_| {});
 9670    let mut cx = EditorLspTestContext::new_rust(
 9671        lsp::ServerCapabilities {
 9672            completion_provider: Some(lsp::CompletionOptions {
 9673                resolve_provider: Some(true),
 9674                ..Default::default()
 9675            }),
 9676            ..Default::default()
 9677        },
 9678        cx,
 9679    )
 9680    .await;
 9681
 9682    struct Run {
 9683        run_description: &'static str,
 9684        initial_state: String,
 9685        buffer_marked_text: String,
 9686        completion_text: &'static str,
 9687        expected_with_insert_mode: String,
 9688        expected_with_replace_mode: String,
 9689        expected_with_replace_subsequence_mode: String,
 9690        expected_with_replace_suffix_mode: String,
 9691    }
 9692
 9693    let runs = [
 9694        Run {
 9695            run_description: "Start of word matches completion text",
 9696            initial_state: "before ediˇ after".into(),
 9697            buffer_marked_text: "before <edi|> after".into(),
 9698            completion_text: "editor",
 9699            expected_with_insert_mode: "before editorˇ after".into(),
 9700            expected_with_replace_mode: "before editorˇ after".into(),
 9701            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9702            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9703        },
 9704        Run {
 9705            run_description: "Accept same text at the middle of the word",
 9706            initial_state: "before ediˇtor after".into(),
 9707            buffer_marked_text: "before <edi|tor> after".into(),
 9708            completion_text: "editor",
 9709            expected_with_insert_mode: "before editorˇtor after".into(),
 9710            expected_with_replace_mode: "before editorˇ after".into(),
 9711            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9712            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9713        },
 9714        Run {
 9715            run_description: "End of word matches completion text -- cursor at end",
 9716            initial_state: "before torˇ after".into(),
 9717            buffer_marked_text: "before <tor|> after".into(),
 9718            completion_text: "editor",
 9719            expected_with_insert_mode: "before editorˇ after".into(),
 9720            expected_with_replace_mode: "before editorˇ after".into(),
 9721            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9722            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9723        },
 9724        Run {
 9725            run_description: "End of word matches completion text -- cursor at start",
 9726            initial_state: "before ˇtor after".into(),
 9727            buffer_marked_text: "before <|tor> after".into(),
 9728            completion_text: "editor",
 9729            expected_with_insert_mode: "before editorˇtor after".into(),
 9730            expected_with_replace_mode: "before editorˇ after".into(),
 9731            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9732            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9733        },
 9734        Run {
 9735            run_description: "Prepend text containing whitespace",
 9736            initial_state: "pˇfield: bool".into(),
 9737            buffer_marked_text: "<p|field>: bool".into(),
 9738            completion_text: "pub ",
 9739            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9740            expected_with_replace_mode: "pub ˇ: bool".into(),
 9741            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9742            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9743        },
 9744        Run {
 9745            run_description: "Add element to start of list",
 9746            initial_state: "[element_ˇelement_2]".into(),
 9747            buffer_marked_text: "[<element_|element_2>]".into(),
 9748            completion_text: "element_1",
 9749            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9750            expected_with_replace_mode: "[element_1ˇ]".into(),
 9751            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9752            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9753        },
 9754        Run {
 9755            run_description: "Add element to start of list -- first and second elements are equal",
 9756            initial_state: "[elˇelement]".into(),
 9757            buffer_marked_text: "[<el|element>]".into(),
 9758            completion_text: "element",
 9759            expected_with_insert_mode: "[elementˇelement]".into(),
 9760            expected_with_replace_mode: "[elementˇ]".into(),
 9761            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9762            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9763        },
 9764        Run {
 9765            run_description: "Ends with matching suffix",
 9766            initial_state: "SubˇError".into(),
 9767            buffer_marked_text: "<Sub|Error>".into(),
 9768            completion_text: "SubscriptionError",
 9769            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9770            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9771            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9772            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9773        },
 9774        Run {
 9775            run_description: "Suffix is a subsequence -- contiguous",
 9776            initial_state: "SubˇErr".into(),
 9777            buffer_marked_text: "<Sub|Err>".into(),
 9778            completion_text: "SubscriptionError",
 9779            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9780            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9781            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9782            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9783        },
 9784        Run {
 9785            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9786            initial_state: "Suˇscrirr".into(),
 9787            buffer_marked_text: "<Su|scrirr>".into(),
 9788            completion_text: "SubscriptionError",
 9789            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9790            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9791            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9792            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9793        },
 9794        Run {
 9795            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9796            initial_state: "foo(indˇix)".into(),
 9797            buffer_marked_text: "foo(<ind|ix>)".into(),
 9798            completion_text: "node_index",
 9799            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9800            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9801            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9802            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9803        },
 9804    ];
 9805
 9806    for run in runs {
 9807        let run_variations = [
 9808            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9809            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9810            (
 9811                LspInsertMode::ReplaceSubsequence,
 9812                run.expected_with_replace_subsequence_mode,
 9813            ),
 9814            (
 9815                LspInsertMode::ReplaceSuffix,
 9816                run.expected_with_replace_suffix_mode,
 9817            ),
 9818        ];
 9819
 9820        for (lsp_insert_mode, expected_text) in run_variations {
 9821            eprintln!(
 9822                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9823                run.run_description,
 9824            );
 9825
 9826            update_test_language_settings(&mut cx, |settings| {
 9827                settings.defaults.completions = Some(CompletionSettings {
 9828                    lsp_insert_mode,
 9829                    words: WordsCompletionMode::Disabled,
 9830                    lsp: true,
 9831                    lsp_fetch_timeout_ms: 0,
 9832                });
 9833            });
 9834
 9835            cx.set_state(&run.initial_state);
 9836            cx.update_editor(|editor, window, cx| {
 9837                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9838            });
 9839
 9840            let counter = Arc::new(AtomicUsize::new(0));
 9841            handle_completion_request_with_insert_and_replace(
 9842                &mut cx,
 9843                &run.buffer_marked_text,
 9844                vec![run.completion_text],
 9845                counter.clone(),
 9846            )
 9847            .await;
 9848            cx.condition(|editor, _| editor.context_menu_visible())
 9849                .await;
 9850            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9851
 9852            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9853                editor
 9854                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9855                    .unwrap()
 9856            });
 9857            cx.assert_editor_state(&expected_text);
 9858            handle_resolve_completion_request(&mut cx, None).await;
 9859            apply_additional_edits.await.unwrap();
 9860        }
 9861    }
 9862}
 9863
 9864#[gpui::test]
 9865async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9866    init_test(cx, |_| {});
 9867    let mut cx = EditorLspTestContext::new_rust(
 9868        lsp::ServerCapabilities {
 9869            completion_provider: Some(lsp::CompletionOptions {
 9870                resolve_provider: Some(true),
 9871                ..Default::default()
 9872            }),
 9873            ..Default::default()
 9874        },
 9875        cx,
 9876    )
 9877    .await;
 9878
 9879    let initial_state = "SubˇError";
 9880    let buffer_marked_text = "<Sub|Error>";
 9881    let completion_text = "SubscriptionError";
 9882    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9883    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9884
 9885    update_test_language_settings(&mut cx, |settings| {
 9886        settings.defaults.completions = Some(CompletionSettings {
 9887            words: WordsCompletionMode::Disabled,
 9888            // set the opposite here to ensure that the action is overriding the default behavior
 9889            lsp_insert_mode: LspInsertMode::Insert,
 9890            lsp: true,
 9891            lsp_fetch_timeout_ms: 0,
 9892        });
 9893    });
 9894
 9895    cx.set_state(initial_state);
 9896    cx.update_editor(|editor, window, cx| {
 9897        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9898    });
 9899
 9900    let counter = Arc::new(AtomicUsize::new(0));
 9901    handle_completion_request_with_insert_and_replace(
 9902        &mut cx,
 9903        &buffer_marked_text,
 9904        vec![completion_text],
 9905        counter.clone(),
 9906    )
 9907    .await;
 9908    cx.condition(|editor, _| editor.context_menu_visible())
 9909        .await;
 9910    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9911
 9912    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9913        editor
 9914            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9915            .unwrap()
 9916    });
 9917    cx.assert_editor_state(&expected_with_replace_mode);
 9918    handle_resolve_completion_request(&mut cx, None).await;
 9919    apply_additional_edits.await.unwrap();
 9920
 9921    update_test_language_settings(&mut cx, |settings| {
 9922        settings.defaults.completions = Some(CompletionSettings {
 9923            words: WordsCompletionMode::Disabled,
 9924            // set the opposite here to ensure that the action is overriding the default behavior
 9925            lsp_insert_mode: LspInsertMode::Replace,
 9926            lsp: true,
 9927            lsp_fetch_timeout_ms: 0,
 9928        });
 9929    });
 9930
 9931    cx.set_state(initial_state);
 9932    cx.update_editor(|editor, window, cx| {
 9933        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9934    });
 9935    handle_completion_request_with_insert_and_replace(
 9936        &mut cx,
 9937        &buffer_marked_text,
 9938        vec![completion_text],
 9939        counter.clone(),
 9940    )
 9941    .await;
 9942    cx.condition(|editor, _| editor.context_menu_visible())
 9943        .await;
 9944    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9945
 9946    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9947        editor
 9948            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9949            .unwrap()
 9950    });
 9951    cx.assert_editor_state(&expected_with_insert_mode);
 9952    handle_resolve_completion_request(&mut cx, None).await;
 9953    apply_additional_edits.await.unwrap();
 9954}
 9955
 9956#[gpui::test]
 9957async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
 9958    init_test(cx, |_| {});
 9959    let mut cx = EditorLspTestContext::new_rust(
 9960        lsp::ServerCapabilities {
 9961            completion_provider: Some(lsp::CompletionOptions {
 9962                resolve_provider: Some(true),
 9963                ..Default::default()
 9964            }),
 9965            ..Default::default()
 9966        },
 9967        cx,
 9968    )
 9969    .await;
 9970
 9971    // scenario: surrounding text matches completion text
 9972    let completion_text = "to_offset";
 9973    let initial_state = indoc! {"
 9974        1. buf.to_offˇsuffix
 9975        2. buf.to_offˇsuf
 9976        3. buf.to_offˇfix
 9977        4. buf.to_offˇ
 9978        5. into_offˇensive
 9979        6. ˇsuffix
 9980        7. let ˇ //
 9981        8. aaˇzz
 9982        9. buf.to_off«zzzzzˇ»suffix
 9983        10. buf.«ˇzzzzz»suffix
 9984        11. to_off«ˇzzzzz»
 9985
 9986        buf.to_offˇsuffix  // newest cursor
 9987    "};
 9988    let completion_marked_buffer = indoc! {"
 9989        1. buf.to_offsuffix
 9990        2. buf.to_offsuf
 9991        3. buf.to_offfix
 9992        4. buf.to_off
 9993        5. into_offensive
 9994        6. suffix
 9995        7. let  //
 9996        8. aazz
 9997        9. buf.to_offzzzzzsuffix
 9998        10. buf.zzzzzsuffix
 9999        11. to_offzzzzz
10000
10001        buf.<to_off|suffix>  // newest cursor
10002    "};
10003    let expected = indoc! {"
10004        1. buf.to_offsetˇ
10005        2. buf.to_offsetˇsuf
10006        3. buf.to_offsetˇfix
10007        4. buf.to_offsetˇ
10008        5. into_offsetˇensive
10009        6. to_offsetˇsuffix
10010        7. let to_offsetˇ //
10011        8. aato_offsetˇzz
10012        9. buf.to_offsetˇ
10013        10. buf.to_offsetˇsuffix
10014        11. to_offsetˇ
10015
10016        buf.to_offsetˇ  // newest cursor
10017    "};
10018    cx.set_state(initial_state);
10019    cx.update_editor(|editor, window, cx| {
10020        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10021    });
10022    handle_completion_request_with_insert_and_replace(
10023        &mut cx,
10024        completion_marked_buffer,
10025        vec![completion_text],
10026        Arc::new(AtomicUsize::new(0)),
10027    )
10028    .await;
10029    cx.condition(|editor, _| editor.context_menu_visible())
10030        .await;
10031    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10032        editor
10033            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10034            .unwrap()
10035    });
10036    cx.assert_editor_state(expected);
10037    handle_resolve_completion_request(&mut cx, None).await;
10038    apply_additional_edits.await.unwrap();
10039
10040    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10041    let completion_text = "foo_and_bar";
10042    let initial_state = indoc! {"
10043        1. ooanbˇ
10044        2. zooanbˇ
10045        3. ooanbˇz
10046        4. zooanbˇz
10047        5. ooanˇ
10048        6. oanbˇ
10049
10050        ooanbˇ
10051    "};
10052    let completion_marked_buffer = indoc! {"
10053        1. ooanb
10054        2. zooanb
10055        3. ooanbz
10056        4. zooanbz
10057        5. ooan
10058        6. oanb
10059
10060        <ooanb|>
10061    "};
10062    let expected = indoc! {"
10063        1. foo_and_barˇ
10064        2. zfoo_and_barˇ
10065        3. foo_and_barˇz
10066        4. zfoo_and_barˇz
10067        5. ooanfoo_and_barˇ
10068        6. oanbfoo_and_barˇ
10069
10070        foo_and_barˇ
10071    "};
10072    cx.set_state(initial_state);
10073    cx.update_editor(|editor, window, cx| {
10074        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10075    });
10076    handle_completion_request_with_insert_and_replace(
10077        &mut cx,
10078        completion_marked_buffer,
10079        vec![completion_text],
10080        Arc::new(AtomicUsize::new(0)),
10081    )
10082    .await;
10083    cx.condition(|editor, _| editor.context_menu_visible())
10084        .await;
10085    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10086        editor
10087            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10088            .unwrap()
10089    });
10090    cx.assert_editor_state(expected);
10091    handle_resolve_completion_request(&mut cx, None).await;
10092    apply_additional_edits.await.unwrap();
10093
10094    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10095    // (expects the same as if it was inserted at the end)
10096    let completion_text = "foo_and_bar";
10097    let initial_state = indoc! {"
10098        1. ooˇanb
10099        2. zooˇanb
10100        3. ooˇanbz
10101        4. zooˇanbz
10102
10103        ooˇanb
10104    "};
10105    let completion_marked_buffer = indoc! {"
10106        1. ooanb
10107        2. zooanb
10108        3. ooanbz
10109        4. zooanbz
10110
10111        <oo|anb>
10112    "};
10113    let expected = indoc! {"
10114        1. foo_and_barˇ
10115        2. zfoo_and_barˇ
10116        3. foo_and_barˇz
10117        4. zfoo_and_barˇz
10118
10119        foo_and_barˇ
10120    "};
10121    cx.set_state(initial_state);
10122    cx.update_editor(|editor, window, cx| {
10123        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10124    });
10125    handle_completion_request_with_insert_and_replace(
10126        &mut cx,
10127        completion_marked_buffer,
10128        vec![completion_text],
10129        Arc::new(AtomicUsize::new(0)),
10130    )
10131    .await;
10132    cx.condition(|editor, _| editor.context_menu_visible())
10133        .await;
10134    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10135        editor
10136            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10137            .unwrap()
10138    });
10139    cx.assert_editor_state(expected);
10140    handle_resolve_completion_request(&mut cx, None).await;
10141    apply_additional_edits.await.unwrap();
10142}
10143
10144// This used to crash
10145#[gpui::test]
10146async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10147    init_test(cx, |_| {});
10148
10149    let buffer_text = indoc! {"
10150        fn main() {
10151            10.satu;
10152
10153            //
10154            // separate cursors so they open in different excerpts (manually reproducible)
10155            //
10156
10157            10.satu20;
10158        }
10159    "};
10160    let multibuffer_text_with_selections = indoc! {"
10161        fn main() {
10162            10.satuˇ;
10163
10164            //
10165
10166            //
10167
10168            10.satuˇ20;
10169        }
10170    "};
10171    let expected_multibuffer = indoc! {"
10172        fn main() {
10173            10.saturating_sub()ˇ;
10174
10175            //
10176
10177            //
10178
10179            10.saturating_sub()ˇ;
10180        }
10181    "};
10182
10183    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10184    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10185
10186    let fs = FakeFs::new(cx.executor());
10187    fs.insert_tree(
10188        path!("/a"),
10189        json!({
10190            "main.rs": buffer_text,
10191        }),
10192    )
10193    .await;
10194
10195    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10196    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10197    language_registry.add(rust_lang());
10198    let mut fake_servers = language_registry.register_fake_lsp(
10199        "Rust",
10200        FakeLspAdapter {
10201            capabilities: lsp::ServerCapabilities {
10202                completion_provider: Some(lsp::CompletionOptions {
10203                    resolve_provider: None,
10204                    ..lsp::CompletionOptions::default()
10205                }),
10206                ..lsp::ServerCapabilities::default()
10207            },
10208            ..FakeLspAdapter::default()
10209        },
10210    );
10211    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10212    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10213    let buffer = project
10214        .update(cx, |project, cx| {
10215            project.open_local_buffer(path!("/a/main.rs"), cx)
10216        })
10217        .await
10218        .unwrap();
10219
10220    let multi_buffer = cx.new(|cx| {
10221        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10222        multi_buffer.push_excerpts(
10223            buffer.clone(),
10224            [ExcerptRange::new(0..first_excerpt_end)],
10225            cx,
10226        );
10227        multi_buffer.push_excerpts(
10228            buffer.clone(),
10229            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10230            cx,
10231        );
10232        multi_buffer
10233    });
10234
10235    let editor = workspace
10236        .update(cx, |_, window, cx| {
10237            cx.new(|cx| {
10238                Editor::new(
10239                    EditorMode::Full {
10240                        scale_ui_elements_with_buffer_font_size: false,
10241                        show_active_line_background: false,
10242                    },
10243                    multi_buffer.clone(),
10244                    Some(project.clone()),
10245                    window,
10246                    cx,
10247                )
10248            })
10249        })
10250        .unwrap();
10251
10252    let pane = workspace
10253        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10254        .unwrap();
10255    pane.update_in(cx, |pane, window, cx| {
10256        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10257    });
10258
10259    let fake_server = fake_servers.next().await.unwrap();
10260
10261    editor.update_in(cx, |editor, window, cx| {
10262        editor.change_selections(None, window, cx, |s| {
10263            s.select_ranges([
10264                Point::new(1, 11)..Point::new(1, 11),
10265                Point::new(7, 11)..Point::new(7, 11),
10266            ])
10267        });
10268
10269        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10270    });
10271
10272    editor.update_in(cx, |editor, window, cx| {
10273        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10274    });
10275
10276    fake_server
10277        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10278            let completion_item = lsp::CompletionItem {
10279                label: "saturating_sub()".into(),
10280                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10281                    lsp::InsertReplaceEdit {
10282                        new_text: "saturating_sub()".to_owned(),
10283                        insert: lsp::Range::new(
10284                            lsp::Position::new(7, 7),
10285                            lsp::Position::new(7, 11),
10286                        ),
10287                        replace: lsp::Range::new(
10288                            lsp::Position::new(7, 7),
10289                            lsp::Position::new(7, 13),
10290                        ),
10291                    },
10292                )),
10293                ..lsp::CompletionItem::default()
10294            };
10295
10296            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10297        })
10298        .next()
10299        .await
10300        .unwrap();
10301
10302    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10303        .await;
10304
10305    editor
10306        .update_in(cx, |editor, window, cx| {
10307            editor
10308                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10309                .unwrap()
10310        })
10311        .await
10312        .unwrap();
10313
10314    editor.update(cx, |editor, cx| {
10315        assert_text_with_selections(editor, expected_multibuffer, cx);
10316    })
10317}
10318
10319#[gpui::test]
10320async fn test_completion(cx: &mut TestAppContext) {
10321    init_test(cx, |_| {});
10322
10323    let mut cx = EditorLspTestContext::new_rust(
10324        lsp::ServerCapabilities {
10325            completion_provider: Some(lsp::CompletionOptions {
10326                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10327                resolve_provider: Some(true),
10328                ..Default::default()
10329            }),
10330            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10331            ..Default::default()
10332        },
10333        cx,
10334    )
10335    .await;
10336    let counter = Arc::new(AtomicUsize::new(0));
10337
10338    cx.set_state(indoc! {"
10339        oneˇ
10340        two
10341        three
10342    "});
10343    cx.simulate_keystroke(".");
10344    handle_completion_request(
10345        &mut cx,
10346        indoc! {"
10347            one.|<>
10348            two
10349            three
10350        "},
10351        vec!["first_completion", "second_completion"],
10352        counter.clone(),
10353    )
10354    .await;
10355    cx.condition(|editor, _| editor.context_menu_visible())
10356        .await;
10357    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10358
10359    let _handler = handle_signature_help_request(
10360        &mut cx,
10361        lsp::SignatureHelp {
10362            signatures: vec![lsp::SignatureInformation {
10363                label: "test signature".to_string(),
10364                documentation: None,
10365                parameters: Some(vec![lsp::ParameterInformation {
10366                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10367                    documentation: None,
10368                }]),
10369                active_parameter: None,
10370            }],
10371            active_signature: None,
10372            active_parameter: None,
10373        },
10374    );
10375    cx.update_editor(|editor, window, cx| {
10376        assert!(
10377            !editor.signature_help_state.is_shown(),
10378            "No signature help was called for"
10379        );
10380        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10381    });
10382    cx.run_until_parked();
10383    cx.update_editor(|editor, _, _| {
10384        assert!(
10385            !editor.signature_help_state.is_shown(),
10386            "No signature help should be shown when completions menu is open"
10387        );
10388    });
10389
10390    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10391        editor.context_menu_next(&Default::default(), window, cx);
10392        editor
10393            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10394            .unwrap()
10395    });
10396    cx.assert_editor_state(indoc! {"
10397        one.second_completionˇ
10398        two
10399        three
10400    "});
10401
10402    handle_resolve_completion_request(
10403        &mut cx,
10404        Some(vec![
10405            (
10406                //This overlaps with the primary completion edit which is
10407                //misbehavior from the LSP spec, test that we filter it out
10408                indoc! {"
10409                    one.second_ˇcompletion
10410                    two
10411                    threeˇ
10412                "},
10413                "overlapping additional edit",
10414            ),
10415            (
10416                indoc! {"
10417                    one.second_completion
10418                    two
10419                    threeˇ
10420                "},
10421                "\nadditional edit",
10422            ),
10423        ]),
10424    )
10425    .await;
10426    apply_additional_edits.await.unwrap();
10427    cx.assert_editor_state(indoc! {"
10428        one.second_completionˇ
10429        two
10430        three
10431        additional edit
10432    "});
10433
10434    cx.set_state(indoc! {"
10435        one.second_completion
10436        twoˇ
10437        threeˇ
10438        additional edit
10439    "});
10440    cx.simulate_keystroke(" ");
10441    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10442    cx.simulate_keystroke("s");
10443    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10444
10445    cx.assert_editor_state(indoc! {"
10446        one.second_completion
10447        two sˇ
10448        three sˇ
10449        additional edit
10450    "});
10451    handle_completion_request(
10452        &mut cx,
10453        indoc! {"
10454            one.second_completion
10455            two s
10456            three <s|>
10457            additional edit
10458        "},
10459        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10460        counter.clone(),
10461    )
10462    .await;
10463    cx.condition(|editor, _| editor.context_menu_visible())
10464        .await;
10465    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10466
10467    cx.simulate_keystroke("i");
10468
10469    handle_completion_request(
10470        &mut cx,
10471        indoc! {"
10472            one.second_completion
10473            two si
10474            three <si|>
10475            additional edit
10476        "},
10477        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10478        counter.clone(),
10479    )
10480    .await;
10481    cx.condition(|editor, _| editor.context_menu_visible())
10482        .await;
10483    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10484
10485    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10486        editor
10487            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10488            .unwrap()
10489    });
10490    cx.assert_editor_state(indoc! {"
10491        one.second_completion
10492        two sixth_completionˇ
10493        three sixth_completionˇ
10494        additional edit
10495    "});
10496
10497    apply_additional_edits.await.unwrap();
10498
10499    update_test_language_settings(&mut cx, |settings| {
10500        settings.defaults.show_completions_on_input = Some(false);
10501    });
10502    cx.set_state("editorˇ");
10503    cx.simulate_keystroke(".");
10504    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10505    cx.simulate_keystrokes("c l o");
10506    cx.assert_editor_state("editor.cloˇ");
10507    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10508    cx.update_editor(|editor, window, cx| {
10509        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10510    });
10511    handle_completion_request(
10512        &mut cx,
10513        "editor.<clo|>",
10514        vec!["close", "clobber"],
10515        counter.clone(),
10516    )
10517    .await;
10518    cx.condition(|editor, _| editor.context_menu_visible())
10519        .await;
10520    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10521
10522    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10523        editor
10524            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10525            .unwrap()
10526    });
10527    cx.assert_editor_state("editor.closeˇ");
10528    handle_resolve_completion_request(&mut cx, None).await;
10529    apply_additional_edits.await.unwrap();
10530}
10531
10532#[gpui::test]
10533async fn test_word_completion(cx: &mut TestAppContext) {
10534    let lsp_fetch_timeout_ms = 10;
10535    init_test(cx, |language_settings| {
10536        language_settings.defaults.completions = Some(CompletionSettings {
10537            words: WordsCompletionMode::Fallback,
10538            lsp: true,
10539            lsp_fetch_timeout_ms: 10,
10540            lsp_insert_mode: LspInsertMode::Insert,
10541        });
10542    });
10543
10544    let mut cx = EditorLspTestContext::new_rust(
10545        lsp::ServerCapabilities {
10546            completion_provider: Some(lsp::CompletionOptions {
10547                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10548                ..lsp::CompletionOptions::default()
10549            }),
10550            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10551            ..lsp::ServerCapabilities::default()
10552        },
10553        cx,
10554    )
10555    .await;
10556
10557    let throttle_completions = Arc::new(AtomicBool::new(false));
10558
10559    let lsp_throttle_completions = throttle_completions.clone();
10560    let _completion_requests_handler =
10561        cx.lsp
10562            .server
10563            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10564                let lsp_throttle_completions = lsp_throttle_completions.clone();
10565                let cx = cx.clone();
10566                async move {
10567                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10568                        cx.background_executor()
10569                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10570                            .await;
10571                    }
10572                    Ok(Some(lsp::CompletionResponse::Array(vec![
10573                        lsp::CompletionItem {
10574                            label: "first".into(),
10575                            ..lsp::CompletionItem::default()
10576                        },
10577                        lsp::CompletionItem {
10578                            label: "last".into(),
10579                            ..lsp::CompletionItem::default()
10580                        },
10581                    ])))
10582                }
10583            });
10584
10585    cx.set_state(indoc! {"
10586        oneˇ
10587        two
10588        three
10589    "});
10590    cx.simulate_keystroke(".");
10591    cx.executor().run_until_parked();
10592    cx.condition(|editor, _| editor.context_menu_visible())
10593        .await;
10594    cx.update_editor(|editor, window, cx| {
10595        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10596        {
10597            assert_eq!(
10598                completion_menu_entries(&menu),
10599                &["first", "last"],
10600                "When LSP server is fast to reply, no fallback word completions are used"
10601            );
10602        } else {
10603            panic!("expected completion menu to be open");
10604        }
10605        editor.cancel(&Cancel, window, cx);
10606    });
10607    cx.executor().run_until_parked();
10608    cx.condition(|editor, _| !editor.context_menu_visible())
10609        .await;
10610
10611    throttle_completions.store(true, atomic::Ordering::Release);
10612    cx.simulate_keystroke(".");
10613    cx.executor()
10614        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10615    cx.executor().run_until_parked();
10616    cx.condition(|editor, _| editor.context_menu_visible())
10617        .await;
10618    cx.update_editor(|editor, _, _| {
10619        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10620        {
10621            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10622                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10623        } else {
10624            panic!("expected completion menu to be open");
10625        }
10626    });
10627}
10628
10629#[gpui::test]
10630async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10631    init_test(cx, |language_settings| {
10632        language_settings.defaults.completions = Some(CompletionSettings {
10633            words: WordsCompletionMode::Enabled,
10634            lsp: true,
10635            lsp_fetch_timeout_ms: 0,
10636            lsp_insert_mode: LspInsertMode::Insert,
10637        });
10638    });
10639
10640    let mut cx = EditorLspTestContext::new_rust(
10641        lsp::ServerCapabilities {
10642            completion_provider: Some(lsp::CompletionOptions {
10643                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10644                ..lsp::CompletionOptions::default()
10645            }),
10646            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10647            ..lsp::ServerCapabilities::default()
10648        },
10649        cx,
10650    )
10651    .await;
10652
10653    let _completion_requests_handler =
10654        cx.lsp
10655            .server
10656            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10657                Ok(Some(lsp::CompletionResponse::Array(vec![
10658                    lsp::CompletionItem {
10659                        label: "first".into(),
10660                        ..lsp::CompletionItem::default()
10661                    },
10662                    lsp::CompletionItem {
10663                        label: "last".into(),
10664                        ..lsp::CompletionItem::default()
10665                    },
10666                ])))
10667            });
10668
10669    cx.set_state(indoc! {"ˇ
10670        first
10671        last
10672        second
10673    "});
10674    cx.simulate_keystroke(".");
10675    cx.executor().run_until_parked();
10676    cx.condition(|editor, _| editor.context_menu_visible())
10677        .await;
10678    cx.update_editor(|editor, _, _| {
10679        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10680        {
10681            assert_eq!(
10682                completion_menu_entries(&menu),
10683                &["first", "last", "second"],
10684                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10685            );
10686        } else {
10687            panic!("expected completion menu to be open");
10688        }
10689    });
10690}
10691
10692#[gpui::test]
10693async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10694    init_test(cx, |language_settings| {
10695        language_settings.defaults.completions = Some(CompletionSettings {
10696            words: WordsCompletionMode::Disabled,
10697            lsp: true,
10698            lsp_fetch_timeout_ms: 0,
10699            lsp_insert_mode: LspInsertMode::Insert,
10700        });
10701    });
10702
10703    let mut cx = EditorLspTestContext::new_rust(
10704        lsp::ServerCapabilities {
10705            completion_provider: Some(lsp::CompletionOptions {
10706                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10707                ..lsp::CompletionOptions::default()
10708            }),
10709            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10710            ..lsp::ServerCapabilities::default()
10711        },
10712        cx,
10713    )
10714    .await;
10715
10716    let _completion_requests_handler =
10717        cx.lsp
10718            .server
10719            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10720                panic!("LSP completions should not be queried when dealing with word completions")
10721            });
10722
10723    cx.set_state(indoc! {"ˇ
10724        first
10725        last
10726        second
10727    "});
10728    cx.update_editor(|editor, window, cx| {
10729        editor.show_word_completions(&ShowWordCompletions, window, cx);
10730    });
10731    cx.executor().run_until_parked();
10732    cx.condition(|editor, _| editor.context_menu_visible())
10733        .await;
10734    cx.update_editor(|editor, _, _| {
10735        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10736        {
10737            assert_eq!(
10738                completion_menu_entries(&menu),
10739                &["first", "last", "second"],
10740                "`ShowWordCompletions` action should show word completions"
10741            );
10742        } else {
10743            panic!("expected completion menu to be open");
10744        }
10745    });
10746
10747    cx.simulate_keystroke("l");
10748    cx.executor().run_until_parked();
10749    cx.condition(|editor, _| editor.context_menu_visible())
10750        .await;
10751    cx.update_editor(|editor, _, _| {
10752        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10753        {
10754            assert_eq!(
10755                completion_menu_entries(&menu),
10756                &["last"],
10757                "After showing word completions, further editing should filter them and not query the LSP"
10758            );
10759        } else {
10760            panic!("expected completion menu to be open");
10761        }
10762    });
10763}
10764
10765#[gpui::test]
10766async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10767    init_test(cx, |language_settings| {
10768        language_settings.defaults.completions = Some(CompletionSettings {
10769            words: WordsCompletionMode::Fallback,
10770            lsp: false,
10771            lsp_fetch_timeout_ms: 0,
10772            lsp_insert_mode: LspInsertMode::Insert,
10773        });
10774    });
10775
10776    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10777
10778    cx.set_state(indoc! {"ˇ
10779        0_usize
10780        let
10781        33
10782        4.5f32
10783    "});
10784    cx.update_editor(|editor, window, cx| {
10785        editor.show_completions(&ShowCompletions::default(), window, cx);
10786    });
10787    cx.executor().run_until_parked();
10788    cx.condition(|editor, _| editor.context_menu_visible())
10789        .await;
10790    cx.update_editor(|editor, window, cx| {
10791        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10792        {
10793            assert_eq!(
10794                completion_menu_entries(&menu),
10795                &["let"],
10796                "With no digits in the completion query, no digits should be in the word completions"
10797            );
10798        } else {
10799            panic!("expected completion menu to be open");
10800        }
10801        editor.cancel(&Cancel, window, cx);
10802    });
10803
10804    cx.set_state(indoc! {"10805        0_usize
10806        let
10807        3
10808        33.35f32
10809    "});
10810    cx.update_editor(|editor, window, cx| {
10811        editor.show_completions(&ShowCompletions::default(), window, cx);
10812    });
10813    cx.executor().run_until_parked();
10814    cx.condition(|editor, _| editor.context_menu_visible())
10815        .await;
10816    cx.update_editor(|editor, _, _| {
10817        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10818        {
10819            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10820                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10821        } else {
10822            panic!("expected completion menu to be open");
10823        }
10824    });
10825}
10826
10827fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10828    let position = || lsp::Position {
10829        line: params.text_document_position.position.line,
10830        character: params.text_document_position.position.character,
10831    };
10832    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10833        range: lsp::Range {
10834            start: position(),
10835            end: position(),
10836        },
10837        new_text: text.to_string(),
10838    }))
10839}
10840
10841#[gpui::test]
10842async fn test_multiline_completion(cx: &mut TestAppContext) {
10843    init_test(cx, |_| {});
10844
10845    let fs = FakeFs::new(cx.executor());
10846    fs.insert_tree(
10847        path!("/a"),
10848        json!({
10849            "main.ts": "a",
10850        }),
10851    )
10852    .await;
10853
10854    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10855    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10856    let typescript_language = Arc::new(Language::new(
10857        LanguageConfig {
10858            name: "TypeScript".into(),
10859            matcher: LanguageMatcher {
10860                path_suffixes: vec!["ts".to_string()],
10861                ..LanguageMatcher::default()
10862            },
10863            line_comments: vec!["// ".into()],
10864            ..LanguageConfig::default()
10865        },
10866        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10867    ));
10868    language_registry.add(typescript_language.clone());
10869    let mut fake_servers = language_registry.register_fake_lsp(
10870        "TypeScript",
10871        FakeLspAdapter {
10872            capabilities: lsp::ServerCapabilities {
10873                completion_provider: Some(lsp::CompletionOptions {
10874                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10875                    ..lsp::CompletionOptions::default()
10876                }),
10877                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10878                ..lsp::ServerCapabilities::default()
10879            },
10880            // Emulate vtsls label generation
10881            label_for_completion: Some(Box::new(|item, _| {
10882                let text = if let Some(description) = item
10883                    .label_details
10884                    .as_ref()
10885                    .and_then(|label_details| label_details.description.as_ref())
10886                {
10887                    format!("{} {}", item.label, description)
10888                } else if let Some(detail) = &item.detail {
10889                    format!("{} {}", item.label, detail)
10890                } else {
10891                    item.label.clone()
10892                };
10893                let len = text.len();
10894                Some(language::CodeLabel {
10895                    text,
10896                    runs: Vec::new(),
10897                    filter_range: 0..len,
10898                })
10899            })),
10900            ..FakeLspAdapter::default()
10901        },
10902    );
10903    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10904    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10905    let worktree_id = workspace
10906        .update(cx, |workspace, _window, cx| {
10907            workspace.project().update(cx, |project, cx| {
10908                project.worktrees(cx).next().unwrap().read(cx).id()
10909            })
10910        })
10911        .unwrap();
10912    let _buffer = project
10913        .update(cx, |project, cx| {
10914            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10915        })
10916        .await
10917        .unwrap();
10918    let editor = workspace
10919        .update(cx, |workspace, window, cx| {
10920            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10921        })
10922        .unwrap()
10923        .await
10924        .unwrap()
10925        .downcast::<Editor>()
10926        .unwrap();
10927    let fake_server = fake_servers.next().await.unwrap();
10928
10929    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10930    let multiline_label_2 = "a\nb\nc\n";
10931    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10932    let multiline_description = "d\ne\nf\n";
10933    let multiline_detail_2 = "g\nh\ni\n";
10934
10935    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10936        move |params, _| async move {
10937            Ok(Some(lsp::CompletionResponse::Array(vec![
10938                lsp::CompletionItem {
10939                    label: multiline_label.to_string(),
10940                    text_edit: gen_text_edit(&params, "new_text_1"),
10941                    ..lsp::CompletionItem::default()
10942                },
10943                lsp::CompletionItem {
10944                    label: "single line label 1".to_string(),
10945                    detail: Some(multiline_detail.to_string()),
10946                    text_edit: gen_text_edit(&params, "new_text_2"),
10947                    ..lsp::CompletionItem::default()
10948                },
10949                lsp::CompletionItem {
10950                    label: "single line label 2".to_string(),
10951                    label_details: Some(lsp::CompletionItemLabelDetails {
10952                        description: Some(multiline_description.to_string()),
10953                        detail: None,
10954                    }),
10955                    text_edit: gen_text_edit(&params, "new_text_2"),
10956                    ..lsp::CompletionItem::default()
10957                },
10958                lsp::CompletionItem {
10959                    label: multiline_label_2.to_string(),
10960                    detail: Some(multiline_detail_2.to_string()),
10961                    text_edit: gen_text_edit(&params, "new_text_3"),
10962                    ..lsp::CompletionItem::default()
10963                },
10964                lsp::CompletionItem {
10965                    label: "Label with many     spaces and \t but without newlines".to_string(),
10966                    detail: Some(
10967                        "Details with many     spaces and \t but without newlines".to_string(),
10968                    ),
10969                    text_edit: gen_text_edit(&params, "new_text_4"),
10970                    ..lsp::CompletionItem::default()
10971                },
10972            ])))
10973        },
10974    );
10975
10976    editor.update_in(cx, |editor, window, cx| {
10977        cx.focus_self(window);
10978        editor.move_to_end(&MoveToEnd, window, cx);
10979        editor.handle_input(".", window, cx);
10980    });
10981    cx.run_until_parked();
10982    completion_handle.next().await.unwrap();
10983
10984    editor.update(cx, |editor, _| {
10985        assert!(editor.context_menu_visible());
10986        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10987        {
10988            let completion_labels = menu
10989                .completions
10990                .borrow()
10991                .iter()
10992                .map(|c| c.label.text.clone())
10993                .collect::<Vec<_>>();
10994            assert_eq!(
10995                completion_labels,
10996                &[
10997                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10998                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10999                    "single line label 2 d e f ",
11000                    "a b c g h i ",
11001                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
11002                ],
11003                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
11004            );
11005
11006            for completion in menu
11007                .completions
11008                .borrow()
11009                .iter() {
11010                    assert_eq!(
11011                        completion.label.filter_range,
11012                        0..completion.label.text.len(),
11013                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
11014                    );
11015                }
11016        } else {
11017            panic!("expected completion menu to be open");
11018        }
11019    });
11020}
11021
11022#[gpui::test]
11023async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
11024    init_test(cx, |_| {});
11025    let mut cx = EditorLspTestContext::new_rust(
11026        lsp::ServerCapabilities {
11027            completion_provider: Some(lsp::CompletionOptions {
11028                trigger_characters: Some(vec![".".to_string()]),
11029                ..Default::default()
11030            }),
11031            ..Default::default()
11032        },
11033        cx,
11034    )
11035    .await;
11036    cx.lsp
11037        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11038            Ok(Some(lsp::CompletionResponse::Array(vec![
11039                lsp::CompletionItem {
11040                    label: "first".into(),
11041                    ..Default::default()
11042                },
11043                lsp::CompletionItem {
11044                    label: "last".into(),
11045                    ..Default::default()
11046                },
11047            ])))
11048        });
11049    cx.set_state("variableˇ");
11050    cx.simulate_keystroke(".");
11051    cx.executor().run_until_parked();
11052
11053    cx.update_editor(|editor, _, _| {
11054        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11055        {
11056            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11057        } else {
11058            panic!("expected completion menu to be open");
11059        }
11060    });
11061
11062    cx.update_editor(|editor, window, cx| {
11063        editor.move_page_down(&MovePageDown::default(), window, cx);
11064        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11065        {
11066            assert!(
11067                menu.selected_item == 1,
11068                "expected PageDown to select the last item from the context menu"
11069            );
11070        } else {
11071            panic!("expected completion menu to stay open after PageDown");
11072        }
11073    });
11074
11075    cx.update_editor(|editor, window, cx| {
11076        editor.move_page_up(&MovePageUp::default(), window, cx);
11077        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11078        {
11079            assert!(
11080                menu.selected_item == 0,
11081                "expected PageUp to select the first item from the context menu"
11082            );
11083        } else {
11084            panic!("expected completion menu to stay open after PageUp");
11085        }
11086    });
11087}
11088
11089#[gpui::test]
11090async fn test_completion_sort(cx: &mut TestAppContext) {
11091    init_test(cx, |_| {});
11092    let mut cx = EditorLspTestContext::new_rust(
11093        lsp::ServerCapabilities {
11094            completion_provider: Some(lsp::CompletionOptions {
11095                trigger_characters: Some(vec![".".to_string()]),
11096                ..Default::default()
11097            }),
11098            ..Default::default()
11099        },
11100        cx,
11101    )
11102    .await;
11103    cx.lsp
11104        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11105            Ok(Some(lsp::CompletionResponse::Array(vec![
11106                lsp::CompletionItem {
11107                    label: "Range".into(),
11108                    sort_text: Some("a".into()),
11109                    ..Default::default()
11110                },
11111                lsp::CompletionItem {
11112                    label: "r".into(),
11113                    sort_text: Some("b".into()),
11114                    ..Default::default()
11115                },
11116                lsp::CompletionItem {
11117                    label: "ret".into(),
11118                    sort_text: Some("c".into()),
11119                    ..Default::default()
11120                },
11121                lsp::CompletionItem {
11122                    label: "return".into(),
11123                    sort_text: Some("d".into()),
11124                    ..Default::default()
11125                },
11126                lsp::CompletionItem {
11127                    label: "slice".into(),
11128                    sort_text: Some("d".into()),
11129                    ..Default::default()
11130                },
11131            ])))
11132        });
11133    cx.set_state("");
11134    cx.executor().run_until_parked();
11135    cx.update_editor(|editor, window, cx| {
11136        editor.show_completions(
11137            &ShowCompletions {
11138                trigger: Some("r".into()),
11139            },
11140            window,
11141            cx,
11142        );
11143    });
11144    cx.executor().run_until_parked();
11145
11146    cx.update_editor(|editor, _, _| {
11147        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11148        {
11149            assert_eq!(
11150                completion_menu_entries(&menu),
11151                &["r", "ret", "Range", "return"]
11152            );
11153        } else {
11154            panic!("expected completion menu to be open");
11155        }
11156    });
11157}
11158
11159#[gpui::test]
11160async fn test_as_is_completions(cx: &mut TestAppContext) {
11161    init_test(cx, |_| {});
11162    let mut cx = EditorLspTestContext::new_rust(
11163        lsp::ServerCapabilities {
11164            completion_provider: Some(lsp::CompletionOptions {
11165                ..Default::default()
11166            }),
11167            ..Default::default()
11168        },
11169        cx,
11170    )
11171    .await;
11172    cx.lsp
11173        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11174            Ok(Some(lsp::CompletionResponse::Array(vec![
11175                lsp::CompletionItem {
11176                    label: "unsafe".into(),
11177                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11178                        range: lsp::Range {
11179                            start: lsp::Position {
11180                                line: 1,
11181                                character: 2,
11182                            },
11183                            end: lsp::Position {
11184                                line: 1,
11185                                character: 3,
11186                            },
11187                        },
11188                        new_text: "unsafe".to_string(),
11189                    })),
11190                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11191                    ..Default::default()
11192                },
11193            ])))
11194        });
11195    cx.set_state("fn a() {}\n");
11196    cx.executor().run_until_parked();
11197    cx.update_editor(|editor, window, cx| {
11198        editor.show_completions(
11199            &ShowCompletions {
11200                trigger: Some("\n".into()),
11201            },
11202            window,
11203            cx,
11204        );
11205    });
11206    cx.executor().run_until_parked();
11207
11208    cx.update_editor(|editor, window, cx| {
11209        editor.confirm_completion(&Default::default(), window, cx)
11210    });
11211    cx.executor().run_until_parked();
11212    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11213}
11214
11215#[gpui::test]
11216async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11217    init_test(cx, |_| {});
11218
11219    let mut cx = EditorLspTestContext::new_rust(
11220        lsp::ServerCapabilities {
11221            completion_provider: Some(lsp::CompletionOptions {
11222                trigger_characters: Some(vec![".".to_string()]),
11223                resolve_provider: Some(true),
11224                ..Default::default()
11225            }),
11226            ..Default::default()
11227        },
11228        cx,
11229    )
11230    .await;
11231
11232    cx.set_state("fn main() { let a = 2ˇ; }");
11233    cx.simulate_keystroke(".");
11234    let completion_item = lsp::CompletionItem {
11235        label: "Some".into(),
11236        kind: Some(lsp::CompletionItemKind::SNIPPET),
11237        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11238        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11239            kind: lsp::MarkupKind::Markdown,
11240            value: "```rust\nSome(2)\n```".to_string(),
11241        })),
11242        deprecated: Some(false),
11243        sort_text: Some("Some".to_string()),
11244        filter_text: Some("Some".to_string()),
11245        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11246        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11247            range: lsp::Range {
11248                start: lsp::Position {
11249                    line: 0,
11250                    character: 22,
11251                },
11252                end: lsp::Position {
11253                    line: 0,
11254                    character: 22,
11255                },
11256            },
11257            new_text: "Some(2)".to_string(),
11258        })),
11259        additional_text_edits: Some(vec![lsp::TextEdit {
11260            range: lsp::Range {
11261                start: lsp::Position {
11262                    line: 0,
11263                    character: 20,
11264                },
11265                end: lsp::Position {
11266                    line: 0,
11267                    character: 22,
11268                },
11269            },
11270            new_text: "".to_string(),
11271        }]),
11272        ..Default::default()
11273    };
11274
11275    let closure_completion_item = completion_item.clone();
11276    let counter = Arc::new(AtomicUsize::new(0));
11277    let counter_clone = counter.clone();
11278    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11279        let task_completion_item = closure_completion_item.clone();
11280        counter_clone.fetch_add(1, atomic::Ordering::Release);
11281        async move {
11282            Ok(Some(lsp::CompletionResponse::Array(vec![
11283                task_completion_item,
11284            ])))
11285        }
11286    });
11287
11288    cx.condition(|editor, _| editor.context_menu_visible())
11289        .await;
11290    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11291    assert!(request.next().await.is_some());
11292    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11293
11294    cx.simulate_keystrokes("S o m");
11295    cx.condition(|editor, _| editor.context_menu_visible())
11296        .await;
11297    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11298    assert!(request.next().await.is_some());
11299    assert!(request.next().await.is_some());
11300    assert!(request.next().await.is_some());
11301    request.close();
11302    assert!(request.next().await.is_none());
11303    assert_eq!(
11304        counter.load(atomic::Ordering::Acquire),
11305        4,
11306        "With the completions menu open, only one LSP request should happen per input"
11307    );
11308}
11309
11310#[gpui::test]
11311async fn test_toggle_comment(cx: &mut TestAppContext) {
11312    init_test(cx, |_| {});
11313    let mut cx = EditorTestContext::new(cx).await;
11314    let language = Arc::new(Language::new(
11315        LanguageConfig {
11316            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11317            ..Default::default()
11318        },
11319        Some(tree_sitter_rust::LANGUAGE.into()),
11320    ));
11321    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11322
11323    // If multiple selections intersect a line, the line is only toggled once.
11324    cx.set_state(indoc! {"
11325        fn a() {
11326            «//b();
11327            ˇ»// «c();
11328            //ˇ»  d();
11329        }
11330    "});
11331
11332    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11333
11334    cx.assert_editor_state(indoc! {"
11335        fn a() {
11336            «b();
11337            c();
11338            ˇ» d();
11339        }
11340    "});
11341
11342    // The comment prefix is inserted at the same column for every line in a
11343    // selection.
11344    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11345
11346    cx.assert_editor_state(indoc! {"
11347        fn a() {
11348            // «b();
11349            // c();
11350            ˇ»//  d();
11351        }
11352    "});
11353
11354    // If a selection ends at the beginning of a line, that line is not toggled.
11355    cx.set_selections_state(indoc! {"
11356        fn a() {
11357            // b();
11358            «// c();
11359        ˇ»    //  d();
11360        }
11361    "});
11362
11363    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11364
11365    cx.assert_editor_state(indoc! {"
11366        fn a() {
11367            // b();
11368            «c();
11369        ˇ»    //  d();
11370        }
11371    "});
11372
11373    // If a selection span a single line and is empty, the line is toggled.
11374    cx.set_state(indoc! {"
11375        fn a() {
11376            a();
11377            b();
11378        ˇ
11379        }
11380    "});
11381
11382    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11383
11384    cx.assert_editor_state(indoc! {"
11385        fn a() {
11386            a();
11387            b();
11388        //•ˇ
11389        }
11390    "});
11391
11392    // If a selection span multiple lines, empty lines are not toggled.
11393    cx.set_state(indoc! {"
11394        fn a() {
11395            «a();
11396
11397            c();ˇ»
11398        }
11399    "});
11400
11401    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11402
11403    cx.assert_editor_state(indoc! {"
11404        fn a() {
11405            // «a();
11406
11407            // c();ˇ»
11408        }
11409    "});
11410
11411    // If a selection includes multiple comment prefixes, all lines are uncommented.
11412    cx.set_state(indoc! {"
11413        fn a() {
11414            «// a();
11415            /// b();
11416            //! c();ˇ»
11417        }
11418    "});
11419
11420    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11421
11422    cx.assert_editor_state(indoc! {"
11423        fn a() {
11424            «a();
11425            b();
11426            c();ˇ»
11427        }
11428    "});
11429}
11430
11431#[gpui::test]
11432async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11433    init_test(cx, |_| {});
11434    let mut cx = EditorTestContext::new(cx).await;
11435    let language = Arc::new(Language::new(
11436        LanguageConfig {
11437            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11438            ..Default::default()
11439        },
11440        Some(tree_sitter_rust::LANGUAGE.into()),
11441    ));
11442    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11443
11444    let toggle_comments = &ToggleComments {
11445        advance_downwards: false,
11446        ignore_indent: true,
11447    };
11448
11449    // If multiple selections intersect a line, the line is only toggled once.
11450    cx.set_state(indoc! {"
11451        fn a() {
11452        //    «b();
11453        //    c();
11454        //    ˇ» d();
11455        }
11456    "});
11457
11458    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11459
11460    cx.assert_editor_state(indoc! {"
11461        fn a() {
11462            «b();
11463            c();
11464            ˇ» d();
11465        }
11466    "});
11467
11468    // The comment prefix is inserted at the beginning of each line
11469    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11470
11471    cx.assert_editor_state(indoc! {"
11472        fn a() {
11473        //    «b();
11474        //    c();
11475        //    ˇ» d();
11476        }
11477    "});
11478
11479    // If a selection ends at the beginning of a line, that line is not toggled.
11480    cx.set_selections_state(indoc! {"
11481        fn a() {
11482        //    b();
11483        //    «c();
11484        ˇ»//     d();
11485        }
11486    "});
11487
11488    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11489
11490    cx.assert_editor_state(indoc! {"
11491        fn a() {
11492        //    b();
11493            «c();
11494        ˇ»//     d();
11495        }
11496    "});
11497
11498    // If a selection span a single line and is empty, the line is toggled.
11499    cx.set_state(indoc! {"
11500        fn a() {
11501            a();
11502            b();
11503        ˇ
11504        }
11505    "});
11506
11507    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11508
11509    cx.assert_editor_state(indoc! {"
11510        fn a() {
11511            a();
11512            b();
11513        //ˇ
11514        }
11515    "});
11516
11517    // If a selection span multiple lines, empty lines are not toggled.
11518    cx.set_state(indoc! {"
11519        fn a() {
11520            «a();
11521
11522            c();ˇ»
11523        }
11524    "});
11525
11526    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11527
11528    cx.assert_editor_state(indoc! {"
11529        fn a() {
11530        //    «a();
11531
11532        //    c();ˇ»
11533        }
11534    "});
11535
11536    // If a selection includes multiple comment prefixes, all lines are uncommented.
11537    cx.set_state(indoc! {"
11538        fn a() {
11539        //    «a();
11540        ///    b();
11541        //!    c();ˇ»
11542        }
11543    "});
11544
11545    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11546
11547    cx.assert_editor_state(indoc! {"
11548        fn a() {
11549            «a();
11550            b();
11551            c();ˇ»
11552        }
11553    "});
11554}
11555
11556#[gpui::test]
11557async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11558    init_test(cx, |_| {});
11559
11560    let language = Arc::new(Language::new(
11561        LanguageConfig {
11562            line_comments: vec!["// ".into()],
11563            ..Default::default()
11564        },
11565        Some(tree_sitter_rust::LANGUAGE.into()),
11566    ));
11567
11568    let mut cx = EditorTestContext::new(cx).await;
11569
11570    cx.language_registry().add(language.clone());
11571    cx.update_buffer(|buffer, cx| {
11572        buffer.set_language(Some(language), cx);
11573    });
11574
11575    let toggle_comments = &ToggleComments {
11576        advance_downwards: true,
11577        ignore_indent: false,
11578    };
11579
11580    // Single cursor on one line -> advance
11581    // Cursor moves horizontally 3 characters as well on non-blank line
11582    cx.set_state(indoc!(
11583        "fn a() {
11584             ˇdog();
11585             cat();
11586        }"
11587    ));
11588    cx.update_editor(|editor, window, cx| {
11589        editor.toggle_comments(toggle_comments, window, cx);
11590    });
11591    cx.assert_editor_state(indoc!(
11592        "fn a() {
11593             // dog();
11594             catˇ();
11595        }"
11596    ));
11597
11598    // Single selection on one line -> don't advance
11599    cx.set_state(indoc!(
11600        "fn a() {
11601             «dog()ˇ»;
11602             cat();
11603        }"
11604    ));
11605    cx.update_editor(|editor, window, cx| {
11606        editor.toggle_comments(toggle_comments, window, cx);
11607    });
11608    cx.assert_editor_state(indoc!(
11609        "fn a() {
11610             // «dog()ˇ»;
11611             cat();
11612        }"
11613    ));
11614
11615    // Multiple cursors on one line -> advance
11616    cx.set_state(indoc!(
11617        "fn a() {
11618             ˇdˇog();
11619             cat();
11620        }"
11621    ));
11622    cx.update_editor(|editor, window, cx| {
11623        editor.toggle_comments(toggle_comments, window, cx);
11624    });
11625    cx.assert_editor_state(indoc!(
11626        "fn a() {
11627             // dog();
11628             catˇ(ˇ);
11629        }"
11630    ));
11631
11632    // Multiple cursors on one line, with selection -> don't advance
11633    cx.set_state(indoc!(
11634        "fn a() {
11635             ˇdˇog«()ˇ»;
11636             cat();
11637        }"
11638    ));
11639    cx.update_editor(|editor, window, cx| {
11640        editor.toggle_comments(toggle_comments, window, cx);
11641    });
11642    cx.assert_editor_state(indoc!(
11643        "fn a() {
11644             // ˇdˇog«()ˇ»;
11645             cat();
11646        }"
11647    ));
11648
11649    // Single cursor on one line -> advance
11650    // Cursor moves to column 0 on blank line
11651    cx.set_state(indoc!(
11652        "fn a() {
11653             ˇdog();
11654
11655             cat();
11656        }"
11657    ));
11658    cx.update_editor(|editor, window, cx| {
11659        editor.toggle_comments(toggle_comments, window, cx);
11660    });
11661    cx.assert_editor_state(indoc!(
11662        "fn a() {
11663             // dog();
11664        ˇ
11665             cat();
11666        }"
11667    ));
11668
11669    // Single cursor on one line -> advance
11670    // Cursor starts and ends at column 0
11671    cx.set_state(indoc!(
11672        "fn a() {
11673         ˇ    dog();
11674             cat();
11675        }"
11676    ));
11677    cx.update_editor(|editor, window, cx| {
11678        editor.toggle_comments(toggle_comments, window, cx);
11679    });
11680    cx.assert_editor_state(indoc!(
11681        "fn a() {
11682             // dog();
11683         ˇ    cat();
11684        }"
11685    ));
11686}
11687
11688#[gpui::test]
11689async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11690    init_test(cx, |_| {});
11691
11692    let mut cx = EditorTestContext::new(cx).await;
11693
11694    let html_language = Arc::new(
11695        Language::new(
11696            LanguageConfig {
11697                name: "HTML".into(),
11698                block_comment: Some(("<!-- ".into(), " -->".into())),
11699                ..Default::default()
11700            },
11701            Some(tree_sitter_html::LANGUAGE.into()),
11702        )
11703        .with_injection_query(
11704            r#"
11705            (script_element
11706                (raw_text) @injection.content
11707                (#set! injection.language "javascript"))
11708            "#,
11709        )
11710        .unwrap(),
11711    );
11712
11713    let javascript_language = Arc::new(Language::new(
11714        LanguageConfig {
11715            name: "JavaScript".into(),
11716            line_comments: vec!["// ".into()],
11717            ..Default::default()
11718        },
11719        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11720    ));
11721
11722    cx.language_registry().add(html_language.clone());
11723    cx.language_registry().add(javascript_language.clone());
11724    cx.update_buffer(|buffer, cx| {
11725        buffer.set_language(Some(html_language), cx);
11726    });
11727
11728    // Toggle comments for empty selections
11729    cx.set_state(
11730        &r#"
11731            <p>A</p>ˇ
11732            <p>B</p>ˇ
11733            <p>C</p>ˇ
11734        "#
11735        .unindent(),
11736    );
11737    cx.update_editor(|editor, window, cx| {
11738        editor.toggle_comments(&ToggleComments::default(), window, cx)
11739    });
11740    cx.assert_editor_state(
11741        &r#"
11742            <!-- <p>A</p>ˇ -->
11743            <!-- <p>B</p>ˇ -->
11744            <!-- <p>C</p>ˇ -->
11745        "#
11746        .unindent(),
11747    );
11748    cx.update_editor(|editor, window, cx| {
11749        editor.toggle_comments(&ToggleComments::default(), window, cx)
11750    });
11751    cx.assert_editor_state(
11752        &r#"
11753            <p>A</p>ˇ
11754            <p>B</p>ˇ
11755            <p>C</p>ˇ
11756        "#
11757        .unindent(),
11758    );
11759
11760    // Toggle comments for mixture of empty and non-empty selections, where
11761    // multiple selections occupy a given line.
11762    cx.set_state(
11763        &r#"
11764            <p>A«</p>
11765            <p>ˇ»B</p>ˇ
11766            <p>C«</p>
11767            <p>ˇ»D</p>ˇ
11768        "#
11769        .unindent(),
11770    );
11771
11772    cx.update_editor(|editor, window, cx| {
11773        editor.toggle_comments(&ToggleComments::default(), window, cx)
11774    });
11775    cx.assert_editor_state(
11776        &r#"
11777            <!-- <p>A«</p>
11778            <p>ˇ»B</p>ˇ -->
11779            <!-- <p>C«</p>
11780            <p>ˇ»D</p>ˇ -->
11781        "#
11782        .unindent(),
11783    );
11784    cx.update_editor(|editor, window, cx| {
11785        editor.toggle_comments(&ToggleComments::default(), window, cx)
11786    });
11787    cx.assert_editor_state(
11788        &r#"
11789            <p>A«</p>
11790            <p>ˇ»B</p>ˇ
11791            <p>C«</p>
11792            <p>ˇ»D</p>ˇ
11793        "#
11794        .unindent(),
11795    );
11796
11797    // Toggle comments when different languages are active for different
11798    // selections.
11799    cx.set_state(
11800        &r#"
11801            ˇ<script>
11802                ˇvar x = new Y();
11803            ˇ</script>
11804        "#
11805        .unindent(),
11806    );
11807    cx.executor().run_until_parked();
11808    cx.update_editor(|editor, window, cx| {
11809        editor.toggle_comments(&ToggleComments::default(), window, cx)
11810    });
11811    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11812    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11813    cx.assert_editor_state(
11814        &r#"
11815            <!-- ˇ<script> -->
11816                // ˇvar x = new Y();
11817            <!-- ˇ</script> -->
11818        "#
11819        .unindent(),
11820    );
11821}
11822
11823#[gpui::test]
11824fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11825    init_test(cx, |_| {});
11826
11827    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11828    let multibuffer = cx.new(|cx| {
11829        let mut multibuffer = MultiBuffer::new(ReadWrite);
11830        multibuffer.push_excerpts(
11831            buffer.clone(),
11832            [
11833                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11834                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11835            ],
11836            cx,
11837        );
11838        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11839        multibuffer
11840    });
11841
11842    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11843    editor.update_in(cx, |editor, window, cx| {
11844        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11845        editor.change_selections(None, window, cx, |s| {
11846            s.select_ranges([
11847                Point::new(0, 0)..Point::new(0, 0),
11848                Point::new(1, 0)..Point::new(1, 0),
11849            ])
11850        });
11851
11852        editor.handle_input("X", window, cx);
11853        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11854        assert_eq!(
11855            editor.selections.ranges(cx),
11856            [
11857                Point::new(0, 1)..Point::new(0, 1),
11858                Point::new(1, 1)..Point::new(1, 1),
11859            ]
11860        );
11861
11862        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11863        editor.change_selections(None, window, cx, |s| {
11864            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11865        });
11866        editor.backspace(&Default::default(), window, cx);
11867        assert_eq!(editor.text(cx), "Xa\nbbb");
11868        assert_eq!(
11869            editor.selections.ranges(cx),
11870            [Point::new(1, 0)..Point::new(1, 0)]
11871        );
11872
11873        editor.change_selections(None, window, cx, |s| {
11874            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11875        });
11876        editor.backspace(&Default::default(), window, cx);
11877        assert_eq!(editor.text(cx), "X\nbb");
11878        assert_eq!(
11879            editor.selections.ranges(cx),
11880            [Point::new(0, 1)..Point::new(0, 1)]
11881        );
11882    });
11883}
11884
11885#[gpui::test]
11886fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11887    init_test(cx, |_| {});
11888
11889    let markers = vec![('[', ']').into(), ('(', ')').into()];
11890    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11891        indoc! {"
11892            [aaaa
11893            (bbbb]
11894            cccc)",
11895        },
11896        markers.clone(),
11897    );
11898    let excerpt_ranges = markers.into_iter().map(|marker| {
11899        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11900        ExcerptRange::new(context.clone())
11901    });
11902    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11903    let multibuffer = cx.new(|cx| {
11904        let mut multibuffer = MultiBuffer::new(ReadWrite);
11905        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11906        multibuffer
11907    });
11908
11909    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11910    editor.update_in(cx, |editor, window, cx| {
11911        let (expected_text, selection_ranges) = marked_text_ranges(
11912            indoc! {"
11913                aaaa
11914                bˇbbb
11915                bˇbbˇb
11916                cccc"
11917            },
11918            true,
11919        );
11920        assert_eq!(editor.text(cx), expected_text);
11921        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11922
11923        editor.handle_input("X", window, cx);
11924
11925        let (expected_text, expected_selections) = marked_text_ranges(
11926            indoc! {"
11927                aaaa
11928                bXˇbbXb
11929                bXˇbbXˇb
11930                cccc"
11931            },
11932            false,
11933        );
11934        assert_eq!(editor.text(cx), expected_text);
11935        assert_eq!(editor.selections.ranges(cx), expected_selections);
11936
11937        editor.newline(&Newline, window, cx);
11938        let (expected_text, expected_selections) = marked_text_ranges(
11939            indoc! {"
11940                aaaa
11941                bX
11942                ˇbbX
11943                b
11944                bX
11945                ˇbbX
11946                ˇb
11947                cccc"
11948            },
11949            false,
11950        );
11951        assert_eq!(editor.text(cx), expected_text);
11952        assert_eq!(editor.selections.ranges(cx), expected_selections);
11953    });
11954}
11955
11956#[gpui::test]
11957fn test_refresh_selections(cx: &mut TestAppContext) {
11958    init_test(cx, |_| {});
11959
11960    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11961    let mut excerpt1_id = None;
11962    let multibuffer = cx.new(|cx| {
11963        let mut multibuffer = MultiBuffer::new(ReadWrite);
11964        excerpt1_id = multibuffer
11965            .push_excerpts(
11966                buffer.clone(),
11967                [
11968                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11969                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11970                ],
11971                cx,
11972            )
11973            .into_iter()
11974            .next();
11975        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11976        multibuffer
11977    });
11978
11979    let editor = cx.add_window(|window, cx| {
11980        let mut editor = build_editor(multibuffer.clone(), window, cx);
11981        let snapshot = editor.snapshot(window, cx);
11982        editor.change_selections(None, window, cx, |s| {
11983            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11984        });
11985        editor.begin_selection(
11986            Point::new(2, 1).to_display_point(&snapshot),
11987            true,
11988            1,
11989            window,
11990            cx,
11991        );
11992        assert_eq!(
11993            editor.selections.ranges(cx),
11994            [
11995                Point::new(1, 3)..Point::new(1, 3),
11996                Point::new(2, 1)..Point::new(2, 1),
11997            ]
11998        );
11999        editor
12000    });
12001
12002    // Refreshing selections is a no-op when excerpts haven't changed.
12003    _ = editor.update(cx, |editor, window, cx| {
12004        editor.change_selections(None, window, cx, |s| s.refresh());
12005        assert_eq!(
12006            editor.selections.ranges(cx),
12007            [
12008                Point::new(1, 3)..Point::new(1, 3),
12009                Point::new(2, 1)..Point::new(2, 1),
12010            ]
12011        );
12012    });
12013
12014    multibuffer.update(cx, |multibuffer, cx| {
12015        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12016    });
12017    _ = editor.update(cx, |editor, window, cx| {
12018        // Removing an excerpt causes the first selection to become degenerate.
12019        assert_eq!(
12020            editor.selections.ranges(cx),
12021            [
12022                Point::new(0, 0)..Point::new(0, 0),
12023                Point::new(0, 1)..Point::new(0, 1)
12024            ]
12025        );
12026
12027        // Refreshing selections will relocate the first selection to the original buffer
12028        // location.
12029        editor.change_selections(None, window, cx, |s| s.refresh());
12030        assert_eq!(
12031            editor.selections.ranges(cx),
12032            [
12033                Point::new(0, 1)..Point::new(0, 1),
12034                Point::new(0, 3)..Point::new(0, 3)
12035            ]
12036        );
12037        assert!(editor.selections.pending_anchor().is_some());
12038    });
12039}
12040
12041#[gpui::test]
12042fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12043    init_test(cx, |_| {});
12044
12045    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12046    let mut excerpt1_id = None;
12047    let multibuffer = cx.new(|cx| {
12048        let mut multibuffer = MultiBuffer::new(ReadWrite);
12049        excerpt1_id = multibuffer
12050            .push_excerpts(
12051                buffer.clone(),
12052                [
12053                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12054                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12055                ],
12056                cx,
12057            )
12058            .into_iter()
12059            .next();
12060        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12061        multibuffer
12062    });
12063
12064    let editor = cx.add_window(|window, cx| {
12065        let mut editor = build_editor(multibuffer.clone(), window, cx);
12066        let snapshot = editor.snapshot(window, cx);
12067        editor.begin_selection(
12068            Point::new(1, 3).to_display_point(&snapshot),
12069            false,
12070            1,
12071            window,
12072            cx,
12073        );
12074        assert_eq!(
12075            editor.selections.ranges(cx),
12076            [Point::new(1, 3)..Point::new(1, 3)]
12077        );
12078        editor
12079    });
12080
12081    multibuffer.update(cx, |multibuffer, cx| {
12082        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12083    });
12084    _ = editor.update(cx, |editor, window, cx| {
12085        assert_eq!(
12086            editor.selections.ranges(cx),
12087            [Point::new(0, 0)..Point::new(0, 0)]
12088        );
12089
12090        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12091        editor.change_selections(None, window, cx, |s| s.refresh());
12092        assert_eq!(
12093            editor.selections.ranges(cx),
12094            [Point::new(0, 3)..Point::new(0, 3)]
12095        );
12096        assert!(editor.selections.pending_anchor().is_some());
12097    });
12098}
12099
12100#[gpui::test]
12101async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12102    init_test(cx, |_| {});
12103
12104    let language = Arc::new(
12105        Language::new(
12106            LanguageConfig {
12107                brackets: BracketPairConfig {
12108                    pairs: vec![
12109                        BracketPair {
12110                            start: "{".to_string(),
12111                            end: "}".to_string(),
12112                            close: true,
12113                            surround: true,
12114                            newline: true,
12115                        },
12116                        BracketPair {
12117                            start: "/* ".to_string(),
12118                            end: " */".to_string(),
12119                            close: true,
12120                            surround: true,
12121                            newline: true,
12122                        },
12123                    ],
12124                    ..Default::default()
12125                },
12126                ..Default::default()
12127            },
12128            Some(tree_sitter_rust::LANGUAGE.into()),
12129        )
12130        .with_indents_query("")
12131        .unwrap(),
12132    );
12133
12134    let text = concat!(
12135        "{   }\n",     //
12136        "  x\n",       //
12137        "  /*   */\n", //
12138        "x\n",         //
12139        "{{} }\n",     //
12140    );
12141
12142    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12143    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12144    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12145    editor
12146        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12147        .await;
12148
12149    editor.update_in(cx, |editor, window, cx| {
12150        editor.change_selections(None, window, cx, |s| {
12151            s.select_display_ranges([
12152                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12153                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12154                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12155            ])
12156        });
12157        editor.newline(&Newline, window, cx);
12158
12159        assert_eq!(
12160            editor.buffer().read(cx).read(cx).text(),
12161            concat!(
12162                "{ \n",    // Suppress rustfmt
12163                "\n",      //
12164                "}\n",     //
12165                "  x\n",   //
12166                "  /* \n", //
12167                "  \n",    //
12168                "  */\n",  //
12169                "x\n",     //
12170                "{{} \n",  //
12171                "}\n",     //
12172            )
12173        );
12174    });
12175}
12176
12177#[gpui::test]
12178fn test_highlighted_ranges(cx: &mut TestAppContext) {
12179    init_test(cx, |_| {});
12180
12181    let editor = cx.add_window(|window, cx| {
12182        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12183        build_editor(buffer.clone(), window, cx)
12184    });
12185
12186    _ = editor.update(cx, |editor, window, cx| {
12187        struct Type1;
12188        struct Type2;
12189
12190        let buffer = editor.buffer.read(cx).snapshot(cx);
12191
12192        let anchor_range =
12193            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12194
12195        editor.highlight_background::<Type1>(
12196            &[
12197                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12198                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12199                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12200                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12201            ],
12202            |_| Hsla::red(),
12203            cx,
12204        );
12205        editor.highlight_background::<Type2>(
12206            &[
12207                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12208                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12209                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12210                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12211            ],
12212            |_| Hsla::green(),
12213            cx,
12214        );
12215
12216        let snapshot = editor.snapshot(window, cx);
12217        let mut highlighted_ranges = editor.background_highlights_in_range(
12218            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12219            &snapshot,
12220            cx.theme().colors(),
12221        );
12222        // Enforce a consistent ordering based on color without relying on the ordering of the
12223        // highlight's `TypeId` which is non-executor.
12224        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12225        assert_eq!(
12226            highlighted_ranges,
12227            &[
12228                (
12229                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12230                    Hsla::red(),
12231                ),
12232                (
12233                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12234                    Hsla::red(),
12235                ),
12236                (
12237                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12238                    Hsla::green(),
12239                ),
12240                (
12241                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12242                    Hsla::green(),
12243                ),
12244            ]
12245        );
12246        assert_eq!(
12247            editor.background_highlights_in_range(
12248                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12249                &snapshot,
12250                cx.theme().colors(),
12251            ),
12252            &[(
12253                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12254                Hsla::red(),
12255            )]
12256        );
12257    });
12258}
12259
12260#[gpui::test]
12261async fn test_following(cx: &mut TestAppContext) {
12262    init_test(cx, |_| {});
12263
12264    let fs = FakeFs::new(cx.executor());
12265    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12266
12267    let buffer = project.update(cx, |project, cx| {
12268        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12269        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12270    });
12271    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12272    let follower = cx.update(|cx| {
12273        cx.open_window(
12274            WindowOptions {
12275                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12276                    gpui::Point::new(px(0.), px(0.)),
12277                    gpui::Point::new(px(10.), px(80.)),
12278                ))),
12279                ..Default::default()
12280            },
12281            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12282        )
12283        .unwrap()
12284    });
12285
12286    let is_still_following = Rc::new(RefCell::new(true));
12287    let follower_edit_event_count = Rc::new(RefCell::new(0));
12288    let pending_update = Rc::new(RefCell::new(None));
12289    let leader_entity = leader.root(cx).unwrap();
12290    let follower_entity = follower.root(cx).unwrap();
12291    _ = follower.update(cx, {
12292        let update = pending_update.clone();
12293        let is_still_following = is_still_following.clone();
12294        let follower_edit_event_count = follower_edit_event_count.clone();
12295        |_, window, cx| {
12296            cx.subscribe_in(
12297                &leader_entity,
12298                window,
12299                move |_, leader, event, window, cx| {
12300                    leader.read(cx).add_event_to_update_proto(
12301                        event,
12302                        &mut update.borrow_mut(),
12303                        window,
12304                        cx,
12305                    );
12306                },
12307            )
12308            .detach();
12309
12310            cx.subscribe_in(
12311                &follower_entity,
12312                window,
12313                move |_, _, event: &EditorEvent, _window, _cx| {
12314                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12315                        *is_still_following.borrow_mut() = false;
12316                    }
12317
12318                    if let EditorEvent::BufferEdited = event {
12319                        *follower_edit_event_count.borrow_mut() += 1;
12320                    }
12321                },
12322            )
12323            .detach();
12324        }
12325    });
12326
12327    // Update the selections only
12328    _ = leader.update(cx, |leader, window, cx| {
12329        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12330    });
12331    follower
12332        .update(cx, |follower, window, cx| {
12333            follower.apply_update_proto(
12334                &project,
12335                pending_update.borrow_mut().take().unwrap(),
12336                window,
12337                cx,
12338            )
12339        })
12340        .unwrap()
12341        .await
12342        .unwrap();
12343    _ = follower.update(cx, |follower, _, cx| {
12344        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12345    });
12346    assert!(*is_still_following.borrow());
12347    assert_eq!(*follower_edit_event_count.borrow(), 0);
12348
12349    // Update the scroll position only
12350    _ = leader.update(cx, |leader, window, cx| {
12351        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12352    });
12353    follower
12354        .update(cx, |follower, window, cx| {
12355            follower.apply_update_proto(
12356                &project,
12357                pending_update.borrow_mut().take().unwrap(),
12358                window,
12359                cx,
12360            )
12361        })
12362        .unwrap()
12363        .await
12364        .unwrap();
12365    assert_eq!(
12366        follower
12367            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12368            .unwrap(),
12369        gpui::Point::new(1.5, 3.5)
12370    );
12371    assert!(*is_still_following.borrow());
12372    assert_eq!(*follower_edit_event_count.borrow(), 0);
12373
12374    // Update the selections and scroll position. The follower's scroll position is updated
12375    // via autoscroll, not via the leader's exact scroll position.
12376    _ = leader.update(cx, |leader, window, cx| {
12377        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12378        leader.request_autoscroll(Autoscroll::newest(), cx);
12379        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12380    });
12381    follower
12382        .update(cx, |follower, window, cx| {
12383            follower.apply_update_proto(
12384                &project,
12385                pending_update.borrow_mut().take().unwrap(),
12386                window,
12387                cx,
12388            )
12389        })
12390        .unwrap()
12391        .await
12392        .unwrap();
12393    _ = follower.update(cx, |follower, _, cx| {
12394        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12395        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12396    });
12397    assert!(*is_still_following.borrow());
12398
12399    // Creating a pending selection that precedes another selection
12400    _ = leader.update(cx, |leader, window, cx| {
12401        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12402        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12403    });
12404    follower
12405        .update(cx, |follower, window, cx| {
12406            follower.apply_update_proto(
12407                &project,
12408                pending_update.borrow_mut().take().unwrap(),
12409                window,
12410                cx,
12411            )
12412        })
12413        .unwrap()
12414        .await
12415        .unwrap();
12416    _ = follower.update(cx, |follower, _, cx| {
12417        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12418    });
12419    assert!(*is_still_following.borrow());
12420
12421    // Extend the pending selection so that it surrounds another selection
12422    _ = leader.update(cx, |leader, window, cx| {
12423        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12424    });
12425    follower
12426        .update(cx, |follower, window, cx| {
12427            follower.apply_update_proto(
12428                &project,
12429                pending_update.borrow_mut().take().unwrap(),
12430                window,
12431                cx,
12432            )
12433        })
12434        .unwrap()
12435        .await
12436        .unwrap();
12437    _ = follower.update(cx, |follower, _, cx| {
12438        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12439    });
12440
12441    // Scrolling locally breaks the follow
12442    _ = follower.update(cx, |follower, window, cx| {
12443        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12444        follower.set_scroll_anchor(
12445            ScrollAnchor {
12446                anchor: top_anchor,
12447                offset: gpui::Point::new(0.0, 0.5),
12448            },
12449            window,
12450            cx,
12451        );
12452    });
12453    assert!(!(*is_still_following.borrow()));
12454}
12455
12456#[gpui::test]
12457async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12458    init_test(cx, |_| {});
12459
12460    let fs = FakeFs::new(cx.executor());
12461    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12462    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12463    let pane = workspace
12464        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12465        .unwrap();
12466
12467    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12468
12469    let leader = pane.update_in(cx, |_, window, cx| {
12470        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12471        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12472    });
12473
12474    // Start following the editor when it has no excerpts.
12475    let mut state_message =
12476        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12477    let workspace_entity = workspace.root(cx).unwrap();
12478    let follower_1 = cx
12479        .update_window(*workspace.deref(), |_, window, cx| {
12480            Editor::from_state_proto(
12481                workspace_entity,
12482                ViewId {
12483                    creator: Default::default(),
12484                    id: 0,
12485                },
12486                &mut state_message,
12487                window,
12488                cx,
12489            )
12490        })
12491        .unwrap()
12492        .unwrap()
12493        .await
12494        .unwrap();
12495
12496    let update_message = Rc::new(RefCell::new(None));
12497    follower_1.update_in(cx, {
12498        let update = update_message.clone();
12499        |_, window, cx| {
12500            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12501                leader.read(cx).add_event_to_update_proto(
12502                    event,
12503                    &mut update.borrow_mut(),
12504                    window,
12505                    cx,
12506                );
12507            })
12508            .detach();
12509        }
12510    });
12511
12512    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12513        (
12514            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12515            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12516        )
12517    });
12518
12519    // Insert some excerpts.
12520    leader.update(cx, |leader, cx| {
12521        leader.buffer.update(cx, |multibuffer, cx| {
12522            let excerpt_ids = multibuffer.push_excerpts(
12523                buffer_1.clone(),
12524                [
12525                    ExcerptRange::new(1..6),
12526                    ExcerptRange::new(12..15),
12527                    ExcerptRange::new(0..3),
12528                ],
12529                cx,
12530            );
12531            multibuffer.insert_excerpts_after(
12532                excerpt_ids[0],
12533                buffer_2.clone(),
12534                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
12535                cx,
12536            );
12537        });
12538    });
12539
12540    // Apply the update of adding the excerpts.
12541    follower_1
12542        .update_in(cx, |follower, window, cx| {
12543            follower.apply_update_proto(
12544                &project,
12545                update_message.borrow().clone().unwrap(),
12546                window,
12547                cx,
12548            )
12549        })
12550        .await
12551        .unwrap();
12552    assert_eq!(
12553        follower_1.update(cx, |editor, cx| editor.text(cx)),
12554        leader.update(cx, |editor, cx| editor.text(cx))
12555    );
12556    update_message.borrow_mut().take();
12557
12558    // Start following separately after it already has excerpts.
12559    let mut state_message =
12560        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12561    let workspace_entity = workspace.root(cx).unwrap();
12562    let follower_2 = cx
12563        .update_window(*workspace.deref(), |_, window, cx| {
12564            Editor::from_state_proto(
12565                workspace_entity,
12566                ViewId {
12567                    creator: Default::default(),
12568                    id: 0,
12569                },
12570                &mut state_message,
12571                window,
12572                cx,
12573            )
12574        })
12575        .unwrap()
12576        .unwrap()
12577        .await
12578        .unwrap();
12579    assert_eq!(
12580        follower_2.update(cx, |editor, cx| editor.text(cx)),
12581        leader.update(cx, |editor, cx| editor.text(cx))
12582    );
12583
12584    // Remove some excerpts.
12585    leader.update(cx, |leader, cx| {
12586        leader.buffer.update(cx, |multibuffer, cx| {
12587            let excerpt_ids = multibuffer.excerpt_ids();
12588            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12589            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12590        });
12591    });
12592
12593    // Apply the update of removing the excerpts.
12594    follower_1
12595        .update_in(cx, |follower, window, cx| {
12596            follower.apply_update_proto(
12597                &project,
12598                update_message.borrow().clone().unwrap(),
12599                window,
12600                cx,
12601            )
12602        })
12603        .await
12604        .unwrap();
12605    follower_2
12606        .update_in(cx, |follower, window, cx| {
12607            follower.apply_update_proto(
12608                &project,
12609                update_message.borrow().clone().unwrap(),
12610                window,
12611                cx,
12612            )
12613        })
12614        .await
12615        .unwrap();
12616    update_message.borrow_mut().take();
12617    assert_eq!(
12618        follower_1.update(cx, |editor, cx| editor.text(cx)),
12619        leader.update(cx, |editor, cx| editor.text(cx))
12620    );
12621}
12622
12623#[gpui::test]
12624async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12625    init_test(cx, |_| {});
12626
12627    let mut cx = EditorTestContext::new(cx).await;
12628    let lsp_store =
12629        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12630
12631    cx.set_state(indoc! {"
12632        ˇfn func(abc def: i32) -> u32 {
12633        }
12634    "});
12635
12636    cx.update(|_, cx| {
12637        lsp_store.update(cx, |lsp_store, cx| {
12638            lsp_store
12639                .update_diagnostics(
12640                    LanguageServerId(0),
12641                    lsp::PublishDiagnosticsParams {
12642                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12643                        version: None,
12644                        diagnostics: vec![
12645                            lsp::Diagnostic {
12646                                range: lsp::Range::new(
12647                                    lsp::Position::new(0, 11),
12648                                    lsp::Position::new(0, 12),
12649                                ),
12650                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12651                                ..Default::default()
12652                            },
12653                            lsp::Diagnostic {
12654                                range: lsp::Range::new(
12655                                    lsp::Position::new(0, 12),
12656                                    lsp::Position::new(0, 15),
12657                                ),
12658                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12659                                ..Default::default()
12660                            },
12661                            lsp::Diagnostic {
12662                                range: lsp::Range::new(
12663                                    lsp::Position::new(0, 25),
12664                                    lsp::Position::new(0, 28),
12665                                ),
12666                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12667                                ..Default::default()
12668                            },
12669                        ],
12670                    },
12671                    &[],
12672                    cx,
12673                )
12674                .unwrap()
12675        });
12676    });
12677
12678    executor.run_until_parked();
12679
12680    cx.update_editor(|editor, window, cx| {
12681        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12682    });
12683
12684    cx.assert_editor_state(indoc! {"
12685        fn func(abc def: i32) -> ˇu32 {
12686        }
12687    "});
12688
12689    cx.update_editor(|editor, window, cx| {
12690        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12691    });
12692
12693    cx.assert_editor_state(indoc! {"
12694        fn func(abc ˇdef: i32) -> u32 {
12695        }
12696    "});
12697
12698    cx.update_editor(|editor, window, cx| {
12699        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12700    });
12701
12702    cx.assert_editor_state(indoc! {"
12703        fn func(abcˇ def: i32) -> u32 {
12704        }
12705    "});
12706
12707    cx.update_editor(|editor, window, cx| {
12708        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12709    });
12710
12711    cx.assert_editor_state(indoc! {"
12712        fn func(abc def: i32) -> ˇu32 {
12713        }
12714    "});
12715}
12716
12717#[gpui::test]
12718async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12719    init_test(cx, |_| {});
12720
12721    let mut cx = EditorTestContext::new(cx).await;
12722
12723    cx.set_state(indoc! {"
12724        fn func(abˇc def: i32) -> u32 {
12725        }
12726    "});
12727    let lsp_store =
12728        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12729
12730    cx.update(|_, cx| {
12731        lsp_store.update(cx, |lsp_store, cx| {
12732            lsp_store.update_diagnostics(
12733                LanguageServerId(0),
12734                lsp::PublishDiagnosticsParams {
12735                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12736                    version: None,
12737                    diagnostics: vec![lsp::Diagnostic {
12738                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12739                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12740                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12741                        ..Default::default()
12742                    }],
12743                },
12744                &[],
12745                cx,
12746            )
12747        })
12748    }).unwrap();
12749    cx.run_until_parked();
12750    cx.update_editor(|editor, window, cx| {
12751        hover_popover::hover(editor, &Default::default(), window, cx)
12752    });
12753    cx.run_until_parked();
12754    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12755}
12756
12757#[gpui::test]
12758async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12759    init_test(cx, |_| {});
12760
12761    let mut cx = EditorTestContext::new(cx).await;
12762
12763    let diff_base = r#"
12764        use some::mod;
12765
12766        const A: u32 = 42;
12767
12768        fn main() {
12769            println!("hello");
12770
12771            println!("world");
12772        }
12773        "#
12774    .unindent();
12775
12776    // Edits are modified, removed, modified, added
12777    cx.set_state(
12778        &r#"
12779        use some::modified;
12780
12781        ˇ
12782        fn main() {
12783            println!("hello there");
12784
12785            println!("around the");
12786            println!("world");
12787        }
12788        "#
12789        .unindent(),
12790    );
12791
12792    cx.set_head_text(&diff_base);
12793    executor.run_until_parked();
12794
12795    cx.update_editor(|editor, window, cx| {
12796        //Wrap around the bottom of the buffer
12797        for _ in 0..3 {
12798            editor.go_to_next_hunk(&GoToHunk, window, cx);
12799        }
12800    });
12801
12802    cx.assert_editor_state(
12803        &r#"
12804        ˇuse some::modified;
12805
12806
12807        fn main() {
12808            println!("hello there");
12809
12810            println!("around the");
12811            println!("world");
12812        }
12813        "#
12814        .unindent(),
12815    );
12816
12817    cx.update_editor(|editor, window, cx| {
12818        //Wrap around the top of the buffer
12819        for _ in 0..2 {
12820            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12821        }
12822    });
12823
12824    cx.assert_editor_state(
12825        &r#"
12826        use some::modified;
12827
12828
12829        fn main() {
12830        ˇ    println!("hello there");
12831
12832            println!("around the");
12833            println!("world");
12834        }
12835        "#
12836        .unindent(),
12837    );
12838
12839    cx.update_editor(|editor, window, cx| {
12840        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12841    });
12842
12843    cx.assert_editor_state(
12844        &r#"
12845        use some::modified;
12846
12847        ˇ
12848        fn main() {
12849            println!("hello there");
12850
12851            println!("around the");
12852            println!("world");
12853        }
12854        "#
12855        .unindent(),
12856    );
12857
12858    cx.update_editor(|editor, window, cx| {
12859        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12860    });
12861
12862    cx.assert_editor_state(
12863        &r#"
12864        ˇuse some::modified;
12865
12866
12867        fn main() {
12868            println!("hello there");
12869
12870            println!("around the");
12871            println!("world");
12872        }
12873        "#
12874        .unindent(),
12875    );
12876
12877    cx.update_editor(|editor, window, cx| {
12878        for _ in 0..2 {
12879            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12880        }
12881    });
12882
12883    cx.assert_editor_state(
12884        &r#"
12885        use some::modified;
12886
12887
12888        fn main() {
12889        ˇ    println!("hello there");
12890
12891            println!("around the");
12892            println!("world");
12893        }
12894        "#
12895        .unindent(),
12896    );
12897
12898    cx.update_editor(|editor, window, cx| {
12899        editor.fold(&Fold, window, cx);
12900    });
12901
12902    cx.update_editor(|editor, window, cx| {
12903        editor.go_to_next_hunk(&GoToHunk, window, cx);
12904    });
12905
12906    cx.assert_editor_state(
12907        &r#"
12908        ˇuse some::modified;
12909
12910
12911        fn main() {
12912            println!("hello there");
12913
12914            println!("around the");
12915            println!("world");
12916        }
12917        "#
12918        .unindent(),
12919    );
12920}
12921
12922#[test]
12923fn test_split_words() {
12924    fn split(text: &str) -> Vec<&str> {
12925        split_words(text).collect()
12926    }
12927
12928    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12929    assert_eq!(split("hello_world"), &["hello_", "world"]);
12930    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12931    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12932    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12933    assert_eq!(split("helloworld"), &["helloworld"]);
12934
12935    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12936}
12937
12938#[gpui::test]
12939async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12940    init_test(cx, |_| {});
12941
12942    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12943    let mut assert = |before, after| {
12944        let _state_context = cx.set_state(before);
12945        cx.run_until_parked();
12946        cx.update_editor(|editor, window, cx| {
12947            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12948        });
12949        cx.run_until_parked();
12950        cx.assert_editor_state(after);
12951    };
12952
12953    // Outside bracket jumps to outside of matching bracket
12954    assert("console.logˇ(var);", "console.log(var)ˇ;");
12955    assert("console.log(var)ˇ;", "console.logˇ(var);");
12956
12957    // Inside bracket jumps to inside of matching bracket
12958    assert("console.log(ˇvar);", "console.log(varˇ);");
12959    assert("console.log(varˇ);", "console.log(ˇvar);");
12960
12961    // When outside a bracket and inside, favor jumping to the inside bracket
12962    assert(
12963        "console.log('foo', [1, 2, 3]ˇ);",
12964        "console.log(ˇ'foo', [1, 2, 3]);",
12965    );
12966    assert(
12967        "console.log(ˇ'foo', [1, 2, 3]);",
12968        "console.log('foo', [1, 2, 3]ˇ);",
12969    );
12970
12971    // Bias forward if two options are equally likely
12972    assert(
12973        "let result = curried_fun()ˇ();",
12974        "let result = curried_fun()()ˇ;",
12975    );
12976
12977    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12978    assert(
12979        indoc! {"
12980            function test() {
12981                console.log('test')ˇ
12982            }"},
12983        indoc! {"
12984            function test() {
12985                console.logˇ('test')
12986            }"},
12987    );
12988}
12989
12990#[gpui::test]
12991async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12992    init_test(cx, |_| {});
12993
12994    let fs = FakeFs::new(cx.executor());
12995    fs.insert_tree(
12996        path!("/a"),
12997        json!({
12998            "main.rs": "fn main() { let a = 5; }",
12999            "other.rs": "// Test file",
13000        }),
13001    )
13002    .await;
13003    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13004
13005    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13006    language_registry.add(Arc::new(Language::new(
13007        LanguageConfig {
13008            name: "Rust".into(),
13009            matcher: LanguageMatcher {
13010                path_suffixes: vec!["rs".to_string()],
13011                ..Default::default()
13012            },
13013            brackets: BracketPairConfig {
13014                pairs: vec![BracketPair {
13015                    start: "{".to_string(),
13016                    end: "}".to_string(),
13017                    close: true,
13018                    surround: true,
13019                    newline: true,
13020                }],
13021                disabled_scopes_by_bracket_ix: Vec::new(),
13022            },
13023            ..Default::default()
13024        },
13025        Some(tree_sitter_rust::LANGUAGE.into()),
13026    )));
13027    let mut fake_servers = language_registry.register_fake_lsp(
13028        "Rust",
13029        FakeLspAdapter {
13030            capabilities: lsp::ServerCapabilities {
13031                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13032                    first_trigger_character: "{".to_string(),
13033                    more_trigger_character: None,
13034                }),
13035                ..Default::default()
13036            },
13037            ..Default::default()
13038        },
13039    );
13040
13041    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13042
13043    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13044
13045    let worktree_id = workspace
13046        .update(cx, |workspace, _, cx| {
13047            workspace.project().update(cx, |project, cx| {
13048                project.worktrees(cx).next().unwrap().read(cx).id()
13049            })
13050        })
13051        .unwrap();
13052
13053    let buffer = project
13054        .update(cx, |project, cx| {
13055            project.open_local_buffer(path!("/a/main.rs"), cx)
13056        })
13057        .await
13058        .unwrap();
13059    let editor_handle = workspace
13060        .update(cx, |workspace, window, cx| {
13061            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13062        })
13063        .unwrap()
13064        .await
13065        .unwrap()
13066        .downcast::<Editor>()
13067        .unwrap();
13068
13069    cx.executor().start_waiting();
13070    let fake_server = fake_servers.next().await.unwrap();
13071
13072    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13073        |params, _| async move {
13074            assert_eq!(
13075                params.text_document_position.text_document.uri,
13076                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13077            );
13078            assert_eq!(
13079                params.text_document_position.position,
13080                lsp::Position::new(0, 21),
13081            );
13082
13083            Ok(Some(vec![lsp::TextEdit {
13084                new_text: "]".to_string(),
13085                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13086            }]))
13087        },
13088    );
13089
13090    editor_handle.update_in(cx, |editor, window, cx| {
13091        window.focus(&editor.focus_handle(cx));
13092        editor.change_selections(None, window, cx, |s| {
13093            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13094        });
13095        editor.handle_input("{", window, cx);
13096    });
13097
13098    cx.executor().run_until_parked();
13099
13100    buffer.update(cx, |buffer, _| {
13101        assert_eq!(
13102            buffer.text(),
13103            "fn main() { let a = {5}; }",
13104            "No extra braces from on type formatting should appear in the buffer"
13105        )
13106    });
13107}
13108
13109#[gpui::test]
13110async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13111    init_test(cx, |_| {});
13112
13113    let fs = FakeFs::new(cx.executor());
13114    fs.insert_tree(
13115        path!("/a"),
13116        json!({
13117            "main.rs": "fn main() { let a = 5; }",
13118            "other.rs": "// Test file",
13119        }),
13120    )
13121    .await;
13122
13123    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13124
13125    let server_restarts = Arc::new(AtomicUsize::new(0));
13126    let closure_restarts = Arc::clone(&server_restarts);
13127    let language_server_name = "test language server";
13128    let language_name: LanguageName = "Rust".into();
13129
13130    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13131    language_registry.add(Arc::new(Language::new(
13132        LanguageConfig {
13133            name: language_name.clone(),
13134            matcher: LanguageMatcher {
13135                path_suffixes: vec!["rs".to_string()],
13136                ..Default::default()
13137            },
13138            ..Default::default()
13139        },
13140        Some(tree_sitter_rust::LANGUAGE.into()),
13141    )));
13142    let mut fake_servers = language_registry.register_fake_lsp(
13143        "Rust",
13144        FakeLspAdapter {
13145            name: language_server_name,
13146            initialization_options: Some(json!({
13147                "testOptionValue": true
13148            })),
13149            initializer: Some(Box::new(move |fake_server| {
13150                let task_restarts = Arc::clone(&closure_restarts);
13151                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13152                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13153                    futures::future::ready(Ok(()))
13154                });
13155            })),
13156            ..Default::default()
13157        },
13158    );
13159
13160    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13161    let _buffer = project
13162        .update(cx, |project, cx| {
13163            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13164        })
13165        .await
13166        .unwrap();
13167    let _fake_server = fake_servers.next().await.unwrap();
13168    update_test_language_settings(cx, |language_settings| {
13169        language_settings.languages.insert(
13170            language_name.clone(),
13171            LanguageSettingsContent {
13172                tab_size: NonZeroU32::new(8),
13173                ..Default::default()
13174            },
13175        );
13176    });
13177    cx.executor().run_until_parked();
13178    assert_eq!(
13179        server_restarts.load(atomic::Ordering::Acquire),
13180        0,
13181        "Should not restart LSP server on an unrelated change"
13182    );
13183
13184    update_test_project_settings(cx, |project_settings| {
13185        project_settings.lsp.insert(
13186            "Some other server name".into(),
13187            LspSettings {
13188                binary: None,
13189                settings: None,
13190                initialization_options: Some(json!({
13191                    "some other init value": false
13192                })),
13193                enable_lsp_tasks: false,
13194            },
13195        );
13196    });
13197    cx.executor().run_until_parked();
13198    assert_eq!(
13199        server_restarts.load(atomic::Ordering::Acquire),
13200        0,
13201        "Should not restart LSP server on an unrelated LSP settings change"
13202    );
13203
13204    update_test_project_settings(cx, |project_settings| {
13205        project_settings.lsp.insert(
13206            language_server_name.into(),
13207            LspSettings {
13208                binary: None,
13209                settings: None,
13210                initialization_options: Some(json!({
13211                    "anotherInitValue": false
13212                })),
13213                enable_lsp_tasks: false,
13214            },
13215        );
13216    });
13217    cx.executor().run_until_parked();
13218    assert_eq!(
13219        server_restarts.load(atomic::Ordering::Acquire),
13220        1,
13221        "Should restart LSP server on a related LSP settings change"
13222    );
13223
13224    update_test_project_settings(cx, |project_settings| {
13225        project_settings.lsp.insert(
13226            language_server_name.into(),
13227            LspSettings {
13228                binary: None,
13229                settings: None,
13230                initialization_options: Some(json!({
13231                    "anotherInitValue": false
13232                })),
13233                enable_lsp_tasks: false,
13234            },
13235        );
13236    });
13237    cx.executor().run_until_parked();
13238    assert_eq!(
13239        server_restarts.load(atomic::Ordering::Acquire),
13240        1,
13241        "Should not restart LSP server on a related LSP settings change that is the same"
13242    );
13243
13244    update_test_project_settings(cx, |project_settings| {
13245        project_settings.lsp.insert(
13246            language_server_name.into(),
13247            LspSettings {
13248                binary: None,
13249                settings: None,
13250                initialization_options: None,
13251                enable_lsp_tasks: false,
13252            },
13253        );
13254    });
13255    cx.executor().run_until_parked();
13256    assert_eq!(
13257        server_restarts.load(atomic::Ordering::Acquire),
13258        2,
13259        "Should restart LSP server on another related LSP settings change"
13260    );
13261}
13262
13263#[gpui::test]
13264async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13265    init_test(cx, |_| {});
13266
13267    let mut cx = EditorLspTestContext::new_rust(
13268        lsp::ServerCapabilities {
13269            completion_provider: Some(lsp::CompletionOptions {
13270                trigger_characters: Some(vec![".".to_string()]),
13271                resolve_provider: Some(true),
13272                ..Default::default()
13273            }),
13274            ..Default::default()
13275        },
13276        cx,
13277    )
13278    .await;
13279
13280    cx.set_state("fn main() { let a = 2ˇ; }");
13281    cx.simulate_keystroke(".");
13282    let completion_item = lsp::CompletionItem {
13283        label: "some".into(),
13284        kind: Some(lsp::CompletionItemKind::SNIPPET),
13285        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13286        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13287            kind: lsp::MarkupKind::Markdown,
13288            value: "```rust\nSome(2)\n```".to_string(),
13289        })),
13290        deprecated: Some(false),
13291        sort_text: Some("fffffff2".to_string()),
13292        filter_text: Some("some".to_string()),
13293        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13294        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13295            range: lsp::Range {
13296                start: lsp::Position {
13297                    line: 0,
13298                    character: 22,
13299                },
13300                end: lsp::Position {
13301                    line: 0,
13302                    character: 22,
13303                },
13304            },
13305            new_text: "Some(2)".to_string(),
13306        })),
13307        additional_text_edits: Some(vec![lsp::TextEdit {
13308            range: lsp::Range {
13309                start: lsp::Position {
13310                    line: 0,
13311                    character: 20,
13312                },
13313                end: lsp::Position {
13314                    line: 0,
13315                    character: 22,
13316                },
13317            },
13318            new_text: "".to_string(),
13319        }]),
13320        ..Default::default()
13321    };
13322
13323    let closure_completion_item = completion_item.clone();
13324    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13325        let task_completion_item = closure_completion_item.clone();
13326        async move {
13327            Ok(Some(lsp::CompletionResponse::Array(vec![
13328                task_completion_item,
13329            ])))
13330        }
13331    });
13332
13333    request.next().await;
13334
13335    cx.condition(|editor, _| editor.context_menu_visible())
13336        .await;
13337    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13338        editor
13339            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13340            .unwrap()
13341    });
13342    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13343
13344    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13345        let task_completion_item = completion_item.clone();
13346        async move { Ok(task_completion_item) }
13347    })
13348    .next()
13349    .await
13350    .unwrap();
13351    apply_additional_edits.await.unwrap();
13352    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13353}
13354
13355#[gpui::test]
13356async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13357    init_test(cx, |_| {});
13358
13359    let mut cx = EditorLspTestContext::new_rust(
13360        lsp::ServerCapabilities {
13361            completion_provider: Some(lsp::CompletionOptions {
13362                trigger_characters: Some(vec![".".to_string()]),
13363                resolve_provider: Some(true),
13364                ..Default::default()
13365            }),
13366            ..Default::default()
13367        },
13368        cx,
13369    )
13370    .await;
13371
13372    cx.set_state("fn main() { let a = 2ˇ; }");
13373    cx.simulate_keystroke(".");
13374
13375    let item1 = lsp::CompletionItem {
13376        label: "method id()".to_string(),
13377        filter_text: Some("id".to_string()),
13378        detail: None,
13379        documentation: None,
13380        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13381            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13382            new_text: ".id".to_string(),
13383        })),
13384        ..lsp::CompletionItem::default()
13385    };
13386
13387    let item2 = lsp::CompletionItem {
13388        label: "other".to_string(),
13389        filter_text: Some("other".to_string()),
13390        detail: None,
13391        documentation: None,
13392        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13393            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13394            new_text: ".other".to_string(),
13395        })),
13396        ..lsp::CompletionItem::default()
13397    };
13398
13399    let item1 = item1.clone();
13400    cx.set_request_handler::<lsp::request::Completion, _, _>({
13401        let item1 = item1.clone();
13402        move |_, _, _| {
13403            let item1 = item1.clone();
13404            let item2 = item2.clone();
13405            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13406        }
13407    })
13408    .next()
13409    .await;
13410
13411    cx.condition(|editor, _| editor.context_menu_visible())
13412        .await;
13413    cx.update_editor(|editor, _, _| {
13414        let context_menu = editor.context_menu.borrow_mut();
13415        let context_menu = context_menu
13416            .as_ref()
13417            .expect("Should have the context menu deployed");
13418        match context_menu {
13419            CodeContextMenu::Completions(completions_menu) => {
13420                let completions = completions_menu.completions.borrow_mut();
13421                assert_eq!(
13422                    completions
13423                        .iter()
13424                        .map(|completion| &completion.label.text)
13425                        .collect::<Vec<_>>(),
13426                    vec!["method id()", "other"]
13427                )
13428            }
13429            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13430        }
13431    });
13432
13433    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13434        let item1 = item1.clone();
13435        move |_, item_to_resolve, _| {
13436            let item1 = item1.clone();
13437            async move {
13438                if item1 == item_to_resolve {
13439                    Ok(lsp::CompletionItem {
13440                        label: "method id()".to_string(),
13441                        filter_text: Some("id".to_string()),
13442                        detail: Some("Now resolved!".to_string()),
13443                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13444                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13445                            range: lsp::Range::new(
13446                                lsp::Position::new(0, 22),
13447                                lsp::Position::new(0, 22),
13448                            ),
13449                            new_text: ".id".to_string(),
13450                        })),
13451                        ..lsp::CompletionItem::default()
13452                    })
13453                } else {
13454                    Ok(item_to_resolve)
13455                }
13456            }
13457        }
13458    })
13459    .next()
13460    .await
13461    .unwrap();
13462    cx.run_until_parked();
13463
13464    cx.update_editor(|editor, window, cx| {
13465        editor.context_menu_next(&Default::default(), window, cx);
13466    });
13467
13468    cx.update_editor(|editor, _, _| {
13469        let context_menu = editor.context_menu.borrow_mut();
13470        let context_menu = context_menu
13471            .as_ref()
13472            .expect("Should have the context menu deployed");
13473        match context_menu {
13474            CodeContextMenu::Completions(completions_menu) => {
13475                let completions = completions_menu.completions.borrow_mut();
13476                assert_eq!(
13477                    completions
13478                        .iter()
13479                        .map(|completion| &completion.label.text)
13480                        .collect::<Vec<_>>(),
13481                    vec!["method id() Now resolved!", "other"],
13482                    "Should update first completion label, but not second as the filter text did not match."
13483                );
13484            }
13485            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13486        }
13487    });
13488}
13489
13490#[gpui::test]
13491async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13492    init_test(cx, |_| {});
13493
13494    let mut cx = EditorLspTestContext::new_rust(
13495        lsp::ServerCapabilities {
13496            completion_provider: Some(lsp::CompletionOptions {
13497                trigger_characters: Some(vec![".".to_string()]),
13498                resolve_provider: Some(true),
13499                ..Default::default()
13500            }),
13501            ..Default::default()
13502        },
13503        cx,
13504    )
13505    .await;
13506
13507    cx.set_state("fn main() { let a = 2ˇ; }");
13508    cx.simulate_keystroke(".");
13509
13510    let unresolved_item_1 = lsp::CompletionItem {
13511        label: "id".to_string(),
13512        filter_text: Some("id".to_string()),
13513        detail: None,
13514        documentation: None,
13515        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13516            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13517            new_text: ".id".to_string(),
13518        })),
13519        ..lsp::CompletionItem::default()
13520    };
13521    let resolved_item_1 = lsp::CompletionItem {
13522        additional_text_edits: Some(vec![lsp::TextEdit {
13523            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13524            new_text: "!!".to_string(),
13525        }]),
13526        ..unresolved_item_1.clone()
13527    };
13528    let unresolved_item_2 = lsp::CompletionItem {
13529        label: "other".to_string(),
13530        filter_text: Some("other".to_string()),
13531        detail: None,
13532        documentation: None,
13533        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13534            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13535            new_text: ".other".to_string(),
13536        })),
13537        ..lsp::CompletionItem::default()
13538    };
13539    let resolved_item_2 = lsp::CompletionItem {
13540        additional_text_edits: Some(vec![lsp::TextEdit {
13541            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13542            new_text: "??".to_string(),
13543        }]),
13544        ..unresolved_item_2.clone()
13545    };
13546
13547    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13548    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13549    cx.lsp
13550        .server
13551        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13552            let unresolved_item_1 = unresolved_item_1.clone();
13553            let resolved_item_1 = resolved_item_1.clone();
13554            let unresolved_item_2 = unresolved_item_2.clone();
13555            let resolved_item_2 = resolved_item_2.clone();
13556            let resolve_requests_1 = resolve_requests_1.clone();
13557            let resolve_requests_2 = resolve_requests_2.clone();
13558            move |unresolved_request, _| {
13559                let unresolved_item_1 = unresolved_item_1.clone();
13560                let resolved_item_1 = resolved_item_1.clone();
13561                let unresolved_item_2 = unresolved_item_2.clone();
13562                let resolved_item_2 = resolved_item_2.clone();
13563                let resolve_requests_1 = resolve_requests_1.clone();
13564                let resolve_requests_2 = resolve_requests_2.clone();
13565                async move {
13566                    if unresolved_request == unresolved_item_1 {
13567                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13568                        Ok(resolved_item_1.clone())
13569                    } else if unresolved_request == unresolved_item_2 {
13570                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13571                        Ok(resolved_item_2.clone())
13572                    } else {
13573                        panic!("Unexpected completion item {unresolved_request:?}")
13574                    }
13575                }
13576            }
13577        })
13578        .detach();
13579
13580    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13581        let unresolved_item_1 = unresolved_item_1.clone();
13582        let unresolved_item_2 = unresolved_item_2.clone();
13583        async move {
13584            Ok(Some(lsp::CompletionResponse::Array(vec![
13585                unresolved_item_1,
13586                unresolved_item_2,
13587            ])))
13588        }
13589    })
13590    .next()
13591    .await;
13592
13593    cx.condition(|editor, _| editor.context_menu_visible())
13594        .await;
13595    cx.update_editor(|editor, _, _| {
13596        let context_menu = editor.context_menu.borrow_mut();
13597        let context_menu = context_menu
13598            .as_ref()
13599            .expect("Should have the context menu deployed");
13600        match context_menu {
13601            CodeContextMenu::Completions(completions_menu) => {
13602                let completions = completions_menu.completions.borrow_mut();
13603                assert_eq!(
13604                    completions
13605                        .iter()
13606                        .map(|completion| &completion.label.text)
13607                        .collect::<Vec<_>>(),
13608                    vec!["id", "other"]
13609                )
13610            }
13611            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13612        }
13613    });
13614    cx.run_until_parked();
13615
13616    cx.update_editor(|editor, window, cx| {
13617        editor.context_menu_next(&ContextMenuNext, window, cx);
13618    });
13619    cx.run_until_parked();
13620    cx.update_editor(|editor, window, cx| {
13621        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13622    });
13623    cx.run_until_parked();
13624    cx.update_editor(|editor, window, cx| {
13625        editor.context_menu_next(&ContextMenuNext, window, cx);
13626    });
13627    cx.run_until_parked();
13628    cx.update_editor(|editor, window, cx| {
13629        editor
13630            .compose_completion(&ComposeCompletion::default(), window, cx)
13631            .expect("No task returned")
13632    })
13633    .await
13634    .expect("Completion failed");
13635    cx.run_until_parked();
13636
13637    cx.update_editor(|editor, _, cx| {
13638        assert_eq!(
13639            resolve_requests_1.load(atomic::Ordering::Acquire),
13640            1,
13641            "Should always resolve once despite multiple selections"
13642        );
13643        assert_eq!(
13644            resolve_requests_2.load(atomic::Ordering::Acquire),
13645            1,
13646            "Should always resolve once after multiple selections and applying the completion"
13647        );
13648        assert_eq!(
13649            editor.text(cx),
13650            "fn main() { let a = ??.other; }",
13651            "Should use resolved data when applying the completion"
13652        );
13653    });
13654}
13655
13656#[gpui::test]
13657async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13658    init_test(cx, |_| {});
13659
13660    let item_0 = lsp::CompletionItem {
13661        label: "abs".into(),
13662        insert_text: Some("abs".into()),
13663        data: Some(json!({ "very": "special"})),
13664        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13665        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13666            lsp::InsertReplaceEdit {
13667                new_text: "abs".to_string(),
13668                insert: lsp::Range::default(),
13669                replace: lsp::Range::default(),
13670            },
13671        )),
13672        ..lsp::CompletionItem::default()
13673    };
13674    let items = iter::once(item_0.clone())
13675        .chain((11..51).map(|i| lsp::CompletionItem {
13676            label: format!("item_{}", i),
13677            insert_text: Some(format!("item_{}", i)),
13678            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13679            ..lsp::CompletionItem::default()
13680        }))
13681        .collect::<Vec<_>>();
13682
13683    let default_commit_characters = vec!["?".to_string()];
13684    let default_data = json!({ "default": "data"});
13685    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13686    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13687    let default_edit_range = lsp::Range {
13688        start: lsp::Position {
13689            line: 0,
13690            character: 5,
13691        },
13692        end: lsp::Position {
13693            line: 0,
13694            character: 5,
13695        },
13696    };
13697
13698    let mut cx = EditorLspTestContext::new_rust(
13699        lsp::ServerCapabilities {
13700            completion_provider: Some(lsp::CompletionOptions {
13701                trigger_characters: Some(vec![".".to_string()]),
13702                resolve_provider: Some(true),
13703                ..Default::default()
13704            }),
13705            ..Default::default()
13706        },
13707        cx,
13708    )
13709    .await;
13710
13711    cx.set_state("fn main() { let a = 2ˇ; }");
13712    cx.simulate_keystroke(".");
13713
13714    let completion_data = default_data.clone();
13715    let completion_characters = default_commit_characters.clone();
13716    let completion_items = items.clone();
13717    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13718        let default_data = completion_data.clone();
13719        let default_commit_characters = completion_characters.clone();
13720        let items = completion_items.clone();
13721        async move {
13722            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13723                items,
13724                item_defaults: Some(lsp::CompletionListItemDefaults {
13725                    data: Some(default_data.clone()),
13726                    commit_characters: Some(default_commit_characters.clone()),
13727                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13728                        default_edit_range,
13729                    )),
13730                    insert_text_format: Some(default_insert_text_format),
13731                    insert_text_mode: Some(default_insert_text_mode),
13732                }),
13733                ..lsp::CompletionList::default()
13734            })))
13735        }
13736    })
13737    .next()
13738    .await;
13739
13740    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13741    cx.lsp
13742        .server
13743        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13744            let closure_resolved_items = resolved_items.clone();
13745            move |item_to_resolve, _| {
13746                let closure_resolved_items = closure_resolved_items.clone();
13747                async move {
13748                    closure_resolved_items.lock().push(item_to_resolve.clone());
13749                    Ok(item_to_resolve)
13750                }
13751            }
13752        })
13753        .detach();
13754
13755    cx.condition(|editor, _| editor.context_menu_visible())
13756        .await;
13757    cx.run_until_parked();
13758    cx.update_editor(|editor, _, _| {
13759        let menu = editor.context_menu.borrow_mut();
13760        match menu.as_ref().expect("should have the completions menu") {
13761            CodeContextMenu::Completions(completions_menu) => {
13762                assert_eq!(
13763                    completions_menu
13764                        .entries
13765                        .borrow()
13766                        .iter()
13767                        .map(|mat| mat.string.clone())
13768                        .collect::<Vec<String>>(),
13769                    items
13770                        .iter()
13771                        .map(|completion| completion.label.clone())
13772                        .collect::<Vec<String>>()
13773                );
13774            }
13775            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13776        }
13777    });
13778    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13779    // with 4 from the end.
13780    assert_eq!(
13781        *resolved_items.lock(),
13782        [&items[0..16], &items[items.len() - 4..items.len()]]
13783            .concat()
13784            .iter()
13785            .cloned()
13786            .map(|mut item| {
13787                if item.data.is_none() {
13788                    item.data = Some(default_data.clone());
13789                }
13790                item
13791            })
13792            .collect::<Vec<lsp::CompletionItem>>(),
13793        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13794    );
13795    resolved_items.lock().clear();
13796
13797    cx.update_editor(|editor, window, cx| {
13798        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13799    });
13800    cx.run_until_parked();
13801    // Completions that have already been resolved are skipped.
13802    assert_eq!(
13803        *resolved_items.lock(),
13804        items[items.len() - 16..items.len() - 4]
13805            .iter()
13806            .cloned()
13807            .map(|mut item| {
13808                if item.data.is_none() {
13809                    item.data = Some(default_data.clone());
13810                }
13811                item
13812            })
13813            .collect::<Vec<lsp::CompletionItem>>()
13814    );
13815    resolved_items.lock().clear();
13816}
13817
13818#[gpui::test]
13819async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13820    init_test(cx, |_| {});
13821
13822    let mut cx = EditorLspTestContext::new(
13823        Language::new(
13824            LanguageConfig {
13825                matcher: LanguageMatcher {
13826                    path_suffixes: vec!["jsx".into()],
13827                    ..Default::default()
13828                },
13829                overrides: [(
13830                    "element".into(),
13831                    LanguageConfigOverride {
13832                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13833                        ..Default::default()
13834                    },
13835                )]
13836                .into_iter()
13837                .collect(),
13838                ..Default::default()
13839            },
13840            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13841        )
13842        .with_override_query("(jsx_self_closing_element) @element")
13843        .unwrap(),
13844        lsp::ServerCapabilities {
13845            completion_provider: Some(lsp::CompletionOptions {
13846                trigger_characters: Some(vec![":".to_string()]),
13847                ..Default::default()
13848            }),
13849            ..Default::default()
13850        },
13851        cx,
13852    )
13853    .await;
13854
13855    cx.lsp
13856        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13857            Ok(Some(lsp::CompletionResponse::Array(vec![
13858                lsp::CompletionItem {
13859                    label: "bg-blue".into(),
13860                    ..Default::default()
13861                },
13862                lsp::CompletionItem {
13863                    label: "bg-red".into(),
13864                    ..Default::default()
13865                },
13866                lsp::CompletionItem {
13867                    label: "bg-yellow".into(),
13868                    ..Default::default()
13869                },
13870            ])))
13871        });
13872
13873    cx.set_state(r#"<p class="bgˇ" />"#);
13874
13875    // Trigger completion when typing a dash, because the dash is an extra
13876    // word character in the 'element' scope, which contains the cursor.
13877    cx.simulate_keystroke("-");
13878    cx.executor().run_until_parked();
13879    cx.update_editor(|editor, _, _| {
13880        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13881        {
13882            assert_eq!(
13883                completion_menu_entries(&menu),
13884                &["bg-red", "bg-blue", "bg-yellow"]
13885            );
13886        } else {
13887            panic!("expected completion menu to be open");
13888        }
13889    });
13890
13891    cx.simulate_keystroke("l");
13892    cx.executor().run_until_parked();
13893    cx.update_editor(|editor, _, _| {
13894        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13895        {
13896            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13897        } else {
13898            panic!("expected completion menu to be open");
13899        }
13900    });
13901
13902    // When filtering completions, consider the character after the '-' to
13903    // be the start of a subword.
13904    cx.set_state(r#"<p class="yelˇ" />"#);
13905    cx.simulate_keystroke("l");
13906    cx.executor().run_until_parked();
13907    cx.update_editor(|editor, _, _| {
13908        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13909        {
13910            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13911        } else {
13912            panic!("expected completion menu to be open");
13913        }
13914    });
13915}
13916
13917fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13918    let entries = menu.entries.borrow();
13919    entries.iter().map(|mat| mat.string.clone()).collect()
13920}
13921
13922#[gpui::test]
13923async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13924    init_test(cx, |settings| {
13925        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13926            FormatterList(vec![Formatter::Prettier].into()),
13927        ))
13928    });
13929
13930    let fs = FakeFs::new(cx.executor());
13931    fs.insert_file(path!("/file.ts"), Default::default()).await;
13932
13933    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13934    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13935
13936    language_registry.add(Arc::new(Language::new(
13937        LanguageConfig {
13938            name: "TypeScript".into(),
13939            matcher: LanguageMatcher {
13940                path_suffixes: vec!["ts".to_string()],
13941                ..Default::default()
13942            },
13943            ..Default::default()
13944        },
13945        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13946    )));
13947    update_test_language_settings(cx, |settings| {
13948        settings.defaults.prettier = Some(PrettierSettings {
13949            allowed: true,
13950            ..PrettierSettings::default()
13951        });
13952    });
13953
13954    let test_plugin = "test_plugin";
13955    let _ = language_registry.register_fake_lsp(
13956        "TypeScript",
13957        FakeLspAdapter {
13958            prettier_plugins: vec![test_plugin],
13959            ..Default::default()
13960        },
13961    );
13962
13963    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13964    let buffer = project
13965        .update(cx, |project, cx| {
13966            project.open_local_buffer(path!("/file.ts"), cx)
13967        })
13968        .await
13969        .unwrap();
13970
13971    let buffer_text = "one\ntwo\nthree\n";
13972    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13973    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13974    editor.update_in(cx, |editor, window, cx| {
13975        editor.set_text(buffer_text, window, cx)
13976    });
13977
13978    editor
13979        .update_in(cx, |editor, window, cx| {
13980            editor.perform_format(
13981                project.clone(),
13982                FormatTrigger::Manual,
13983                FormatTarget::Buffers,
13984                window,
13985                cx,
13986            )
13987        })
13988        .unwrap()
13989        .await;
13990    assert_eq!(
13991        editor.update(cx, |editor, cx| editor.text(cx)),
13992        buffer_text.to_string() + prettier_format_suffix,
13993        "Test prettier formatting was not applied to the original buffer text",
13994    );
13995
13996    update_test_language_settings(cx, |settings| {
13997        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13998    });
13999    let format = editor.update_in(cx, |editor, window, cx| {
14000        editor.perform_format(
14001            project.clone(),
14002            FormatTrigger::Manual,
14003            FormatTarget::Buffers,
14004            window,
14005            cx,
14006        )
14007    });
14008    format.await.unwrap();
14009    assert_eq!(
14010        editor.update(cx, |editor, cx| editor.text(cx)),
14011        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
14012        "Autoformatting (via test prettier) was not applied to the original buffer text",
14013    );
14014}
14015
14016#[gpui::test]
14017async fn test_addition_reverts(cx: &mut TestAppContext) {
14018    init_test(cx, |_| {});
14019    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14020    let base_text = indoc! {r#"
14021        struct Row;
14022        struct Row1;
14023        struct Row2;
14024
14025        struct Row4;
14026        struct Row5;
14027        struct Row6;
14028
14029        struct Row8;
14030        struct Row9;
14031        struct Row10;"#};
14032
14033    // When addition hunks are not adjacent to carets, no hunk revert is performed
14034    assert_hunk_revert(
14035        indoc! {r#"struct Row;
14036                   struct Row1;
14037                   struct Row1.1;
14038                   struct Row1.2;
14039                   struct Row2;ˇ
14040
14041                   struct Row4;
14042                   struct Row5;
14043                   struct Row6;
14044
14045                   struct Row8;
14046                   ˇstruct Row9;
14047                   struct Row9.1;
14048                   struct Row9.2;
14049                   struct Row9.3;
14050                   struct Row10;"#},
14051        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14052        indoc! {r#"struct Row;
14053                   struct Row1;
14054                   struct Row1.1;
14055                   struct Row1.2;
14056                   struct Row2;ˇ
14057
14058                   struct Row4;
14059                   struct Row5;
14060                   struct Row6;
14061
14062                   struct Row8;
14063                   ˇstruct Row9;
14064                   struct Row9.1;
14065                   struct Row9.2;
14066                   struct Row9.3;
14067                   struct Row10;"#},
14068        base_text,
14069        &mut cx,
14070    );
14071    // Same for selections
14072    assert_hunk_revert(
14073        indoc! {r#"struct Row;
14074                   struct Row1;
14075                   struct Row2;
14076                   struct Row2.1;
14077                   struct Row2.2;
14078                   «ˇ
14079                   struct Row4;
14080                   struct» Row5;
14081                   «struct Row6;
14082                   ˇ»
14083                   struct Row9.1;
14084                   struct Row9.2;
14085                   struct Row9.3;
14086                   struct Row8;
14087                   struct Row9;
14088                   struct Row10;"#},
14089        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14090        indoc! {r#"struct Row;
14091                   struct Row1;
14092                   struct Row2;
14093                   struct Row2.1;
14094                   struct Row2.2;
14095                   «ˇ
14096                   struct Row4;
14097                   struct» Row5;
14098                   «struct Row6;
14099                   ˇ»
14100                   struct Row9.1;
14101                   struct Row9.2;
14102                   struct Row9.3;
14103                   struct Row8;
14104                   struct Row9;
14105                   struct Row10;"#},
14106        base_text,
14107        &mut cx,
14108    );
14109
14110    // When carets and selections intersect the addition hunks, those are reverted.
14111    // Adjacent carets got merged.
14112    assert_hunk_revert(
14113        indoc! {r#"struct Row;
14114                   ˇ// something on the top
14115                   struct Row1;
14116                   struct Row2;
14117                   struct Roˇw3.1;
14118                   struct Row2.2;
14119                   struct Row2.3;ˇ
14120
14121                   struct Row4;
14122                   struct ˇRow5.1;
14123                   struct Row5.2;
14124                   struct «Rowˇ»5.3;
14125                   struct Row5;
14126                   struct Row6;
14127                   ˇ
14128                   struct Row9.1;
14129                   struct «Rowˇ»9.2;
14130                   struct «ˇRow»9.3;
14131                   struct Row8;
14132                   struct Row9;
14133                   «ˇ// something on bottom»
14134                   struct Row10;"#},
14135        vec![
14136            DiffHunkStatusKind::Added,
14137            DiffHunkStatusKind::Added,
14138            DiffHunkStatusKind::Added,
14139            DiffHunkStatusKind::Added,
14140            DiffHunkStatusKind::Added,
14141        ],
14142        indoc! {r#"struct Row;
14143                   ˇstruct Row1;
14144                   struct Row2;
14145                   ˇ
14146                   struct Row4;
14147                   ˇstruct Row5;
14148                   struct Row6;
14149                   ˇ
14150                   ˇstruct Row8;
14151                   struct Row9;
14152                   ˇstruct Row10;"#},
14153        base_text,
14154        &mut cx,
14155    );
14156}
14157
14158#[gpui::test]
14159async fn test_modification_reverts(cx: &mut TestAppContext) {
14160    init_test(cx, |_| {});
14161    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14162    let base_text = indoc! {r#"
14163        struct Row;
14164        struct Row1;
14165        struct Row2;
14166
14167        struct Row4;
14168        struct Row5;
14169        struct Row6;
14170
14171        struct Row8;
14172        struct Row9;
14173        struct Row10;"#};
14174
14175    // Modification hunks behave the same as the addition ones.
14176    assert_hunk_revert(
14177        indoc! {r#"struct Row;
14178                   struct Row1;
14179                   struct Row33;
14180                   ˇ
14181                   struct Row4;
14182                   struct Row5;
14183                   struct Row6;
14184                   ˇ
14185                   struct Row99;
14186                   struct Row9;
14187                   struct Row10;"#},
14188        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14189        indoc! {r#"struct Row;
14190                   struct Row1;
14191                   struct Row33;
14192                   ˇ
14193                   struct Row4;
14194                   struct Row5;
14195                   struct Row6;
14196                   ˇ
14197                   struct Row99;
14198                   struct Row9;
14199                   struct Row10;"#},
14200        base_text,
14201        &mut cx,
14202    );
14203    assert_hunk_revert(
14204        indoc! {r#"struct Row;
14205                   struct Row1;
14206                   struct Row33;
14207                   «ˇ
14208                   struct Row4;
14209                   struct» Row5;
14210                   «struct Row6;
14211                   ˇ»
14212                   struct Row99;
14213                   struct Row9;
14214                   struct Row10;"#},
14215        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14216        indoc! {r#"struct Row;
14217                   struct Row1;
14218                   struct Row33;
14219                   «ˇ
14220                   struct Row4;
14221                   struct» Row5;
14222                   «struct Row6;
14223                   ˇ»
14224                   struct Row99;
14225                   struct Row9;
14226                   struct Row10;"#},
14227        base_text,
14228        &mut cx,
14229    );
14230
14231    assert_hunk_revert(
14232        indoc! {r#"ˇstruct Row1.1;
14233                   struct Row1;
14234                   «ˇstr»uct Row22;
14235
14236                   struct ˇRow44;
14237                   struct Row5;
14238                   struct «Rˇ»ow66;ˇ
14239
14240                   «struˇ»ct Row88;
14241                   struct Row9;
14242                   struct Row1011;ˇ"#},
14243        vec![
14244            DiffHunkStatusKind::Modified,
14245            DiffHunkStatusKind::Modified,
14246            DiffHunkStatusKind::Modified,
14247            DiffHunkStatusKind::Modified,
14248            DiffHunkStatusKind::Modified,
14249            DiffHunkStatusKind::Modified,
14250        ],
14251        indoc! {r#"struct Row;
14252                   ˇstruct Row1;
14253                   struct Row2;
14254                   ˇ
14255                   struct Row4;
14256                   ˇstruct Row5;
14257                   struct Row6;
14258                   ˇ
14259                   struct Row8;
14260                   ˇstruct Row9;
14261                   struct Row10;ˇ"#},
14262        base_text,
14263        &mut cx,
14264    );
14265}
14266
14267#[gpui::test]
14268async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14269    init_test(cx, |_| {});
14270    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14271    let base_text = indoc! {r#"
14272        one
14273
14274        two
14275        three
14276        "#};
14277
14278    cx.set_head_text(base_text);
14279    cx.set_state("\nˇ\n");
14280    cx.executor().run_until_parked();
14281    cx.update_editor(|editor, _window, cx| {
14282        editor.expand_selected_diff_hunks(cx);
14283    });
14284    cx.executor().run_until_parked();
14285    cx.update_editor(|editor, window, cx| {
14286        editor.backspace(&Default::default(), window, cx);
14287    });
14288    cx.run_until_parked();
14289    cx.assert_state_with_diff(
14290        indoc! {r#"
14291
14292        - two
14293        - threeˇ
14294        +
14295        "#}
14296        .to_string(),
14297    );
14298}
14299
14300#[gpui::test]
14301async fn test_deletion_reverts(cx: &mut TestAppContext) {
14302    init_test(cx, |_| {});
14303    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14304    let base_text = indoc! {r#"struct Row;
14305struct Row1;
14306struct Row2;
14307
14308struct Row4;
14309struct Row5;
14310struct Row6;
14311
14312struct Row8;
14313struct Row9;
14314struct Row10;"#};
14315
14316    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14317    assert_hunk_revert(
14318        indoc! {r#"struct Row;
14319                   struct Row2;
14320
14321                   ˇstruct Row4;
14322                   struct Row5;
14323                   struct Row6;
14324                   ˇ
14325                   struct Row8;
14326                   struct Row10;"#},
14327        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14328        indoc! {r#"struct Row;
14329                   struct Row2;
14330
14331                   ˇstruct Row4;
14332                   struct Row5;
14333                   struct Row6;
14334                   ˇ
14335                   struct Row8;
14336                   struct Row10;"#},
14337        base_text,
14338        &mut cx,
14339    );
14340    assert_hunk_revert(
14341        indoc! {r#"struct Row;
14342                   struct Row2;
14343
14344                   «ˇstruct Row4;
14345                   struct» Row5;
14346                   «struct Row6;
14347                   ˇ»
14348                   struct Row8;
14349                   struct Row10;"#},
14350        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14351        indoc! {r#"struct Row;
14352                   struct Row2;
14353
14354                   «ˇstruct Row4;
14355                   struct» Row5;
14356                   «struct Row6;
14357                   ˇ»
14358                   struct Row8;
14359                   struct Row10;"#},
14360        base_text,
14361        &mut cx,
14362    );
14363
14364    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14365    assert_hunk_revert(
14366        indoc! {r#"struct Row;
14367                   ˇstruct Row2;
14368
14369                   struct Row4;
14370                   struct Row5;
14371                   struct Row6;
14372
14373                   struct Row8;ˇ
14374                   struct Row10;"#},
14375        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14376        indoc! {r#"struct Row;
14377                   struct Row1;
14378                   ˇstruct Row2;
14379
14380                   struct Row4;
14381                   struct Row5;
14382                   struct Row6;
14383
14384                   struct Row8;ˇ
14385                   struct Row9;
14386                   struct Row10;"#},
14387        base_text,
14388        &mut cx,
14389    );
14390    assert_hunk_revert(
14391        indoc! {r#"struct Row;
14392                   struct Row2«ˇ;
14393                   struct Row4;
14394                   struct» Row5;
14395                   «struct Row6;
14396
14397                   struct Row8;ˇ»
14398                   struct Row10;"#},
14399        vec![
14400            DiffHunkStatusKind::Deleted,
14401            DiffHunkStatusKind::Deleted,
14402            DiffHunkStatusKind::Deleted,
14403        ],
14404        indoc! {r#"struct Row;
14405                   struct Row1;
14406                   struct Row2«ˇ;
14407
14408                   struct Row4;
14409                   struct» Row5;
14410                   «struct Row6;
14411
14412                   struct Row8;ˇ»
14413                   struct Row9;
14414                   struct Row10;"#},
14415        base_text,
14416        &mut cx,
14417    );
14418}
14419
14420#[gpui::test]
14421async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14422    init_test(cx, |_| {});
14423
14424    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14425    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14426    let base_text_3 =
14427        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14428
14429    let text_1 = edit_first_char_of_every_line(base_text_1);
14430    let text_2 = edit_first_char_of_every_line(base_text_2);
14431    let text_3 = edit_first_char_of_every_line(base_text_3);
14432
14433    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14434    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14435    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14436
14437    let multibuffer = cx.new(|cx| {
14438        let mut multibuffer = MultiBuffer::new(ReadWrite);
14439        multibuffer.push_excerpts(
14440            buffer_1.clone(),
14441            [
14442                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14443                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14444                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14445            ],
14446            cx,
14447        );
14448        multibuffer.push_excerpts(
14449            buffer_2.clone(),
14450            [
14451                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14452                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14453                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14454            ],
14455            cx,
14456        );
14457        multibuffer.push_excerpts(
14458            buffer_3.clone(),
14459            [
14460                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14461                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14462                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14463            ],
14464            cx,
14465        );
14466        multibuffer
14467    });
14468
14469    let fs = FakeFs::new(cx.executor());
14470    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14471    let (editor, cx) = cx
14472        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14473    editor.update_in(cx, |editor, _window, cx| {
14474        for (buffer, diff_base) in [
14475            (buffer_1.clone(), base_text_1),
14476            (buffer_2.clone(), base_text_2),
14477            (buffer_3.clone(), base_text_3),
14478        ] {
14479            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14480            editor
14481                .buffer
14482                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14483        }
14484    });
14485    cx.executor().run_until_parked();
14486
14487    editor.update_in(cx, |editor, window, cx| {
14488        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}");
14489        editor.select_all(&SelectAll, window, cx);
14490        editor.git_restore(&Default::default(), window, cx);
14491    });
14492    cx.executor().run_until_parked();
14493
14494    // When all ranges are selected, all buffer hunks are reverted.
14495    editor.update(cx, |editor, cx| {
14496        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");
14497    });
14498    buffer_1.update(cx, |buffer, _| {
14499        assert_eq!(buffer.text(), base_text_1);
14500    });
14501    buffer_2.update(cx, |buffer, _| {
14502        assert_eq!(buffer.text(), base_text_2);
14503    });
14504    buffer_3.update(cx, |buffer, _| {
14505        assert_eq!(buffer.text(), base_text_3);
14506    });
14507
14508    editor.update_in(cx, |editor, window, cx| {
14509        editor.undo(&Default::default(), window, cx);
14510    });
14511
14512    editor.update_in(cx, |editor, window, cx| {
14513        editor.change_selections(None, window, cx, |s| {
14514            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14515        });
14516        editor.git_restore(&Default::default(), window, cx);
14517    });
14518
14519    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14520    // but not affect buffer_2 and its related excerpts.
14521    editor.update(cx, |editor, cx| {
14522        assert_eq!(
14523            editor.text(cx),
14524            "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}"
14525        );
14526    });
14527    buffer_1.update(cx, |buffer, _| {
14528        assert_eq!(buffer.text(), base_text_1);
14529    });
14530    buffer_2.update(cx, |buffer, _| {
14531        assert_eq!(
14532            buffer.text(),
14533            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14534        );
14535    });
14536    buffer_3.update(cx, |buffer, _| {
14537        assert_eq!(
14538            buffer.text(),
14539            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14540        );
14541    });
14542
14543    fn edit_first_char_of_every_line(text: &str) -> String {
14544        text.split('\n')
14545            .map(|line| format!("X{}", &line[1..]))
14546            .collect::<Vec<_>>()
14547            .join("\n")
14548    }
14549}
14550
14551#[gpui::test]
14552async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14553    init_test(cx, |_| {});
14554
14555    let cols = 4;
14556    let rows = 10;
14557    let sample_text_1 = sample_text(rows, cols, 'a');
14558    assert_eq!(
14559        sample_text_1,
14560        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14561    );
14562    let sample_text_2 = sample_text(rows, cols, 'l');
14563    assert_eq!(
14564        sample_text_2,
14565        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14566    );
14567    let sample_text_3 = sample_text(rows, cols, 'v');
14568    assert_eq!(
14569        sample_text_3,
14570        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14571    );
14572
14573    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14574    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14575    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14576
14577    let multi_buffer = cx.new(|cx| {
14578        let mut multibuffer = MultiBuffer::new(ReadWrite);
14579        multibuffer.push_excerpts(
14580            buffer_1.clone(),
14581            [
14582                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14583                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14584                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14585            ],
14586            cx,
14587        );
14588        multibuffer.push_excerpts(
14589            buffer_2.clone(),
14590            [
14591                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14592                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14593                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14594            ],
14595            cx,
14596        );
14597        multibuffer.push_excerpts(
14598            buffer_3.clone(),
14599            [
14600                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14601                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14602                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14603            ],
14604            cx,
14605        );
14606        multibuffer
14607    });
14608
14609    let fs = FakeFs::new(cx.executor());
14610    fs.insert_tree(
14611        "/a",
14612        json!({
14613            "main.rs": sample_text_1,
14614            "other.rs": sample_text_2,
14615            "lib.rs": sample_text_3,
14616        }),
14617    )
14618    .await;
14619    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14620    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14621    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14622    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14623        Editor::new(
14624            EditorMode::full(),
14625            multi_buffer,
14626            Some(project.clone()),
14627            window,
14628            cx,
14629        )
14630    });
14631    let multibuffer_item_id = workspace
14632        .update(cx, |workspace, window, cx| {
14633            assert!(
14634                workspace.active_item(cx).is_none(),
14635                "active item should be None before the first item is added"
14636            );
14637            workspace.add_item_to_active_pane(
14638                Box::new(multi_buffer_editor.clone()),
14639                None,
14640                true,
14641                window,
14642                cx,
14643            );
14644            let active_item = workspace
14645                .active_item(cx)
14646                .expect("should have an active item after adding the multi buffer");
14647            assert!(
14648                !active_item.is_singleton(cx),
14649                "A multi buffer was expected to active after adding"
14650            );
14651            active_item.item_id()
14652        })
14653        .unwrap();
14654    cx.executor().run_until_parked();
14655
14656    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14657        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14658            s.select_ranges(Some(1..2))
14659        });
14660        editor.open_excerpts(&OpenExcerpts, window, cx);
14661    });
14662    cx.executor().run_until_parked();
14663    let first_item_id = workspace
14664        .update(cx, |workspace, window, cx| {
14665            let active_item = workspace
14666                .active_item(cx)
14667                .expect("should have an active item after navigating into the 1st buffer");
14668            let first_item_id = active_item.item_id();
14669            assert_ne!(
14670                first_item_id, multibuffer_item_id,
14671                "Should navigate into the 1st buffer and activate it"
14672            );
14673            assert!(
14674                active_item.is_singleton(cx),
14675                "New active item should be a singleton buffer"
14676            );
14677            assert_eq!(
14678                active_item
14679                    .act_as::<Editor>(cx)
14680                    .expect("should have navigated into an editor for the 1st buffer")
14681                    .read(cx)
14682                    .text(cx),
14683                sample_text_1
14684            );
14685
14686            workspace
14687                .go_back(workspace.active_pane().downgrade(), window, cx)
14688                .detach_and_log_err(cx);
14689
14690            first_item_id
14691        })
14692        .unwrap();
14693    cx.executor().run_until_parked();
14694    workspace
14695        .update(cx, |workspace, _, cx| {
14696            let active_item = workspace
14697                .active_item(cx)
14698                .expect("should have an active item after navigating back");
14699            assert_eq!(
14700                active_item.item_id(),
14701                multibuffer_item_id,
14702                "Should navigate back to the multi buffer"
14703            );
14704            assert!(!active_item.is_singleton(cx));
14705        })
14706        .unwrap();
14707
14708    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14709        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14710            s.select_ranges(Some(39..40))
14711        });
14712        editor.open_excerpts(&OpenExcerpts, window, cx);
14713    });
14714    cx.executor().run_until_parked();
14715    let second_item_id = workspace
14716        .update(cx, |workspace, window, cx| {
14717            let active_item = workspace
14718                .active_item(cx)
14719                .expect("should have an active item after navigating into the 2nd buffer");
14720            let second_item_id = active_item.item_id();
14721            assert_ne!(
14722                second_item_id, multibuffer_item_id,
14723                "Should navigate away from the multibuffer"
14724            );
14725            assert_ne!(
14726                second_item_id, first_item_id,
14727                "Should navigate into the 2nd buffer and activate it"
14728            );
14729            assert!(
14730                active_item.is_singleton(cx),
14731                "New active item should be a singleton buffer"
14732            );
14733            assert_eq!(
14734                active_item
14735                    .act_as::<Editor>(cx)
14736                    .expect("should have navigated into an editor")
14737                    .read(cx)
14738                    .text(cx),
14739                sample_text_2
14740            );
14741
14742            workspace
14743                .go_back(workspace.active_pane().downgrade(), window, cx)
14744                .detach_and_log_err(cx);
14745
14746            second_item_id
14747        })
14748        .unwrap();
14749    cx.executor().run_until_parked();
14750    workspace
14751        .update(cx, |workspace, _, cx| {
14752            let active_item = workspace
14753                .active_item(cx)
14754                .expect("should have an active item after navigating back from the 2nd buffer");
14755            assert_eq!(
14756                active_item.item_id(),
14757                multibuffer_item_id,
14758                "Should navigate back from the 2nd buffer to the multi buffer"
14759            );
14760            assert!(!active_item.is_singleton(cx));
14761        })
14762        .unwrap();
14763
14764    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14765        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14766            s.select_ranges(Some(70..70))
14767        });
14768        editor.open_excerpts(&OpenExcerpts, window, cx);
14769    });
14770    cx.executor().run_until_parked();
14771    workspace
14772        .update(cx, |workspace, window, cx| {
14773            let active_item = workspace
14774                .active_item(cx)
14775                .expect("should have an active item after navigating into the 3rd buffer");
14776            let third_item_id = active_item.item_id();
14777            assert_ne!(
14778                third_item_id, multibuffer_item_id,
14779                "Should navigate into the 3rd buffer and activate it"
14780            );
14781            assert_ne!(third_item_id, first_item_id);
14782            assert_ne!(third_item_id, second_item_id);
14783            assert!(
14784                active_item.is_singleton(cx),
14785                "New active item should be a singleton buffer"
14786            );
14787            assert_eq!(
14788                active_item
14789                    .act_as::<Editor>(cx)
14790                    .expect("should have navigated into an editor")
14791                    .read(cx)
14792                    .text(cx),
14793                sample_text_3
14794            );
14795
14796            workspace
14797                .go_back(workspace.active_pane().downgrade(), window, cx)
14798                .detach_and_log_err(cx);
14799        })
14800        .unwrap();
14801    cx.executor().run_until_parked();
14802    workspace
14803        .update(cx, |workspace, _, cx| {
14804            let active_item = workspace
14805                .active_item(cx)
14806                .expect("should have an active item after navigating back from the 3rd buffer");
14807            assert_eq!(
14808                active_item.item_id(),
14809                multibuffer_item_id,
14810                "Should navigate back from the 3rd buffer to the multi buffer"
14811            );
14812            assert!(!active_item.is_singleton(cx));
14813        })
14814        .unwrap();
14815}
14816
14817#[gpui::test]
14818async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14819    init_test(cx, |_| {});
14820
14821    let mut cx = EditorTestContext::new(cx).await;
14822
14823    let diff_base = r#"
14824        use some::mod;
14825
14826        const A: u32 = 42;
14827
14828        fn main() {
14829            println!("hello");
14830
14831            println!("world");
14832        }
14833        "#
14834    .unindent();
14835
14836    cx.set_state(
14837        &r#"
14838        use some::modified;
14839
14840        ˇ
14841        fn main() {
14842            println!("hello there");
14843
14844            println!("around the");
14845            println!("world");
14846        }
14847        "#
14848        .unindent(),
14849    );
14850
14851    cx.set_head_text(&diff_base);
14852    executor.run_until_parked();
14853
14854    cx.update_editor(|editor, window, cx| {
14855        editor.go_to_next_hunk(&GoToHunk, window, cx);
14856        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14857    });
14858    executor.run_until_parked();
14859    cx.assert_state_with_diff(
14860        r#"
14861          use some::modified;
14862
14863
14864          fn main() {
14865        -     println!("hello");
14866        + ˇ    println!("hello there");
14867
14868              println!("around the");
14869              println!("world");
14870          }
14871        "#
14872        .unindent(),
14873    );
14874
14875    cx.update_editor(|editor, window, cx| {
14876        for _ in 0..2 {
14877            editor.go_to_next_hunk(&GoToHunk, window, cx);
14878            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14879        }
14880    });
14881    executor.run_until_parked();
14882    cx.assert_state_with_diff(
14883        r#"
14884        - use some::mod;
14885        + ˇuse some::modified;
14886
14887
14888          fn main() {
14889        -     println!("hello");
14890        +     println!("hello there");
14891
14892        +     println!("around the");
14893              println!("world");
14894          }
14895        "#
14896        .unindent(),
14897    );
14898
14899    cx.update_editor(|editor, window, cx| {
14900        editor.go_to_next_hunk(&GoToHunk, window, cx);
14901        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14902    });
14903    executor.run_until_parked();
14904    cx.assert_state_with_diff(
14905        r#"
14906        - use some::mod;
14907        + use some::modified;
14908
14909        - const A: u32 = 42;
14910          ˇ
14911          fn main() {
14912        -     println!("hello");
14913        +     println!("hello there");
14914
14915        +     println!("around the");
14916              println!("world");
14917          }
14918        "#
14919        .unindent(),
14920    );
14921
14922    cx.update_editor(|editor, window, cx| {
14923        editor.cancel(&Cancel, window, cx);
14924    });
14925
14926    cx.assert_state_with_diff(
14927        r#"
14928          use some::modified;
14929
14930          ˇ
14931          fn main() {
14932              println!("hello there");
14933
14934              println!("around the");
14935              println!("world");
14936          }
14937        "#
14938        .unindent(),
14939    );
14940}
14941
14942#[gpui::test]
14943async fn test_diff_base_change_with_expanded_diff_hunks(
14944    executor: BackgroundExecutor,
14945    cx: &mut TestAppContext,
14946) {
14947    init_test(cx, |_| {});
14948
14949    let mut cx = EditorTestContext::new(cx).await;
14950
14951    let diff_base = r#"
14952        use some::mod1;
14953        use some::mod2;
14954
14955        const A: u32 = 42;
14956        const B: u32 = 42;
14957        const C: u32 = 42;
14958
14959        fn main() {
14960            println!("hello");
14961
14962            println!("world");
14963        }
14964        "#
14965    .unindent();
14966
14967    cx.set_state(
14968        &r#"
14969        use some::mod2;
14970
14971        const A: u32 = 42;
14972        const C: u32 = 42;
14973
14974        fn main(ˇ) {
14975            //println!("hello");
14976
14977            println!("world");
14978            //
14979            //
14980        }
14981        "#
14982        .unindent(),
14983    );
14984
14985    cx.set_head_text(&diff_base);
14986    executor.run_until_parked();
14987
14988    cx.update_editor(|editor, window, cx| {
14989        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14990    });
14991    executor.run_until_parked();
14992    cx.assert_state_with_diff(
14993        r#"
14994        - use some::mod1;
14995          use some::mod2;
14996
14997          const A: u32 = 42;
14998        - const B: u32 = 42;
14999          const C: u32 = 42;
15000
15001          fn main(ˇ) {
15002        -     println!("hello");
15003        +     //println!("hello");
15004
15005              println!("world");
15006        +     //
15007        +     //
15008          }
15009        "#
15010        .unindent(),
15011    );
15012
15013    cx.set_head_text("new diff base!");
15014    executor.run_until_parked();
15015    cx.assert_state_with_diff(
15016        r#"
15017        - new diff base!
15018        + use some::mod2;
15019        +
15020        + const A: u32 = 42;
15021        + const C: u32 = 42;
15022        +
15023        + fn main(ˇ) {
15024        +     //println!("hello");
15025        +
15026        +     println!("world");
15027        +     //
15028        +     //
15029        + }
15030        "#
15031        .unindent(),
15032    );
15033}
15034
15035#[gpui::test]
15036async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15037    init_test(cx, |_| {});
15038
15039    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15040    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15041    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15042    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15043    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15044    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15045
15046    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15047    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15048    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15049
15050    let multi_buffer = cx.new(|cx| {
15051        let mut multibuffer = MultiBuffer::new(ReadWrite);
15052        multibuffer.push_excerpts(
15053            buffer_1.clone(),
15054            [
15055                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15056                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15057                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15058            ],
15059            cx,
15060        );
15061        multibuffer.push_excerpts(
15062            buffer_2.clone(),
15063            [
15064                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15065                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15066                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15067            ],
15068            cx,
15069        );
15070        multibuffer.push_excerpts(
15071            buffer_3.clone(),
15072            [
15073                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15074                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15075                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15076            ],
15077            cx,
15078        );
15079        multibuffer
15080    });
15081
15082    let editor =
15083        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15084    editor
15085        .update(cx, |editor, _window, cx| {
15086            for (buffer, diff_base) in [
15087                (buffer_1.clone(), file_1_old),
15088                (buffer_2.clone(), file_2_old),
15089                (buffer_3.clone(), file_3_old),
15090            ] {
15091                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15092                editor
15093                    .buffer
15094                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15095            }
15096        })
15097        .unwrap();
15098
15099    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15100    cx.run_until_parked();
15101
15102    cx.assert_editor_state(
15103        &"
15104            ˇaaa
15105            ccc
15106            ddd
15107
15108            ggg
15109            hhh
15110
15111
15112            lll
15113            mmm
15114            NNN
15115
15116            qqq
15117            rrr
15118
15119            uuu
15120            111
15121            222
15122            333
15123
15124            666
15125            777
15126
15127            000
15128            !!!"
15129        .unindent(),
15130    );
15131
15132    cx.update_editor(|editor, window, cx| {
15133        editor.select_all(&SelectAll, window, cx);
15134        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15135    });
15136    cx.executor().run_until_parked();
15137
15138    cx.assert_state_with_diff(
15139        "
15140            «aaa
15141          - bbb
15142            ccc
15143            ddd
15144
15145            ggg
15146            hhh
15147
15148
15149            lll
15150            mmm
15151          - nnn
15152          + NNN
15153
15154            qqq
15155            rrr
15156
15157            uuu
15158            111
15159            222
15160            333
15161
15162          + 666
15163            777
15164
15165            000
15166            !!!ˇ»"
15167            .unindent(),
15168    );
15169}
15170
15171#[gpui::test]
15172async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15173    init_test(cx, |_| {});
15174
15175    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15176    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15177
15178    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15179    let multi_buffer = cx.new(|cx| {
15180        let mut multibuffer = MultiBuffer::new(ReadWrite);
15181        multibuffer.push_excerpts(
15182            buffer.clone(),
15183            [
15184                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15185                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15186                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15187            ],
15188            cx,
15189        );
15190        multibuffer
15191    });
15192
15193    let editor =
15194        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15195    editor
15196        .update(cx, |editor, _window, cx| {
15197            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15198            editor
15199                .buffer
15200                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15201        })
15202        .unwrap();
15203
15204    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15205    cx.run_until_parked();
15206
15207    cx.update_editor(|editor, window, cx| {
15208        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15209    });
15210    cx.executor().run_until_parked();
15211
15212    // When the start of a hunk coincides with the start of its excerpt,
15213    // the hunk is expanded. When the start of a a hunk is earlier than
15214    // the start of its excerpt, the hunk is not expanded.
15215    cx.assert_state_with_diff(
15216        "
15217            ˇaaa
15218          - bbb
15219          + BBB
15220
15221          - ddd
15222          - eee
15223          + DDD
15224          + EEE
15225            fff
15226
15227            iii
15228        "
15229        .unindent(),
15230    );
15231}
15232
15233#[gpui::test]
15234async fn test_edits_around_expanded_insertion_hunks(
15235    executor: BackgroundExecutor,
15236    cx: &mut TestAppContext,
15237) {
15238    init_test(cx, |_| {});
15239
15240    let mut cx = EditorTestContext::new(cx).await;
15241
15242    let diff_base = r#"
15243        use some::mod1;
15244        use some::mod2;
15245
15246        const A: u32 = 42;
15247
15248        fn main() {
15249            println!("hello");
15250
15251            println!("world");
15252        }
15253        "#
15254    .unindent();
15255    executor.run_until_parked();
15256    cx.set_state(
15257        &r#"
15258        use some::mod1;
15259        use some::mod2;
15260
15261        const A: u32 = 42;
15262        const B: u32 = 42;
15263        const C: u32 = 42;
15264        ˇ
15265
15266        fn main() {
15267            println!("hello");
15268
15269            println!("world");
15270        }
15271        "#
15272        .unindent(),
15273    );
15274
15275    cx.set_head_text(&diff_base);
15276    executor.run_until_parked();
15277
15278    cx.update_editor(|editor, window, cx| {
15279        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15280    });
15281    executor.run_until_parked();
15282
15283    cx.assert_state_with_diff(
15284        r#"
15285        use some::mod1;
15286        use some::mod2;
15287
15288        const A: u32 = 42;
15289      + const B: u32 = 42;
15290      + const C: u32 = 42;
15291      + ˇ
15292
15293        fn main() {
15294            println!("hello");
15295
15296            println!("world");
15297        }
15298      "#
15299        .unindent(),
15300    );
15301
15302    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15303    executor.run_until_parked();
15304
15305    cx.assert_state_with_diff(
15306        r#"
15307        use some::mod1;
15308        use some::mod2;
15309
15310        const A: u32 = 42;
15311      + const B: u32 = 42;
15312      + const C: u32 = 42;
15313      + const D: u32 = 42;
15314      + ˇ
15315
15316        fn main() {
15317            println!("hello");
15318
15319            println!("world");
15320        }
15321      "#
15322        .unindent(),
15323    );
15324
15325    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15326    executor.run_until_parked();
15327
15328    cx.assert_state_with_diff(
15329        r#"
15330        use some::mod1;
15331        use some::mod2;
15332
15333        const A: u32 = 42;
15334      + const B: u32 = 42;
15335      + const C: u32 = 42;
15336      + const D: u32 = 42;
15337      + const E: u32 = 42;
15338      + ˇ
15339
15340        fn main() {
15341            println!("hello");
15342
15343            println!("world");
15344        }
15345      "#
15346        .unindent(),
15347    );
15348
15349    cx.update_editor(|editor, window, cx| {
15350        editor.delete_line(&DeleteLine, window, cx);
15351    });
15352    executor.run_until_parked();
15353
15354    cx.assert_state_with_diff(
15355        r#"
15356        use some::mod1;
15357        use some::mod2;
15358
15359        const A: u32 = 42;
15360      + const B: u32 = 42;
15361      + const C: u32 = 42;
15362      + const D: u32 = 42;
15363      + const E: u32 = 42;
15364        ˇ
15365        fn main() {
15366            println!("hello");
15367
15368            println!("world");
15369        }
15370      "#
15371        .unindent(),
15372    );
15373
15374    cx.update_editor(|editor, window, cx| {
15375        editor.move_up(&MoveUp, window, cx);
15376        editor.delete_line(&DeleteLine, window, cx);
15377        editor.move_up(&MoveUp, window, cx);
15378        editor.delete_line(&DeleteLine, window, cx);
15379        editor.move_up(&MoveUp, window, cx);
15380        editor.delete_line(&DeleteLine, window, cx);
15381    });
15382    executor.run_until_parked();
15383    cx.assert_state_with_diff(
15384        r#"
15385        use some::mod1;
15386        use some::mod2;
15387
15388        const A: u32 = 42;
15389      + const B: u32 = 42;
15390        ˇ
15391        fn main() {
15392            println!("hello");
15393
15394            println!("world");
15395        }
15396      "#
15397        .unindent(),
15398    );
15399
15400    cx.update_editor(|editor, window, cx| {
15401        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15402        editor.delete_line(&DeleteLine, window, cx);
15403    });
15404    executor.run_until_parked();
15405    cx.assert_state_with_diff(
15406        r#"
15407        ˇ
15408        fn main() {
15409            println!("hello");
15410
15411            println!("world");
15412        }
15413      "#
15414        .unindent(),
15415    );
15416}
15417
15418#[gpui::test]
15419async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15420    init_test(cx, |_| {});
15421
15422    let mut cx = EditorTestContext::new(cx).await;
15423    cx.set_head_text(indoc! { "
15424        one
15425        two
15426        three
15427        four
15428        five
15429        "
15430    });
15431    cx.set_state(indoc! { "
15432        one
15433        ˇthree
15434        five
15435    "});
15436    cx.run_until_parked();
15437    cx.update_editor(|editor, window, cx| {
15438        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15439    });
15440    cx.assert_state_with_diff(
15441        indoc! { "
15442        one
15443      - two
15444        ˇthree
15445      - four
15446        five
15447    "}
15448        .to_string(),
15449    );
15450    cx.update_editor(|editor, window, cx| {
15451        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15452    });
15453
15454    cx.assert_state_with_diff(
15455        indoc! { "
15456        one
15457        ˇthree
15458        five
15459    "}
15460        .to_string(),
15461    );
15462
15463    cx.set_state(indoc! { "
15464        one
15465        ˇTWO
15466        three
15467        four
15468        five
15469    "});
15470    cx.run_until_parked();
15471    cx.update_editor(|editor, window, cx| {
15472        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15473    });
15474
15475    cx.assert_state_with_diff(
15476        indoc! { "
15477            one
15478          - two
15479          + ˇTWO
15480            three
15481            four
15482            five
15483        "}
15484        .to_string(),
15485    );
15486    cx.update_editor(|editor, window, cx| {
15487        editor.move_up(&Default::default(), window, cx);
15488        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15489    });
15490    cx.assert_state_with_diff(
15491        indoc! { "
15492            one
15493            ˇTWO
15494            three
15495            four
15496            five
15497        "}
15498        .to_string(),
15499    );
15500}
15501
15502#[gpui::test]
15503async fn test_edits_around_expanded_deletion_hunks(
15504    executor: BackgroundExecutor,
15505    cx: &mut TestAppContext,
15506) {
15507    init_test(cx, |_| {});
15508
15509    let mut cx = EditorTestContext::new(cx).await;
15510
15511    let diff_base = r#"
15512        use some::mod1;
15513        use some::mod2;
15514
15515        const A: u32 = 42;
15516        const B: u32 = 42;
15517        const C: u32 = 42;
15518
15519
15520        fn main() {
15521            println!("hello");
15522
15523            println!("world");
15524        }
15525    "#
15526    .unindent();
15527    executor.run_until_parked();
15528    cx.set_state(
15529        &r#"
15530        use some::mod1;
15531        use some::mod2;
15532
15533        ˇconst B: u32 = 42;
15534        const C: u32 = 42;
15535
15536
15537        fn main() {
15538            println!("hello");
15539
15540            println!("world");
15541        }
15542        "#
15543        .unindent(),
15544    );
15545
15546    cx.set_head_text(&diff_base);
15547    executor.run_until_parked();
15548
15549    cx.update_editor(|editor, window, cx| {
15550        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15551    });
15552    executor.run_until_parked();
15553
15554    cx.assert_state_with_diff(
15555        r#"
15556        use some::mod1;
15557        use some::mod2;
15558
15559      - const A: u32 = 42;
15560        ˇconst B: u32 = 42;
15561        const C: u32 = 42;
15562
15563
15564        fn main() {
15565            println!("hello");
15566
15567            println!("world");
15568        }
15569      "#
15570        .unindent(),
15571    );
15572
15573    cx.update_editor(|editor, window, cx| {
15574        editor.delete_line(&DeleteLine, window, cx);
15575    });
15576    executor.run_until_parked();
15577    cx.assert_state_with_diff(
15578        r#"
15579        use some::mod1;
15580        use some::mod2;
15581
15582      - const A: u32 = 42;
15583      - const B: u32 = 42;
15584        ˇconst C: u32 = 42;
15585
15586
15587        fn main() {
15588            println!("hello");
15589
15590            println!("world");
15591        }
15592      "#
15593        .unindent(),
15594    );
15595
15596    cx.update_editor(|editor, window, cx| {
15597        editor.delete_line(&DeleteLine, window, cx);
15598    });
15599    executor.run_until_parked();
15600    cx.assert_state_with_diff(
15601        r#"
15602        use some::mod1;
15603        use some::mod2;
15604
15605      - const A: u32 = 42;
15606      - const B: u32 = 42;
15607      - const C: u32 = 42;
15608        ˇ
15609
15610        fn main() {
15611            println!("hello");
15612
15613            println!("world");
15614        }
15615      "#
15616        .unindent(),
15617    );
15618
15619    cx.update_editor(|editor, window, cx| {
15620        editor.handle_input("replacement", window, cx);
15621    });
15622    executor.run_until_parked();
15623    cx.assert_state_with_diff(
15624        r#"
15625        use some::mod1;
15626        use some::mod2;
15627
15628      - const A: u32 = 42;
15629      - const B: u32 = 42;
15630      - const C: u32 = 42;
15631      -
15632      + replacementˇ
15633
15634        fn main() {
15635            println!("hello");
15636
15637            println!("world");
15638        }
15639      "#
15640        .unindent(),
15641    );
15642}
15643
15644#[gpui::test]
15645async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15646    init_test(cx, |_| {});
15647
15648    let mut cx = EditorTestContext::new(cx).await;
15649
15650    let base_text = r#"
15651        one
15652        two
15653        three
15654        four
15655        five
15656    "#
15657    .unindent();
15658    executor.run_until_parked();
15659    cx.set_state(
15660        &r#"
15661        one
15662        two
15663        fˇour
15664        five
15665        "#
15666        .unindent(),
15667    );
15668
15669    cx.set_head_text(&base_text);
15670    executor.run_until_parked();
15671
15672    cx.update_editor(|editor, window, cx| {
15673        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15674    });
15675    executor.run_until_parked();
15676
15677    cx.assert_state_with_diff(
15678        r#"
15679          one
15680          two
15681        - three
15682          fˇour
15683          five
15684        "#
15685        .unindent(),
15686    );
15687
15688    cx.update_editor(|editor, window, cx| {
15689        editor.backspace(&Backspace, window, cx);
15690        editor.backspace(&Backspace, window, cx);
15691    });
15692    executor.run_until_parked();
15693    cx.assert_state_with_diff(
15694        r#"
15695          one
15696          two
15697        - threeˇ
15698        - four
15699        + our
15700          five
15701        "#
15702        .unindent(),
15703    );
15704}
15705
15706#[gpui::test]
15707async fn test_edit_after_expanded_modification_hunk(
15708    executor: BackgroundExecutor,
15709    cx: &mut TestAppContext,
15710) {
15711    init_test(cx, |_| {});
15712
15713    let mut cx = EditorTestContext::new(cx).await;
15714
15715    let diff_base = r#"
15716        use some::mod1;
15717        use some::mod2;
15718
15719        const A: u32 = 42;
15720        const B: u32 = 42;
15721        const C: u32 = 42;
15722        const D: u32 = 42;
15723
15724
15725        fn main() {
15726            println!("hello");
15727
15728            println!("world");
15729        }"#
15730    .unindent();
15731
15732    cx.set_state(
15733        &r#"
15734        use some::mod1;
15735        use some::mod2;
15736
15737        const A: u32 = 42;
15738        const B: u32 = 42;
15739        const C: u32 = 43ˇ
15740        const D: u32 = 42;
15741
15742
15743        fn main() {
15744            println!("hello");
15745
15746            println!("world");
15747        }"#
15748        .unindent(),
15749    );
15750
15751    cx.set_head_text(&diff_base);
15752    executor.run_until_parked();
15753    cx.update_editor(|editor, window, cx| {
15754        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15755    });
15756    executor.run_until_parked();
15757
15758    cx.assert_state_with_diff(
15759        r#"
15760        use some::mod1;
15761        use some::mod2;
15762
15763        const A: u32 = 42;
15764        const B: u32 = 42;
15765      - const C: u32 = 42;
15766      + const C: u32 = 43ˇ
15767        const D: u32 = 42;
15768
15769
15770        fn main() {
15771            println!("hello");
15772
15773            println!("world");
15774        }"#
15775        .unindent(),
15776    );
15777
15778    cx.update_editor(|editor, window, cx| {
15779        editor.handle_input("\nnew_line\n", window, cx);
15780    });
15781    executor.run_until_parked();
15782
15783    cx.assert_state_with_diff(
15784        r#"
15785        use some::mod1;
15786        use some::mod2;
15787
15788        const A: u32 = 42;
15789        const B: u32 = 42;
15790      - const C: u32 = 42;
15791      + const C: u32 = 43
15792      + new_line
15793      + ˇ
15794        const D: u32 = 42;
15795
15796
15797        fn main() {
15798            println!("hello");
15799
15800            println!("world");
15801        }"#
15802        .unindent(),
15803    );
15804}
15805
15806#[gpui::test]
15807async fn test_stage_and_unstage_added_file_hunk(
15808    executor: BackgroundExecutor,
15809    cx: &mut TestAppContext,
15810) {
15811    init_test(cx, |_| {});
15812
15813    let mut cx = EditorTestContext::new(cx).await;
15814    cx.update_editor(|editor, _, cx| {
15815        editor.set_expand_all_diff_hunks(cx);
15816    });
15817
15818    let working_copy = r#"
15819            ˇfn main() {
15820                println!("hello, world!");
15821            }
15822        "#
15823    .unindent();
15824
15825    cx.set_state(&working_copy);
15826    executor.run_until_parked();
15827
15828    cx.assert_state_with_diff(
15829        r#"
15830            + ˇfn main() {
15831            +     println!("hello, world!");
15832            + }
15833        "#
15834        .unindent(),
15835    );
15836    cx.assert_index_text(None);
15837
15838    cx.update_editor(|editor, window, cx| {
15839        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15840    });
15841    executor.run_until_parked();
15842    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15843    cx.assert_state_with_diff(
15844        r#"
15845            + ˇfn main() {
15846            +     println!("hello, world!");
15847            + }
15848        "#
15849        .unindent(),
15850    );
15851
15852    cx.update_editor(|editor, window, cx| {
15853        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15854    });
15855    executor.run_until_parked();
15856    cx.assert_index_text(None);
15857}
15858
15859async fn setup_indent_guides_editor(
15860    text: &str,
15861    cx: &mut TestAppContext,
15862) -> (BufferId, EditorTestContext) {
15863    init_test(cx, |_| {});
15864
15865    let mut cx = EditorTestContext::new(cx).await;
15866
15867    let buffer_id = cx.update_editor(|editor, window, cx| {
15868        editor.set_text(text, window, cx);
15869        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15870
15871        buffer_ids[0]
15872    });
15873
15874    (buffer_id, cx)
15875}
15876
15877fn assert_indent_guides(
15878    range: Range<u32>,
15879    expected: Vec<IndentGuide>,
15880    active_indices: Option<Vec<usize>>,
15881    cx: &mut EditorTestContext,
15882) {
15883    let indent_guides = cx.update_editor(|editor, window, cx| {
15884        let snapshot = editor.snapshot(window, cx).display_snapshot;
15885        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15886            editor,
15887            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15888            true,
15889            &snapshot,
15890            cx,
15891        );
15892
15893        indent_guides.sort_by(|a, b| {
15894            a.depth.cmp(&b.depth).then(
15895                a.start_row
15896                    .cmp(&b.start_row)
15897                    .then(a.end_row.cmp(&b.end_row)),
15898            )
15899        });
15900        indent_guides
15901    });
15902
15903    if let Some(expected) = active_indices {
15904        let active_indices = cx.update_editor(|editor, window, cx| {
15905            let snapshot = editor.snapshot(window, cx).display_snapshot;
15906            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15907        });
15908
15909        assert_eq!(
15910            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15911            expected,
15912            "Active indent guide indices do not match"
15913        );
15914    }
15915
15916    assert_eq!(indent_guides, expected, "Indent guides do not match");
15917}
15918
15919fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15920    IndentGuide {
15921        buffer_id,
15922        start_row: MultiBufferRow(start_row),
15923        end_row: MultiBufferRow(end_row),
15924        depth,
15925        tab_size: 4,
15926        settings: IndentGuideSettings {
15927            enabled: true,
15928            line_width: 1,
15929            active_line_width: 1,
15930            ..Default::default()
15931        },
15932    }
15933}
15934
15935#[gpui::test]
15936async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15937    let (buffer_id, mut cx) = setup_indent_guides_editor(
15938        &"
15939    fn main() {
15940        let a = 1;
15941    }"
15942        .unindent(),
15943        cx,
15944    )
15945    .await;
15946
15947    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15948}
15949
15950#[gpui::test]
15951async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15952    let (buffer_id, mut cx) = setup_indent_guides_editor(
15953        &"
15954    fn main() {
15955        let a = 1;
15956        let b = 2;
15957    }"
15958        .unindent(),
15959        cx,
15960    )
15961    .await;
15962
15963    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15964}
15965
15966#[gpui::test]
15967async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15968    let (buffer_id, mut cx) = setup_indent_guides_editor(
15969        &"
15970    fn main() {
15971        let a = 1;
15972        if a == 3 {
15973            let b = 2;
15974        } else {
15975            let c = 3;
15976        }
15977    }"
15978        .unindent(),
15979        cx,
15980    )
15981    .await;
15982
15983    assert_indent_guides(
15984        0..8,
15985        vec![
15986            indent_guide(buffer_id, 1, 6, 0),
15987            indent_guide(buffer_id, 3, 3, 1),
15988            indent_guide(buffer_id, 5, 5, 1),
15989        ],
15990        None,
15991        &mut cx,
15992    );
15993}
15994
15995#[gpui::test]
15996async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15997    let (buffer_id, mut cx) = setup_indent_guides_editor(
15998        &"
15999    fn main() {
16000        let a = 1;
16001            let b = 2;
16002        let c = 3;
16003    }"
16004        .unindent(),
16005        cx,
16006    )
16007    .await;
16008
16009    assert_indent_guides(
16010        0..5,
16011        vec![
16012            indent_guide(buffer_id, 1, 3, 0),
16013            indent_guide(buffer_id, 2, 2, 1),
16014        ],
16015        None,
16016        &mut cx,
16017    );
16018}
16019
16020#[gpui::test]
16021async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
16022    let (buffer_id, mut cx) = setup_indent_guides_editor(
16023        &"
16024        fn main() {
16025            let a = 1;
16026
16027            let c = 3;
16028        }"
16029        .unindent(),
16030        cx,
16031    )
16032    .await;
16033
16034    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16035}
16036
16037#[gpui::test]
16038async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16039    let (buffer_id, mut cx) = setup_indent_guides_editor(
16040        &"
16041        fn main() {
16042            let a = 1;
16043
16044            let c = 3;
16045
16046            if a == 3 {
16047                let b = 2;
16048            } else {
16049                let c = 3;
16050            }
16051        }"
16052        .unindent(),
16053        cx,
16054    )
16055    .await;
16056
16057    assert_indent_guides(
16058        0..11,
16059        vec![
16060            indent_guide(buffer_id, 1, 9, 0),
16061            indent_guide(buffer_id, 6, 6, 1),
16062            indent_guide(buffer_id, 8, 8, 1),
16063        ],
16064        None,
16065        &mut cx,
16066    );
16067}
16068
16069#[gpui::test]
16070async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16071    let (buffer_id, mut cx) = setup_indent_guides_editor(
16072        &"
16073        fn main() {
16074            let a = 1;
16075
16076            let c = 3;
16077
16078            if a == 3 {
16079                let b = 2;
16080            } else {
16081                let c = 3;
16082            }
16083        }"
16084        .unindent(),
16085        cx,
16086    )
16087    .await;
16088
16089    assert_indent_guides(
16090        1..11,
16091        vec![
16092            indent_guide(buffer_id, 1, 9, 0),
16093            indent_guide(buffer_id, 6, 6, 1),
16094            indent_guide(buffer_id, 8, 8, 1),
16095        ],
16096        None,
16097        &mut cx,
16098    );
16099}
16100
16101#[gpui::test]
16102async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16103    let (buffer_id, mut cx) = setup_indent_guides_editor(
16104        &"
16105        fn main() {
16106            let a = 1;
16107
16108            let c = 3;
16109
16110            if a == 3 {
16111                let b = 2;
16112            } else {
16113                let c = 3;
16114            }
16115        }"
16116        .unindent(),
16117        cx,
16118    )
16119    .await;
16120
16121    assert_indent_guides(
16122        1..10,
16123        vec![
16124            indent_guide(buffer_id, 1, 9, 0),
16125            indent_guide(buffer_id, 6, 6, 1),
16126            indent_guide(buffer_id, 8, 8, 1),
16127        ],
16128        None,
16129        &mut cx,
16130    );
16131}
16132
16133#[gpui::test]
16134async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16135    let (buffer_id, mut cx) = setup_indent_guides_editor(
16136        &"
16137        block1
16138            block2
16139                block3
16140                    block4
16141            block2
16142        block1
16143        block1"
16144            .unindent(),
16145        cx,
16146    )
16147    .await;
16148
16149    assert_indent_guides(
16150        1..10,
16151        vec![
16152            indent_guide(buffer_id, 1, 4, 0),
16153            indent_guide(buffer_id, 2, 3, 1),
16154            indent_guide(buffer_id, 3, 3, 2),
16155        ],
16156        None,
16157        &mut cx,
16158    );
16159}
16160
16161#[gpui::test]
16162async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16163    let (buffer_id, mut cx) = setup_indent_guides_editor(
16164        &"
16165        block1
16166            block2
16167                block3
16168
16169        block1
16170        block1"
16171            .unindent(),
16172        cx,
16173    )
16174    .await;
16175
16176    assert_indent_guides(
16177        0..6,
16178        vec![
16179            indent_guide(buffer_id, 1, 2, 0),
16180            indent_guide(buffer_id, 2, 2, 1),
16181        ],
16182        None,
16183        &mut cx,
16184    );
16185}
16186
16187#[gpui::test]
16188async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16189    let (buffer_id, mut cx) = setup_indent_guides_editor(
16190        &"
16191        block1
16192
16193
16194
16195            block2
16196        "
16197        .unindent(),
16198        cx,
16199    )
16200    .await;
16201
16202    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16203}
16204
16205#[gpui::test]
16206async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16207    let (buffer_id, mut cx) = setup_indent_guides_editor(
16208        &"
16209        def a:
16210        \tb = 3
16211        \tif True:
16212        \t\tc = 4
16213        \t\td = 5
16214        \tprint(b)
16215        "
16216        .unindent(),
16217        cx,
16218    )
16219    .await;
16220
16221    assert_indent_guides(
16222        0..6,
16223        vec![
16224            indent_guide(buffer_id, 1, 6, 0),
16225            indent_guide(buffer_id, 3, 4, 1),
16226        ],
16227        None,
16228        &mut cx,
16229    );
16230}
16231
16232#[gpui::test]
16233async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16234    let (buffer_id, mut cx) = setup_indent_guides_editor(
16235        &"
16236    fn main() {
16237        let a = 1;
16238    }"
16239        .unindent(),
16240        cx,
16241    )
16242    .await;
16243
16244    cx.update_editor(|editor, window, cx| {
16245        editor.change_selections(None, window, cx, |s| {
16246            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16247        });
16248    });
16249
16250    assert_indent_guides(
16251        0..3,
16252        vec![indent_guide(buffer_id, 1, 1, 0)],
16253        Some(vec![0]),
16254        &mut cx,
16255    );
16256}
16257
16258#[gpui::test]
16259async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16260    let (buffer_id, mut cx) = setup_indent_guides_editor(
16261        &"
16262    fn main() {
16263        if 1 == 2 {
16264            let a = 1;
16265        }
16266    }"
16267        .unindent(),
16268        cx,
16269    )
16270    .await;
16271
16272    cx.update_editor(|editor, window, cx| {
16273        editor.change_selections(None, window, cx, |s| {
16274            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16275        });
16276    });
16277
16278    assert_indent_guides(
16279        0..4,
16280        vec![
16281            indent_guide(buffer_id, 1, 3, 0),
16282            indent_guide(buffer_id, 2, 2, 1),
16283        ],
16284        Some(vec![1]),
16285        &mut cx,
16286    );
16287
16288    cx.update_editor(|editor, window, cx| {
16289        editor.change_selections(None, window, cx, |s| {
16290            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16291        });
16292    });
16293
16294    assert_indent_guides(
16295        0..4,
16296        vec![
16297            indent_guide(buffer_id, 1, 3, 0),
16298            indent_guide(buffer_id, 2, 2, 1),
16299        ],
16300        Some(vec![1]),
16301        &mut cx,
16302    );
16303
16304    cx.update_editor(|editor, window, cx| {
16305        editor.change_selections(None, window, cx, |s| {
16306            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16307        });
16308    });
16309
16310    assert_indent_guides(
16311        0..4,
16312        vec![
16313            indent_guide(buffer_id, 1, 3, 0),
16314            indent_guide(buffer_id, 2, 2, 1),
16315        ],
16316        Some(vec![0]),
16317        &mut cx,
16318    );
16319}
16320
16321#[gpui::test]
16322async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16323    let (buffer_id, mut cx) = setup_indent_guides_editor(
16324        &"
16325    fn main() {
16326        let a = 1;
16327
16328        let b = 2;
16329    }"
16330        .unindent(),
16331        cx,
16332    )
16333    .await;
16334
16335    cx.update_editor(|editor, window, cx| {
16336        editor.change_selections(None, window, cx, |s| {
16337            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16338        });
16339    });
16340
16341    assert_indent_guides(
16342        0..5,
16343        vec![indent_guide(buffer_id, 1, 3, 0)],
16344        Some(vec![0]),
16345        &mut cx,
16346    );
16347}
16348
16349#[gpui::test]
16350async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16351    let (buffer_id, mut cx) = setup_indent_guides_editor(
16352        &"
16353    def m:
16354        a = 1
16355        pass"
16356            .unindent(),
16357        cx,
16358    )
16359    .await;
16360
16361    cx.update_editor(|editor, window, cx| {
16362        editor.change_selections(None, window, cx, |s| {
16363            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16364        });
16365    });
16366
16367    assert_indent_guides(
16368        0..3,
16369        vec![indent_guide(buffer_id, 1, 2, 0)],
16370        Some(vec![0]),
16371        &mut cx,
16372    );
16373}
16374
16375#[gpui::test]
16376async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16377    init_test(cx, |_| {});
16378    let mut cx = EditorTestContext::new(cx).await;
16379    let text = indoc! {
16380        "
16381        impl A {
16382            fn b() {
16383                0;
16384                3;
16385                5;
16386                6;
16387                7;
16388            }
16389        }
16390        "
16391    };
16392    let base_text = indoc! {
16393        "
16394        impl A {
16395            fn b() {
16396                0;
16397                1;
16398                2;
16399                3;
16400                4;
16401            }
16402            fn c() {
16403                5;
16404                6;
16405                7;
16406            }
16407        }
16408        "
16409    };
16410
16411    cx.update_editor(|editor, window, cx| {
16412        editor.set_text(text, window, cx);
16413
16414        editor.buffer().update(cx, |multibuffer, cx| {
16415            let buffer = multibuffer.as_singleton().unwrap();
16416            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16417
16418            multibuffer.set_all_diff_hunks_expanded(cx);
16419            multibuffer.add_diff(diff, cx);
16420
16421            buffer.read(cx).remote_id()
16422        })
16423    });
16424    cx.run_until_parked();
16425
16426    cx.assert_state_with_diff(
16427        indoc! { "
16428          impl A {
16429              fn b() {
16430                  0;
16431        -         1;
16432        -         2;
16433                  3;
16434        -         4;
16435        -     }
16436        -     fn c() {
16437                  5;
16438                  6;
16439                  7;
16440              }
16441          }
16442          ˇ"
16443        }
16444        .to_string(),
16445    );
16446
16447    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16448        editor
16449            .snapshot(window, cx)
16450            .buffer_snapshot
16451            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16452            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16453            .collect::<Vec<_>>()
16454    });
16455    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16456    assert_eq!(
16457        actual_guides,
16458        vec![
16459            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16460            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16461            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16462        ]
16463    );
16464}
16465
16466#[gpui::test]
16467async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16468    init_test(cx, |_| {});
16469    let mut cx = EditorTestContext::new(cx).await;
16470
16471    let diff_base = r#"
16472        a
16473        b
16474        c
16475        "#
16476    .unindent();
16477
16478    cx.set_state(
16479        &r#"
16480        ˇA
16481        b
16482        C
16483        "#
16484        .unindent(),
16485    );
16486    cx.set_head_text(&diff_base);
16487    cx.update_editor(|editor, window, cx| {
16488        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16489    });
16490    executor.run_until_parked();
16491
16492    let both_hunks_expanded = r#"
16493        - a
16494        + ˇA
16495          b
16496        - c
16497        + C
16498        "#
16499    .unindent();
16500
16501    cx.assert_state_with_diff(both_hunks_expanded.clone());
16502
16503    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16504        let snapshot = editor.snapshot(window, cx);
16505        let hunks = editor
16506            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16507            .collect::<Vec<_>>();
16508        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16509        let buffer_id = hunks[0].buffer_id;
16510        hunks
16511            .into_iter()
16512            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16513            .collect::<Vec<_>>()
16514    });
16515    assert_eq!(hunk_ranges.len(), 2);
16516
16517    cx.update_editor(|editor, _, cx| {
16518        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16519    });
16520    executor.run_until_parked();
16521
16522    let second_hunk_expanded = r#"
16523          ˇA
16524          b
16525        - c
16526        + C
16527        "#
16528    .unindent();
16529
16530    cx.assert_state_with_diff(second_hunk_expanded);
16531
16532    cx.update_editor(|editor, _, cx| {
16533        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16534    });
16535    executor.run_until_parked();
16536
16537    cx.assert_state_with_diff(both_hunks_expanded.clone());
16538
16539    cx.update_editor(|editor, _, cx| {
16540        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16541    });
16542    executor.run_until_parked();
16543
16544    let first_hunk_expanded = r#"
16545        - a
16546        + ˇA
16547          b
16548          C
16549        "#
16550    .unindent();
16551
16552    cx.assert_state_with_diff(first_hunk_expanded);
16553
16554    cx.update_editor(|editor, _, cx| {
16555        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16556    });
16557    executor.run_until_parked();
16558
16559    cx.assert_state_with_diff(both_hunks_expanded);
16560
16561    cx.set_state(
16562        &r#"
16563        ˇA
16564        b
16565        "#
16566        .unindent(),
16567    );
16568    cx.run_until_parked();
16569
16570    // TODO this cursor position seems bad
16571    cx.assert_state_with_diff(
16572        r#"
16573        - ˇa
16574        + A
16575          b
16576        "#
16577        .unindent(),
16578    );
16579
16580    cx.update_editor(|editor, window, cx| {
16581        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16582    });
16583
16584    cx.assert_state_with_diff(
16585        r#"
16586            - ˇa
16587            + A
16588              b
16589            - c
16590            "#
16591        .unindent(),
16592    );
16593
16594    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16595        let snapshot = editor.snapshot(window, cx);
16596        let hunks = editor
16597            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16598            .collect::<Vec<_>>();
16599        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16600        let buffer_id = hunks[0].buffer_id;
16601        hunks
16602            .into_iter()
16603            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16604            .collect::<Vec<_>>()
16605    });
16606    assert_eq!(hunk_ranges.len(), 2);
16607
16608    cx.update_editor(|editor, _, cx| {
16609        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16610    });
16611    executor.run_until_parked();
16612
16613    cx.assert_state_with_diff(
16614        r#"
16615        - ˇa
16616        + A
16617          b
16618        "#
16619        .unindent(),
16620    );
16621}
16622
16623#[gpui::test]
16624async fn test_toggle_deletion_hunk_at_start_of_file(
16625    executor: BackgroundExecutor,
16626    cx: &mut TestAppContext,
16627) {
16628    init_test(cx, |_| {});
16629    let mut cx = EditorTestContext::new(cx).await;
16630
16631    let diff_base = r#"
16632        a
16633        b
16634        c
16635        "#
16636    .unindent();
16637
16638    cx.set_state(
16639        &r#"
16640        ˇb
16641        c
16642        "#
16643        .unindent(),
16644    );
16645    cx.set_head_text(&diff_base);
16646    cx.update_editor(|editor, window, cx| {
16647        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16648    });
16649    executor.run_until_parked();
16650
16651    let hunk_expanded = r#"
16652        - a
16653          ˇb
16654          c
16655        "#
16656    .unindent();
16657
16658    cx.assert_state_with_diff(hunk_expanded.clone());
16659
16660    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16661        let snapshot = editor.snapshot(window, cx);
16662        let hunks = editor
16663            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16664            .collect::<Vec<_>>();
16665        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16666        let buffer_id = hunks[0].buffer_id;
16667        hunks
16668            .into_iter()
16669            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16670            .collect::<Vec<_>>()
16671    });
16672    assert_eq!(hunk_ranges.len(), 1);
16673
16674    cx.update_editor(|editor, _, cx| {
16675        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16676    });
16677    executor.run_until_parked();
16678
16679    let hunk_collapsed = r#"
16680          ˇb
16681          c
16682        "#
16683    .unindent();
16684
16685    cx.assert_state_with_diff(hunk_collapsed);
16686
16687    cx.update_editor(|editor, _, cx| {
16688        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16689    });
16690    executor.run_until_parked();
16691
16692    cx.assert_state_with_diff(hunk_expanded.clone());
16693}
16694
16695#[gpui::test]
16696async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16697    init_test(cx, |_| {});
16698
16699    let fs = FakeFs::new(cx.executor());
16700    fs.insert_tree(
16701        path!("/test"),
16702        json!({
16703            ".git": {},
16704            "file-1": "ONE\n",
16705            "file-2": "TWO\n",
16706            "file-3": "THREE\n",
16707        }),
16708    )
16709    .await;
16710
16711    fs.set_head_for_repo(
16712        path!("/test/.git").as_ref(),
16713        &[
16714            ("file-1".into(), "one\n".into()),
16715            ("file-2".into(), "two\n".into()),
16716            ("file-3".into(), "three\n".into()),
16717        ],
16718    );
16719
16720    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16721    let mut buffers = vec![];
16722    for i in 1..=3 {
16723        let buffer = project
16724            .update(cx, |project, cx| {
16725                let path = format!(path!("/test/file-{}"), i);
16726                project.open_local_buffer(path, cx)
16727            })
16728            .await
16729            .unwrap();
16730        buffers.push(buffer);
16731    }
16732
16733    let multibuffer = cx.new(|cx| {
16734        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16735        multibuffer.set_all_diff_hunks_expanded(cx);
16736        for buffer in &buffers {
16737            let snapshot = buffer.read(cx).snapshot();
16738            multibuffer.set_excerpts_for_path(
16739                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16740                buffer.clone(),
16741                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16742                DEFAULT_MULTIBUFFER_CONTEXT,
16743                cx,
16744            );
16745        }
16746        multibuffer
16747    });
16748
16749    let editor = cx.add_window(|window, cx| {
16750        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16751    });
16752    cx.run_until_parked();
16753
16754    let snapshot = editor
16755        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16756        .unwrap();
16757    let hunks = snapshot
16758        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16759        .map(|hunk| match hunk {
16760            DisplayDiffHunk::Unfolded {
16761                display_row_range, ..
16762            } => display_row_range,
16763            DisplayDiffHunk::Folded { .. } => unreachable!(),
16764        })
16765        .collect::<Vec<_>>();
16766    assert_eq!(
16767        hunks,
16768        [
16769            DisplayRow(2)..DisplayRow(4),
16770            DisplayRow(7)..DisplayRow(9),
16771            DisplayRow(12)..DisplayRow(14),
16772        ]
16773    );
16774}
16775
16776#[gpui::test]
16777async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16778    init_test(cx, |_| {});
16779
16780    let mut cx = EditorTestContext::new(cx).await;
16781    cx.set_head_text(indoc! { "
16782        one
16783        two
16784        three
16785        four
16786        five
16787        "
16788    });
16789    cx.set_index_text(indoc! { "
16790        one
16791        two
16792        three
16793        four
16794        five
16795        "
16796    });
16797    cx.set_state(indoc! {"
16798        one
16799        TWO
16800        ˇTHREE
16801        FOUR
16802        five
16803    "});
16804    cx.run_until_parked();
16805    cx.update_editor(|editor, window, cx| {
16806        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16807    });
16808    cx.run_until_parked();
16809    cx.assert_index_text(Some(indoc! {"
16810        one
16811        TWO
16812        THREE
16813        FOUR
16814        five
16815    "}));
16816    cx.set_state(indoc! { "
16817        one
16818        TWO
16819        ˇTHREE-HUNDRED
16820        FOUR
16821        five
16822    "});
16823    cx.run_until_parked();
16824    cx.update_editor(|editor, window, cx| {
16825        let snapshot = editor.snapshot(window, cx);
16826        let hunks = editor
16827            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16828            .collect::<Vec<_>>();
16829        assert_eq!(hunks.len(), 1);
16830        assert_eq!(
16831            hunks[0].status(),
16832            DiffHunkStatus {
16833                kind: DiffHunkStatusKind::Modified,
16834                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16835            }
16836        );
16837
16838        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16839    });
16840    cx.run_until_parked();
16841    cx.assert_index_text(Some(indoc! {"
16842        one
16843        TWO
16844        THREE-HUNDRED
16845        FOUR
16846        five
16847    "}));
16848}
16849
16850#[gpui::test]
16851fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16852    init_test(cx, |_| {});
16853
16854    let editor = cx.add_window(|window, cx| {
16855        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16856        build_editor(buffer, window, cx)
16857    });
16858
16859    let render_args = Arc::new(Mutex::new(None));
16860    let snapshot = editor
16861        .update(cx, |editor, window, cx| {
16862            let snapshot = editor.buffer().read(cx).snapshot(cx);
16863            let range =
16864                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16865
16866            struct RenderArgs {
16867                row: MultiBufferRow,
16868                folded: bool,
16869                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16870            }
16871
16872            let crease = Crease::inline(
16873                range,
16874                FoldPlaceholder::test(),
16875                {
16876                    let toggle_callback = render_args.clone();
16877                    move |row, folded, callback, _window, _cx| {
16878                        *toggle_callback.lock() = Some(RenderArgs {
16879                            row,
16880                            folded,
16881                            callback,
16882                        });
16883                        div()
16884                    }
16885                },
16886                |_row, _folded, _window, _cx| div(),
16887            );
16888
16889            editor.insert_creases(Some(crease), cx);
16890            let snapshot = editor.snapshot(window, cx);
16891            let _div = snapshot.render_crease_toggle(
16892                MultiBufferRow(1),
16893                false,
16894                cx.entity().clone(),
16895                window,
16896                cx,
16897            );
16898            snapshot
16899        })
16900        .unwrap();
16901
16902    let render_args = render_args.lock().take().unwrap();
16903    assert_eq!(render_args.row, MultiBufferRow(1));
16904    assert!(!render_args.folded);
16905    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16906
16907    cx.update_window(*editor, |_, window, cx| {
16908        (render_args.callback)(true, window, cx)
16909    })
16910    .unwrap();
16911    let snapshot = editor
16912        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16913        .unwrap();
16914    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16915
16916    cx.update_window(*editor, |_, window, cx| {
16917        (render_args.callback)(false, window, cx)
16918    })
16919    .unwrap();
16920    let snapshot = editor
16921        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16922        .unwrap();
16923    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16924}
16925
16926#[gpui::test]
16927async fn test_input_text(cx: &mut TestAppContext) {
16928    init_test(cx, |_| {});
16929    let mut cx = EditorTestContext::new(cx).await;
16930
16931    cx.set_state(
16932        &r#"ˇone
16933        two
16934
16935        three
16936        fourˇ
16937        five
16938
16939        siˇx"#
16940            .unindent(),
16941    );
16942
16943    cx.dispatch_action(HandleInput(String::new()));
16944    cx.assert_editor_state(
16945        &r#"ˇone
16946        two
16947
16948        three
16949        fourˇ
16950        five
16951
16952        siˇx"#
16953            .unindent(),
16954    );
16955
16956    cx.dispatch_action(HandleInput("AAAA".to_string()));
16957    cx.assert_editor_state(
16958        &r#"AAAAˇone
16959        two
16960
16961        three
16962        fourAAAAˇ
16963        five
16964
16965        siAAAAˇx"#
16966            .unindent(),
16967    );
16968}
16969
16970#[gpui::test]
16971async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16972    init_test(cx, |_| {});
16973
16974    let mut cx = EditorTestContext::new(cx).await;
16975    cx.set_state(
16976        r#"let foo = 1;
16977let foo = 2;
16978let foo = 3;
16979let fooˇ = 4;
16980let foo = 5;
16981let foo = 6;
16982let foo = 7;
16983let foo = 8;
16984let foo = 9;
16985let foo = 10;
16986let foo = 11;
16987let foo = 12;
16988let foo = 13;
16989let foo = 14;
16990let foo = 15;"#,
16991    );
16992
16993    cx.update_editor(|e, window, cx| {
16994        assert_eq!(
16995            e.next_scroll_position,
16996            NextScrollCursorCenterTopBottom::Center,
16997            "Default next scroll direction is center",
16998        );
16999
17000        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17001        assert_eq!(
17002            e.next_scroll_position,
17003            NextScrollCursorCenterTopBottom::Top,
17004            "After center, next scroll direction should be top",
17005        );
17006
17007        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17008        assert_eq!(
17009            e.next_scroll_position,
17010            NextScrollCursorCenterTopBottom::Bottom,
17011            "After top, next scroll direction should be bottom",
17012        );
17013
17014        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17015        assert_eq!(
17016            e.next_scroll_position,
17017            NextScrollCursorCenterTopBottom::Center,
17018            "After bottom, scrolling should start over",
17019        );
17020
17021        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
17022        assert_eq!(
17023            e.next_scroll_position,
17024            NextScrollCursorCenterTopBottom::Top,
17025            "Scrolling continues if retriggered fast enough"
17026        );
17027    });
17028
17029    cx.executor()
17030        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17031    cx.executor().run_until_parked();
17032    cx.update_editor(|e, _, _| {
17033        assert_eq!(
17034            e.next_scroll_position,
17035            NextScrollCursorCenterTopBottom::Center,
17036            "If scrolling is not triggered fast enough, it should reset"
17037        );
17038    });
17039}
17040
17041#[gpui::test]
17042async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17043    init_test(cx, |_| {});
17044    let mut cx = EditorLspTestContext::new_rust(
17045        lsp::ServerCapabilities {
17046            definition_provider: Some(lsp::OneOf::Left(true)),
17047            references_provider: Some(lsp::OneOf::Left(true)),
17048            ..lsp::ServerCapabilities::default()
17049        },
17050        cx,
17051    )
17052    .await;
17053
17054    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17055        let go_to_definition = cx
17056            .lsp
17057            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17058                move |params, _| async move {
17059                    if empty_go_to_definition {
17060                        Ok(None)
17061                    } else {
17062                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17063                            uri: params.text_document_position_params.text_document.uri,
17064                            range: lsp::Range::new(
17065                                lsp::Position::new(4, 3),
17066                                lsp::Position::new(4, 6),
17067                            ),
17068                        })))
17069                    }
17070                },
17071            );
17072        let references = cx
17073            .lsp
17074            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17075                Ok(Some(vec![lsp::Location {
17076                    uri: params.text_document_position.text_document.uri,
17077                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17078                }]))
17079            });
17080        (go_to_definition, references)
17081    };
17082
17083    cx.set_state(
17084        &r#"fn one() {
17085            let mut a = ˇtwo();
17086        }
17087
17088        fn two() {}"#
17089            .unindent(),
17090    );
17091    set_up_lsp_handlers(false, &mut cx);
17092    let navigated = cx
17093        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17094        .await
17095        .expect("Failed to navigate to definition");
17096    assert_eq!(
17097        navigated,
17098        Navigated::Yes,
17099        "Should have navigated to definition from the GetDefinition response"
17100    );
17101    cx.assert_editor_state(
17102        &r#"fn one() {
17103            let mut a = two();
17104        }
17105
17106        fn «twoˇ»() {}"#
17107            .unindent(),
17108    );
17109
17110    let editors = cx.update_workspace(|workspace, _, cx| {
17111        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17112    });
17113    cx.update_editor(|_, _, test_editor_cx| {
17114        assert_eq!(
17115            editors.len(),
17116            1,
17117            "Initially, only one, test, editor should be open in the workspace"
17118        );
17119        assert_eq!(
17120            test_editor_cx.entity(),
17121            editors.last().expect("Asserted len is 1").clone()
17122        );
17123    });
17124
17125    set_up_lsp_handlers(true, &mut cx);
17126    let navigated = cx
17127        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17128        .await
17129        .expect("Failed to navigate to lookup references");
17130    assert_eq!(
17131        navigated,
17132        Navigated::Yes,
17133        "Should have navigated to references as a fallback after empty GoToDefinition response"
17134    );
17135    // We should not change the selections in the existing file,
17136    // if opening another milti buffer with the references
17137    cx.assert_editor_state(
17138        &r#"fn one() {
17139            let mut a = two();
17140        }
17141
17142        fn «twoˇ»() {}"#
17143            .unindent(),
17144    );
17145    let editors = cx.update_workspace(|workspace, _, cx| {
17146        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17147    });
17148    cx.update_editor(|_, _, test_editor_cx| {
17149        assert_eq!(
17150            editors.len(),
17151            2,
17152            "After falling back to references search, we open a new editor with the results"
17153        );
17154        let references_fallback_text = editors
17155            .into_iter()
17156            .find(|new_editor| *new_editor != test_editor_cx.entity())
17157            .expect("Should have one non-test editor now")
17158            .read(test_editor_cx)
17159            .text(test_editor_cx);
17160        assert_eq!(
17161            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17162            "Should use the range from the references response and not the GoToDefinition one"
17163        );
17164    });
17165}
17166
17167#[gpui::test]
17168async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17169    init_test(cx, |_| {});
17170    cx.update(|cx| {
17171        let mut editor_settings = EditorSettings::get_global(cx).clone();
17172        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17173        EditorSettings::override_global(editor_settings, cx);
17174    });
17175    let mut cx = EditorLspTestContext::new_rust(
17176        lsp::ServerCapabilities {
17177            definition_provider: Some(lsp::OneOf::Left(true)),
17178            references_provider: Some(lsp::OneOf::Left(true)),
17179            ..lsp::ServerCapabilities::default()
17180        },
17181        cx,
17182    )
17183    .await;
17184    let original_state = r#"fn one() {
17185        let mut a = ˇtwo();
17186    }
17187
17188    fn two() {}"#
17189        .unindent();
17190    cx.set_state(&original_state);
17191
17192    let mut go_to_definition = cx
17193        .lsp
17194        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17195            move |_, _| async move { Ok(None) },
17196        );
17197    let _references = cx
17198        .lsp
17199        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17200            panic!("Should not call for references with no go to definition fallback")
17201        });
17202
17203    let navigated = cx
17204        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17205        .await
17206        .expect("Failed to navigate to lookup references");
17207    go_to_definition
17208        .next()
17209        .await
17210        .expect("Should have called the go_to_definition handler");
17211
17212    assert_eq!(
17213        navigated,
17214        Navigated::No,
17215        "Should have navigated to references as a fallback after empty GoToDefinition response"
17216    );
17217    cx.assert_editor_state(&original_state);
17218    let editors = cx.update_workspace(|workspace, _, cx| {
17219        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17220    });
17221    cx.update_editor(|_, _, _| {
17222        assert_eq!(
17223            editors.len(),
17224            1,
17225            "After unsuccessful fallback, no other editor should have been opened"
17226        );
17227    });
17228}
17229
17230#[gpui::test]
17231async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17232    init_test(cx, |_| {});
17233
17234    let language = Arc::new(Language::new(
17235        LanguageConfig::default(),
17236        Some(tree_sitter_rust::LANGUAGE.into()),
17237    ));
17238
17239    let text = r#"
17240        #[cfg(test)]
17241        mod tests() {
17242            #[test]
17243            fn runnable_1() {
17244                let a = 1;
17245            }
17246
17247            #[test]
17248            fn runnable_2() {
17249                let a = 1;
17250                let b = 2;
17251            }
17252        }
17253    "#
17254    .unindent();
17255
17256    let fs = FakeFs::new(cx.executor());
17257    fs.insert_file("/file.rs", Default::default()).await;
17258
17259    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17260    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17261    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17262    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17263    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17264
17265    let editor = cx.new_window_entity(|window, cx| {
17266        Editor::new(
17267            EditorMode::full(),
17268            multi_buffer,
17269            Some(project.clone()),
17270            window,
17271            cx,
17272        )
17273    });
17274
17275    editor.update_in(cx, |editor, window, cx| {
17276        let snapshot = editor.buffer().read(cx).snapshot(cx);
17277        editor.tasks.insert(
17278            (buffer.read(cx).remote_id(), 3),
17279            RunnableTasks {
17280                templates: vec![],
17281                offset: snapshot.anchor_before(43),
17282                column: 0,
17283                extra_variables: HashMap::default(),
17284                context_range: BufferOffset(43)..BufferOffset(85),
17285            },
17286        );
17287        editor.tasks.insert(
17288            (buffer.read(cx).remote_id(), 8),
17289            RunnableTasks {
17290                templates: vec![],
17291                offset: snapshot.anchor_before(86),
17292                column: 0,
17293                extra_variables: HashMap::default(),
17294                context_range: BufferOffset(86)..BufferOffset(191),
17295            },
17296        );
17297
17298        // Test finding task when cursor is inside function body
17299        editor.change_selections(None, window, cx, |s| {
17300            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17301        });
17302        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17303        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17304
17305        // Test finding task when cursor is on function name
17306        editor.change_selections(None, window, cx, |s| {
17307            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17308        });
17309        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17310        assert_eq!(row, 8, "Should find task when cursor is on function name");
17311    });
17312}
17313
17314#[gpui::test]
17315async fn test_folding_buffers(cx: &mut TestAppContext) {
17316    init_test(cx, |_| {});
17317
17318    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17319    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17320    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17321
17322    let fs = FakeFs::new(cx.executor());
17323    fs.insert_tree(
17324        path!("/a"),
17325        json!({
17326            "first.rs": sample_text_1,
17327            "second.rs": sample_text_2,
17328            "third.rs": sample_text_3,
17329        }),
17330    )
17331    .await;
17332    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17333    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17334    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17335    let worktree = project.update(cx, |project, cx| {
17336        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17337        assert_eq!(worktrees.len(), 1);
17338        worktrees.pop().unwrap()
17339    });
17340    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17341
17342    let buffer_1 = project
17343        .update(cx, |project, cx| {
17344            project.open_buffer((worktree_id, "first.rs"), cx)
17345        })
17346        .await
17347        .unwrap();
17348    let buffer_2 = project
17349        .update(cx, |project, cx| {
17350            project.open_buffer((worktree_id, "second.rs"), cx)
17351        })
17352        .await
17353        .unwrap();
17354    let buffer_3 = project
17355        .update(cx, |project, cx| {
17356            project.open_buffer((worktree_id, "third.rs"), cx)
17357        })
17358        .await
17359        .unwrap();
17360
17361    let multi_buffer = cx.new(|cx| {
17362        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17363        multi_buffer.push_excerpts(
17364            buffer_1.clone(),
17365            [
17366                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17367                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17368                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17369            ],
17370            cx,
17371        );
17372        multi_buffer.push_excerpts(
17373            buffer_2.clone(),
17374            [
17375                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17376                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17377                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17378            ],
17379            cx,
17380        );
17381        multi_buffer.push_excerpts(
17382            buffer_3.clone(),
17383            [
17384                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17385                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17386                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17387            ],
17388            cx,
17389        );
17390        multi_buffer
17391    });
17392    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17393        Editor::new(
17394            EditorMode::full(),
17395            multi_buffer.clone(),
17396            Some(project.clone()),
17397            window,
17398            cx,
17399        )
17400    });
17401
17402    assert_eq!(
17403        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17404        "\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",
17405    );
17406
17407    multi_buffer_editor.update(cx, |editor, cx| {
17408        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17409    });
17410    assert_eq!(
17411        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17412        "\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",
17413        "After folding the first buffer, its text should not be displayed"
17414    );
17415
17416    multi_buffer_editor.update(cx, |editor, cx| {
17417        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17418    });
17419    assert_eq!(
17420        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17421        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17422        "After folding the second buffer, its text should not be displayed"
17423    );
17424
17425    multi_buffer_editor.update(cx, |editor, cx| {
17426        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17427    });
17428    assert_eq!(
17429        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17430        "\n\n\n\n\n",
17431        "After folding the third buffer, its text should not be displayed"
17432    );
17433
17434    // Emulate selection inside the fold logic, that should work
17435    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17436        editor
17437            .snapshot(window, cx)
17438            .next_line_boundary(Point::new(0, 4));
17439    });
17440
17441    multi_buffer_editor.update(cx, |editor, cx| {
17442        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17443    });
17444    assert_eq!(
17445        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17446        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17447        "After unfolding the second buffer, its text should be displayed"
17448    );
17449
17450    // Typing inside of buffer 1 causes that buffer to be unfolded.
17451    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17452        assert_eq!(
17453            multi_buffer
17454                .read(cx)
17455                .snapshot(cx)
17456                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17457                .collect::<String>(),
17458            "bbbb"
17459        );
17460        editor.change_selections(None, window, cx, |selections| {
17461            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17462        });
17463        editor.handle_input("B", window, cx);
17464    });
17465
17466    assert_eq!(
17467        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17468        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17469        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17470    );
17471
17472    multi_buffer_editor.update(cx, |editor, cx| {
17473        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17474    });
17475    assert_eq!(
17476        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17477        "\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",
17478        "After unfolding the all buffers, all original text should be displayed"
17479    );
17480}
17481
17482#[gpui::test]
17483async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17484    init_test(cx, |_| {});
17485
17486    let sample_text_1 = "1111\n2222\n3333".to_string();
17487    let sample_text_2 = "4444\n5555\n6666".to_string();
17488    let sample_text_3 = "7777\n8888\n9999".to_string();
17489
17490    let fs = FakeFs::new(cx.executor());
17491    fs.insert_tree(
17492        path!("/a"),
17493        json!({
17494            "first.rs": sample_text_1,
17495            "second.rs": sample_text_2,
17496            "third.rs": sample_text_3,
17497        }),
17498    )
17499    .await;
17500    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17501    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17502    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17503    let worktree = project.update(cx, |project, cx| {
17504        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17505        assert_eq!(worktrees.len(), 1);
17506        worktrees.pop().unwrap()
17507    });
17508    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17509
17510    let buffer_1 = project
17511        .update(cx, |project, cx| {
17512            project.open_buffer((worktree_id, "first.rs"), cx)
17513        })
17514        .await
17515        .unwrap();
17516    let buffer_2 = project
17517        .update(cx, |project, cx| {
17518            project.open_buffer((worktree_id, "second.rs"), cx)
17519        })
17520        .await
17521        .unwrap();
17522    let buffer_3 = project
17523        .update(cx, |project, cx| {
17524            project.open_buffer((worktree_id, "third.rs"), cx)
17525        })
17526        .await
17527        .unwrap();
17528
17529    let multi_buffer = cx.new(|cx| {
17530        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17531        multi_buffer.push_excerpts(
17532            buffer_1.clone(),
17533            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17534            cx,
17535        );
17536        multi_buffer.push_excerpts(
17537            buffer_2.clone(),
17538            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17539            cx,
17540        );
17541        multi_buffer.push_excerpts(
17542            buffer_3.clone(),
17543            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17544            cx,
17545        );
17546        multi_buffer
17547    });
17548
17549    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17550        Editor::new(
17551            EditorMode::full(),
17552            multi_buffer,
17553            Some(project.clone()),
17554            window,
17555            cx,
17556        )
17557    });
17558
17559    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17560    assert_eq!(
17561        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17562        full_text,
17563    );
17564
17565    multi_buffer_editor.update(cx, |editor, cx| {
17566        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17567    });
17568    assert_eq!(
17569        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17570        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17571        "After folding the first buffer, its text should not be displayed"
17572    );
17573
17574    multi_buffer_editor.update(cx, |editor, cx| {
17575        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17576    });
17577
17578    assert_eq!(
17579        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17580        "\n\n\n\n\n\n7777\n8888\n9999",
17581        "After folding the second buffer, its text should not be displayed"
17582    );
17583
17584    multi_buffer_editor.update(cx, |editor, cx| {
17585        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17586    });
17587    assert_eq!(
17588        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17589        "\n\n\n\n\n",
17590        "After folding the third buffer, its text should not be displayed"
17591    );
17592
17593    multi_buffer_editor.update(cx, |editor, cx| {
17594        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17595    });
17596    assert_eq!(
17597        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17598        "\n\n\n\n4444\n5555\n6666\n\n",
17599        "After unfolding the second buffer, its text should be displayed"
17600    );
17601
17602    multi_buffer_editor.update(cx, |editor, cx| {
17603        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17604    });
17605    assert_eq!(
17606        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17607        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17608        "After unfolding the first buffer, its text should be displayed"
17609    );
17610
17611    multi_buffer_editor.update(cx, |editor, cx| {
17612        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17613    });
17614    assert_eq!(
17615        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17616        full_text,
17617        "After unfolding all buffers, all original text should be displayed"
17618    );
17619}
17620
17621#[gpui::test]
17622async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17623    init_test(cx, |_| {});
17624
17625    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17626
17627    let fs = FakeFs::new(cx.executor());
17628    fs.insert_tree(
17629        path!("/a"),
17630        json!({
17631            "main.rs": sample_text,
17632        }),
17633    )
17634    .await;
17635    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17636    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17637    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17638    let worktree = project.update(cx, |project, cx| {
17639        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17640        assert_eq!(worktrees.len(), 1);
17641        worktrees.pop().unwrap()
17642    });
17643    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17644
17645    let buffer_1 = project
17646        .update(cx, |project, cx| {
17647            project.open_buffer((worktree_id, "main.rs"), cx)
17648        })
17649        .await
17650        .unwrap();
17651
17652    let multi_buffer = cx.new(|cx| {
17653        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17654        multi_buffer.push_excerpts(
17655            buffer_1.clone(),
17656            [ExcerptRange::new(
17657                Point::new(0, 0)
17658                    ..Point::new(
17659                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17660                        0,
17661                    ),
17662            )],
17663            cx,
17664        );
17665        multi_buffer
17666    });
17667    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17668        Editor::new(
17669            EditorMode::full(),
17670            multi_buffer,
17671            Some(project.clone()),
17672            window,
17673            cx,
17674        )
17675    });
17676
17677    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17678    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17679        enum TestHighlight {}
17680        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17681        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17682        editor.highlight_text::<TestHighlight>(
17683            vec![highlight_range.clone()],
17684            HighlightStyle::color(Hsla::green()),
17685            cx,
17686        );
17687        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17688    });
17689
17690    let full_text = format!("\n\n{sample_text}");
17691    assert_eq!(
17692        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17693        full_text,
17694    );
17695}
17696
17697#[gpui::test]
17698async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17699    init_test(cx, |_| {});
17700    cx.update(|cx| {
17701        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17702            "keymaps/default-linux.json",
17703            cx,
17704        )
17705        .unwrap();
17706        cx.bind_keys(default_key_bindings);
17707    });
17708
17709    let (editor, cx) = cx.add_window_view(|window, cx| {
17710        let multi_buffer = MultiBuffer::build_multi(
17711            [
17712                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17713                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17714                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17715                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17716            ],
17717            cx,
17718        );
17719        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17720
17721        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17722        // fold all but the second buffer, so that we test navigating between two
17723        // adjacent folded buffers, as well as folded buffers at the start and
17724        // end the multibuffer
17725        editor.fold_buffer(buffer_ids[0], cx);
17726        editor.fold_buffer(buffer_ids[2], cx);
17727        editor.fold_buffer(buffer_ids[3], cx);
17728
17729        editor
17730    });
17731    cx.simulate_resize(size(px(1000.), px(1000.)));
17732
17733    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17734    cx.assert_excerpts_with_selections(indoc! {"
17735        [EXCERPT]
17736        ˇ[FOLDED]
17737        [EXCERPT]
17738        a1
17739        b1
17740        [EXCERPT]
17741        [FOLDED]
17742        [EXCERPT]
17743        [FOLDED]
17744        "
17745    });
17746    cx.simulate_keystroke("down");
17747    cx.assert_excerpts_with_selections(indoc! {"
17748        [EXCERPT]
17749        [FOLDED]
17750        [EXCERPT]
17751        ˇa1
17752        b1
17753        [EXCERPT]
17754        [FOLDED]
17755        [EXCERPT]
17756        [FOLDED]
17757        "
17758    });
17759    cx.simulate_keystroke("down");
17760    cx.assert_excerpts_with_selections(indoc! {"
17761        [EXCERPT]
17762        [FOLDED]
17763        [EXCERPT]
17764        a1
17765        ˇb1
17766        [EXCERPT]
17767        [FOLDED]
17768        [EXCERPT]
17769        [FOLDED]
17770        "
17771    });
17772    cx.simulate_keystroke("down");
17773    cx.assert_excerpts_with_selections(indoc! {"
17774        [EXCERPT]
17775        [FOLDED]
17776        [EXCERPT]
17777        a1
17778        b1
17779        ˇ[EXCERPT]
17780        [FOLDED]
17781        [EXCERPT]
17782        [FOLDED]
17783        "
17784    });
17785    cx.simulate_keystroke("down");
17786    cx.assert_excerpts_with_selections(indoc! {"
17787        [EXCERPT]
17788        [FOLDED]
17789        [EXCERPT]
17790        a1
17791        b1
17792        [EXCERPT]
17793        ˇ[FOLDED]
17794        [EXCERPT]
17795        [FOLDED]
17796        "
17797    });
17798    for _ in 0..5 {
17799        cx.simulate_keystroke("down");
17800        cx.assert_excerpts_with_selections(indoc! {"
17801            [EXCERPT]
17802            [FOLDED]
17803            [EXCERPT]
17804            a1
17805            b1
17806            [EXCERPT]
17807            [FOLDED]
17808            [EXCERPT]
17809            ˇ[FOLDED]
17810            "
17811        });
17812    }
17813
17814    cx.simulate_keystroke("up");
17815    cx.assert_excerpts_with_selections(indoc! {"
17816        [EXCERPT]
17817        [FOLDED]
17818        [EXCERPT]
17819        a1
17820        b1
17821        [EXCERPT]
17822        ˇ[FOLDED]
17823        [EXCERPT]
17824        [FOLDED]
17825        "
17826    });
17827    cx.simulate_keystroke("up");
17828    cx.assert_excerpts_with_selections(indoc! {"
17829        [EXCERPT]
17830        [FOLDED]
17831        [EXCERPT]
17832        a1
17833        b1
17834        ˇ[EXCERPT]
17835        [FOLDED]
17836        [EXCERPT]
17837        [FOLDED]
17838        "
17839    });
17840    cx.simulate_keystroke("up");
17841    cx.assert_excerpts_with_selections(indoc! {"
17842        [EXCERPT]
17843        [FOLDED]
17844        [EXCERPT]
17845        a1
17846        ˇb1
17847        [EXCERPT]
17848        [FOLDED]
17849        [EXCERPT]
17850        [FOLDED]
17851        "
17852    });
17853    cx.simulate_keystroke("up");
17854    cx.assert_excerpts_with_selections(indoc! {"
17855        [EXCERPT]
17856        [FOLDED]
17857        [EXCERPT]
17858        ˇa1
17859        b1
17860        [EXCERPT]
17861        [FOLDED]
17862        [EXCERPT]
17863        [FOLDED]
17864        "
17865    });
17866    for _ in 0..5 {
17867        cx.simulate_keystroke("up");
17868        cx.assert_excerpts_with_selections(indoc! {"
17869            [EXCERPT]
17870            ˇ[FOLDED]
17871            [EXCERPT]
17872            a1
17873            b1
17874            [EXCERPT]
17875            [FOLDED]
17876            [EXCERPT]
17877            [FOLDED]
17878            "
17879        });
17880    }
17881}
17882
17883#[gpui::test]
17884async fn test_inline_completion_text(cx: &mut TestAppContext) {
17885    init_test(cx, |_| {});
17886
17887    // Simple insertion
17888    assert_highlighted_edits(
17889        "Hello, world!",
17890        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17891        true,
17892        cx,
17893        |highlighted_edits, cx| {
17894            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17895            assert_eq!(highlighted_edits.highlights.len(), 1);
17896            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17897            assert_eq!(
17898                highlighted_edits.highlights[0].1.background_color,
17899                Some(cx.theme().status().created_background)
17900            );
17901        },
17902    )
17903    .await;
17904
17905    // Replacement
17906    assert_highlighted_edits(
17907        "This is a test.",
17908        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17909        false,
17910        cx,
17911        |highlighted_edits, cx| {
17912            assert_eq!(highlighted_edits.text, "That is a test.");
17913            assert_eq!(highlighted_edits.highlights.len(), 1);
17914            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17915            assert_eq!(
17916                highlighted_edits.highlights[0].1.background_color,
17917                Some(cx.theme().status().created_background)
17918            );
17919        },
17920    )
17921    .await;
17922
17923    // Multiple edits
17924    assert_highlighted_edits(
17925        "Hello, world!",
17926        vec![
17927            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17928            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17929        ],
17930        false,
17931        cx,
17932        |highlighted_edits, cx| {
17933            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17934            assert_eq!(highlighted_edits.highlights.len(), 2);
17935            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17936            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17937            assert_eq!(
17938                highlighted_edits.highlights[0].1.background_color,
17939                Some(cx.theme().status().created_background)
17940            );
17941            assert_eq!(
17942                highlighted_edits.highlights[1].1.background_color,
17943                Some(cx.theme().status().created_background)
17944            );
17945        },
17946    )
17947    .await;
17948
17949    // Multiple lines with edits
17950    assert_highlighted_edits(
17951        "First line\nSecond line\nThird line\nFourth line",
17952        vec![
17953            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17954            (
17955                Point::new(2, 0)..Point::new(2, 10),
17956                "New third line".to_string(),
17957            ),
17958            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17959        ],
17960        false,
17961        cx,
17962        |highlighted_edits, cx| {
17963            assert_eq!(
17964                highlighted_edits.text,
17965                "Second modified\nNew third line\nFourth updated line"
17966            );
17967            assert_eq!(highlighted_edits.highlights.len(), 3);
17968            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17969            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17970            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17971            for highlight in &highlighted_edits.highlights {
17972                assert_eq!(
17973                    highlight.1.background_color,
17974                    Some(cx.theme().status().created_background)
17975                );
17976            }
17977        },
17978    )
17979    .await;
17980}
17981
17982#[gpui::test]
17983async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17984    init_test(cx, |_| {});
17985
17986    // Deletion
17987    assert_highlighted_edits(
17988        "Hello, world!",
17989        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17990        true,
17991        cx,
17992        |highlighted_edits, cx| {
17993            assert_eq!(highlighted_edits.text, "Hello, world!");
17994            assert_eq!(highlighted_edits.highlights.len(), 1);
17995            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17996            assert_eq!(
17997                highlighted_edits.highlights[0].1.background_color,
17998                Some(cx.theme().status().deleted_background)
17999            );
18000        },
18001    )
18002    .await;
18003
18004    // Insertion
18005    assert_highlighted_edits(
18006        "Hello, world!",
18007        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
18008        true,
18009        cx,
18010        |highlighted_edits, cx| {
18011            assert_eq!(highlighted_edits.highlights.len(), 1);
18012            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
18013            assert_eq!(
18014                highlighted_edits.highlights[0].1.background_color,
18015                Some(cx.theme().status().created_background)
18016            );
18017        },
18018    )
18019    .await;
18020}
18021
18022async fn assert_highlighted_edits(
18023    text: &str,
18024    edits: Vec<(Range<Point>, String)>,
18025    include_deletions: bool,
18026    cx: &mut TestAppContext,
18027    assertion_fn: impl Fn(HighlightedText, &App),
18028) {
18029    let window = cx.add_window(|window, cx| {
18030        let buffer = MultiBuffer::build_simple(text, cx);
18031        Editor::new(EditorMode::full(), buffer, None, window, cx)
18032    });
18033    let cx = &mut VisualTestContext::from_window(*window, cx);
18034
18035    let (buffer, snapshot) = window
18036        .update(cx, |editor, _window, cx| {
18037            (
18038                editor.buffer().clone(),
18039                editor.buffer().read(cx).snapshot(cx),
18040            )
18041        })
18042        .unwrap();
18043
18044    let edits = edits
18045        .into_iter()
18046        .map(|(range, edit)| {
18047            (
18048                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18049                edit,
18050            )
18051        })
18052        .collect::<Vec<_>>();
18053
18054    let text_anchor_edits = edits
18055        .clone()
18056        .into_iter()
18057        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18058        .collect::<Vec<_>>();
18059
18060    let edit_preview = window
18061        .update(cx, |_, _window, cx| {
18062            buffer
18063                .read(cx)
18064                .as_singleton()
18065                .unwrap()
18066                .read(cx)
18067                .preview_edits(text_anchor_edits.into(), cx)
18068        })
18069        .unwrap()
18070        .await;
18071
18072    cx.update(|_window, cx| {
18073        let highlighted_edits = inline_completion_edit_text(
18074            &snapshot.as_singleton().unwrap().2,
18075            &edits,
18076            &edit_preview,
18077            include_deletions,
18078            cx,
18079        );
18080        assertion_fn(highlighted_edits, cx)
18081    });
18082}
18083
18084#[track_caller]
18085fn assert_breakpoint(
18086    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18087    path: &Arc<Path>,
18088    expected: Vec<(u32, Breakpoint)>,
18089) {
18090    if expected.len() == 0usize {
18091        assert!(!breakpoints.contains_key(path), "{}", path.display());
18092    } else {
18093        let mut breakpoint = breakpoints
18094            .get(path)
18095            .unwrap()
18096            .into_iter()
18097            .map(|breakpoint| {
18098                (
18099                    breakpoint.row,
18100                    Breakpoint {
18101                        message: breakpoint.message.clone(),
18102                        state: breakpoint.state,
18103                        condition: breakpoint.condition.clone(),
18104                        hit_condition: breakpoint.hit_condition.clone(),
18105                    },
18106                )
18107            })
18108            .collect::<Vec<_>>();
18109
18110        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18111
18112        assert_eq!(expected, breakpoint);
18113    }
18114}
18115
18116fn add_log_breakpoint_at_cursor(
18117    editor: &mut Editor,
18118    log_message: &str,
18119    window: &mut Window,
18120    cx: &mut Context<Editor>,
18121) {
18122    let (anchor, bp) = editor
18123        .breakpoints_at_cursors(window, cx)
18124        .first()
18125        .and_then(|(anchor, bp)| {
18126            if let Some(bp) = bp {
18127                Some((*anchor, bp.clone()))
18128            } else {
18129                None
18130            }
18131        })
18132        .unwrap_or_else(|| {
18133            let cursor_position: Point = editor.selections.newest(cx).head();
18134
18135            let breakpoint_position = editor
18136                .snapshot(window, cx)
18137                .display_snapshot
18138                .buffer_snapshot
18139                .anchor_before(Point::new(cursor_position.row, 0));
18140
18141            (breakpoint_position, Breakpoint::new_log(&log_message))
18142        });
18143
18144    editor.edit_breakpoint_at_anchor(
18145        anchor,
18146        bp,
18147        BreakpointEditAction::EditLogMessage(log_message.into()),
18148        cx,
18149    );
18150}
18151
18152#[gpui::test]
18153async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18154    init_test(cx, |_| {});
18155
18156    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18157    let fs = FakeFs::new(cx.executor());
18158    fs.insert_tree(
18159        path!("/a"),
18160        json!({
18161            "main.rs": sample_text,
18162        }),
18163    )
18164    .await;
18165    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18166    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18167    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18168
18169    let fs = FakeFs::new(cx.executor());
18170    fs.insert_tree(
18171        path!("/a"),
18172        json!({
18173            "main.rs": sample_text,
18174        }),
18175    )
18176    .await;
18177    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18178    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18179    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18180    let worktree_id = workspace
18181        .update(cx, |workspace, _window, cx| {
18182            workspace.project().update(cx, |project, cx| {
18183                project.worktrees(cx).next().unwrap().read(cx).id()
18184            })
18185        })
18186        .unwrap();
18187
18188    let buffer = project
18189        .update(cx, |project, cx| {
18190            project.open_buffer((worktree_id, "main.rs"), cx)
18191        })
18192        .await
18193        .unwrap();
18194
18195    let (editor, cx) = cx.add_window_view(|window, cx| {
18196        Editor::new(
18197            EditorMode::full(),
18198            MultiBuffer::build_from_buffer(buffer, cx),
18199            Some(project.clone()),
18200            window,
18201            cx,
18202        )
18203    });
18204
18205    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18206    let abs_path = project.read_with(cx, |project, cx| {
18207        project
18208            .absolute_path(&project_path, cx)
18209            .map(|path_buf| Arc::from(path_buf.to_owned()))
18210            .unwrap()
18211    });
18212
18213    // assert we can add breakpoint on the first line
18214    editor.update_in(cx, |editor, window, cx| {
18215        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18216        editor.move_to_end(&MoveToEnd, window, cx);
18217        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18218    });
18219
18220    let breakpoints = editor.update(cx, |editor, cx| {
18221        editor
18222            .breakpoint_store()
18223            .as_ref()
18224            .unwrap()
18225            .read(cx)
18226            .all_breakpoints(cx)
18227            .clone()
18228    });
18229
18230    assert_eq!(1, breakpoints.len());
18231    assert_breakpoint(
18232        &breakpoints,
18233        &abs_path,
18234        vec![
18235            (0, Breakpoint::new_standard()),
18236            (3, Breakpoint::new_standard()),
18237        ],
18238    );
18239
18240    editor.update_in(cx, |editor, window, cx| {
18241        editor.move_to_beginning(&MoveToBeginning, window, cx);
18242        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18243    });
18244
18245    let breakpoints = editor.update(cx, |editor, cx| {
18246        editor
18247            .breakpoint_store()
18248            .as_ref()
18249            .unwrap()
18250            .read(cx)
18251            .all_breakpoints(cx)
18252            .clone()
18253    });
18254
18255    assert_eq!(1, breakpoints.len());
18256    assert_breakpoint(
18257        &breakpoints,
18258        &abs_path,
18259        vec![(3, Breakpoint::new_standard())],
18260    );
18261
18262    editor.update_in(cx, |editor, window, cx| {
18263        editor.move_to_end(&MoveToEnd, window, cx);
18264        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18265    });
18266
18267    let breakpoints = editor.update(cx, |editor, cx| {
18268        editor
18269            .breakpoint_store()
18270            .as_ref()
18271            .unwrap()
18272            .read(cx)
18273            .all_breakpoints(cx)
18274            .clone()
18275    });
18276
18277    assert_eq!(0, breakpoints.len());
18278    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18279}
18280
18281#[gpui::test]
18282async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18283    init_test(cx, |_| {});
18284
18285    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18286
18287    let fs = FakeFs::new(cx.executor());
18288    fs.insert_tree(
18289        path!("/a"),
18290        json!({
18291            "main.rs": sample_text,
18292        }),
18293    )
18294    .await;
18295    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18296    let (workspace, cx) =
18297        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18298
18299    let worktree_id = workspace.update(cx, |workspace, cx| {
18300        workspace.project().update(cx, |project, cx| {
18301            project.worktrees(cx).next().unwrap().read(cx).id()
18302        })
18303    });
18304
18305    let buffer = project
18306        .update(cx, |project, cx| {
18307            project.open_buffer((worktree_id, "main.rs"), cx)
18308        })
18309        .await
18310        .unwrap();
18311
18312    let (editor, cx) = cx.add_window_view(|window, cx| {
18313        Editor::new(
18314            EditorMode::full(),
18315            MultiBuffer::build_from_buffer(buffer, cx),
18316            Some(project.clone()),
18317            window,
18318            cx,
18319        )
18320    });
18321
18322    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18323    let abs_path = project.read_with(cx, |project, cx| {
18324        project
18325            .absolute_path(&project_path, cx)
18326            .map(|path_buf| Arc::from(path_buf.to_owned()))
18327            .unwrap()
18328    });
18329
18330    editor.update_in(cx, |editor, window, cx| {
18331        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18332    });
18333
18334    let breakpoints = editor.update(cx, |editor, cx| {
18335        editor
18336            .breakpoint_store()
18337            .as_ref()
18338            .unwrap()
18339            .read(cx)
18340            .all_breakpoints(cx)
18341            .clone()
18342    });
18343
18344    assert_breakpoint(
18345        &breakpoints,
18346        &abs_path,
18347        vec![(0, Breakpoint::new_log("hello world"))],
18348    );
18349
18350    // Removing a log message from a log breakpoint should remove it
18351    editor.update_in(cx, |editor, window, cx| {
18352        add_log_breakpoint_at_cursor(editor, "", window, cx);
18353    });
18354
18355    let breakpoints = editor.update(cx, |editor, cx| {
18356        editor
18357            .breakpoint_store()
18358            .as_ref()
18359            .unwrap()
18360            .read(cx)
18361            .all_breakpoints(cx)
18362            .clone()
18363    });
18364
18365    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18366
18367    editor.update_in(cx, |editor, window, cx| {
18368        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18369        editor.move_to_end(&MoveToEnd, window, cx);
18370        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18371        // Not adding a log message to a standard breakpoint shouldn't remove it
18372        add_log_breakpoint_at_cursor(editor, "", window, cx);
18373    });
18374
18375    let breakpoints = editor.update(cx, |editor, cx| {
18376        editor
18377            .breakpoint_store()
18378            .as_ref()
18379            .unwrap()
18380            .read(cx)
18381            .all_breakpoints(cx)
18382            .clone()
18383    });
18384
18385    assert_breakpoint(
18386        &breakpoints,
18387        &abs_path,
18388        vec![
18389            (0, Breakpoint::new_standard()),
18390            (3, Breakpoint::new_standard()),
18391        ],
18392    );
18393
18394    editor.update_in(cx, |editor, window, cx| {
18395        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18396    });
18397
18398    let breakpoints = editor.update(cx, |editor, cx| {
18399        editor
18400            .breakpoint_store()
18401            .as_ref()
18402            .unwrap()
18403            .read(cx)
18404            .all_breakpoints(cx)
18405            .clone()
18406    });
18407
18408    assert_breakpoint(
18409        &breakpoints,
18410        &abs_path,
18411        vec![
18412            (0, Breakpoint::new_standard()),
18413            (3, Breakpoint::new_log("hello world")),
18414        ],
18415    );
18416
18417    editor.update_in(cx, |editor, window, cx| {
18418        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18419    });
18420
18421    let breakpoints = editor.update(cx, |editor, cx| {
18422        editor
18423            .breakpoint_store()
18424            .as_ref()
18425            .unwrap()
18426            .read(cx)
18427            .all_breakpoints(cx)
18428            .clone()
18429    });
18430
18431    assert_breakpoint(
18432        &breakpoints,
18433        &abs_path,
18434        vec![
18435            (0, Breakpoint::new_standard()),
18436            (3, Breakpoint::new_log("hello Earth!!")),
18437        ],
18438    );
18439}
18440
18441/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18442/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18443/// or when breakpoints were placed out of order. This tests for a regression too
18444#[gpui::test]
18445async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18446    init_test(cx, |_| {});
18447
18448    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18449    let fs = FakeFs::new(cx.executor());
18450    fs.insert_tree(
18451        path!("/a"),
18452        json!({
18453            "main.rs": sample_text,
18454        }),
18455    )
18456    .await;
18457    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18458    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18459    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18460
18461    let fs = FakeFs::new(cx.executor());
18462    fs.insert_tree(
18463        path!("/a"),
18464        json!({
18465            "main.rs": sample_text,
18466        }),
18467    )
18468    .await;
18469    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18470    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18471    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18472    let worktree_id = workspace
18473        .update(cx, |workspace, _window, cx| {
18474            workspace.project().update(cx, |project, cx| {
18475                project.worktrees(cx).next().unwrap().read(cx).id()
18476            })
18477        })
18478        .unwrap();
18479
18480    let buffer = project
18481        .update(cx, |project, cx| {
18482            project.open_buffer((worktree_id, "main.rs"), cx)
18483        })
18484        .await
18485        .unwrap();
18486
18487    let (editor, cx) = cx.add_window_view(|window, cx| {
18488        Editor::new(
18489            EditorMode::full(),
18490            MultiBuffer::build_from_buffer(buffer, cx),
18491            Some(project.clone()),
18492            window,
18493            cx,
18494        )
18495    });
18496
18497    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18498    let abs_path = project.read_with(cx, |project, cx| {
18499        project
18500            .absolute_path(&project_path, cx)
18501            .map(|path_buf| Arc::from(path_buf.to_owned()))
18502            .unwrap()
18503    });
18504
18505    // assert we can add breakpoint on the first line
18506    editor.update_in(cx, |editor, window, cx| {
18507        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18508        editor.move_to_end(&MoveToEnd, window, cx);
18509        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18510        editor.move_up(&MoveUp, window, cx);
18511        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18512    });
18513
18514    let breakpoints = editor.update(cx, |editor, cx| {
18515        editor
18516            .breakpoint_store()
18517            .as_ref()
18518            .unwrap()
18519            .read(cx)
18520            .all_breakpoints(cx)
18521            .clone()
18522    });
18523
18524    assert_eq!(1, breakpoints.len());
18525    assert_breakpoint(
18526        &breakpoints,
18527        &abs_path,
18528        vec![
18529            (0, Breakpoint::new_standard()),
18530            (2, Breakpoint::new_standard()),
18531            (3, Breakpoint::new_standard()),
18532        ],
18533    );
18534
18535    editor.update_in(cx, |editor, window, cx| {
18536        editor.move_to_beginning(&MoveToBeginning, window, cx);
18537        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18538        editor.move_to_end(&MoveToEnd, window, cx);
18539        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18540        // Disabling a breakpoint that doesn't exist should do nothing
18541        editor.move_up(&MoveUp, window, cx);
18542        editor.move_up(&MoveUp, window, cx);
18543        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18544    });
18545
18546    let breakpoints = editor.update(cx, |editor, cx| {
18547        editor
18548            .breakpoint_store()
18549            .as_ref()
18550            .unwrap()
18551            .read(cx)
18552            .all_breakpoints(cx)
18553            .clone()
18554    });
18555
18556    let disable_breakpoint = {
18557        let mut bp = Breakpoint::new_standard();
18558        bp.state = BreakpointState::Disabled;
18559        bp
18560    };
18561
18562    assert_eq!(1, breakpoints.len());
18563    assert_breakpoint(
18564        &breakpoints,
18565        &abs_path,
18566        vec![
18567            (0, disable_breakpoint.clone()),
18568            (2, Breakpoint::new_standard()),
18569            (3, disable_breakpoint.clone()),
18570        ],
18571    );
18572
18573    editor.update_in(cx, |editor, window, cx| {
18574        editor.move_to_beginning(&MoveToBeginning, window, cx);
18575        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18576        editor.move_to_end(&MoveToEnd, window, cx);
18577        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18578        editor.move_up(&MoveUp, window, cx);
18579        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18580    });
18581
18582    let breakpoints = editor.update(cx, |editor, cx| {
18583        editor
18584            .breakpoint_store()
18585            .as_ref()
18586            .unwrap()
18587            .read(cx)
18588            .all_breakpoints(cx)
18589            .clone()
18590    });
18591
18592    assert_eq!(1, breakpoints.len());
18593    assert_breakpoint(
18594        &breakpoints,
18595        &abs_path,
18596        vec![
18597            (0, Breakpoint::new_standard()),
18598            (2, disable_breakpoint),
18599            (3, Breakpoint::new_standard()),
18600        ],
18601    );
18602}
18603
18604#[gpui::test]
18605async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18606    init_test(cx, |_| {});
18607    let capabilities = lsp::ServerCapabilities {
18608        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18609            prepare_provider: Some(true),
18610            work_done_progress_options: Default::default(),
18611        })),
18612        ..Default::default()
18613    };
18614    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18615
18616    cx.set_state(indoc! {"
18617        struct Fˇoo {}
18618    "});
18619
18620    cx.update_editor(|editor, _, cx| {
18621        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18622        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18623        editor.highlight_background::<DocumentHighlightRead>(
18624            &[highlight_range],
18625            |c| c.editor_document_highlight_read_background,
18626            cx,
18627        );
18628    });
18629
18630    let mut prepare_rename_handler = cx
18631        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18632            move |_, _, _| async move {
18633                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18634                    start: lsp::Position {
18635                        line: 0,
18636                        character: 7,
18637                    },
18638                    end: lsp::Position {
18639                        line: 0,
18640                        character: 10,
18641                    },
18642                })))
18643            },
18644        );
18645    let prepare_rename_task = cx
18646        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18647        .expect("Prepare rename was not started");
18648    prepare_rename_handler.next().await.unwrap();
18649    prepare_rename_task.await.expect("Prepare rename failed");
18650
18651    let mut rename_handler =
18652        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18653            let edit = lsp::TextEdit {
18654                range: lsp::Range {
18655                    start: lsp::Position {
18656                        line: 0,
18657                        character: 7,
18658                    },
18659                    end: lsp::Position {
18660                        line: 0,
18661                        character: 10,
18662                    },
18663                },
18664                new_text: "FooRenamed".to_string(),
18665            };
18666            Ok(Some(lsp::WorkspaceEdit::new(
18667                // Specify the same edit twice
18668                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18669            )))
18670        });
18671    let rename_task = cx
18672        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18673        .expect("Confirm rename was not started");
18674    rename_handler.next().await.unwrap();
18675    rename_task.await.expect("Confirm rename failed");
18676    cx.run_until_parked();
18677
18678    // Despite two edits, only one is actually applied as those are identical
18679    cx.assert_editor_state(indoc! {"
18680        struct FooRenamedˇ {}
18681    "});
18682}
18683
18684#[gpui::test]
18685async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18686    init_test(cx, |_| {});
18687    // These capabilities indicate that the server does not support prepare rename.
18688    let capabilities = lsp::ServerCapabilities {
18689        rename_provider: Some(lsp::OneOf::Left(true)),
18690        ..Default::default()
18691    };
18692    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18693
18694    cx.set_state(indoc! {"
18695        struct Fˇoo {}
18696    "});
18697
18698    cx.update_editor(|editor, _window, cx| {
18699        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18700        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18701        editor.highlight_background::<DocumentHighlightRead>(
18702            &[highlight_range],
18703            |c| c.editor_document_highlight_read_background,
18704            cx,
18705        );
18706    });
18707
18708    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18709        .expect("Prepare rename was not started")
18710        .await
18711        .expect("Prepare rename failed");
18712
18713    let mut rename_handler =
18714        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18715            let edit = lsp::TextEdit {
18716                range: lsp::Range {
18717                    start: lsp::Position {
18718                        line: 0,
18719                        character: 7,
18720                    },
18721                    end: lsp::Position {
18722                        line: 0,
18723                        character: 10,
18724                    },
18725                },
18726                new_text: "FooRenamed".to_string(),
18727            };
18728            Ok(Some(lsp::WorkspaceEdit::new(
18729                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18730            )))
18731        });
18732    let rename_task = cx
18733        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18734        .expect("Confirm rename was not started");
18735    rename_handler.next().await.unwrap();
18736    rename_task.await.expect("Confirm rename failed");
18737    cx.run_until_parked();
18738
18739    // Correct range is renamed, as `surrounding_word` is used to find it.
18740    cx.assert_editor_state(indoc! {"
18741        struct FooRenamedˇ {}
18742    "});
18743}
18744
18745#[gpui::test]
18746async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18747    init_test(cx, |_| {});
18748    let mut cx = EditorTestContext::new(cx).await;
18749
18750    let language = Arc::new(
18751        Language::new(
18752            LanguageConfig::default(),
18753            Some(tree_sitter_html::LANGUAGE.into()),
18754        )
18755        .with_brackets_query(
18756            r#"
18757            ("<" @open "/>" @close)
18758            ("</" @open ">" @close)
18759            ("<" @open ">" @close)
18760            ("\"" @open "\"" @close)
18761            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18762        "#,
18763        )
18764        .unwrap(),
18765    );
18766    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18767
18768    cx.set_state(indoc! {"
18769        <span>ˇ</span>
18770    "});
18771    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18772    cx.assert_editor_state(indoc! {"
18773        <span>
18774        ˇ
18775        </span>
18776    "});
18777
18778    cx.set_state(indoc! {"
18779        <span><span></span>ˇ</span>
18780    "});
18781    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18782    cx.assert_editor_state(indoc! {"
18783        <span><span></span>
18784        ˇ</span>
18785    "});
18786
18787    cx.set_state(indoc! {"
18788        <span>ˇ
18789        </span>
18790    "});
18791    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18792    cx.assert_editor_state(indoc! {"
18793        <span>
18794        ˇ
18795        </span>
18796    "});
18797}
18798
18799#[gpui::test(iterations = 10)]
18800async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18801    init_test(cx, |_| {});
18802
18803    let fs = FakeFs::new(cx.executor());
18804    fs.insert_tree(
18805        path!("/dir"),
18806        json!({
18807            "a.ts": "a",
18808        }),
18809    )
18810    .await;
18811
18812    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18813    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18814    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18815
18816    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18817    language_registry.add(Arc::new(Language::new(
18818        LanguageConfig {
18819            name: "TypeScript".into(),
18820            matcher: LanguageMatcher {
18821                path_suffixes: vec!["ts".to_string()],
18822                ..Default::default()
18823            },
18824            ..Default::default()
18825        },
18826        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18827    )));
18828    let mut fake_language_servers = language_registry.register_fake_lsp(
18829        "TypeScript",
18830        FakeLspAdapter {
18831            capabilities: lsp::ServerCapabilities {
18832                code_lens_provider: Some(lsp::CodeLensOptions {
18833                    resolve_provider: Some(true),
18834                }),
18835                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18836                    commands: vec!["_the/command".to_string()],
18837                    ..lsp::ExecuteCommandOptions::default()
18838                }),
18839                ..lsp::ServerCapabilities::default()
18840            },
18841            ..FakeLspAdapter::default()
18842        },
18843    );
18844
18845    let (buffer, _handle) = project
18846        .update(cx, |p, cx| {
18847            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18848        })
18849        .await
18850        .unwrap();
18851    cx.executor().run_until_parked();
18852
18853    let fake_server = fake_language_servers.next().await.unwrap();
18854
18855    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18856    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18857    drop(buffer_snapshot);
18858    let actions = cx
18859        .update_window(*workspace, |_, window, cx| {
18860            project.code_actions(&buffer, anchor..anchor, window, cx)
18861        })
18862        .unwrap();
18863
18864    fake_server
18865        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18866            Ok(Some(vec![
18867                lsp::CodeLens {
18868                    range: lsp::Range::default(),
18869                    command: Some(lsp::Command {
18870                        title: "Code lens command".to_owned(),
18871                        command: "_the/command".to_owned(),
18872                        arguments: None,
18873                    }),
18874                    data: None,
18875                },
18876                lsp::CodeLens {
18877                    range: lsp::Range::default(),
18878                    command: Some(lsp::Command {
18879                        title: "Command not in capabilities".to_owned(),
18880                        command: "not in capabilities".to_owned(),
18881                        arguments: None,
18882                    }),
18883                    data: None,
18884                },
18885                lsp::CodeLens {
18886                    range: lsp::Range {
18887                        start: lsp::Position {
18888                            line: 1,
18889                            character: 1,
18890                        },
18891                        end: lsp::Position {
18892                            line: 1,
18893                            character: 1,
18894                        },
18895                    },
18896                    command: Some(lsp::Command {
18897                        title: "Command not in range".to_owned(),
18898                        command: "_the/command".to_owned(),
18899                        arguments: None,
18900                    }),
18901                    data: None,
18902                },
18903            ]))
18904        })
18905        .next()
18906        .await;
18907
18908    let actions = actions.await.unwrap();
18909    assert_eq!(
18910        actions.len(),
18911        1,
18912        "Should have only one valid action for the 0..0 range"
18913    );
18914    let action = actions[0].clone();
18915    let apply = project.update(cx, |project, cx| {
18916        project.apply_code_action(buffer.clone(), action, true, cx)
18917    });
18918
18919    // Resolving the code action does not populate its edits. In absence of
18920    // edits, we must execute the given command.
18921    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18922        |mut lens, _| async move {
18923            let lens_command = lens.command.as_mut().expect("should have a command");
18924            assert_eq!(lens_command.title, "Code lens command");
18925            lens_command.arguments = Some(vec![json!("the-argument")]);
18926            Ok(lens)
18927        },
18928    );
18929
18930    // While executing the command, the language server sends the editor
18931    // a `workspaceEdit` request.
18932    fake_server
18933        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18934            let fake = fake_server.clone();
18935            move |params, _| {
18936                assert_eq!(params.command, "_the/command");
18937                let fake = fake.clone();
18938                async move {
18939                    fake.server
18940                        .request::<lsp::request::ApplyWorkspaceEdit>(
18941                            lsp::ApplyWorkspaceEditParams {
18942                                label: None,
18943                                edit: lsp::WorkspaceEdit {
18944                                    changes: Some(
18945                                        [(
18946                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18947                                            vec![lsp::TextEdit {
18948                                                range: lsp::Range::new(
18949                                                    lsp::Position::new(0, 0),
18950                                                    lsp::Position::new(0, 0),
18951                                                ),
18952                                                new_text: "X".into(),
18953                                            }],
18954                                        )]
18955                                        .into_iter()
18956                                        .collect(),
18957                                    ),
18958                                    ..Default::default()
18959                                },
18960                            },
18961                        )
18962                        .await
18963                        .unwrap();
18964                    Ok(Some(json!(null)))
18965                }
18966            }
18967        })
18968        .next()
18969        .await;
18970
18971    // Applying the code lens command returns a project transaction containing the edits
18972    // sent by the language server in its `workspaceEdit` request.
18973    let transaction = apply.await.unwrap();
18974    assert!(transaction.0.contains_key(&buffer));
18975    buffer.update(cx, |buffer, cx| {
18976        assert_eq!(buffer.text(), "Xa");
18977        buffer.undo(cx);
18978        assert_eq!(buffer.text(), "a");
18979    });
18980}
18981
18982#[gpui::test]
18983async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18984    init_test(cx, |_| {});
18985
18986    let fs = FakeFs::new(cx.executor());
18987    let main_text = r#"fn main() {
18988println!("1");
18989println!("2");
18990println!("3");
18991println!("4");
18992println!("5");
18993}"#;
18994    let lib_text = "mod foo {}";
18995    fs.insert_tree(
18996        path!("/a"),
18997        json!({
18998            "lib.rs": lib_text,
18999            "main.rs": main_text,
19000        }),
19001    )
19002    .await;
19003
19004    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19005    let (workspace, cx) =
19006        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19007    let worktree_id = workspace.update(cx, |workspace, cx| {
19008        workspace.project().update(cx, |project, cx| {
19009            project.worktrees(cx).next().unwrap().read(cx).id()
19010        })
19011    });
19012
19013    let expected_ranges = vec![
19014        Point::new(0, 0)..Point::new(0, 0),
19015        Point::new(1, 0)..Point::new(1, 1),
19016        Point::new(2, 0)..Point::new(2, 2),
19017        Point::new(3, 0)..Point::new(3, 3),
19018    ];
19019
19020    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19021    let editor_1 = workspace
19022        .update_in(cx, |workspace, window, cx| {
19023            workspace.open_path(
19024                (worktree_id, "main.rs"),
19025                Some(pane_1.downgrade()),
19026                true,
19027                window,
19028                cx,
19029            )
19030        })
19031        .unwrap()
19032        .await
19033        .downcast::<Editor>()
19034        .unwrap();
19035    pane_1.update(cx, |pane, cx| {
19036        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19037        open_editor.update(cx, |editor, cx| {
19038            assert_eq!(
19039                editor.display_text(cx),
19040                main_text,
19041                "Original main.rs text on initial open",
19042            );
19043            assert_eq!(
19044                editor
19045                    .selections
19046                    .all::<Point>(cx)
19047                    .into_iter()
19048                    .map(|s| s.range())
19049                    .collect::<Vec<_>>(),
19050                vec![Point::zero()..Point::zero()],
19051                "Default selections on initial open",
19052            );
19053        })
19054    });
19055    editor_1.update_in(cx, |editor, window, cx| {
19056        editor.change_selections(None, window, cx, |s| {
19057            s.select_ranges(expected_ranges.clone());
19058        });
19059    });
19060
19061    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19062        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19063    });
19064    let editor_2 = workspace
19065        .update_in(cx, |workspace, window, cx| {
19066            workspace.open_path(
19067                (worktree_id, "main.rs"),
19068                Some(pane_2.downgrade()),
19069                true,
19070                window,
19071                cx,
19072            )
19073        })
19074        .unwrap()
19075        .await
19076        .downcast::<Editor>()
19077        .unwrap();
19078    pane_2.update(cx, |pane, cx| {
19079        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19080        open_editor.update(cx, |editor, cx| {
19081            assert_eq!(
19082                editor.display_text(cx),
19083                main_text,
19084                "Original main.rs text on initial open in another panel",
19085            );
19086            assert_eq!(
19087                editor
19088                    .selections
19089                    .all::<Point>(cx)
19090                    .into_iter()
19091                    .map(|s| s.range())
19092                    .collect::<Vec<_>>(),
19093                vec![Point::zero()..Point::zero()],
19094                "Default selections on initial open in another panel",
19095            );
19096        })
19097    });
19098
19099    editor_2.update_in(cx, |editor, window, cx| {
19100        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19101    });
19102
19103    let _other_editor_1 = workspace
19104        .update_in(cx, |workspace, window, cx| {
19105            workspace.open_path(
19106                (worktree_id, "lib.rs"),
19107                Some(pane_1.downgrade()),
19108                true,
19109                window,
19110                cx,
19111            )
19112        })
19113        .unwrap()
19114        .await
19115        .downcast::<Editor>()
19116        .unwrap();
19117    pane_1
19118        .update_in(cx, |pane, window, cx| {
19119            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19120                .unwrap()
19121        })
19122        .await
19123        .unwrap();
19124    drop(editor_1);
19125    pane_1.update(cx, |pane, cx| {
19126        pane.active_item()
19127            .unwrap()
19128            .downcast::<Editor>()
19129            .unwrap()
19130            .update(cx, |editor, cx| {
19131                assert_eq!(
19132                    editor.display_text(cx),
19133                    lib_text,
19134                    "Other file should be open and active",
19135                );
19136            });
19137        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19138    });
19139
19140    let _other_editor_2 = workspace
19141        .update_in(cx, |workspace, window, cx| {
19142            workspace.open_path(
19143                (worktree_id, "lib.rs"),
19144                Some(pane_2.downgrade()),
19145                true,
19146                window,
19147                cx,
19148            )
19149        })
19150        .unwrap()
19151        .await
19152        .downcast::<Editor>()
19153        .unwrap();
19154    pane_2
19155        .update_in(cx, |pane, window, cx| {
19156            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19157                .unwrap()
19158        })
19159        .await
19160        .unwrap();
19161    drop(editor_2);
19162    pane_2.update(cx, |pane, cx| {
19163        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19164        open_editor.update(cx, |editor, cx| {
19165            assert_eq!(
19166                editor.display_text(cx),
19167                lib_text,
19168                "Other file should be open and active in another panel too",
19169            );
19170        });
19171        assert_eq!(
19172            pane.items().count(),
19173            1,
19174            "No other editors should be open in another pane",
19175        );
19176    });
19177
19178    let _editor_1_reopened = workspace
19179        .update_in(cx, |workspace, window, cx| {
19180            workspace.open_path(
19181                (worktree_id, "main.rs"),
19182                Some(pane_1.downgrade()),
19183                true,
19184                window,
19185                cx,
19186            )
19187        })
19188        .unwrap()
19189        .await
19190        .downcast::<Editor>()
19191        .unwrap();
19192    let _editor_2_reopened = workspace
19193        .update_in(cx, |workspace, window, cx| {
19194            workspace.open_path(
19195                (worktree_id, "main.rs"),
19196                Some(pane_2.downgrade()),
19197                true,
19198                window,
19199                cx,
19200            )
19201        })
19202        .unwrap()
19203        .await
19204        .downcast::<Editor>()
19205        .unwrap();
19206    pane_1.update(cx, |pane, cx| {
19207        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19208        open_editor.update(cx, |editor, cx| {
19209            assert_eq!(
19210                editor.display_text(cx),
19211                main_text,
19212                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19213            );
19214            assert_eq!(
19215                editor
19216                    .selections
19217                    .all::<Point>(cx)
19218                    .into_iter()
19219                    .map(|s| s.range())
19220                    .collect::<Vec<_>>(),
19221                expected_ranges,
19222                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19223            );
19224        })
19225    });
19226    pane_2.update(cx, |pane, cx| {
19227        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19228        open_editor.update(cx, |editor, cx| {
19229            assert_eq!(
19230                editor.display_text(cx),
19231                r#"fn main() {
19232⋯rintln!("1");
19233⋯intln!("2");
19234⋯ntln!("3");
19235println!("4");
19236println!("5");
19237}"#,
19238                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19239            );
19240            assert_eq!(
19241                editor
19242                    .selections
19243                    .all::<Point>(cx)
19244                    .into_iter()
19245                    .map(|s| s.range())
19246                    .collect::<Vec<_>>(),
19247                vec![Point::zero()..Point::zero()],
19248                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19249            );
19250        })
19251    });
19252}
19253
19254#[gpui::test]
19255async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19256    init_test(cx, |_| {});
19257
19258    let fs = FakeFs::new(cx.executor());
19259    let main_text = r#"fn main() {
19260println!("1");
19261println!("2");
19262println!("3");
19263println!("4");
19264println!("5");
19265}"#;
19266    let lib_text = "mod foo {}";
19267    fs.insert_tree(
19268        path!("/a"),
19269        json!({
19270            "lib.rs": lib_text,
19271            "main.rs": main_text,
19272        }),
19273    )
19274    .await;
19275
19276    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19277    let (workspace, cx) =
19278        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19279    let worktree_id = workspace.update(cx, |workspace, cx| {
19280        workspace.project().update(cx, |project, cx| {
19281            project.worktrees(cx).next().unwrap().read(cx).id()
19282        })
19283    });
19284
19285    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19286    let editor = workspace
19287        .update_in(cx, |workspace, window, cx| {
19288            workspace.open_path(
19289                (worktree_id, "main.rs"),
19290                Some(pane.downgrade()),
19291                true,
19292                window,
19293                cx,
19294            )
19295        })
19296        .unwrap()
19297        .await
19298        .downcast::<Editor>()
19299        .unwrap();
19300    pane.update(cx, |pane, cx| {
19301        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19302        open_editor.update(cx, |editor, cx| {
19303            assert_eq!(
19304                editor.display_text(cx),
19305                main_text,
19306                "Original main.rs text on initial open",
19307            );
19308        })
19309    });
19310    editor.update_in(cx, |editor, window, cx| {
19311        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19312    });
19313
19314    cx.update_global(|store: &mut SettingsStore, cx| {
19315        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19316            s.restore_on_file_reopen = Some(false);
19317        });
19318    });
19319    editor.update_in(cx, |editor, window, cx| {
19320        editor.fold_ranges(
19321            vec![
19322                Point::new(1, 0)..Point::new(1, 1),
19323                Point::new(2, 0)..Point::new(2, 2),
19324                Point::new(3, 0)..Point::new(3, 3),
19325            ],
19326            false,
19327            window,
19328            cx,
19329        );
19330    });
19331    pane.update_in(cx, |pane, window, cx| {
19332        pane.close_all_items(&CloseAllItems::default(), window, cx)
19333            .unwrap()
19334    })
19335    .await
19336    .unwrap();
19337    pane.update(cx, |pane, _| {
19338        assert!(pane.active_item().is_none());
19339    });
19340    cx.update_global(|store: &mut SettingsStore, cx| {
19341        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19342            s.restore_on_file_reopen = Some(true);
19343        });
19344    });
19345
19346    let _editor_reopened = workspace
19347        .update_in(cx, |workspace, window, cx| {
19348            workspace.open_path(
19349                (worktree_id, "main.rs"),
19350                Some(pane.downgrade()),
19351                true,
19352                window,
19353                cx,
19354            )
19355        })
19356        .unwrap()
19357        .await
19358        .downcast::<Editor>()
19359        .unwrap();
19360    pane.update(cx, |pane, cx| {
19361        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19362        open_editor.update(cx, |editor, cx| {
19363            assert_eq!(
19364                editor.display_text(cx),
19365                main_text,
19366                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19367            );
19368        })
19369    });
19370}
19371
19372fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19373    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19374    point..point
19375}
19376
19377fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19378    let (text, ranges) = marked_text_ranges(marked_text, true);
19379    assert_eq!(editor.text(cx), text);
19380    assert_eq!(
19381        editor.selections.ranges(cx),
19382        ranges,
19383        "Assert selections are {}",
19384        marked_text
19385    );
19386}
19387
19388pub fn handle_signature_help_request(
19389    cx: &mut EditorLspTestContext,
19390    mocked_response: lsp::SignatureHelp,
19391) -> impl Future<Output = ()> + use<> {
19392    let mut request =
19393        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19394            let mocked_response = mocked_response.clone();
19395            async move { Ok(Some(mocked_response)) }
19396        });
19397
19398    async move {
19399        request.next().await;
19400    }
19401}
19402
19403/// Handle completion request passing a marked string specifying where the completion
19404/// should be triggered from using '|' character, what range should be replaced, and what completions
19405/// should be returned using '<' and '>' to delimit the range.
19406///
19407/// Also see `handle_completion_request_with_insert_and_replace`.
19408#[track_caller]
19409pub fn handle_completion_request(
19410    cx: &mut EditorLspTestContext,
19411    marked_string: &str,
19412    completions: Vec<&'static str>,
19413    counter: Arc<AtomicUsize>,
19414) -> impl Future<Output = ()> {
19415    let complete_from_marker: TextRangeMarker = '|'.into();
19416    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19417    let (_, mut marked_ranges) = marked_text_ranges_by(
19418        marked_string,
19419        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19420    );
19421
19422    let complete_from_position =
19423        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19424    let replace_range =
19425        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19426
19427    let mut request =
19428        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19429            let completions = completions.clone();
19430            counter.fetch_add(1, atomic::Ordering::Release);
19431            async move {
19432                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19433                assert_eq!(
19434                    params.text_document_position.position,
19435                    complete_from_position
19436                );
19437                Ok(Some(lsp::CompletionResponse::Array(
19438                    completions
19439                        .iter()
19440                        .map(|completion_text| lsp::CompletionItem {
19441                            label: completion_text.to_string(),
19442                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19443                                range: replace_range,
19444                                new_text: completion_text.to_string(),
19445                            })),
19446                            ..Default::default()
19447                        })
19448                        .collect(),
19449                )))
19450            }
19451        });
19452
19453    async move {
19454        request.next().await;
19455    }
19456}
19457
19458/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19459/// given instead, which also contains an `insert` range.
19460///
19461/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19462/// that is, `replace_range.start..cursor_pos`.
19463pub fn handle_completion_request_with_insert_and_replace(
19464    cx: &mut EditorLspTestContext,
19465    marked_string: &str,
19466    completions: Vec<&'static str>,
19467    counter: Arc<AtomicUsize>,
19468) -> impl Future<Output = ()> {
19469    let complete_from_marker: TextRangeMarker = '|'.into();
19470    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19471    let (_, mut marked_ranges) = marked_text_ranges_by(
19472        marked_string,
19473        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19474    );
19475
19476    let complete_from_position =
19477        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19478    let replace_range =
19479        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19480
19481    let mut request =
19482        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19483            let completions = completions.clone();
19484            counter.fetch_add(1, atomic::Ordering::Release);
19485            async move {
19486                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19487                assert_eq!(
19488                    params.text_document_position.position, complete_from_position,
19489                    "marker `|` position doesn't match",
19490                );
19491                Ok(Some(lsp::CompletionResponse::Array(
19492                    completions
19493                        .iter()
19494                        .map(|completion_text| lsp::CompletionItem {
19495                            label: completion_text.to_string(),
19496                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19497                                lsp::InsertReplaceEdit {
19498                                    insert: lsp::Range {
19499                                        start: replace_range.start,
19500                                        end: complete_from_position,
19501                                    },
19502                                    replace: replace_range,
19503                                    new_text: completion_text.to_string(),
19504                                },
19505                            )),
19506                            ..Default::default()
19507                        })
19508                        .collect(),
19509                )))
19510            }
19511        });
19512
19513    async move {
19514        request.next().await;
19515    }
19516}
19517
19518fn handle_resolve_completion_request(
19519    cx: &mut EditorLspTestContext,
19520    edits: Option<Vec<(&'static str, &'static str)>>,
19521) -> impl Future<Output = ()> {
19522    let edits = edits.map(|edits| {
19523        edits
19524            .iter()
19525            .map(|(marked_string, new_text)| {
19526                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19527                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19528                lsp::TextEdit::new(replace_range, new_text.to_string())
19529            })
19530            .collect::<Vec<_>>()
19531    });
19532
19533    let mut request =
19534        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19535            let edits = edits.clone();
19536            async move {
19537                Ok(lsp::CompletionItem {
19538                    additional_text_edits: edits,
19539                    ..Default::default()
19540                })
19541            }
19542        });
19543
19544    async move {
19545        request.next().await;
19546    }
19547}
19548
19549pub(crate) fn update_test_language_settings(
19550    cx: &mut TestAppContext,
19551    f: impl Fn(&mut AllLanguageSettingsContent),
19552) {
19553    cx.update(|cx| {
19554        SettingsStore::update_global(cx, |store, cx| {
19555            store.update_user_settings::<AllLanguageSettings>(cx, f);
19556        });
19557    });
19558}
19559
19560pub(crate) fn update_test_project_settings(
19561    cx: &mut TestAppContext,
19562    f: impl Fn(&mut ProjectSettings),
19563) {
19564    cx.update(|cx| {
19565        SettingsStore::update_global(cx, |store, cx| {
19566            store.update_user_settings::<ProjectSettings>(cx, f);
19567        });
19568    });
19569}
19570
19571pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19572    cx.update(|cx| {
19573        assets::Assets.load_test_fonts(cx);
19574        let store = SettingsStore::test(cx);
19575        cx.set_global(store);
19576        theme::init(theme::LoadThemes::JustBase, cx);
19577        release_channel::init(SemanticVersion::default(), cx);
19578        client::init_settings(cx);
19579        language::init(cx);
19580        Project::init_settings(cx);
19581        workspace::init_settings(cx);
19582        crate::init(cx);
19583    });
19584
19585    update_test_language_settings(cx, f);
19586}
19587
19588#[track_caller]
19589fn assert_hunk_revert(
19590    not_reverted_text_with_selections: &str,
19591    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19592    expected_reverted_text_with_selections: &str,
19593    base_text: &str,
19594    cx: &mut EditorLspTestContext,
19595) {
19596    cx.set_state(not_reverted_text_with_selections);
19597    cx.set_head_text(base_text);
19598    cx.executor().run_until_parked();
19599
19600    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19601        let snapshot = editor.snapshot(window, cx);
19602        let reverted_hunk_statuses = snapshot
19603            .buffer_snapshot
19604            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19605            .map(|hunk| hunk.status().kind)
19606            .collect::<Vec<_>>();
19607
19608        editor.git_restore(&Default::default(), window, cx);
19609        reverted_hunk_statuses
19610    });
19611    cx.executor().run_until_parked();
19612    cx.assert_editor_state(expected_reverted_text_with_selections);
19613    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19614}