editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.defaults.tab_size = NonZeroU32::new(3)
 2924    });
 2925
 2926    let mut cx = EditorTestContext::new(cx).await;
 2927    cx.set_state(indoc! {"
 2928         ˇ
 2929        \t ˇ
 2930        \t  ˇ
 2931        \t   ˇ
 2932         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2933    "});
 2934
 2935    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2936    cx.assert_editor_state(indoc! {"
 2937           ˇ
 2938        \t   ˇ
 2939        \t   ˇ
 2940        \t      ˇ
 2941         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2942    "});
 2943}
 2944
 2945#[gpui::test]
 2946async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2947    init_test(cx, |settings| {
 2948        settings.defaults.tab_size = NonZeroU32::new(4)
 2949    });
 2950
 2951    let language = Arc::new(
 2952        Language::new(
 2953            LanguageConfig::default(),
 2954            Some(tree_sitter_rust::LANGUAGE.into()),
 2955        )
 2956        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2957        .unwrap(),
 2958    );
 2959
 2960    let mut cx = EditorTestContext::new(cx).await;
 2961    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2962    cx.set_state(indoc! {"
 2963        fn a() {
 2964            if b {
 2965        \t ˇc
 2966            }
 2967        }
 2968    "});
 2969
 2970    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2971    cx.assert_editor_state(indoc! {"
 2972        fn a() {
 2973            if b {
 2974                ˇc
 2975            }
 2976        }
 2977    "});
 2978}
 2979
 2980#[gpui::test]
 2981async fn test_indent_outdent(cx: &mut TestAppContext) {
 2982    init_test(cx, |settings| {
 2983        settings.defaults.tab_size = NonZeroU32::new(4);
 2984    });
 2985
 2986    let mut cx = EditorTestContext::new(cx).await;
 2987
 2988    cx.set_state(indoc! {"
 2989          «oneˇ» «twoˇ»
 2990        three
 2991         four
 2992    "});
 2993    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2994    cx.assert_editor_state(indoc! {"
 2995            «oneˇ» «twoˇ»
 2996        three
 2997         four
 2998    "});
 2999
 3000    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3001    cx.assert_editor_state(indoc! {"
 3002        «oneˇ» «twoˇ»
 3003        three
 3004         four
 3005    "});
 3006
 3007    // select across line ending
 3008    cx.set_state(indoc! {"
 3009        one two
 3010        t«hree
 3011        ˇ» four
 3012    "});
 3013    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3014    cx.assert_editor_state(indoc! {"
 3015        one two
 3016            t«hree
 3017        ˇ» four
 3018    "});
 3019
 3020    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3021    cx.assert_editor_state(indoc! {"
 3022        one two
 3023        t«hree
 3024        ˇ» four
 3025    "});
 3026
 3027    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3028    cx.set_state(indoc! {"
 3029        one two
 3030        ˇthree
 3031            four
 3032    "});
 3033    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3034    cx.assert_editor_state(indoc! {"
 3035        one two
 3036            ˇthree
 3037            four
 3038    "});
 3039
 3040    cx.set_state(indoc! {"
 3041        one two
 3042        ˇ    three
 3043            four
 3044    "});
 3045    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3046    cx.assert_editor_state(indoc! {"
 3047        one two
 3048        ˇthree
 3049            four
 3050    "});
 3051}
 3052
 3053#[gpui::test]
 3054async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3055    init_test(cx, |settings| {
 3056        settings.defaults.hard_tabs = Some(true);
 3057    });
 3058
 3059    let mut cx = EditorTestContext::new(cx).await;
 3060
 3061    // select two ranges on one line
 3062    cx.set_state(indoc! {"
 3063        «oneˇ» «twoˇ»
 3064        three
 3065        four
 3066    "});
 3067    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3068    cx.assert_editor_state(indoc! {"
 3069        \t«oneˇ» «twoˇ»
 3070        three
 3071        four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        \t\t«oneˇ» «twoˇ»
 3076        three
 3077        four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        \t«oneˇ» «twoˇ»
 3082        three
 3083        four
 3084    "});
 3085    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3086    cx.assert_editor_state(indoc! {"
 3087        «oneˇ» «twoˇ»
 3088        three
 3089        four
 3090    "});
 3091
 3092    // select across a line ending
 3093    cx.set_state(indoc! {"
 3094        one two
 3095        t«hree
 3096        ˇ»four
 3097    "});
 3098    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3099    cx.assert_editor_state(indoc! {"
 3100        one two
 3101        \tt«hree
 3102        ˇ»four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        \t\tt«hree
 3108        ˇ»four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        \tt«hree
 3114        ˇ»four
 3115    "});
 3116    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3117    cx.assert_editor_state(indoc! {"
 3118        one two
 3119        t«hree
 3120        ˇ»four
 3121    "});
 3122
 3123    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3124    cx.set_state(indoc! {"
 3125        one two
 3126        ˇthree
 3127        four
 3128    "});
 3129    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3130    cx.assert_editor_state(indoc! {"
 3131        one two
 3132        ˇthree
 3133        four
 3134    "});
 3135    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3136    cx.assert_editor_state(indoc! {"
 3137        one two
 3138        \tˇthree
 3139        four
 3140    "});
 3141    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3142    cx.assert_editor_state(indoc! {"
 3143        one two
 3144        ˇthree
 3145        four
 3146    "});
 3147}
 3148
 3149#[gpui::test]
 3150fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3151    init_test(cx, |settings| {
 3152        settings.languages.extend([
 3153            (
 3154                "TOML".into(),
 3155                LanguageSettingsContent {
 3156                    tab_size: NonZeroU32::new(2),
 3157                    ..Default::default()
 3158                },
 3159            ),
 3160            (
 3161                "Rust".into(),
 3162                LanguageSettingsContent {
 3163                    tab_size: NonZeroU32::new(4),
 3164                    ..Default::default()
 3165                },
 3166            ),
 3167        ]);
 3168    });
 3169
 3170    let toml_language = Arc::new(Language::new(
 3171        LanguageConfig {
 3172            name: "TOML".into(),
 3173            ..Default::default()
 3174        },
 3175        None,
 3176    ));
 3177    let rust_language = Arc::new(Language::new(
 3178        LanguageConfig {
 3179            name: "Rust".into(),
 3180            ..Default::default()
 3181        },
 3182        None,
 3183    ));
 3184
 3185    let toml_buffer =
 3186        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3187    let rust_buffer =
 3188        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3189    let multibuffer = cx.new(|cx| {
 3190        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3191        multibuffer.push_excerpts(
 3192            toml_buffer.clone(),
 3193            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3194            cx,
 3195        );
 3196        multibuffer.push_excerpts(
 3197            rust_buffer.clone(),
 3198            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3199            cx,
 3200        );
 3201        multibuffer
 3202    });
 3203
 3204    cx.add_window(|window, cx| {
 3205        let mut editor = build_editor(multibuffer, window, cx);
 3206
 3207        assert_eq!(
 3208            editor.text(cx),
 3209            indoc! {"
 3210                a = 1
 3211                b = 2
 3212
 3213                const c: usize = 3;
 3214            "}
 3215        );
 3216
 3217        select_ranges(
 3218            &mut editor,
 3219            indoc! {"
 3220                «aˇ» = 1
 3221                b = 2
 3222
 3223                «const c:ˇ» usize = 3;
 3224            "},
 3225            window,
 3226            cx,
 3227        );
 3228
 3229        editor.tab(&Tab, window, cx);
 3230        assert_text_with_selections(
 3231            &mut editor,
 3232            indoc! {"
 3233                  «aˇ» = 1
 3234                b = 2
 3235
 3236                    «const c:ˇ» usize = 3;
 3237            "},
 3238            cx,
 3239        );
 3240        editor.backtab(&Backtab, window, cx);
 3241        assert_text_with_selections(
 3242            &mut editor,
 3243            indoc! {"
 3244                «aˇ» = 1
 3245                b = 2
 3246
 3247                «const c:ˇ» usize = 3;
 3248            "},
 3249            cx,
 3250        );
 3251
 3252        editor
 3253    });
 3254}
 3255
 3256#[gpui::test]
 3257async fn test_backspace(cx: &mut TestAppContext) {
 3258    init_test(cx, |_| {});
 3259
 3260    let mut cx = EditorTestContext::new(cx).await;
 3261
 3262    // Basic backspace
 3263    cx.set_state(indoc! {"
 3264        onˇe two three
 3265        fou«rˇ» five six
 3266        seven «ˇeight nine
 3267        »ten
 3268    "});
 3269    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3270    cx.assert_editor_state(indoc! {"
 3271        oˇe two three
 3272        fouˇ five six
 3273        seven ˇten
 3274    "});
 3275
 3276    // Test backspace inside and around indents
 3277    cx.set_state(indoc! {"
 3278        zero
 3279            ˇone
 3280                ˇtwo
 3281            ˇ ˇ ˇ  three
 3282        ˇ  ˇ  four
 3283    "});
 3284    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3285    cx.assert_editor_state(indoc! {"
 3286        zero
 3287        ˇone
 3288            ˇtwo
 3289        ˇ  threeˇ  four
 3290    "});
 3291}
 3292
 3293#[gpui::test]
 3294async fn test_delete(cx: &mut TestAppContext) {
 3295    init_test(cx, |_| {});
 3296
 3297    let mut cx = EditorTestContext::new(cx).await;
 3298    cx.set_state(indoc! {"
 3299        onˇe two three
 3300        fou«rˇ» five six
 3301        seven «ˇeight nine
 3302        »ten
 3303    "});
 3304    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3305    cx.assert_editor_state(indoc! {"
 3306        onˇ two three
 3307        fouˇ five six
 3308        seven ˇten
 3309    "});
 3310}
 3311
 3312#[gpui::test]
 3313fn test_delete_line(cx: &mut TestAppContext) {
 3314    init_test(cx, |_| {});
 3315
 3316    let editor = cx.add_window(|window, cx| {
 3317        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3318        build_editor(buffer, window, cx)
 3319    });
 3320    _ = editor.update(cx, |editor, window, cx| {
 3321        editor.change_selections(None, window, cx, |s| {
 3322            s.select_display_ranges([
 3323                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3324                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3325                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3326            ])
 3327        });
 3328        editor.delete_line(&DeleteLine, window, cx);
 3329        assert_eq!(editor.display_text(cx), "ghi");
 3330        assert_eq!(
 3331            editor.selections.display_ranges(cx),
 3332            vec![
 3333                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3334                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3335            ]
 3336        );
 3337    });
 3338
 3339    let editor = cx.add_window(|window, cx| {
 3340        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3341        build_editor(buffer, window, cx)
 3342    });
 3343    _ = editor.update(cx, |editor, window, cx| {
 3344        editor.change_selections(None, window, cx, |s| {
 3345            s.select_display_ranges([
 3346                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3347            ])
 3348        });
 3349        editor.delete_line(&DeleteLine, window, cx);
 3350        assert_eq!(editor.display_text(cx), "ghi\n");
 3351        assert_eq!(
 3352            editor.selections.display_ranges(cx),
 3353            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3354        );
 3355    });
 3356}
 3357
 3358#[gpui::test]
 3359fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3360    init_test(cx, |_| {});
 3361
 3362    cx.add_window(|window, cx| {
 3363        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3364        let mut editor = build_editor(buffer.clone(), window, cx);
 3365        let buffer = buffer.read(cx).as_singleton().unwrap();
 3366
 3367        assert_eq!(
 3368            editor.selections.ranges::<Point>(cx),
 3369            &[Point::new(0, 0)..Point::new(0, 0)]
 3370        );
 3371
 3372        // When on single line, replace newline at end by space
 3373        editor.join_lines(&JoinLines, window, cx);
 3374        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3375        assert_eq!(
 3376            editor.selections.ranges::<Point>(cx),
 3377            &[Point::new(0, 3)..Point::new(0, 3)]
 3378        );
 3379
 3380        // When multiple lines are selected, remove newlines that are spanned by the selection
 3381        editor.change_selections(None, window, cx, |s| {
 3382            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3383        });
 3384        editor.join_lines(&JoinLines, window, cx);
 3385        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3386        assert_eq!(
 3387            editor.selections.ranges::<Point>(cx),
 3388            &[Point::new(0, 11)..Point::new(0, 11)]
 3389        );
 3390
 3391        // Undo should be transactional
 3392        editor.undo(&Undo, window, cx);
 3393        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3394        assert_eq!(
 3395            editor.selections.ranges::<Point>(cx),
 3396            &[Point::new(0, 5)..Point::new(2, 2)]
 3397        );
 3398
 3399        // When joining an empty line don't insert a space
 3400        editor.change_selections(None, window, cx, |s| {
 3401            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3402        });
 3403        editor.join_lines(&JoinLines, window, cx);
 3404        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3405        assert_eq!(
 3406            editor.selections.ranges::<Point>(cx),
 3407            [Point::new(2, 3)..Point::new(2, 3)]
 3408        );
 3409
 3410        // We can remove trailing newlines
 3411        editor.join_lines(&JoinLines, window, cx);
 3412        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3413        assert_eq!(
 3414            editor.selections.ranges::<Point>(cx),
 3415            [Point::new(2, 3)..Point::new(2, 3)]
 3416        );
 3417
 3418        // We don't blow up on the last line
 3419        editor.join_lines(&JoinLines, window, cx);
 3420        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3421        assert_eq!(
 3422            editor.selections.ranges::<Point>(cx),
 3423            [Point::new(2, 3)..Point::new(2, 3)]
 3424        );
 3425
 3426        // reset to test indentation
 3427        editor.buffer.update(cx, |buffer, cx| {
 3428            buffer.edit(
 3429                [
 3430                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3431                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3432                ],
 3433                None,
 3434                cx,
 3435            )
 3436        });
 3437
 3438        // We remove any leading spaces
 3439        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3440        editor.change_selections(None, window, cx, |s| {
 3441            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3442        });
 3443        editor.join_lines(&JoinLines, window, cx);
 3444        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3445
 3446        // We don't insert a space for a line containing only spaces
 3447        editor.join_lines(&JoinLines, window, cx);
 3448        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3449
 3450        // We ignore any leading tabs
 3451        editor.join_lines(&JoinLines, window, cx);
 3452        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3453
 3454        editor
 3455    });
 3456}
 3457
 3458#[gpui::test]
 3459fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3460    init_test(cx, |_| {});
 3461
 3462    cx.add_window(|window, cx| {
 3463        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3464        let mut editor = build_editor(buffer.clone(), window, cx);
 3465        let buffer = buffer.read(cx).as_singleton().unwrap();
 3466
 3467        editor.change_selections(None, window, cx, |s| {
 3468            s.select_ranges([
 3469                Point::new(0, 2)..Point::new(1, 1),
 3470                Point::new(1, 2)..Point::new(1, 2),
 3471                Point::new(3, 1)..Point::new(3, 2),
 3472            ])
 3473        });
 3474
 3475        editor.join_lines(&JoinLines, window, cx);
 3476        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3477
 3478        assert_eq!(
 3479            editor.selections.ranges::<Point>(cx),
 3480            [
 3481                Point::new(0, 7)..Point::new(0, 7),
 3482                Point::new(1, 3)..Point::new(1, 3)
 3483            ]
 3484        );
 3485        editor
 3486    });
 3487}
 3488
 3489#[gpui::test]
 3490async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3491    init_test(cx, |_| {});
 3492
 3493    let mut cx = EditorTestContext::new(cx).await;
 3494
 3495    let diff_base = r#"
 3496        Line 0
 3497        Line 1
 3498        Line 2
 3499        Line 3
 3500        "#
 3501    .unindent();
 3502
 3503    cx.set_state(
 3504        &r#"
 3505        ˇLine 0
 3506        Line 1
 3507        Line 2
 3508        Line 3
 3509        "#
 3510        .unindent(),
 3511    );
 3512
 3513    cx.set_head_text(&diff_base);
 3514    executor.run_until_parked();
 3515
 3516    // Join lines
 3517    cx.update_editor(|editor, window, cx| {
 3518        editor.join_lines(&JoinLines, window, cx);
 3519    });
 3520    executor.run_until_parked();
 3521
 3522    cx.assert_editor_state(
 3523        &r#"
 3524        Line 0ˇ Line 1
 3525        Line 2
 3526        Line 3
 3527        "#
 3528        .unindent(),
 3529    );
 3530    // Join again
 3531    cx.update_editor(|editor, window, cx| {
 3532        editor.join_lines(&JoinLines, window, cx);
 3533    });
 3534    executor.run_until_parked();
 3535
 3536    cx.assert_editor_state(
 3537        &r#"
 3538        Line 0 Line 1ˇ Line 2
 3539        Line 3
 3540        "#
 3541        .unindent(),
 3542    );
 3543}
 3544
 3545#[gpui::test]
 3546async fn test_custom_newlines_cause_no_false_positive_diffs(
 3547    executor: BackgroundExecutor,
 3548    cx: &mut TestAppContext,
 3549) {
 3550    init_test(cx, |_| {});
 3551    let mut cx = EditorTestContext::new(cx).await;
 3552    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3553    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3554    executor.run_until_parked();
 3555
 3556    cx.update_editor(|editor, window, cx| {
 3557        let snapshot = editor.snapshot(window, cx);
 3558        assert_eq!(
 3559            snapshot
 3560                .buffer_snapshot
 3561                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3562                .collect::<Vec<_>>(),
 3563            Vec::new(),
 3564            "Should not have any diffs for files with custom newlines"
 3565        );
 3566    });
 3567}
 3568
 3569#[gpui::test]
 3570async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3571    init_test(cx, |_| {});
 3572
 3573    let mut cx = EditorTestContext::new(cx).await;
 3574
 3575    // Test sort_lines_case_insensitive()
 3576    cx.set_state(indoc! {"
 3577        «z
 3578        y
 3579        x
 3580        Z
 3581        Y
 3582        Xˇ»
 3583    "});
 3584    cx.update_editor(|e, window, cx| {
 3585        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3586    });
 3587    cx.assert_editor_state(indoc! {"
 3588        «x
 3589        X
 3590        y
 3591        Y
 3592        z
 3593        Zˇ»
 3594    "});
 3595
 3596    // Test reverse_lines()
 3597    cx.set_state(indoc! {"
 3598        «5
 3599        4
 3600        3
 3601        2
 3602        1ˇ»
 3603    "});
 3604    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3605    cx.assert_editor_state(indoc! {"
 3606        «1
 3607        2
 3608        3
 3609        4
 3610        5ˇ»
 3611    "});
 3612
 3613    // Skip testing shuffle_line()
 3614
 3615    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3616    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3617
 3618    // Don't manipulate when cursor is on single line, but expand the selection
 3619    cx.set_state(indoc! {"
 3620        ddˇdd
 3621        ccc
 3622        bb
 3623        a
 3624    "});
 3625    cx.update_editor(|e, window, cx| {
 3626        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3627    });
 3628    cx.assert_editor_state(indoc! {"
 3629        «ddddˇ»
 3630        ccc
 3631        bb
 3632        a
 3633    "});
 3634
 3635    // Basic manipulate case
 3636    // Start selection moves to column 0
 3637    // End of selection shrinks to fit shorter line
 3638    cx.set_state(indoc! {"
 3639        dd«d
 3640        ccc
 3641        bb
 3642        aaaaaˇ»
 3643    "});
 3644    cx.update_editor(|e, window, cx| {
 3645        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3646    });
 3647    cx.assert_editor_state(indoc! {"
 3648        «aaaaa
 3649        bb
 3650        ccc
 3651        dddˇ»
 3652    "});
 3653
 3654    // Manipulate case with newlines
 3655    cx.set_state(indoc! {"
 3656        dd«d
 3657        ccc
 3658
 3659        bb
 3660        aaaaa
 3661
 3662        ˇ»
 3663    "});
 3664    cx.update_editor(|e, window, cx| {
 3665        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3666    });
 3667    cx.assert_editor_state(indoc! {"
 3668        «
 3669
 3670        aaaaa
 3671        bb
 3672        ccc
 3673        dddˇ»
 3674
 3675    "});
 3676
 3677    // Adding new line
 3678    cx.set_state(indoc! {"
 3679        aa«a
 3680        bbˇ»b
 3681    "});
 3682    cx.update_editor(|e, window, cx| {
 3683        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3684    });
 3685    cx.assert_editor_state(indoc! {"
 3686        «aaa
 3687        bbb
 3688        added_lineˇ»
 3689    "});
 3690
 3691    // Removing line
 3692    cx.set_state(indoc! {"
 3693        aa«a
 3694        bbbˇ»
 3695    "});
 3696    cx.update_editor(|e, window, cx| {
 3697        e.manipulate_lines(window, cx, |lines| {
 3698            lines.pop();
 3699        })
 3700    });
 3701    cx.assert_editor_state(indoc! {"
 3702        «aaaˇ»
 3703    "});
 3704
 3705    // Removing all lines
 3706    cx.set_state(indoc! {"
 3707        aa«a
 3708        bbbˇ»
 3709    "});
 3710    cx.update_editor(|e, window, cx| {
 3711        e.manipulate_lines(window, cx, |lines| {
 3712            lines.drain(..);
 3713        })
 3714    });
 3715    cx.assert_editor_state(indoc! {"
 3716        ˇ
 3717    "});
 3718}
 3719
 3720#[gpui::test]
 3721async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3722    init_test(cx, |_| {});
 3723
 3724    let mut cx = EditorTestContext::new(cx).await;
 3725
 3726    // Consider continuous selection as single selection
 3727    cx.set_state(indoc! {"
 3728        Aaa«aa
 3729        cˇ»c«c
 3730        bb
 3731        aaaˇ»aa
 3732    "});
 3733    cx.update_editor(|e, window, cx| {
 3734        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3735    });
 3736    cx.assert_editor_state(indoc! {"
 3737        «Aaaaa
 3738        ccc
 3739        bb
 3740        aaaaaˇ»
 3741    "});
 3742
 3743    cx.set_state(indoc! {"
 3744        Aaa«aa
 3745        cˇ»c«c
 3746        bb
 3747        aaaˇ»aa
 3748    "});
 3749    cx.update_editor(|e, window, cx| {
 3750        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3751    });
 3752    cx.assert_editor_state(indoc! {"
 3753        «Aaaaa
 3754        ccc
 3755        bbˇ»
 3756    "});
 3757
 3758    // Consider non continuous selection as distinct dedup operations
 3759    cx.set_state(indoc! {"
 3760        «aaaaa
 3761        bb
 3762        aaaaa
 3763        aaaaaˇ»
 3764
 3765        aaa«aaˇ»
 3766    "});
 3767    cx.update_editor(|e, window, cx| {
 3768        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3769    });
 3770    cx.assert_editor_state(indoc! {"
 3771        «aaaaa
 3772        bbˇ»
 3773
 3774        «aaaaaˇ»
 3775    "});
 3776}
 3777
 3778#[gpui::test]
 3779async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3780    init_test(cx, |_| {});
 3781
 3782    let mut cx = EditorTestContext::new(cx).await;
 3783
 3784    cx.set_state(indoc! {"
 3785        «Aaa
 3786        aAa
 3787        Aaaˇ»
 3788    "});
 3789    cx.update_editor(|e, window, cx| {
 3790        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3791    });
 3792    cx.assert_editor_state(indoc! {"
 3793        «Aaa
 3794        aAaˇ»
 3795    "});
 3796
 3797    cx.set_state(indoc! {"
 3798        «Aaa
 3799        aAa
 3800        aaAˇ»
 3801    "});
 3802    cx.update_editor(|e, window, cx| {
 3803        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3804    });
 3805    cx.assert_editor_state(indoc! {"
 3806        «Aaaˇ»
 3807    "});
 3808}
 3809
 3810#[gpui::test]
 3811async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3812    init_test(cx, |_| {});
 3813
 3814    let mut cx = EditorTestContext::new(cx).await;
 3815
 3816    // Manipulate with multiple selections on a single line
 3817    cx.set_state(indoc! {"
 3818        dd«dd
 3819        cˇ»c«c
 3820        bb
 3821        aaaˇ»aa
 3822    "});
 3823    cx.update_editor(|e, window, cx| {
 3824        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3825    });
 3826    cx.assert_editor_state(indoc! {"
 3827        «aaaaa
 3828        bb
 3829        ccc
 3830        ddddˇ»
 3831    "});
 3832
 3833    // Manipulate with multiple disjoin selections
 3834    cx.set_state(indoc! {"
 3835 3836        4
 3837        3
 3838        2
 3839        1ˇ»
 3840
 3841        dd«dd
 3842        ccc
 3843        bb
 3844        aaaˇ»aa
 3845    "});
 3846    cx.update_editor(|e, window, cx| {
 3847        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3848    });
 3849    cx.assert_editor_state(indoc! {"
 3850        «1
 3851        2
 3852        3
 3853        4
 3854        5ˇ»
 3855
 3856        «aaaaa
 3857        bb
 3858        ccc
 3859        ddddˇ»
 3860    "});
 3861
 3862    // Adding lines on each selection
 3863    cx.set_state(indoc! {"
 3864 3865        1ˇ»
 3866
 3867        bb«bb
 3868        aaaˇ»aa
 3869    "});
 3870    cx.update_editor(|e, window, cx| {
 3871        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3872    });
 3873    cx.assert_editor_state(indoc! {"
 3874        «2
 3875        1
 3876        added lineˇ»
 3877
 3878        «bbbb
 3879        aaaaa
 3880        added lineˇ»
 3881    "});
 3882
 3883    // Removing lines on each selection
 3884    cx.set_state(indoc! {"
 3885 3886        1ˇ»
 3887
 3888        bb«bb
 3889        aaaˇ»aa
 3890    "});
 3891    cx.update_editor(|e, window, cx| {
 3892        e.manipulate_lines(window, cx, |lines| {
 3893            lines.pop();
 3894        })
 3895    });
 3896    cx.assert_editor_state(indoc! {"
 3897        «2ˇ»
 3898
 3899        «bbbbˇ»
 3900    "});
 3901}
 3902
 3903#[gpui::test]
 3904async fn test_toggle_case(cx: &mut TestAppContext) {
 3905    init_test(cx, |_| {});
 3906
 3907    let mut cx = EditorTestContext::new(cx).await;
 3908
 3909    // If all lower case -> upper case
 3910    cx.set_state(indoc! {"
 3911        «hello worldˇ»
 3912    "});
 3913    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3914    cx.assert_editor_state(indoc! {"
 3915        «HELLO WORLDˇ»
 3916    "});
 3917
 3918    // If all upper case -> lower case
 3919    cx.set_state(indoc! {"
 3920        «HELLO WORLDˇ»
 3921    "});
 3922    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3923    cx.assert_editor_state(indoc! {"
 3924        «hello worldˇ»
 3925    "});
 3926
 3927    // If any upper case characters are identified -> lower case
 3928    // This matches JetBrains IDEs
 3929    cx.set_state(indoc! {"
 3930        «hEllo worldˇ»
 3931    "});
 3932    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3933    cx.assert_editor_state(indoc! {"
 3934        «hello worldˇ»
 3935    "});
 3936}
 3937
 3938#[gpui::test]
 3939async fn test_manipulate_text(cx: &mut TestAppContext) {
 3940    init_test(cx, |_| {});
 3941
 3942    let mut cx = EditorTestContext::new(cx).await;
 3943
 3944    // Test convert_to_upper_case()
 3945    cx.set_state(indoc! {"
 3946        «hello worldˇ»
 3947    "});
 3948    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3949    cx.assert_editor_state(indoc! {"
 3950        «HELLO WORLDˇ»
 3951    "});
 3952
 3953    // Test convert_to_lower_case()
 3954    cx.set_state(indoc! {"
 3955        «HELLO WORLDˇ»
 3956    "});
 3957    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3958    cx.assert_editor_state(indoc! {"
 3959        «hello worldˇ»
 3960    "});
 3961
 3962    // Test multiple line, single selection case
 3963    cx.set_state(indoc! {"
 3964        «The quick brown
 3965        fox jumps over
 3966        the lazy dogˇ»
 3967    "});
 3968    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3969    cx.assert_editor_state(indoc! {"
 3970        «The Quick Brown
 3971        Fox Jumps Over
 3972        The Lazy Dogˇ»
 3973    "});
 3974
 3975    // Test multiple line, single selection case
 3976    cx.set_state(indoc! {"
 3977        «The quick brown
 3978        fox jumps over
 3979        the lazy dogˇ»
 3980    "});
 3981    cx.update_editor(|e, window, cx| {
 3982        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3983    });
 3984    cx.assert_editor_state(indoc! {"
 3985        «TheQuickBrown
 3986        FoxJumpsOver
 3987        TheLazyDogˇ»
 3988    "});
 3989
 3990    // From here on out, test more complex cases of manipulate_text()
 3991
 3992    // Test no selection case - should affect words cursors are in
 3993    // Cursor at beginning, middle, and end of word
 3994    cx.set_state(indoc! {"
 3995        ˇhello big beauˇtiful worldˇ
 3996    "});
 3997    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3998    cx.assert_editor_state(indoc! {"
 3999        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 4000    "});
 4001
 4002    // Test multiple selections on a single line and across multiple lines
 4003    cx.set_state(indoc! {"
 4004        «Theˇ» quick «brown
 4005        foxˇ» jumps «overˇ»
 4006        the «lazyˇ» dog
 4007    "});
 4008    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4009    cx.assert_editor_state(indoc! {"
 4010        «THEˇ» quick «BROWN
 4011        FOXˇ» jumps «OVERˇ»
 4012        the «LAZYˇ» dog
 4013    "});
 4014
 4015    // Test case where text length grows
 4016    cx.set_state(indoc! {"
 4017        «tschüߡ»
 4018    "});
 4019    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4020    cx.assert_editor_state(indoc! {"
 4021        «TSCHÜSSˇ»
 4022    "});
 4023
 4024    // Test to make sure we don't crash when text shrinks
 4025    cx.set_state(indoc! {"
 4026        aaa_bbbˇ
 4027    "});
 4028    cx.update_editor(|e, window, cx| {
 4029        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4030    });
 4031    cx.assert_editor_state(indoc! {"
 4032        «aaaBbbˇ»
 4033    "});
 4034
 4035    // Test to make sure we all aware of the fact that each word can grow and shrink
 4036    // Final selections should be aware of this fact
 4037    cx.set_state(indoc! {"
 4038        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4039    "});
 4040    cx.update_editor(|e, window, cx| {
 4041        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4042    });
 4043    cx.assert_editor_state(indoc! {"
 4044        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4045    "});
 4046
 4047    cx.set_state(indoc! {"
 4048        «hElLo, WoRld!ˇ»
 4049    "});
 4050    cx.update_editor(|e, window, cx| {
 4051        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4052    });
 4053    cx.assert_editor_state(indoc! {"
 4054        «HeLlO, wOrLD!ˇ»
 4055    "});
 4056}
 4057
 4058#[gpui::test]
 4059fn test_duplicate_line(cx: &mut TestAppContext) {
 4060    init_test(cx, |_| {});
 4061
 4062    let editor = cx.add_window(|window, cx| {
 4063        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4064        build_editor(buffer, window, cx)
 4065    });
 4066    _ = editor.update(cx, |editor, window, cx| {
 4067        editor.change_selections(None, window, cx, |s| {
 4068            s.select_display_ranges([
 4069                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4070                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4071                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4072                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4073            ])
 4074        });
 4075        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4076        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4077        assert_eq!(
 4078            editor.selections.display_ranges(cx),
 4079            vec![
 4080                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4081                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4082                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4083                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4084            ]
 4085        );
 4086    });
 4087
 4088    let editor = cx.add_window(|window, cx| {
 4089        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4090        build_editor(buffer, window, cx)
 4091    });
 4092    _ = editor.update(cx, |editor, window, cx| {
 4093        editor.change_selections(None, window, cx, |s| {
 4094            s.select_display_ranges([
 4095                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4096                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4097            ])
 4098        });
 4099        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4100        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4101        assert_eq!(
 4102            editor.selections.display_ranges(cx),
 4103            vec![
 4104                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4105                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4106            ]
 4107        );
 4108    });
 4109
 4110    // With `move_upwards` the selections stay in place, except for
 4111    // the lines inserted above them
 4112    let editor = cx.add_window(|window, cx| {
 4113        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4114        build_editor(buffer, window, cx)
 4115    });
 4116    _ = editor.update(cx, |editor, window, cx| {
 4117        editor.change_selections(None, window, cx, |s| {
 4118            s.select_display_ranges([
 4119                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4120                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4121                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4122                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4123            ])
 4124        });
 4125        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4126        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4127        assert_eq!(
 4128            editor.selections.display_ranges(cx),
 4129            vec![
 4130                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4131                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4132                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4133                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4134            ]
 4135        );
 4136    });
 4137
 4138    let editor = cx.add_window(|window, cx| {
 4139        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4140        build_editor(buffer, window, cx)
 4141    });
 4142    _ = editor.update(cx, |editor, window, cx| {
 4143        editor.change_selections(None, window, cx, |s| {
 4144            s.select_display_ranges([
 4145                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4146                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4147            ])
 4148        });
 4149        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4150        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4151        assert_eq!(
 4152            editor.selections.display_ranges(cx),
 4153            vec![
 4154                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4155                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4156            ]
 4157        );
 4158    });
 4159
 4160    let editor = cx.add_window(|window, cx| {
 4161        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4162        build_editor(buffer, window, cx)
 4163    });
 4164    _ = editor.update(cx, |editor, window, cx| {
 4165        editor.change_selections(None, window, cx, |s| {
 4166            s.select_display_ranges([
 4167                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4168                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4169            ])
 4170        });
 4171        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4172        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4173        assert_eq!(
 4174            editor.selections.display_ranges(cx),
 4175            vec![
 4176                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4177                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4178            ]
 4179        );
 4180    });
 4181}
 4182
 4183#[gpui::test]
 4184fn test_move_line_up_down(cx: &mut TestAppContext) {
 4185    init_test(cx, |_| {});
 4186
 4187    let editor = cx.add_window(|window, cx| {
 4188        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4189        build_editor(buffer, window, cx)
 4190    });
 4191    _ = editor.update(cx, |editor, window, cx| {
 4192        editor.fold_creases(
 4193            vec![
 4194                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4195                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4196                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4197            ],
 4198            true,
 4199            window,
 4200            cx,
 4201        );
 4202        editor.change_selections(None, window, cx, |s| {
 4203            s.select_display_ranges([
 4204                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4205                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4206                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4207                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4208            ])
 4209        });
 4210        assert_eq!(
 4211            editor.display_text(cx),
 4212            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4213        );
 4214
 4215        editor.move_line_up(&MoveLineUp, window, cx);
 4216        assert_eq!(
 4217            editor.display_text(cx),
 4218            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4219        );
 4220        assert_eq!(
 4221            editor.selections.display_ranges(cx),
 4222            vec![
 4223                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4224                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4225                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4226                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4227            ]
 4228        );
 4229    });
 4230
 4231    _ = editor.update(cx, |editor, window, cx| {
 4232        editor.move_line_down(&MoveLineDown, window, cx);
 4233        assert_eq!(
 4234            editor.display_text(cx),
 4235            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4236        );
 4237        assert_eq!(
 4238            editor.selections.display_ranges(cx),
 4239            vec![
 4240                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4241                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4242                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4243                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4244            ]
 4245        );
 4246    });
 4247
 4248    _ = editor.update(cx, |editor, window, cx| {
 4249        editor.move_line_down(&MoveLineDown, window, cx);
 4250        assert_eq!(
 4251            editor.display_text(cx),
 4252            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4253        );
 4254        assert_eq!(
 4255            editor.selections.display_ranges(cx),
 4256            vec![
 4257                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4258                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4259                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4260                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4261            ]
 4262        );
 4263    });
 4264
 4265    _ = editor.update(cx, |editor, window, cx| {
 4266        editor.move_line_up(&MoveLineUp, window, cx);
 4267        assert_eq!(
 4268            editor.display_text(cx),
 4269            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4270        );
 4271        assert_eq!(
 4272            editor.selections.display_ranges(cx),
 4273            vec![
 4274                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4275                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4276                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4277                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4278            ]
 4279        );
 4280    });
 4281}
 4282
 4283#[gpui::test]
 4284fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4285    init_test(cx, |_| {});
 4286
 4287    let editor = cx.add_window(|window, cx| {
 4288        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4289        build_editor(buffer, window, cx)
 4290    });
 4291    _ = editor.update(cx, |editor, window, cx| {
 4292        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4293        editor.insert_blocks(
 4294            [BlockProperties {
 4295                style: BlockStyle::Fixed,
 4296                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4297                height: Some(1),
 4298                render: Arc::new(|_| div().into_any()),
 4299                priority: 0,
 4300            }],
 4301            Some(Autoscroll::fit()),
 4302            cx,
 4303        );
 4304        editor.change_selections(None, window, cx, |s| {
 4305            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4306        });
 4307        editor.move_line_down(&MoveLineDown, window, cx);
 4308    });
 4309}
 4310
 4311#[gpui::test]
 4312async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4313    init_test(cx, |_| {});
 4314
 4315    let mut cx = EditorTestContext::new(cx).await;
 4316    cx.set_state(
 4317        &"
 4318            ˇzero
 4319            one
 4320            two
 4321            three
 4322            four
 4323            five
 4324        "
 4325        .unindent(),
 4326    );
 4327
 4328    // Create a four-line block that replaces three lines of text.
 4329    cx.update_editor(|editor, window, cx| {
 4330        let snapshot = editor.snapshot(window, cx);
 4331        let snapshot = &snapshot.buffer_snapshot;
 4332        let placement = BlockPlacement::Replace(
 4333            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4334        );
 4335        editor.insert_blocks(
 4336            [BlockProperties {
 4337                placement,
 4338                height: Some(4),
 4339                style: BlockStyle::Sticky,
 4340                render: Arc::new(|_| gpui::div().into_any_element()),
 4341                priority: 0,
 4342            }],
 4343            None,
 4344            cx,
 4345        );
 4346    });
 4347
 4348    // Move down so that the cursor touches the block.
 4349    cx.update_editor(|editor, window, cx| {
 4350        editor.move_down(&Default::default(), window, cx);
 4351    });
 4352    cx.assert_editor_state(
 4353        &"
 4354            zero
 4355            «one
 4356            two
 4357            threeˇ»
 4358            four
 4359            five
 4360        "
 4361        .unindent(),
 4362    );
 4363
 4364    // Move down past the block.
 4365    cx.update_editor(|editor, window, cx| {
 4366        editor.move_down(&Default::default(), window, cx);
 4367    });
 4368    cx.assert_editor_state(
 4369        &"
 4370            zero
 4371            one
 4372            two
 4373            three
 4374            ˇfour
 4375            five
 4376        "
 4377        .unindent(),
 4378    );
 4379}
 4380
 4381#[gpui::test]
 4382fn test_transpose(cx: &mut TestAppContext) {
 4383    init_test(cx, |_| {});
 4384
 4385    _ = cx.add_window(|window, cx| {
 4386        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4387        editor.set_style(EditorStyle::default(), window, cx);
 4388        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4389        editor.transpose(&Default::default(), window, cx);
 4390        assert_eq!(editor.text(cx), "bac");
 4391        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4392
 4393        editor.transpose(&Default::default(), window, cx);
 4394        assert_eq!(editor.text(cx), "bca");
 4395        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4396
 4397        editor.transpose(&Default::default(), window, cx);
 4398        assert_eq!(editor.text(cx), "bac");
 4399        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4400
 4401        editor
 4402    });
 4403
 4404    _ = cx.add_window(|window, cx| {
 4405        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4406        editor.set_style(EditorStyle::default(), window, cx);
 4407        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4408        editor.transpose(&Default::default(), window, cx);
 4409        assert_eq!(editor.text(cx), "acb\nde");
 4410        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4411
 4412        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4413        editor.transpose(&Default::default(), window, cx);
 4414        assert_eq!(editor.text(cx), "acbd\ne");
 4415        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4416
 4417        editor.transpose(&Default::default(), window, cx);
 4418        assert_eq!(editor.text(cx), "acbde\n");
 4419        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4420
 4421        editor.transpose(&Default::default(), window, cx);
 4422        assert_eq!(editor.text(cx), "acbd\ne");
 4423        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4424
 4425        editor
 4426    });
 4427
 4428    _ = cx.add_window(|window, cx| {
 4429        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4430        editor.set_style(EditorStyle::default(), window, cx);
 4431        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4432        editor.transpose(&Default::default(), window, cx);
 4433        assert_eq!(editor.text(cx), "bacd\ne");
 4434        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4435
 4436        editor.transpose(&Default::default(), window, cx);
 4437        assert_eq!(editor.text(cx), "bcade\n");
 4438        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4439
 4440        editor.transpose(&Default::default(), window, cx);
 4441        assert_eq!(editor.text(cx), "bcda\ne");
 4442        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4443
 4444        editor.transpose(&Default::default(), window, cx);
 4445        assert_eq!(editor.text(cx), "bcade\n");
 4446        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4447
 4448        editor.transpose(&Default::default(), window, cx);
 4449        assert_eq!(editor.text(cx), "bcaed\n");
 4450        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4451
 4452        editor
 4453    });
 4454
 4455    _ = cx.add_window(|window, cx| {
 4456        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4457        editor.set_style(EditorStyle::default(), window, cx);
 4458        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4459        editor.transpose(&Default::default(), window, cx);
 4460        assert_eq!(editor.text(cx), "🏀🍐✋");
 4461        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4462
 4463        editor.transpose(&Default::default(), window, cx);
 4464        assert_eq!(editor.text(cx), "🏀✋🍐");
 4465        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4466
 4467        editor.transpose(&Default::default(), window, cx);
 4468        assert_eq!(editor.text(cx), "🏀🍐✋");
 4469        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4470
 4471        editor
 4472    });
 4473}
 4474
 4475#[gpui::test]
 4476async fn test_rewrap(cx: &mut TestAppContext) {
 4477    init_test(cx, |settings| {
 4478        settings.languages.extend([
 4479            (
 4480                "Markdown".into(),
 4481                LanguageSettingsContent {
 4482                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4483                    ..Default::default()
 4484                },
 4485            ),
 4486            (
 4487                "Plain Text".into(),
 4488                LanguageSettingsContent {
 4489                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4490                    ..Default::default()
 4491                },
 4492            ),
 4493        ])
 4494    });
 4495
 4496    let mut cx = EditorTestContext::new(cx).await;
 4497
 4498    let language_with_c_comments = Arc::new(Language::new(
 4499        LanguageConfig {
 4500            line_comments: vec!["// ".into()],
 4501            ..LanguageConfig::default()
 4502        },
 4503        None,
 4504    ));
 4505    let language_with_pound_comments = Arc::new(Language::new(
 4506        LanguageConfig {
 4507            line_comments: vec!["# ".into()],
 4508            ..LanguageConfig::default()
 4509        },
 4510        None,
 4511    ));
 4512    let markdown_language = Arc::new(Language::new(
 4513        LanguageConfig {
 4514            name: "Markdown".into(),
 4515            ..LanguageConfig::default()
 4516        },
 4517        None,
 4518    ));
 4519    let language_with_doc_comments = Arc::new(Language::new(
 4520        LanguageConfig {
 4521            line_comments: vec!["// ".into(), "/// ".into()],
 4522            ..LanguageConfig::default()
 4523        },
 4524        Some(tree_sitter_rust::LANGUAGE.into()),
 4525    ));
 4526
 4527    let plaintext_language = Arc::new(Language::new(
 4528        LanguageConfig {
 4529            name: "Plain Text".into(),
 4530            ..LanguageConfig::default()
 4531        },
 4532        None,
 4533    ));
 4534
 4535    assert_rewrap(
 4536        indoc! {"
 4537            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4538        "},
 4539        indoc! {"
 4540            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4541            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4542            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4543            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4544            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4545            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4546            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4547            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4548            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4549            // porttitor id. Aliquam id accumsan eros.
 4550        "},
 4551        language_with_c_comments.clone(),
 4552        &mut cx,
 4553    );
 4554
 4555    // Test that rewrapping works inside of a selection
 4556    assert_rewrap(
 4557        indoc! {"
 4558            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ»
 4559        "},
 4560        indoc! {"
 4561            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4562            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4563            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4564            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4565            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4566            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4567            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4568            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4569            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4570            // porttitor id. Aliquam id accumsan eros.ˇ»
 4571        "},
 4572        language_with_c_comments.clone(),
 4573        &mut cx,
 4574    );
 4575
 4576    // Test that cursors that expand to the same region are collapsed.
 4577    assert_rewrap(
 4578        indoc! {"
 4579            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4580            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4581            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4582            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4583        "},
 4584        indoc! {"
 4585            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4586            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4587            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4588            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4589            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4590            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4591            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4592            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4593            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4594            // porttitor id. Aliquam id accumsan eros.
 4595        "},
 4596        language_with_c_comments.clone(),
 4597        &mut cx,
 4598    );
 4599
 4600    // Test that non-contiguous selections are treated separately.
 4601    assert_rewrap(
 4602        indoc! {"
 4603            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4604            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4605            //
 4606            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4607            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4608        "},
 4609        indoc! {"
 4610            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4611            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4612            // auctor, eu lacinia sapien scelerisque.
 4613            //
 4614            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4615            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4616            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4617            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4618            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4619            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4620            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4621        "},
 4622        language_with_c_comments.clone(),
 4623        &mut cx,
 4624    );
 4625
 4626    // Test that different comment prefixes are supported.
 4627    assert_rewrap(
 4628        indoc! {"
 4629            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id accumsan eros.
 4630        "},
 4631        indoc! {"
 4632            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4633            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4634            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4635            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4636            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4637            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4638            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4639            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4640            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4641            # accumsan eros.
 4642        "},
 4643        language_with_pound_comments.clone(),
 4644        &mut cx,
 4645    );
 4646
 4647    // Test that rewrapping is ignored outside of comments in most languages.
 4648    assert_rewrap(
 4649        indoc! {"
 4650            /// Adds two numbers.
 4651            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4652            fn add(a: u32, b: u32) -> u32 {
 4653                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4654            }
 4655        "},
 4656        indoc! {"
 4657            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4658            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4659            fn add(a: u32, b: u32) -> u32 {
 4660                a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + b + a + bˇ
 4661            }
 4662        "},
 4663        language_with_doc_comments.clone(),
 4664        &mut cx,
 4665    );
 4666
 4667    // Test that rewrapping works in Markdown and Plain Text languages.
 4668    assert_rewrap(
 4669        indoc! {"
 4670            # Hello
 4671
 4672            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4673        "},
 4674        indoc! {"
 4675            # Hello
 4676
 4677            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4678            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4679            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4680            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4681            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4682            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4683            Integer sit amet scelerisque nisi.
 4684        "},
 4685        markdown_language,
 4686        &mut cx,
 4687    );
 4688
 4689    assert_rewrap(
 4690        indoc! {"
 4691            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque nisi.
 4692        "},
 4693        indoc! {"
 4694            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4695            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4696            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4697            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4698            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4699            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4700            Integer sit amet scelerisque nisi.
 4701        "},
 4702        plaintext_language,
 4703        &mut cx,
 4704    );
 4705
 4706    // Test rewrapping unaligned comments in a selection.
 4707    assert_rewrap(
 4708        indoc! {"
 4709            fn foo() {
 4710                if true {
 4711            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4712            // Praesent semper egestas tellus id dignissim.ˇ»
 4713                    do_something();
 4714                } else {
 4715                    //
 4716                }
 4717            }
 4718        "},
 4719        indoc! {"
 4720            fn foo() {
 4721                if true {
 4722            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4723                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4724                    // egestas tellus id dignissim.ˇ»
 4725                    do_something();
 4726                } else {
 4727                    //
 4728                }
 4729            }
 4730        "},
 4731        language_with_doc_comments.clone(),
 4732        &mut cx,
 4733    );
 4734
 4735    assert_rewrap(
 4736        indoc! {"
 4737            fn foo() {
 4738                if true {
 4739            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4740            // Praesent semper egestas tellus id dignissim.»
 4741                    do_something();
 4742                } else {
 4743                    //
 4744                }
 4745
 4746            }
 4747        "},
 4748        indoc! {"
 4749            fn foo() {
 4750                if true {
 4751            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4752                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4753                    // egestas tellus id dignissim.»
 4754                    do_something();
 4755                } else {
 4756                    //
 4757                }
 4758
 4759            }
 4760        "},
 4761        language_with_doc_comments.clone(),
 4762        &mut cx,
 4763    );
 4764
 4765    #[track_caller]
 4766    fn assert_rewrap(
 4767        unwrapped_text: &str,
 4768        wrapped_text: &str,
 4769        language: Arc<Language>,
 4770        cx: &mut EditorTestContext,
 4771    ) {
 4772        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4773        cx.set_state(unwrapped_text);
 4774        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4775        cx.assert_editor_state(wrapped_text);
 4776    }
 4777}
 4778
 4779#[gpui::test]
 4780async fn test_hard_wrap(cx: &mut TestAppContext) {
 4781    init_test(cx, |_| {});
 4782    let mut cx = EditorTestContext::new(cx).await;
 4783
 4784    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4785    cx.update_editor(|editor, _, cx| {
 4786        editor.set_hard_wrap(Some(14), cx);
 4787    });
 4788
 4789    cx.set_state(indoc!(
 4790        "
 4791        one two three ˇ
 4792        "
 4793    ));
 4794    cx.simulate_input("four");
 4795    cx.run_until_parked();
 4796
 4797    cx.assert_editor_state(indoc!(
 4798        "
 4799        one two three
 4800        fourˇ
 4801        "
 4802    ));
 4803
 4804    cx.update_editor(|editor, window, cx| {
 4805        editor.newline(&Default::default(), window, cx);
 4806    });
 4807    cx.run_until_parked();
 4808    cx.assert_editor_state(indoc!(
 4809        "
 4810        one two three
 4811        four
 4812        ˇ
 4813        "
 4814    ));
 4815
 4816    cx.simulate_input("five");
 4817    cx.run_until_parked();
 4818    cx.assert_editor_state(indoc!(
 4819        "
 4820        one two three
 4821        four
 4822        fiveˇ
 4823        "
 4824    ));
 4825
 4826    cx.update_editor(|editor, window, cx| {
 4827        editor.newline(&Default::default(), window, cx);
 4828    });
 4829    cx.run_until_parked();
 4830    cx.simulate_input("# ");
 4831    cx.run_until_parked();
 4832    cx.assert_editor_state(indoc!(
 4833        "
 4834        one two three
 4835        four
 4836        five
 4837        # ˇ
 4838        "
 4839    ));
 4840
 4841    cx.update_editor(|editor, window, cx| {
 4842        editor.newline(&Default::default(), window, cx);
 4843    });
 4844    cx.run_until_parked();
 4845    cx.assert_editor_state(indoc!(
 4846        "
 4847        one two three
 4848        four
 4849        five
 4850        #\x20
 4851 4852        "
 4853    ));
 4854
 4855    cx.simulate_input(" 6");
 4856    cx.run_until_parked();
 4857    cx.assert_editor_state(indoc!(
 4858        "
 4859        one two three
 4860        four
 4861        five
 4862        #
 4863        # 6ˇ
 4864        "
 4865    ));
 4866}
 4867
 4868#[gpui::test]
 4869async fn test_clipboard(cx: &mut TestAppContext) {
 4870    init_test(cx, |_| {});
 4871
 4872    let mut cx = EditorTestContext::new(cx).await;
 4873
 4874    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4875    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4876    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4877
 4878    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4879    cx.set_state("two ˇfour ˇsix ˇ");
 4880    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4881    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4882
 4883    // Paste again but with only two cursors. Since the number of cursors doesn't
 4884    // match the number of slices in the clipboard, the entire clipboard text
 4885    // is pasted at each cursor.
 4886    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4887    cx.update_editor(|e, window, cx| {
 4888        e.handle_input("( ", window, cx);
 4889        e.paste(&Paste, window, cx);
 4890        e.handle_input(") ", window, cx);
 4891    });
 4892    cx.assert_editor_state(
 4893        &([
 4894            "( one✅ ",
 4895            "three ",
 4896            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4897            "three ",
 4898            "five ) ˇ",
 4899        ]
 4900        .join("\n")),
 4901    );
 4902
 4903    // Cut with three selections, one of which is full-line.
 4904    cx.set_state(indoc! {"
 4905        1«2ˇ»3
 4906        4ˇ567
 4907        «8ˇ»9"});
 4908    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4909    cx.assert_editor_state(indoc! {"
 4910        1ˇ3
 4911        ˇ9"});
 4912
 4913    // Paste with three selections, noticing how the copied selection that was full-line
 4914    // gets inserted before the second cursor.
 4915    cx.set_state(indoc! {"
 4916        1ˇ3
 4917 4918        «oˇ»ne"});
 4919    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4920    cx.assert_editor_state(indoc! {"
 4921        12ˇ3
 4922        4567
 4923 4924        8ˇne"});
 4925
 4926    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4927    cx.set_state(indoc! {"
 4928        The quick brown
 4929        fox juˇmps over
 4930        the lazy dog"});
 4931    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4932    assert_eq!(
 4933        cx.read_from_clipboard()
 4934            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4935        Some("fox jumps over\n".to_string())
 4936    );
 4937
 4938    // Paste with three selections, noticing how the copied full-line selection is inserted
 4939    // before the empty selections but replaces the selection that is non-empty.
 4940    cx.set_state(indoc! {"
 4941        Tˇhe quick brown
 4942        «foˇ»x jumps over
 4943        tˇhe lazy dog"});
 4944    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4945    cx.assert_editor_state(indoc! {"
 4946        fox jumps over
 4947        Tˇhe quick brown
 4948        fox jumps over
 4949        ˇx jumps over
 4950        fox jumps over
 4951        tˇhe lazy dog"});
 4952}
 4953
 4954#[gpui::test]
 4955async fn test_copy_trim(cx: &mut TestAppContext) {
 4956    init_test(cx, |_| {});
 4957
 4958    let mut cx = EditorTestContext::new(cx).await;
 4959    cx.set_state(
 4960        r#"            «for selection in selections.iter() {
 4961            let mut start = selection.start;
 4962            let mut end = selection.end;
 4963            let is_entire_line = selection.is_empty();
 4964            if is_entire_line {
 4965                start = Point::new(start.row, 0);ˇ»
 4966                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4967            }
 4968        "#,
 4969    );
 4970    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4971    assert_eq!(
 4972        cx.read_from_clipboard()
 4973            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4974        Some(
 4975            "for selection in selections.iter() {
 4976            let mut start = selection.start;
 4977            let mut end = selection.end;
 4978            let is_entire_line = selection.is_empty();
 4979            if is_entire_line {
 4980                start = Point::new(start.row, 0);"
 4981                .to_string()
 4982        ),
 4983        "Regular copying preserves all indentation selected",
 4984    );
 4985    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4986    assert_eq!(
 4987        cx.read_from_clipboard()
 4988            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4989        Some(
 4990            "for selection in selections.iter() {
 4991let mut start = selection.start;
 4992let mut end = selection.end;
 4993let is_entire_line = selection.is_empty();
 4994if is_entire_line {
 4995    start = Point::new(start.row, 0);"
 4996                .to_string()
 4997        ),
 4998        "Copying with stripping should strip all leading whitespaces"
 4999    );
 5000
 5001    cx.set_state(
 5002        r#"       «     for selection in selections.iter() {
 5003            let mut start = selection.start;
 5004            let mut end = selection.end;
 5005            let is_entire_line = selection.is_empty();
 5006            if is_entire_line {
 5007                start = Point::new(start.row, 0);ˇ»
 5008                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5009            }
 5010        "#,
 5011    );
 5012    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5013    assert_eq!(
 5014        cx.read_from_clipboard()
 5015            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5016        Some(
 5017            "     for selection in selections.iter() {
 5018            let mut start = selection.start;
 5019            let mut end = selection.end;
 5020            let is_entire_line = selection.is_empty();
 5021            if is_entire_line {
 5022                start = Point::new(start.row, 0);"
 5023                .to_string()
 5024        ),
 5025        "Regular copying preserves all indentation selected",
 5026    );
 5027    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5028    assert_eq!(
 5029        cx.read_from_clipboard()
 5030            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5031        Some(
 5032            "for selection in selections.iter() {
 5033let mut start = selection.start;
 5034let mut end = selection.end;
 5035let is_entire_line = selection.is_empty();
 5036if is_entire_line {
 5037    start = Point::new(start.row, 0);"
 5038                .to_string()
 5039        ),
 5040        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5041    );
 5042
 5043    cx.set_state(
 5044        r#"       «ˇ     for selection in selections.iter() {
 5045            let mut start = selection.start;
 5046            let mut end = selection.end;
 5047            let is_entire_line = selection.is_empty();
 5048            if is_entire_line {
 5049                start = Point::new(start.row, 0);»
 5050                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5051            }
 5052        "#,
 5053    );
 5054    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5055    assert_eq!(
 5056        cx.read_from_clipboard()
 5057            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5058        Some(
 5059            "     for selection in selections.iter() {
 5060            let mut start = selection.start;
 5061            let mut end = selection.end;
 5062            let is_entire_line = selection.is_empty();
 5063            if is_entire_line {
 5064                start = Point::new(start.row, 0);"
 5065                .to_string()
 5066        ),
 5067        "Regular copying for reverse selection works the same",
 5068    );
 5069    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5070    assert_eq!(
 5071        cx.read_from_clipboard()
 5072            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5073        Some(
 5074            "for selection in selections.iter() {
 5075let mut start = selection.start;
 5076let mut end = selection.end;
 5077let is_entire_line = selection.is_empty();
 5078if is_entire_line {
 5079    start = Point::new(start.row, 0);"
 5080                .to_string()
 5081        ),
 5082        "Copying with stripping for reverse selection works the same"
 5083    );
 5084
 5085    cx.set_state(
 5086        r#"            for selection «in selections.iter() {
 5087            let mut start = selection.start;
 5088            let mut end = selection.end;
 5089            let is_entire_line = selection.is_empty();
 5090            if is_entire_line {
 5091                start = Point::new(start.row, 0);ˇ»
 5092                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5093            }
 5094        "#,
 5095    );
 5096    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5097    assert_eq!(
 5098        cx.read_from_clipboard()
 5099            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5100        Some(
 5101            "in selections.iter() {
 5102            let mut start = selection.start;
 5103            let mut end = selection.end;
 5104            let is_entire_line = selection.is_empty();
 5105            if is_entire_line {
 5106                start = Point::new(start.row, 0);"
 5107                .to_string()
 5108        ),
 5109        "When selecting past the indent, the copying works as usual",
 5110    );
 5111    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5112    assert_eq!(
 5113        cx.read_from_clipboard()
 5114            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5115        Some(
 5116            "in selections.iter() {
 5117            let mut start = selection.start;
 5118            let mut end = selection.end;
 5119            let is_entire_line = selection.is_empty();
 5120            if is_entire_line {
 5121                start = Point::new(start.row, 0);"
 5122                .to_string()
 5123        ),
 5124        "When selecting past the indent, nothing is trimmed"
 5125    );
 5126}
 5127
 5128#[gpui::test]
 5129async fn test_paste_multiline(cx: &mut TestAppContext) {
 5130    init_test(cx, |_| {});
 5131
 5132    let mut cx = EditorTestContext::new(cx).await;
 5133    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5134
 5135    // Cut an indented block, without the leading whitespace.
 5136    cx.set_state(indoc! {"
 5137        const a: B = (
 5138            c(),
 5139            «d(
 5140                e,
 5141                f
 5142            )ˇ»
 5143        );
 5144    "});
 5145    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5146    cx.assert_editor_state(indoc! {"
 5147        const a: B = (
 5148            c(),
 5149            ˇ
 5150        );
 5151    "});
 5152
 5153    // Paste it at the same position.
 5154    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5155    cx.assert_editor_state(indoc! {"
 5156        const a: B = (
 5157            c(),
 5158            d(
 5159                e,
 5160                f
 5161 5162        );
 5163    "});
 5164
 5165    // Paste it at a line with a lower indent level.
 5166    cx.set_state(indoc! {"
 5167        ˇ
 5168        const a: B = (
 5169            c(),
 5170        );
 5171    "});
 5172    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5173    cx.assert_editor_state(indoc! {"
 5174        d(
 5175            e,
 5176            f
 5177 5178        const a: B = (
 5179            c(),
 5180        );
 5181    "});
 5182
 5183    // Cut an indented block, with the leading whitespace.
 5184    cx.set_state(indoc! {"
 5185        const a: B = (
 5186            c(),
 5187        «    d(
 5188                e,
 5189                f
 5190            )
 5191        ˇ»);
 5192    "});
 5193    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5194    cx.assert_editor_state(indoc! {"
 5195        const a: B = (
 5196            c(),
 5197        ˇ);
 5198    "});
 5199
 5200    // Paste it at the same position.
 5201    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5202    cx.assert_editor_state(indoc! {"
 5203        const a: B = (
 5204            c(),
 5205            d(
 5206                e,
 5207                f
 5208            )
 5209        ˇ);
 5210    "});
 5211
 5212    // Paste it at a line with a higher indent level.
 5213    cx.set_state(indoc! {"
 5214        const a: B = (
 5215            c(),
 5216            d(
 5217                e,
 5218 5219            )
 5220        );
 5221    "});
 5222    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5223    cx.assert_editor_state(indoc! {"
 5224        const a: B = (
 5225            c(),
 5226            d(
 5227                e,
 5228                f    d(
 5229                    e,
 5230                    f
 5231                )
 5232        ˇ
 5233            )
 5234        );
 5235    "});
 5236
 5237    // Copy an indented block, starting mid-line
 5238    cx.set_state(indoc! {"
 5239        const a: B = (
 5240            c(),
 5241            somethin«g(
 5242                e,
 5243                f
 5244            )ˇ»
 5245        );
 5246    "});
 5247    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5248
 5249    // Paste it on a line with a lower indent level
 5250    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5251    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5252    cx.assert_editor_state(indoc! {"
 5253        const a: B = (
 5254            c(),
 5255            something(
 5256                e,
 5257                f
 5258            )
 5259        );
 5260        g(
 5261            e,
 5262            f
 5263"});
 5264}
 5265
 5266#[gpui::test]
 5267async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5268    init_test(cx, |_| {});
 5269
 5270    cx.write_to_clipboard(ClipboardItem::new_string(
 5271        "    d(\n        e\n    );\n".into(),
 5272    ));
 5273
 5274    let mut cx = EditorTestContext::new(cx).await;
 5275    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5276
 5277    cx.set_state(indoc! {"
 5278        fn a() {
 5279            b();
 5280            if c() {
 5281                ˇ
 5282            }
 5283        }
 5284    "});
 5285
 5286    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5287    cx.assert_editor_state(indoc! {"
 5288        fn a() {
 5289            b();
 5290            if c() {
 5291                d(
 5292                    e
 5293                );
 5294        ˇ
 5295            }
 5296        }
 5297    "});
 5298
 5299    cx.set_state(indoc! {"
 5300        fn a() {
 5301            b();
 5302            ˇ
 5303        }
 5304    "});
 5305
 5306    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5307    cx.assert_editor_state(indoc! {"
 5308        fn a() {
 5309            b();
 5310            d(
 5311                e
 5312            );
 5313        ˇ
 5314        }
 5315    "});
 5316}
 5317
 5318#[gpui::test]
 5319fn test_select_all(cx: &mut TestAppContext) {
 5320    init_test(cx, |_| {});
 5321
 5322    let editor = cx.add_window(|window, cx| {
 5323        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5324        build_editor(buffer, window, cx)
 5325    });
 5326    _ = editor.update(cx, |editor, window, cx| {
 5327        editor.select_all(&SelectAll, window, cx);
 5328        assert_eq!(
 5329            editor.selections.display_ranges(cx),
 5330            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5331        );
 5332    });
 5333}
 5334
 5335#[gpui::test]
 5336fn test_select_line(cx: &mut TestAppContext) {
 5337    init_test(cx, |_| {});
 5338
 5339    let editor = cx.add_window(|window, cx| {
 5340        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5341        build_editor(buffer, window, cx)
 5342    });
 5343    _ = editor.update(cx, |editor, window, cx| {
 5344        editor.change_selections(None, window, cx, |s| {
 5345            s.select_display_ranges([
 5346                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5347                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5348                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5349                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5350            ])
 5351        });
 5352        editor.select_line(&SelectLine, window, cx);
 5353        assert_eq!(
 5354            editor.selections.display_ranges(cx),
 5355            vec![
 5356                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5357                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5358            ]
 5359        );
 5360    });
 5361
 5362    _ = editor.update(cx, |editor, window, cx| {
 5363        editor.select_line(&SelectLine, window, cx);
 5364        assert_eq!(
 5365            editor.selections.display_ranges(cx),
 5366            vec![
 5367                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5368                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5369            ]
 5370        );
 5371    });
 5372
 5373    _ = editor.update(cx, |editor, window, cx| {
 5374        editor.select_line(&SelectLine, window, cx);
 5375        assert_eq!(
 5376            editor.selections.display_ranges(cx),
 5377            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5378        );
 5379    });
 5380}
 5381
 5382#[gpui::test]
 5383async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5384    init_test(cx, |_| {});
 5385    let mut cx = EditorTestContext::new(cx).await;
 5386
 5387    #[track_caller]
 5388    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5389        cx.set_state(initial_state);
 5390        cx.update_editor(|e, window, cx| {
 5391            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5392        });
 5393        cx.assert_editor_state(expected_state);
 5394    }
 5395
 5396    // Selection starts and ends at the middle of lines, left-to-right
 5397    test(
 5398        &mut cx,
 5399        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5400        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5401    );
 5402    // Same thing, right-to-left
 5403    test(
 5404        &mut cx,
 5405        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5406        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5407    );
 5408
 5409    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5410    test(
 5411        &mut cx,
 5412        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5413        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5414    );
 5415    // Same thing, right-to-left
 5416    test(
 5417        &mut cx,
 5418        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5419        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5420    );
 5421
 5422    // Whole buffer, left-to-right, last line ends with newline
 5423    test(
 5424        &mut cx,
 5425        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5426        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5427    );
 5428    // Same thing, right-to-left
 5429    test(
 5430        &mut cx,
 5431        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5432        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5433    );
 5434
 5435    // Starts at the end of a line, ends at the start of another
 5436    test(
 5437        &mut cx,
 5438        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5439        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5440    );
 5441}
 5442
 5443#[gpui::test]
 5444async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5445    init_test(cx, |_| {});
 5446
 5447    let editor = cx.add_window(|window, cx| {
 5448        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5449        build_editor(buffer, window, cx)
 5450    });
 5451
 5452    // setup
 5453    _ = editor.update(cx, |editor, window, cx| {
 5454        editor.fold_creases(
 5455            vec![
 5456                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5457                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5458                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5459            ],
 5460            true,
 5461            window,
 5462            cx,
 5463        );
 5464        assert_eq!(
 5465            editor.display_text(cx),
 5466            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5467        );
 5468    });
 5469
 5470    _ = editor.update(cx, |editor, window, cx| {
 5471        editor.change_selections(None, window, cx, |s| {
 5472            s.select_display_ranges([
 5473                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5474                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5475                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5476                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5477            ])
 5478        });
 5479        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5480        assert_eq!(
 5481            editor.display_text(cx),
 5482            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5483        );
 5484    });
 5485    EditorTestContext::for_editor(editor, cx)
 5486        .await
 5487        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5488
 5489    _ = editor.update(cx, |editor, window, cx| {
 5490        editor.change_selections(None, window, cx, |s| {
 5491            s.select_display_ranges([
 5492                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5493            ])
 5494        });
 5495        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5496        assert_eq!(
 5497            editor.display_text(cx),
 5498            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5499        );
 5500        assert_eq!(
 5501            editor.selections.display_ranges(cx),
 5502            [
 5503                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5504                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5505                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5506                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5507                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5508                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5509                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5510            ]
 5511        );
 5512    });
 5513    EditorTestContext::for_editor(editor, cx)
 5514        .await
 5515        .assert_editor_state(
 5516            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5517        );
 5518}
 5519
 5520#[gpui::test]
 5521async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5522    init_test(cx, |_| {});
 5523
 5524    let mut cx = EditorTestContext::new(cx).await;
 5525
 5526    cx.set_state(indoc!(
 5527        r#"abc
 5528           defˇghi
 5529
 5530           jk
 5531           nlmo
 5532           "#
 5533    ));
 5534
 5535    cx.update_editor(|editor, window, cx| {
 5536        editor.add_selection_above(&Default::default(), window, cx);
 5537    });
 5538
 5539    cx.assert_editor_state(indoc!(
 5540        r#"abcˇ
 5541           defˇghi
 5542
 5543           jk
 5544           nlmo
 5545           "#
 5546    ));
 5547
 5548    cx.update_editor(|editor, window, cx| {
 5549        editor.add_selection_above(&Default::default(), window, cx);
 5550    });
 5551
 5552    cx.assert_editor_state(indoc!(
 5553        r#"abcˇ
 5554            defˇghi
 5555
 5556            jk
 5557            nlmo
 5558            "#
 5559    ));
 5560
 5561    cx.update_editor(|editor, window, cx| {
 5562        editor.add_selection_below(&Default::default(), window, cx);
 5563    });
 5564
 5565    cx.assert_editor_state(indoc!(
 5566        r#"abc
 5567           defˇghi
 5568
 5569           jk
 5570           nlmo
 5571           "#
 5572    ));
 5573
 5574    cx.update_editor(|editor, window, cx| {
 5575        editor.undo_selection(&Default::default(), window, cx);
 5576    });
 5577
 5578    cx.assert_editor_state(indoc!(
 5579        r#"abcˇ
 5580           defˇghi
 5581
 5582           jk
 5583           nlmo
 5584           "#
 5585    ));
 5586
 5587    cx.update_editor(|editor, window, cx| {
 5588        editor.redo_selection(&Default::default(), window, cx);
 5589    });
 5590
 5591    cx.assert_editor_state(indoc!(
 5592        r#"abc
 5593           defˇghi
 5594
 5595           jk
 5596           nlmo
 5597           "#
 5598    ));
 5599
 5600    cx.update_editor(|editor, window, cx| {
 5601        editor.add_selection_below(&Default::default(), window, cx);
 5602    });
 5603
 5604    cx.assert_editor_state(indoc!(
 5605        r#"abc
 5606           defˇghi
 5607
 5608           jk
 5609           nlmˇo
 5610           "#
 5611    ));
 5612
 5613    cx.update_editor(|editor, window, cx| {
 5614        editor.add_selection_below(&Default::default(), window, cx);
 5615    });
 5616
 5617    cx.assert_editor_state(indoc!(
 5618        r#"abc
 5619           defˇghi
 5620
 5621           jk
 5622           nlmˇo
 5623           "#
 5624    ));
 5625
 5626    // change selections
 5627    cx.set_state(indoc!(
 5628        r#"abc
 5629           def«ˇg»hi
 5630
 5631           jk
 5632           nlmo
 5633           "#
 5634    ));
 5635
 5636    cx.update_editor(|editor, window, cx| {
 5637        editor.add_selection_below(&Default::default(), window, cx);
 5638    });
 5639
 5640    cx.assert_editor_state(indoc!(
 5641        r#"abc
 5642           def«ˇg»hi
 5643
 5644           jk
 5645           nlm«ˇo»
 5646           "#
 5647    ));
 5648
 5649    cx.update_editor(|editor, window, cx| {
 5650        editor.add_selection_below(&Default::default(), window, cx);
 5651    });
 5652
 5653    cx.assert_editor_state(indoc!(
 5654        r#"abc
 5655           def«ˇg»hi
 5656
 5657           jk
 5658           nlm«ˇo»
 5659           "#
 5660    ));
 5661
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_above(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"abc
 5668           def«ˇg»hi
 5669
 5670           jk
 5671           nlmo
 5672           "#
 5673    ));
 5674
 5675    cx.update_editor(|editor, window, cx| {
 5676        editor.add_selection_above(&Default::default(), window, cx);
 5677    });
 5678
 5679    cx.assert_editor_state(indoc!(
 5680        r#"abc
 5681           def«ˇg»hi
 5682
 5683           jk
 5684           nlmo
 5685           "#
 5686    ));
 5687
 5688    // Change selections again
 5689    cx.set_state(indoc!(
 5690        r#"a«bc
 5691           defgˇ»hi
 5692
 5693           jk
 5694           nlmo
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"a«bcˇ»
 5704           d«efgˇ»hi
 5705
 5706           j«kˇ»
 5707           nlmo
 5708           "#
 5709    ));
 5710
 5711    cx.update_editor(|editor, window, cx| {
 5712        editor.add_selection_below(&Default::default(), window, cx);
 5713    });
 5714    cx.assert_editor_state(indoc!(
 5715        r#"a«bcˇ»
 5716           d«efgˇ»hi
 5717
 5718           j«kˇ»
 5719           n«lmoˇ»
 5720           "#
 5721    ));
 5722    cx.update_editor(|editor, window, cx| {
 5723        editor.add_selection_above(&Default::default(), window, cx);
 5724    });
 5725
 5726    cx.assert_editor_state(indoc!(
 5727        r#"a«bcˇ»
 5728           d«efgˇ»hi
 5729
 5730           j«kˇ»
 5731           nlmo
 5732           "#
 5733    ));
 5734
 5735    // Change selections again
 5736    cx.set_state(indoc!(
 5737        r#"abc
 5738           d«ˇefghi
 5739
 5740           jk
 5741           nlm»o
 5742           "#
 5743    ));
 5744
 5745    cx.update_editor(|editor, window, cx| {
 5746        editor.add_selection_above(&Default::default(), window, cx);
 5747    });
 5748
 5749    cx.assert_editor_state(indoc!(
 5750        r#"a«ˇbc»
 5751           d«ˇef»ghi
 5752
 5753           j«ˇk»
 5754           n«ˇlm»o
 5755           "#
 5756    ));
 5757
 5758    cx.update_editor(|editor, window, cx| {
 5759        editor.add_selection_below(&Default::default(), window, cx);
 5760    });
 5761
 5762    cx.assert_editor_state(indoc!(
 5763        r#"abc
 5764           d«ˇef»ghi
 5765
 5766           j«ˇk»
 5767           n«ˇlm»o
 5768           "#
 5769    ));
 5770}
 5771
 5772#[gpui::test]
 5773async fn test_select_next(cx: &mut TestAppContext) {
 5774    init_test(cx, |_| {});
 5775
 5776    let mut cx = EditorTestContext::new(cx).await;
 5777    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5778
 5779    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5780        .unwrap();
 5781    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5782
 5783    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5784        .unwrap();
 5785    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5786
 5787    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5788    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5789
 5790    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5791    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5792
 5793    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5794        .unwrap();
 5795    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5796
 5797    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5798        .unwrap();
 5799    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5800}
 5801
 5802#[gpui::test]
 5803async fn test_select_all_matches(cx: &mut TestAppContext) {
 5804    init_test(cx, |_| {});
 5805
 5806    let mut cx = EditorTestContext::new(cx).await;
 5807
 5808    // Test caret-only selections
 5809    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5810    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5811        .unwrap();
 5812    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5813
 5814    // Test left-to-right selections
 5815    cx.set_state("abc\n«abcˇ»\nabc");
 5816    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5817        .unwrap();
 5818    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5819
 5820    // Test right-to-left selections
 5821    cx.set_state("abc\n«ˇabc»\nabc");
 5822    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5823        .unwrap();
 5824    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5825
 5826    // Test selecting whitespace with caret selection
 5827    cx.set_state("abc\nˇ   abc\nabc");
 5828    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5829        .unwrap();
 5830    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5831
 5832    // Test selecting whitespace with left-to-right selection
 5833    cx.set_state("abc\n«ˇ  »abc\nabc");
 5834    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5835        .unwrap();
 5836    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5837
 5838    // Test no matches with right-to-left selection
 5839    cx.set_state("abc\n«  ˇ»abc\nabc");
 5840    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5841        .unwrap();
 5842    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5843}
 5844
 5845#[gpui::test]
 5846async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5847    init_test(cx, |_| {});
 5848
 5849    let mut cx = EditorTestContext::new(cx).await;
 5850
 5851    let large_body_1 = "\nd".repeat(200);
 5852    let large_body_2 = "\ne".repeat(200);
 5853
 5854    cx.set_state(&format!(
 5855        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5856    ));
 5857    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5858        let scroll_position = editor.scroll_position(cx);
 5859        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5860        scroll_position
 5861    });
 5862
 5863    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state(&format!(
 5866        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5867    ));
 5868    let scroll_position_after_selection =
 5869        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5870    assert_eq!(
 5871        initial_scroll_position, scroll_position_after_selection,
 5872        "Scroll position should not change after selecting all matches"
 5873    );
 5874}
 5875
 5876#[gpui::test]
 5877async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5878    init_test(cx, |_| {});
 5879
 5880    let mut cx = EditorTestContext::new(cx).await;
 5881    cx.set_state(
 5882        r#"let foo = 2;
 5883lˇet foo = 2;
 5884let fooˇ = 2;
 5885let foo = 2;
 5886let foo = ˇ2;"#,
 5887    );
 5888
 5889    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5890        .unwrap();
 5891    cx.assert_editor_state(
 5892        r#"let foo = 2;
 5893«letˇ» foo = 2;
 5894let «fooˇ» = 2;
 5895let foo = 2;
 5896let foo = «2ˇ»;"#,
 5897    );
 5898
 5899    // noop for multiple selections with different contents
 5900    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5901        .unwrap();
 5902    cx.assert_editor_state(
 5903        r#"let foo = 2;
 5904«letˇ» foo = 2;
 5905let «fooˇ» = 2;
 5906let foo = 2;
 5907let foo = «2ˇ»;"#,
 5908    );
 5909}
 5910
 5911#[gpui::test]
 5912async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5913    init_test(cx, |_| {});
 5914
 5915    let mut cx =
 5916        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5917
 5918    cx.assert_editor_state(indoc! {"
 5919        ˇbbb
 5920        ccc
 5921
 5922        bbb
 5923        ccc
 5924        "});
 5925    cx.dispatch_action(SelectPrevious::default());
 5926    cx.assert_editor_state(indoc! {"
 5927                «bbbˇ»
 5928                ccc
 5929
 5930                bbb
 5931                ccc
 5932                "});
 5933    cx.dispatch_action(SelectPrevious::default());
 5934    cx.assert_editor_state(indoc! {"
 5935                «bbbˇ»
 5936                ccc
 5937
 5938                «bbbˇ»
 5939                ccc
 5940                "});
 5941}
 5942
 5943#[gpui::test]
 5944async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5945    init_test(cx, |_| {});
 5946
 5947    let mut cx = EditorTestContext::new(cx).await;
 5948    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5949
 5950    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5951        .unwrap();
 5952    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5953
 5954    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5955        .unwrap();
 5956    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5957
 5958    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5959    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5960
 5961    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5962    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5963
 5964    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5965        .unwrap();
 5966    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5967
 5968    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5969        .unwrap();
 5970    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5971
 5972    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5973        .unwrap();
 5974    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5975}
 5976
 5977#[gpui::test]
 5978async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5979    init_test(cx, |_| {});
 5980
 5981    let mut cx = EditorTestContext::new(cx).await;
 5982    cx.set_state("");
 5983
 5984    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5985        .unwrap();
 5986    cx.assert_editor_state("«aˇ»");
 5987    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5988        .unwrap();
 5989    cx.assert_editor_state("«aˇ»");
 5990}
 5991
 5992#[gpui::test]
 5993async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5994    init_test(cx, |_| {});
 5995
 5996    let mut cx = EditorTestContext::new(cx).await;
 5997    cx.set_state(
 5998        r#"let foo = 2;
 5999lˇet foo = 2;
 6000let fooˇ = 2;
 6001let foo = 2;
 6002let foo = ˇ2;"#,
 6003    );
 6004
 6005    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::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    // noop for multiple selections with different contents
 6016    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6017        .unwrap();
 6018    cx.assert_editor_state(
 6019        r#"let foo = 2;
 6020«letˇ» foo = 2;
 6021let «fooˇ» = 2;
 6022let foo = 2;
 6023let foo = «2ˇ»;"#,
 6024    );
 6025}
 6026
 6027#[gpui::test]
 6028async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6029    init_test(cx, |_| {});
 6030
 6031    let mut cx = EditorTestContext::new(cx).await;
 6032    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6033
 6034    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6035        .unwrap();
 6036    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6037
 6038    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6039        .unwrap();
 6040    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6041
 6042    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6043    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6044
 6045    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6046    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6047
 6048    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6049        .unwrap();
 6050    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 6051
 6052    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6053        .unwrap();
 6054    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6055}
 6056
 6057#[gpui::test]
 6058async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6059    init_test(cx, |_| {});
 6060
 6061    let language = Arc::new(Language::new(
 6062        LanguageConfig::default(),
 6063        Some(tree_sitter_rust::LANGUAGE.into()),
 6064    ));
 6065
 6066    let text = r#"
 6067        use mod1::mod2::{mod3, mod4};
 6068
 6069        fn fn_1(param1: bool, param2: &str) {
 6070            let var1 = "text";
 6071        }
 6072    "#
 6073    .unindent();
 6074
 6075    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6076    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6077    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6078
 6079    editor
 6080        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6081        .await;
 6082
 6083    editor.update_in(cx, |editor, window, cx| {
 6084        editor.change_selections(None, window, cx, |s| {
 6085            s.select_display_ranges([
 6086                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6087                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6088                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6089            ]);
 6090        });
 6091        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6092    });
 6093    editor.update(cx, |editor, cx| {
 6094        assert_text_with_selections(
 6095            editor,
 6096            indoc! {r#"
 6097                use mod1::mod2::{mod3, «mod4ˇ»};
 6098
 6099                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6100                    let var1 = "«ˇtext»";
 6101                }
 6102            "#},
 6103            cx,
 6104        );
 6105    });
 6106
 6107    editor.update_in(cx, |editor, window, cx| {
 6108        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6109    });
 6110    editor.update(cx, |editor, cx| {
 6111        assert_text_with_selections(
 6112            editor,
 6113            indoc! {r#"
 6114                use mod1::mod2::«{mod3, mod4}ˇ»;
 6115
 6116                «ˇfn fn_1(param1: bool, param2: &str) {
 6117                    let var1 = "text";
 6118 6119            "#},
 6120            cx,
 6121        );
 6122    });
 6123
 6124    editor.update_in(cx, |editor, window, cx| {
 6125        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6126    });
 6127    assert_eq!(
 6128        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6129        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6130    );
 6131
 6132    // Trying to expand the selected syntax node one more time has no effect.
 6133    editor.update_in(cx, |editor, window, cx| {
 6134        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6135    });
 6136    assert_eq!(
 6137        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6138        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6139    );
 6140
 6141    editor.update_in(cx, |editor, window, cx| {
 6142        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6143    });
 6144    editor.update(cx, |editor, cx| {
 6145        assert_text_with_selections(
 6146            editor,
 6147            indoc! {r#"
 6148                use mod1::mod2::«{mod3, mod4}ˇ»;
 6149
 6150                «ˇfn fn_1(param1: bool, param2: &str) {
 6151                    let var1 = "text";
 6152 6153            "#},
 6154            cx,
 6155        );
 6156    });
 6157
 6158    editor.update_in(cx, |editor, window, cx| {
 6159        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6160    });
 6161    editor.update(cx, |editor, cx| {
 6162        assert_text_with_selections(
 6163            editor,
 6164            indoc! {r#"
 6165                use mod1::mod2::{mod3, «mod4ˇ»};
 6166
 6167                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6168                    let var1 = "«ˇtext»";
 6169                }
 6170            "#},
 6171            cx,
 6172        );
 6173    });
 6174
 6175    editor.update_in(cx, |editor, window, cx| {
 6176        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6177    });
 6178    editor.update(cx, |editor, cx| {
 6179        assert_text_with_selections(
 6180            editor,
 6181            indoc! {r#"
 6182                use mod1::mod2::{mod3, mo«ˇ»d4};
 6183
 6184                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6185                    let var1 = "te«ˇ»xt";
 6186                }
 6187            "#},
 6188            cx,
 6189        );
 6190    });
 6191
 6192    // Trying to shrink the selected syntax node one more time has no effect.
 6193    editor.update_in(cx, |editor, window, cx| {
 6194        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6195    });
 6196    editor.update_in(cx, |editor, _, cx| {
 6197        assert_text_with_selections(
 6198            editor,
 6199            indoc! {r#"
 6200                use mod1::mod2::{mod3, mo«ˇ»d4};
 6201
 6202                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6203                    let var1 = "te«ˇ»xt";
 6204                }
 6205            "#},
 6206            cx,
 6207        );
 6208    });
 6209
 6210    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6211    // a fold.
 6212    editor.update_in(cx, |editor, window, cx| {
 6213        editor.fold_creases(
 6214            vec![
 6215                Crease::simple(
 6216                    Point::new(0, 21)..Point::new(0, 24),
 6217                    FoldPlaceholder::test(),
 6218                ),
 6219                Crease::simple(
 6220                    Point::new(3, 20)..Point::new(3, 22),
 6221                    FoldPlaceholder::test(),
 6222                ),
 6223            ],
 6224            true,
 6225            window,
 6226            cx,
 6227        );
 6228        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6229    });
 6230    editor.update(cx, |editor, cx| {
 6231        assert_text_with_selections(
 6232            editor,
 6233            indoc! {r#"
 6234                use mod1::mod2::«{mod3, mod4}ˇ»;
 6235
 6236                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6237                    «ˇlet var1 = "text";»
 6238                }
 6239            "#},
 6240            cx,
 6241        );
 6242    });
 6243}
 6244
 6245#[gpui::test]
 6246async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6247    init_test(cx, |_| {});
 6248
 6249    let base_text = r#"
 6250        impl A {
 6251            // this is an uncommitted comment
 6252
 6253            fn b() {
 6254                c();
 6255            }
 6256
 6257            // this is another uncommitted comment
 6258
 6259            fn d() {
 6260                // e
 6261                // f
 6262            }
 6263        }
 6264
 6265        fn g() {
 6266            // h
 6267        }
 6268    "#
 6269    .unindent();
 6270
 6271    let text = r#"
 6272        ˇimpl A {
 6273
 6274            fn b() {
 6275                c();
 6276            }
 6277
 6278            fn d() {
 6279                // e
 6280                // f
 6281            }
 6282        }
 6283
 6284        fn g() {
 6285            // h
 6286        }
 6287    "#
 6288    .unindent();
 6289
 6290    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6291    cx.set_state(&text);
 6292    cx.set_head_text(&base_text);
 6293    cx.update_editor(|editor, window, cx| {
 6294        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6295    });
 6296
 6297    cx.assert_state_with_diff(
 6298        "
 6299        ˇimpl A {
 6300      -     // this is an uncommitted comment
 6301
 6302            fn b() {
 6303                c();
 6304            }
 6305
 6306      -     // this is another uncommitted comment
 6307      -
 6308            fn d() {
 6309                // e
 6310                // f
 6311            }
 6312        }
 6313
 6314        fn g() {
 6315            // h
 6316        }
 6317    "
 6318        .unindent(),
 6319    );
 6320
 6321    let expected_display_text = "
 6322        impl A {
 6323            // this is an uncommitted comment
 6324
 6325            fn b() {
 6326 6327            }
 6328
 6329            // this is another uncommitted comment
 6330
 6331            fn d() {
 6332 6333            }
 6334        }
 6335
 6336        fn g() {
 6337 6338        }
 6339        "
 6340    .unindent();
 6341
 6342    cx.update_editor(|editor, window, cx| {
 6343        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6344        assert_eq!(editor.display_text(cx), expected_display_text);
 6345    });
 6346}
 6347
 6348#[gpui::test]
 6349async fn test_autoindent(cx: &mut TestAppContext) {
 6350    init_test(cx, |_| {});
 6351
 6352    let language = Arc::new(
 6353        Language::new(
 6354            LanguageConfig {
 6355                brackets: BracketPairConfig {
 6356                    pairs: vec![
 6357                        BracketPair {
 6358                            start: "{".to_string(),
 6359                            end: "}".to_string(),
 6360                            close: false,
 6361                            surround: false,
 6362                            newline: true,
 6363                        },
 6364                        BracketPair {
 6365                            start: "(".to_string(),
 6366                            end: ")".to_string(),
 6367                            close: false,
 6368                            surround: false,
 6369                            newline: true,
 6370                        },
 6371                    ],
 6372                    ..Default::default()
 6373                },
 6374                ..Default::default()
 6375            },
 6376            Some(tree_sitter_rust::LANGUAGE.into()),
 6377        )
 6378        .with_indents_query(
 6379            r#"
 6380                (_ "(" ")" @end) @indent
 6381                (_ "{" "}" @end) @indent
 6382            "#,
 6383        )
 6384        .unwrap(),
 6385    );
 6386
 6387    let text = "fn a() {}";
 6388
 6389    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6390    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6391    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6392    editor
 6393        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6394        .await;
 6395
 6396    editor.update_in(cx, |editor, window, cx| {
 6397        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6398        editor.newline(&Newline, window, cx);
 6399        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6400        assert_eq!(
 6401            editor.selections.ranges(cx),
 6402            &[
 6403                Point::new(1, 4)..Point::new(1, 4),
 6404                Point::new(3, 4)..Point::new(3, 4),
 6405                Point::new(5, 0)..Point::new(5, 0)
 6406            ]
 6407        );
 6408    });
 6409}
 6410
 6411#[gpui::test]
 6412async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6413    init_test(cx, |_| {});
 6414
 6415    {
 6416        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6417        cx.set_state(indoc! {"
 6418            impl A {
 6419
 6420                fn b() {}
 6421
 6422            «fn c() {
 6423
 6424            }ˇ»
 6425            }
 6426        "});
 6427
 6428        cx.update_editor(|editor, window, cx| {
 6429            editor.autoindent(&Default::default(), window, cx);
 6430        });
 6431
 6432        cx.assert_editor_state(indoc! {"
 6433            impl A {
 6434
 6435                fn b() {}
 6436
 6437                «fn c() {
 6438
 6439                }ˇ»
 6440            }
 6441        "});
 6442    }
 6443
 6444    {
 6445        let mut cx = EditorTestContext::new_multibuffer(
 6446            cx,
 6447            [indoc! { "
 6448                impl A {
 6449                «
 6450                // a
 6451                fn b(){}
 6452                »
 6453                «
 6454                    }
 6455                    fn c(){}
 6456                »
 6457            "}],
 6458        );
 6459
 6460        let buffer = cx.update_editor(|editor, _, cx| {
 6461            let buffer = editor.buffer().update(cx, |buffer, _| {
 6462                buffer.all_buffers().iter().next().unwrap().clone()
 6463            });
 6464            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6465            buffer
 6466        });
 6467
 6468        cx.run_until_parked();
 6469        cx.update_editor(|editor, window, cx| {
 6470            editor.select_all(&Default::default(), window, cx);
 6471            editor.autoindent(&Default::default(), window, cx)
 6472        });
 6473        cx.run_until_parked();
 6474
 6475        cx.update(|_, cx| {
 6476            assert_eq!(
 6477                buffer.read(cx).text(),
 6478                indoc! { "
 6479                    impl A {
 6480
 6481                        // a
 6482                        fn b(){}
 6483
 6484
 6485                    }
 6486                    fn c(){}
 6487
 6488                " }
 6489            )
 6490        });
 6491    }
 6492}
 6493
 6494#[gpui::test]
 6495async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6496    init_test(cx, |_| {});
 6497
 6498    let mut cx = EditorTestContext::new(cx).await;
 6499
 6500    let language = Arc::new(Language::new(
 6501        LanguageConfig {
 6502            brackets: BracketPairConfig {
 6503                pairs: vec![
 6504                    BracketPair {
 6505                        start: "{".to_string(),
 6506                        end: "}".to_string(),
 6507                        close: true,
 6508                        surround: true,
 6509                        newline: true,
 6510                    },
 6511                    BracketPair {
 6512                        start: "(".to_string(),
 6513                        end: ")".to_string(),
 6514                        close: true,
 6515                        surround: true,
 6516                        newline: true,
 6517                    },
 6518                    BracketPair {
 6519                        start: "/*".to_string(),
 6520                        end: " */".to_string(),
 6521                        close: true,
 6522                        surround: true,
 6523                        newline: true,
 6524                    },
 6525                    BracketPair {
 6526                        start: "[".to_string(),
 6527                        end: "]".to_string(),
 6528                        close: false,
 6529                        surround: false,
 6530                        newline: true,
 6531                    },
 6532                    BracketPair {
 6533                        start: "\"".to_string(),
 6534                        end: "\"".to_string(),
 6535                        close: true,
 6536                        surround: true,
 6537                        newline: false,
 6538                    },
 6539                    BracketPair {
 6540                        start: "<".to_string(),
 6541                        end: ">".to_string(),
 6542                        close: false,
 6543                        surround: true,
 6544                        newline: true,
 6545                    },
 6546                ],
 6547                ..Default::default()
 6548            },
 6549            autoclose_before: "})]".to_string(),
 6550            ..Default::default()
 6551        },
 6552        Some(tree_sitter_rust::LANGUAGE.into()),
 6553    ));
 6554
 6555    cx.language_registry().add(language.clone());
 6556    cx.update_buffer(|buffer, cx| {
 6557        buffer.set_language(Some(language), cx);
 6558    });
 6559
 6560    cx.set_state(
 6561        &r#"
 6562            🏀ˇ
 6563            εˇ
 6564            ❤️ˇ
 6565        "#
 6566        .unindent(),
 6567    );
 6568
 6569    // autoclose multiple nested brackets at multiple cursors
 6570    cx.update_editor(|editor, window, cx| {
 6571        editor.handle_input("{", window, cx);
 6572        editor.handle_input("{", window, cx);
 6573        editor.handle_input("{", window, cx);
 6574    });
 6575    cx.assert_editor_state(
 6576        &"
 6577            🏀{{{ˇ}}}
 6578            ε{{{ˇ}}}
 6579            ❤️{{{ˇ}}}
 6580        "
 6581        .unindent(),
 6582    );
 6583
 6584    // insert a different closing bracket
 6585    cx.update_editor(|editor, window, cx| {
 6586        editor.handle_input(")", window, cx);
 6587    });
 6588    cx.assert_editor_state(
 6589        &"
 6590            🏀{{{)ˇ}}}
 6591            ε{{{)ˇ}}}
 6592            ❤️{{{)ˇ}}}
 6593        "
 6594        .unindent(),
 6595    );
 6596
 6597    // skip over the auto-closed brackets when typing a closing bracket
 6598    cx.update_editor(|editor, window, cx| {
 6599        editor.move_right(&MoveRight, window, cx);
 6600        editor.handle_input("}", window, cx);
 6601        editor.handle_input("}", window, cx);
 6602        editor.handle_input("}", window, cx);
 6603    });
 6604    cx.assert_editor_state(
 6605        &"
 6606            🏀{{{)}}}}ˇ
 6607            ε{{{)}}}}ˇ
 6608            ❤️{{{)}}}}ˇ
 6609        "
 6610        .unindent(),
 6611    );
 6612
 6613    // autoclose multi-character pairs
 6614    cx.set_state(
 6615        &"
 6616            ˇ
 6617            ˇ
 6618        "
 6619        .unindent(),
 6620    );
 6621    cx.update_editor(|editor, window, cx| {
 6622        editor.handle_input("/", window, cx);
 6623        editor.handle_input("*", window, cx);
 6624    });
 6625    cx.assert_editor_state(
 6626        &"
 6627            /*ˇ */
 6628            /*ˇ */
 6629        "
 6630        .unindent(),
 6631    );
 6632
 6633    // one cursor autocloses a multi-character pair, one cursor
 6634    // does not autoclose.
 6635    cx.set_state(
 6636        &"
 6637 6638            ˇ
 6639        "
 6640        .unindent(),
 6641    );
 6642    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6643    cx.assert_editor_state(
 6644        &"
 6645            /*ˇ */
 6646 6647        "
 6648        .unindent(),
 6649    );
 6650
 6651    // Don't autoclose if the next character isn't whitespace and isn't
 6652    // listed in the language's "autoclose_before" section.
 6653    cx.set_state("ˇa b");
 6654    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6655    cx.assert_editor_state("{ˇa b");
 6656
 6657    // Don't autoclose if `close` is false for the bracket pair
 6658    cx.set_state("ˇ");
 6659    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6660    cx.assert_editor_state("");
 6661
 6662    // Surround with brackets if text is selected
 6663    cx.set_state("«aˇ» b");
 6664    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6665    cx.assert_editor_state("{«aˇ»} b");
 6666
 6667    // Autoclose when not immediately after a word character
 6668    cx.set_state("a ˇ");
 6669    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6670    cx.assert_editor_state("a \"ˇ\"");
 6671
 6672    // Autoclose pair where the start and end characters are the same
 6673    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6674    cx.assert_editor_state("a \"\"ˇ");
 6675
 6676    // Don't autoclose when immediately after a word character
 6677    cx.set_state("");
 6678    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6679    cx.assert_editor_state("a\"ˇ");
 6680
 6681    // Do autoclose when after a non-word character
 6682    cx.set_state("");
 6683    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6684    cx.assert_editor_state("{\"ˇ\"");
 6685
 6686    // Non identical pairs autoclose regardless of preceding character
 6687    cx.set_state("");
 6688    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6689    cx.assert_editor_state("a{ˇ}");
 6690
 6691    // Don't autoclose pair if autoclose is disabled
 6692    cx.set_state("ˇ");
 6693    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6694    cx.assert_editor_state("");
 6695
 6696    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6697    cx.set_state("«aˇ» b");
 6698    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6699    cx.assert_editor_state("<«aˇ»> b");
 6700}
 6701
 6702#[gpui::test]
 6703async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6704    init_test(cx, |settings| {
 6705        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6706    });
 6707
 6708    let mut cx = EditorTestContext::new(cx).await;
 6709
 6710    let language = Arc::new(Language::new(
 6711        LanguageConfig {
 6712            brackets: BracketPairConfig {
 6713                pairs: vec![
 6714                    BracketPair {
 6715                        start: "{".to_string(),
 6716                        end: "}".to_string(),
 6717                        close: true,
 6718                        surround: true,
 6719                        newline: true,
 6720                    },
 6721                    BracketPair {
 6722                        start: "(".to_string(),
 6723                        end: ")".to_string(),
 6724                        close: true,
 6725                        surround: true,
 6726                        newline: true,
 6727                    },
 6728                    BracketPair {
 6729                        start: "[".to_string(),
 6730                        end: "]".to_string(),
 6731                        close: false,
 6732                        surround: false,
 6733                        newline: true,
 6734                    },
 6735                ],
 6736                ..Default::default()
 6737            },
 6738            autoclose_before: "})]".to_string(),
 6739            ..Default::default()
 6740        },
 6741        Some(tree_sitter_rust::LANGUAGE.into()),
 6742    ));
 6743
 6744    cx.language_registry().add(language.clone());
 6745    cx.update_buffer(|buffer, cx| {
 6746        buffer.set_language(Some(language), cx);
 6747    });
 6748
 6749    cx.set_state(
 6750        &"
 6751            ˇ
 6752            ˇ
 6753            ˇ
 6754        "
 6755        .unindent(),
 6756    );
 6757
 6758    // ensure only matching closing brackets are skipped over
 6759    cx.update_editor(|editor, window, cx| {
 6760        editor.handle_input("}", window, cx);
 6761        editor.move_left(&MoveLeft, window, cx);
 6762        editor.handle_input(")", window, cx);
 6763        editor.move_left(&MoveLeft, window, cx);
 6764    });
 6765    cx.assert_editor_state(
 6766        &"
 6767            ˇ)}
 6768            ˇ)}
 6769            ˇ)}
 6770        "
 6771        .unindent(),
 6772    );
 6773
 6774    // skip-over closing brackets at multiple cursors
 6775    cx.update_editor(|editor, window, cx| {
 6776        editor.handle_input(")", window, cx);
 6777        editor.handle_input("}", window, cx);
 6778    });
 6779    cx.assert_editor_state(
 6780        &"
 6781            )}ˇ
 6782            )}ˇ
 6783            )}ˇ
 6784        "
 6785        .unindent(),
 6786    );
 6787
 6788    // ignore non-close brackets
 6789    cx.update_editor(|editor, window, cx| {
 6790        editor.handle_input("]", window, cx);
 6791        editor.move_left(&MoveLeft, window, cx);
 6792        editor.handle_input("]", window, cx);
 6793    });
 6794    cx.assert_editor_state(
 6795        &"
 6796            )}]ˇ]
 6797            )}]ˇ]
 6798            )}]ˇ]
 6799        "
 6800        .unindent(),
 6801    );
 6802}
 6803
 6804#[gpui::test]
 6805async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6806    init_test(cx, |_| {});
 6807
 6808    let mut cx = EditorTestContext::new(cx).await;
 6809
 6810    let html_language = Arc::new(
 6811        Language::new(
 6812            LanguageConfig {
 6813                name: "HTML".into(),
 6814                brackets: BracketPairConfig {
 6815                    pairs: vec![
 6816                        BracketPair {
 6817                            start: "<".into(),
 6818                            end: ">".into(),
 6819                            close: true,
 6820                            ..Default::default()
 6821                        },
 6822                        BracketPair {
 6823                            start: "{".into(),
 6824                            end: "}".into(),
 6825                            close: true,
 6826                            ..Default::default()
 6827                        },
 6828                        BracketPair {
 6829                            start: "(".into(),
 6830                            end: ")".into(),
 6831                            close: true,
 6832                            ..Default::default()
 6833                        },
 6834                    ],
 6835                    ..Default::default()
 6836                },
 6837                autoclose_before: "})]>".into(),
 6838                ..Default::default()
 6839            },
 6840            Some(tree_sitter_html::LANGUAGE.into()),
 6841        )
 6842        .with_injection_query(
 6843            r#"
 6844            (script_element
 6845                (raw_text) @injection.content
 6846                (#set! injection.language "javascript"))
 6847            "#,
 6848        )
 6849        .unwrap(),
 6850    );
 6851
 6852    let javascript_language = Arc::new(Language::new(
 6853        LanguageConfig {
 6854            name: "JavaScript".into(),
 6855            brackets: BracketPairConfig {
 6856                pairs: vec![
 6857                    BracketPair {
 6858                        start: "/*".into(),
 6859                        end: " */".into(),
 6860                        close: true,
 6861                        ..Default::default()
 6862                    },
 6863                    BracketPair {
 6864                        start: "{".into(),
 6865                        end: "}".into(),
 6866                        close: true,
 6867                        ..Default::default()
 6868                    },
 6869                    BracketPair {
 6870                        start: "(".into(),
 6871                        end: ")".into(),
 6872                        close: true,
 6873                        ..Default::default()
 6874                    },
 6875                ],
 6876                ..Default::default()
 6877            },
 6878            autoclose_before: "})]>".into(),
 6879            ..Default::default()
 6880        },
 6881        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6882    ));
 6883
 6884    cx.language_registry().add(html_language.clone());
 6885    cx.language_registry().add(javascript_language.clone());
 6886
 6887    cx.update_buffer(|buffer, cx| {
 6888        buffer.set_language(Some(html_language), cx);
 6889    });
 6890
 6891    cx.set_state(
 6892        &r#"
 6893            <body>ˇ
 6894                <script>
 6895                    var x = 1;ˇ
 6896                </script>
 6897            </body>ˇ
 6898        "#
 6899        .unindent(),
 6900    );
 6901
 6902    // Precondition: different languages are active at different locations.
 6903    cx.update_editor(|editor, window, cx| {
 6904        let snapshot = editor.snapshot(window, cx);
 6905        let cursors = editor.selections.ranges::<usize>(cx);
 6906        let languages = cursors
 6907            .iter()
 6908            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6909            .collect::<Vec<_>>();
 6910        assert_eq!(
 6911            languages,
 6912            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6913        );
 6914    });
 6915
 6916    // Angle brackets autoclose in HTML, but not JavaScript.
 6917    cx.update_editor(|editor, window, cx| {
 6918        editor.handle_input("<", window, cx);
 6919        editor.handle_input("a", window, cx);
 6920    });
 6921    cx.assert_editor_state(
 6922        &r#"
 6923            <body><aˇ>
 6924                <script>
 6925                    var x = 1;<aˇ
 6926                </script>
 6927            </body><aˇ>
 6928        "#
 6929        .unindent(),
 6930    );
 6931
 6932    // Curly braces and parens autoclose in both HTML and JavaScript.
 6933    cx.update_editor(|editor, window, cx| {
 6934        editor.handle_input(" b=", window, cx);
 6935        editor.handle_input("{", window, cx);
 6936        editor.handle_input("c", window, cx);
 6937        editor.handle_input("(", window, cx);
 6938    });
 6939    cx.assert_editor_state(
 6940        &r#"
 6941            <body><a b={c(ˇ)}>
 6942                <script>
 6943                    var x = 1;<a b={c(ˇ)}
 6944                </script>
 6945            </body><a b={c(ˇ)}>
 6946        "#
 6947        .unindent(),
 6948    );
 6949
 6950    // Brackets that were already autoclosed are skipped.
 6951    cx.update_editor(|editor, window, cx| {
 6952        editor.handle_input(")", window, cx);
 6953        editor.handle_input("d", window, cx);
 6954        editor.handle_input("}", window, cx);
 6955    });
 6956    cx.assert_editor_state(
 6957        &r#"
 6958            <body><a b={c()d}ˇ>
 6959                <script>
 6960                    var x = 1;<a b={c()d}ˇ
 6961                </script>
 6962            </body><a b={c()d}ˇ>
 6963        "#
 6964        .unindent(),
 6965    );
 6966    cx.update_editor(|editor, window, cx| {
 6967        editor.handle_input(">", window, cx);
 6968    });
 6969    cx.assert_editor_state(
 6970        &r#"
 6971            <body><a b={c()d}>ˇ
 6972                <script>
 6973                    var x = 1;<a b={c()d}>ˇ
 6974                </script>
 6975            </body><a b={c()d}>ˇ
 6976        "#
 6977        .unindent(),
 6978    );
 6979
 6980    // Reset
 6981    cx.set_state(
 6982        &r#"
 6983            <body>ˇ
 6984                <script>
 6985                    var x = 1;ˇ
 6986                </script>
 6987            </body>ˇ
 6988        "#
 6989        .unindent(),
 6990    );
 6991
 6992    cx.update_editor(|editor, window, cx| {
 6993        editor.handle_input("<", window, cx);
 6994    });
 6995    cx.assert_editor_state(
 6996        &r#"
 6997            <body><ˇ>
 6998                <script>
 6999                    var x = 1;<ˇ
 7000                </script>
 7001            </body><ˇ>
 7002        "#
 7003        .unindent(),
 7004    );
 7005
 7006    // When backspacing, the closing angle brackets are removed.
 7007    cx.update_editor(|editor, window, cx| {
 7008        editor.backspace(&Backspace, window, cx);
 7009    });
 7010    cx.assert_editor_state(
 7011        &r#"
 7012            <body>ˇ
 7013                <script>
 7014                    var x = 1;ˇ
 7015                </script>
 7016            </body>ˇ
 7017        "#
 7018        .unindent(),
 7019    );
 7020
 7021    // Block comments autoclose in JavaScript, but not HTML.
 7022    cx.update_editor(|editor, window, cx| {
 7023        editor.handle_input("/", window, cx);
 7024        editor.handle_input("*", window, cx);
 7025    });
 7026    cx.assert_editor_state(
 7027        &r#"
 7028            <body>/*ˇ
 7029                <script>
 7030                    var x = 1;/*ˇ */
 7031                </script>
 7032            </body>/*ˇ
 7033        "#
 7034        .unindent(),
 7035    );
 7036}
 7037
 7038#[gpui::test]
 7039async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7040    init_test(cx, |_| {});
 7041
 7042    let mut cx = EditorTestContext::new(cx).await;
 7043
 7044    let rust_language = Arc::new(
 7045        Language::new(
 7046            LanguageConfig {
 7047                name: "Rust".into(),
 7048                brackets: serde_json::from_value(json!([
 7049                    { "start": "{", "end": "}", "close": true, "newline": true },
 7050                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7051                ]))
 7052                .unwrap(),
 7053                autoclose_before: "})]>".into(),
 7054                ..Default::default()
 7055            },
 7056            Some(tree_sitter_rust::LANGUAGE.into()),
 7057        )
 7058        .with_override_query("(string_literal) @string")
 7059        .unwrap(),
 7060    );
 7061
 7062    cx.language_registry().add(rust_language.clone());
 7063    cx.update_buffer(|buffer, cx| {
 7064        buffer.set_language(Some(rust_language), cx);
 7065    });
 7066
 7067    cx.set_state(
 7068        &r#"
 7069            let x = ˇ
 7070        "#
 7071        .unindent(),
 7072    );
 7073
 7074    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7075    cx.update_editor(|editor, window, cx| {
 7076        editor.handle_input("\"", window, cx);
 7077    });
 7078    cx.assert_editor_state(
 7079        &r#"
 7080            let x = "ˇ"
 7081        "#
 7082        .unindent(),
 7083    );
 7084
 7085    // Inserting another quotation mark. The cursor moves across the existing
 7086    // automatically-inserted quotation mark.
 7087    cx.update_editor(|editor, window, cx| {
 7088        editor.handle_input("\"", window, cx);
 7089    });
 7090    cx.assert_editor_state(
 7091        &r#"
 7092            let x = ""ˇ
 7093        "#
 7094        .unindent(),
 7095    );
 7096
 7097    // Reset
 7098    cx.set_state(
 7099        &r#"
 7100            let x = ˇ
 7101        "#
 7102        .unindent(),
 7103    );
 7104
 7105    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7106    cx.update_editor(|editor, window, cx| {
 7107        editor.handle_input("\"", window, cx);
 7108        editor.handle_input(" ", window, cx);
 7109        editor.move_left(&Default::default(), window, cx);
 7110        editor.handle_input("\\", window, cx);
 7111        editor.handle_input("\"", window, cx);
 7112    });
 7113    cx.assert_editor_state(
 7114        &r#"
 7115            let x = "\"ˇ "
 7116        "#
 7117        .unindent(),
 7118    );
 7119
 7120    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7121    // mark. Nothing is inserted.
 7122    cx.update_editor(|editor, window, cx| {
 7123        editor.move_right(&Default::default(), window, cx);
 7124        editor.handle_input("\"", window, cx);
 7125    });
 7126    cx.assert_editor_state(
 7127        &r#"
 7128            let x = "\" "ˇ
 7129        "#
 7130        .unindent(),
 7131    );
 7132}
 7133
 7134#[gpui::test]
 7135async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7136    init_test(cx, |_| {});
 7137
 7138    let language = Arc::new(Language::new(
 7139        LanguageConfig {
 7140            brackets: BracketPairConfig {
 7141                pairs: vec![
 7142                    BracketPair {
 7143                        start: "{".to_string(),
 7144                        end: "}".to_string(),
 7145                        close: true,
 7146                        surround: true,
 7147                        newline: true,
 7148                    },
 7149                    BracketPair {
 7150                        start: "/* ".to_string(),
 7151                        end: "*/".to_string(),
 7152                        close: true,
 7153                        surround: true,
 7154                        ..Default::default()
 7155                    },
 7156                ],
 7157                ..Default::default()
 7158            },
 7159            ..Default::default()
 7160        },
 7161        Some(tree_sitter_rust::LANGUAGE.into()),
 7162    ));
 7163
 7164    let text = r#"
 7165        a
 7166        b
 7167        c
 7168    "#
 7169    .unindent();
 7170
 7171    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7172    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7173    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7174    editor
 7175        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7176        .await;
 7177
 7178    editor.update_in(cx, |editor, window, cx| {
 7179        editor.change_selections(None, window, cx, |s| {
 7180            s.select_display_ranges([
 7181                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7182                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7183                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7184            ])
 7185        });
 7186
 7187        editor.handle_input("{", window, cx);
 7188        editor.handle_input("{", window, cx);
 7189        editor.handle_input("{", window, cx);
 7190        assert_eq!(
 7191            editor.text(cx),
 7192            "
 7193                {{{a}}}
 7194                {{{b}}}
 7195                {{{c}}}
 7196            "
 7197            .unindent()
 7198        );
 7199        assert_eq!(
 7200            editor.selections.display_ranges(cx),
 7201            [
 7202                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7203                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7204                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7205            ]
 7206        );
 7207
 7208        editor.undo(&Undo, window, cx);
 7209        editor.undo(&Undo, window, cx);
 7210        editor.undo(&Undo, window, cx);
 7211        assert_eq!(
 7212            editor.text(cx),
 7213            "
 7214                a
 7215                b
 7216                c
 7217            "
 7218            .unindent()
 7219        );
 7220        assert_eq!(
 7221            editor.selections.display_ranges(cx),
 7222            [
 7223                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7224                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7225                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7226            ]
 7227        );
 7228
 7229        // Ensure inserting the first character of a multi-byte bracket pair
 7230        // doesn't surround the selections with the bracket.
 7231        editor.handle_input("/", window, cx);
 7232        assert_eq!(
 7233            editor.text(cx),
 7234            "
 7235                /
 7236                /
 7237                /
 7238            "
 7239            .unindent()
 7240        );
 7241        assert_eq!(
 7242            editor.selections.display_ranges(cx),
 7243            [
 7244                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7245                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7246                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7247            ]
 7248        );
 7249
 7250        editor.undo(&Undo, window, cx);
 7251        assert_eq!(
 7252            editor.text(cx),
 7253            "
 7254                a
 7255                b
 7256                c
 7257            "
 7258            .unindent()
 7259        );
 7260        assert_eq!(
 7261            editor.selections.display_ranges(cx),
 7262            [
 7263                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7264                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7265                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7266            ]
 7267        );
 7268
 7269        // Ensure inserting the last character of a multi-byte bracket pair
 7270        // doesn't surround the selections with the bracket.
 7271        editor.handle_input("*", window, cx);
 7272        assert_eq!(
 7273            editor.text(cx),
 7274            "
 7275                *
 7276                *
 7277                *
 7278            "
 7279            .unindent()
 7280        );
 7281        assert_eq!(
 7282            editor.selections.display_ranges(cx),
 7283            [
 7284                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7285                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7286                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7287            ]
 7288        );
 7289    });
 7290}
 7291
 7292#[gpui::test]
 7293async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7294    init_test(cx, |_| {});
 7295
 7296    let language = Arc::new(Language::new(
 7297        LanguageConfig {
 7298            brackets: BracketPairConfig {
 7299                pairs: vec![BracketPair {
 7300                    start: "{".to_string(),
 7301                    end: "}".to_string(),
 7302                    close: true,
 7303                    surround: true,
 7304                    newline: true,
 7305                }],
 7306                ..Default::default()
 7307            },
 7308            autoclose_before: "}".to_string(),
 7309            ..Default::default()
 7310        },
 7311        Some(tree_sitter_rust::LANGUAGE.into()),
 7312    ));
 7313
 7314    let text = r#"
 7315        a
 7316        b
 7317        c
 7318    "#
 7319    .unindent();
 7320
 7321    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7322    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7323    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7324    editor
 7325        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7326        .await;
 7327
 7328    editor.update_in(cx, |editor, window, cx| {
 7329        editor.change_selections(None, window, cx, |s| {
 7330            s.select_ranges([
 7331                Point::new(0, 1)..Point::new(0, 1),
 7332                Point::new(1, 1)..Point::new(1, 1),
 7333                Point::new(2, 1)..Point::new(2, 1),
 7334            ])
 7335        });
 7336
 7337        editor.handle_input("{", window, cx);
 7338        editor.handle_input("{", window, cx);
 7339        editor.handle_input("_", window, cx);
 7340        assert_eq!(
 7341            editor.text(cx),
 7342            "
 7343                a{{_}}
 7344                b{{_}}
 7345                c{{_}}
 7346            "
 7347            .unindent()
 7348        );
 7349        assert_eq!(
 7350            editor.selections.ranges::<Point>(cx),
 7351            [
 7352                Point::new(0, 4)..Point::new(0, 4),
 7353                Point::new(1, 4)..Point::new(1, 4),
 7354                Point::new(2, 4)..Point::new(2, 4)
 7355            ]
 7356        );
 7357
 7358        editor.backspace(&Default::default(), window, cx);
 7359        editor.backspace(&Default::default(), window, cx);
 7360        assert_eq!(
 7361            editor.text(cx),
 7362            "
 7363                a{}
 7364                b{}
 7365                c{}
 7366            "
 7367            .unindent()
 7368        );
 7369        assert_eq!(
 7370            editor.selections.ranges::<Point>(cx),
 7371            [
 7372                Point::new(0, 2)..Point::new(0, 2),
 7373                Point::new(1, 2)..Point::new(1, 2),
 7374                Point::new(2, 2)..Point::new(2, 2)
 7375            ]
 7376        );
 7377
 7378        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7379        assert_eq!(
 7380            editor.text(cx),
 7381            "
 7382                a
 7383                b
 7384                c
 7385            "
 7386            .unindent()
 7387        );
 7388        assert_eq!(
 7389            editor.selections.ranges::<Point>(cx),
 7390            [
 7391                Point::new(0, 1)..Point::new(0, 1),
 7392                Point::new(1, 1)..Point::new(1, 1),
 7393                Point::new(2, 1)..Point::new(2, 1)
 7394            ]
 7395        );
 7396    });
 7397}
 7398
 7399#[gpui::test]
 7400async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7401    init_test(cx, |settings| {
 7402        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7403    });
 7404
 7405    let mut cx = EditorTestContext::new(cx).await;
 7406
 7407    let language = Arc::new(Language::new(
 7408        LanguageConfig {
 7409            brackets: BracketPairConfig {
 7410                pairs: vec![
 7411                    BracketPair {
 7412                        start: "{".to_string(),
 7413                        end: "}".to_string(),
 7414                        close: true,
 7415                        surround: true,
 7416                        newline: true,
 7417                    },
 7418                    BracketPair {
 7419                        start: "(".to_string(),
 7420                        end: ")".to_string(),
 7421                        close: true,
 7422                        surround: true,
 7423                        newline: true,
 7424                    },
 7425                    BracketPair {
 7426                        start: "[".to_string(),
 7427                        end: "]".to_string(),
 7428                        close: false,
 7429                        surround: true,
 7430                        newline: true,
 7431                    },
 7432                ],
 7433                ..Default::default()
 7434            },
 7435            autoclose_before: "})]".to_string(),
 7436            ..Default::default()
 7437        },
 7438        Some(tree_sitter_rust::LANGUAGE.into()),
 7439    ));
 7440
 7441    cx.language_registry().add(language.clone());
 7442    cx.update_buffer(|buffer, cx| {
 7443        buffer.set_language(Some(language), cx);
 7444    });
 7445
 7446    cx.set_state(
 7447        &"
 7448            {(ˇ)}
 7449            [[ˇ]]
 7450            {(ˇ)}
 7451        "
 7452        .unindent(),
 7453    );
 7454
 7455    cx.update_editor(|editor, window, cx| {
 7456        editor.backspace(&Default::default(), window, cx);
 7457        editor.backspace(&Default::default(), window, cx);
 7458    });
 7459
 7460    cx.assert_editor_state(
 7461        &"
 7462            ˇ
 7463            ˇ]]
 7464            ˇ
 7465        "
 7466        .unindent(),
 7467    );
 7468
 7469    cx.update_editor(|editor, window, cx| {
 7470        editor.handle_input("{", window, cx);
 7471        editor.handle_input("{", window, cx);
 7472        editor.move_right(&MoveRight, window, cx);
 7473        editor.move_right(&MoveRight, window, cx);
 7474        editor.move_left(&MoveLeft, window, cx);
 7475        editor.move_left(&MoveLeft, window, cx);
 7476        editor.backspace(&Default::default(), window, cx);
 7477    });
 7478
 7479    cx.assert_editor_state(
 7480        &"
 7481            {ˇ}
 7482            {ˇ}]]
 7483            {ˇ}
 7484        "
 7485        .unindent(),
 7486    );
 7487
 7488    cx.update_editor(|editor, window, cx| {
 7489        editor.backspace(&Default::default(), window, cx);
 7490    });
 7491
 7492    cx.assert_editor_state(
 7493        &"
 7494            ˇ
 7495            ˇ]]
 7496            ˇ
 7497        "
 7498        .unindent(),
 7499    );
 7500}
 7501
 7502#[gpui::test]
 7503async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7504    init_test(cx, |_| {});
 7505
 7506    let language = Arc::new(Language::new(
 7507        LanguageConfig::default(),
 7508        Some(tree_sitter_rust::LANGUAGE.into()),
 7509    ));
 7510
 7511    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7512    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7513    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7514    editor
 7515        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7516        .await;
 7517
 7518    editor.update_in(cx, |editor, window, cx| {
 7519        editor.set_auto_replace_emoji_shortcode(true);
 7520
 7521        editor.handle_input("Hello ", window, cx);
 7522        editor.handle_input(":wave", window, cx);
 7523        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7524
 7525        editor.handle_input(":", window, cx);
 7526        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7527
 7528        editor.handle_input(" :smile", window, cx);
 7529        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7530
 7531        editor.handle_input(":", window, cx);
 7532        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7533
 7534        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7535        editor.handle_input(":wave", window, cx);
 7536        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7537
 7538        editor.handle_input(":", window, cx);
 7539        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7540
 7541        editor.handle_input(":1", window, cx);
 7542        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7543
 7544        editor.handle_input(":", window, cx);
 7545        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7546
 7547        // Ensure shortcode does not get replaced when it is part of a word
 7548        editor.handle_input(" Test:wave", window, cx);
 7549        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7550
 7551        editor.handle_input(":", window, cx);
 7552        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7553
 7554        editor.set_auto_replace_emoji_shortcode(false);
 7555
 7556        // Ensure shortcode does not get replaced when auto replace is off
 7557        editor.handle_input(" :wave", window, cx);
 7558        assert_eq!(
 7559            editor.text(cx),
 7560            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7561        );
 7562
 7563        editor.handle_input(":", window, cx);
 7564        assert_eq!(
 7565            editor.text(cx),
 7566            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7567        );
 7568    });
 7569}
 7570
 7571#[gpui::test]
 7572async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7573    init_test(cx, |_| {});
 7574
 7575    let (text, insertion_ranges) = marked_text_ranges(
 7576        indoc! {"
 7577            ˇ
 7578        "},
 7579        false,
 7580    );
 7581
 7582    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7583    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7584
 7585    _ = editor.update_in(cx, |editor, window, cx| {
 7586        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7587
 7588        editor
 7589            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7590            .unwrap();
 7591
 7592        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7593            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7594            assert_eq!(editor.text(cx), expected_text);
 7595            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7596        }
 7597
 7598        assert(
 7599            editor,
 7600            cx,
 7601            indoc! {"
 7602            type «» =•
 7603            "},
 7604        );
 7605
 7606        assert!(editor.context_menu_visible(), "There should be a matches");
 7607    });
 7608}
 7609
 7610#[gpui::test]
 7611async fn test_snippets(cx: &mut TestAppContext) {
 7612    init_test(cx, |_| {});
 7613
 7614    let (text, insertion_ranges) = marked_text_ranges(
 7615        indoc! {"
 7616            a.ˇ b
 7617            a.ˇ b
 7618            a.ˇ b
 7619        "},
 7620        false,
 7621    );
 7622
 7623    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7624    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7625
 7626    editor.update_in(cx, |editor, window, cx| {
 7627        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7628
 7629        editor
 7630            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7631            .unwrap();
 7632
 7633        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7634            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7635            assert_eq!(editor.text(cx), expected_text);
 7636            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7637        }
 7638
 7639        assert(
 7640            editor,
 7641            cx,
 7642            indoc! {"
 7643                a.f(«one», two, «three») b
 7644                a.f(«one», two, «three») b
 7645                a.f(«one», two, «three») b
 7646            "},
 7647        );
 7648
 7649        // Can't move earlier than the first tab stop
 7650        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7651        assert(
 7652            editor,
 7653            cx,
 7654            indoc! {"
 7655                a.f(«one», two, «three») b
 7656                a.f(«one», two, «three») b
 7657                a.f(«one», two, «three») b
 7658            "},
 7659        );
 7660
 7661        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7662        assert(
 7663            editor,
 7664            cx,
 7665            indoc! {"
 7666                a.f(one, «two», three) b
 7667                a.f(one, «two», three) b
 7668                a.f(one, «two», three) b
 7669            "},
 7670        );
 7671
 7672        editor.move_to_prev_snippet_tabstop(window, cx);
 7673        assert(
 7674            editor,
 7675            cx,
 7676            indoc! {"
 7677                a.f(«one», two, «three») b
 7678                a.f(«one», two, «three») b
 7679                a.f(«one», two, «three») b
 7680            "},
 7681        );
 7682
 7683        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7684        assert(
 7685            editor,
 7686            cx,
 7687            indoc! {"
 7688                a.f(one, «two», three) b
 7689                a.f(one, «two», three) b
 7690                a.f(one, «two», three) b
 7691            "},
 7692        );
 7693        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7694        assert(
 7695            editor,
 7696            cx,
 7697            indoc! {"
 7698                a.f(one, two, three)ˇ b
 7699                a.f(one, two, three)ˇ b
 7700                a.f(one, two, three)ˇ b
 7701            "},
 7702        );
 7703
 7704        // As soon as the last tab stop is reached, snippet state is gone
 7705        editor.move_to_prev_snippet_tabstop(window, cx);
 7706        assert(
 7707            editor,
 7708            cx,
 7709            indoc! {"
 7710                a.f(one, two, three)ˇ b
 7711                a.f(one, two, three)ˇ b
 7712                a.f(one, two, three)ˇ b
 7713            "},
 7714        );
 7715    });
 7716}
 7717
 7718#[gpui::test]
 7719async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7720    init_test(cx, |_| {});
 7721
 7722    let fs = FakeFs::new(cx.executor());
 7723    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7724
 7725    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7726
 7727    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7728    language_registry.add(rust_lang());
 7729    let mut fake_servers = language_registry.register_fake_lsp(
 7730        "Rust",
 7731        FakeLspAdapter {
 7732            capabilities: lsp::ServerCapabilities {
 7733                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7734                ..Default::default()
 7735            },
 7736            ..Default::default()
 7737        },
 7738    );
 7739
 7740    let buffer = project
 7741        .update(cx, |project, cx| {
 7742            project.open_local_buffer(path!("/file.rs"), cx)
 7743        })
 7744        .await
 7745        .unwrap();
 7746
 7747    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7748    let (editor, cx) = cx.add_window_view(|window, cx| {
 7749        build_editor_with_project(project.clone(), buffer, window, cx)
 7750    });
 7751    editor.update_in(cx, |editor, window, cx| {
 7752        editor.set_text("one\ntwo\nthree\n", window, cx)
 7753    });
 7754    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7755
 7756    cx.executor().start_waiting();
 7757    let fake_server = fake_servers.next().await.unwrap();
 7758
 7759    let save = editor
 7760        .update_in(cx, |editor, window, cx| {
 7761            editor.save(true, project.clone(), window, cx)
 7762        })
 7763        .unwrap();
 7764    fake_server
 7765        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7766            assert_eq!(
 7767                params.text_document.uri,
 7768                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7769            );
 7770            assert_eq!(params.options.tab_size, 4);
 7771            Ok(Some(vec![lsp::TextEdit::new(
 7772                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7773                ", ".to_string(),
 7774            )]))
 7775        })
 7776        .next()
 7777        .await;
 7778    cx.executor().start_waiting();
 7779    save.await;
 7780
 7781    assert_eq!(
 7782        editor.update(cx, |editor, cx| editor.text(cx)),
 7783        "one, two\nthree\n"
 7784    );
 7785    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7786
 7787    editor.update_in(cx, |editor, window, cx| {
 7788        editor.set_text("one\ntwo\nthree\n", window, cx)
 7789    });
 7790    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7791
 7792    // Ensure we can still save even if formatting hangs.
 7793    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7794        move |params, _| async move {
 7795            assert_eq!(
 7796                params.text_document.uri,
 7797                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7798            );
 7799            futures::future::pending::<()>().await;
 7800            unreachable!()
 7801        },
 7802    );
 7803    let save = editor
 7804        .update_in(cx, |editor, window, cx| {
 7805            editor.save(true, project.clone(), window, cx)
 7806        })
 7807        .unwrap();
 7808    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7809    cx.executor().start_waiting();
 7810    save.await;
 7811    assert_eq!(
 7812        editor.update(cx, |editor, cx| editor.text(cx)),
 7813        "one\ntwo\nthree\n"
 7814    );
 7815    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7816
 7817    // For non-dirty buffer, no formatting request should be sent
 7818    let save = editor
 7819        .update_in(cx, |editor, window, cx| {
 7820            editor.save(true, project.clone(), window, cx)
 7821        })
 7822        .unwrap();
 7823    let _pending_format_request = fake_server
 7824        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7825            panic!("Should not be invoked on non-dirty buffer");
 7826        })
 7827        .next();
 7828    cx.executor().start_waiting();
 7829    save.await;
 7830
 7831    // Set rust language override and assert overridden tabsize is sent to language server
 7832    update_test_language_settings(cx, |settings| {
 7833        settings.languages.insert(
 7834            "Rust".into(),
 7835            LanguageSettingsContent {
 7836                tab_size: NonZeroU32::new(8),
 7837                ..Default::default()
 7838            },
 7839        );
 7840    });
 7841
 7842    editor.update_in(cx, |editor, window, cx| {
 7843        editor.set_text("somehting_new\n", window, cx)
 7844    });
 7845    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7846    let save = editor
 7847        .update_in(cx, |editor, window, cx| {
 7848            editor.save(true, project.clone(), window, cx)
 7849        })
 7850        .unwrap();
 7851    fake_server
 7852        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7853            assert_eq!(
 7854                params.text_document.uri,
 7855                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7856            );
 7857            assert_eq!(params.options.tab_size, 8);
 7858            Ok(Some(vec![]))
 7859        })
 7860        .next()
 7861        .await;
 7862    cx.executor().start_waiting();
 7863    save.await;
 7864}
 7865
 7866#[gpui::test]
 7867async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7868    init_test(cx, |_| {});
 7869
 7870    let cols = 4;
 7871    let rows = 10;
 7872    let sample_text_1 = sample_text(rows, cols, 'a');
 7873    assert_eq!(
 7874        sample_text_1,
 7875        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7876    );
 7877    let sample_text_2 = sample_text(rows, cols, 'l');
 7878    assert_eq!(
 7879        sample_text_2,
 7880        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7881    );
 7882    let sample_text_3 = sample_text(rows, cols, 'v');
 7883    assert_eq!(
 7884        sample_text_3,
 7885        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7886    );
 7887
 7888    let fs = FakeFs::new(cx.executor());
 7889    fs.insert_tree(
 7890        path!("/a"),
 7891        json!({
 7892            "main.rs": sample_text_1,
 7893            "other.rs": sample_text_2,
 7894            "lib.rs": sample_text_3,
 7895        }),
 7896    )
 7897    .await;
 7898
 7899    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7900    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7901    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7902
 7903    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7904    language_registry.add(rust_lang());
 7905    let mut fake_servers = language_registry.register_fake_lsp(
 7906        "Rust",
 7907        FakeLspAdapter {
 7908            capabilities: lsp::ServerCapabilities {
 7909                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7910                ..Default::default()
 7911            },
 7912            ..Default::default()
 7913        },
 7914    );
 7915
 7916    let worktree = project.update(cx, |project, cx| {
 7917        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7918        assert_eq!(worktrees.len(), 1);
 7919        worktrees.pop().unwrap()
 7920    });
 7921    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7922
 7923    let buffer_1 = project
 7924        .update(cx, |project, cx| {
 7925            project.open_buffer((worktree_id, "main.rs"), cx)
 7926        })
 7927        .await
 7928        .unwrap();
 7929    let buffer_2 = project
 7930        .update(cx, |project, cx| {
 7931            project.open_buffer((worktree_id, "other.rs"), cx)
 7932        })
 7933        .await
 7934        .unwrap();
 7935    let buffer_3 = project
 7936        .update(cx, |project, cx| {
 7937            project.open_buffer((worktree_id, "lib.rs"), cx)
 7938        })
 7939        .await
 7940        .unwrap();
 7941
 7942    let multi_buffer = cx.new(|cx| {
 7943        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7944        multi_buffer.push_excerpts(
 7945            buffer_1.clone(),
 7946            [
 7947                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7948                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7949                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7950            ],
 7951            cx,
 7952        );
 7953        multi_buffer.push_excerpts(
 7954            buffer_2.clone(),
 7955            [
 7956                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7957                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7958                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7959            ],
 7960            cx,
 7961        );
 7962        multi_buffer.push_excerpts(
 7963            buffer_3.clone(),
 7964            [
 7965                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7966                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7967                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7968            ],
 7969            cx,
 7970        );
 7971        multi_buffer
 7972    });
 7973    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7974        Editor::new(
 7975            EditorMode::Full,
 7976            multi_buffer,
 7977            Some(project.clone()),
 7978            window,
 7979            cx,
 7980        )
 7981    });
 7982
 7983    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7984        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7985            s.select_ranges(Some(1..2))
 7986        });
 7987        editor.insert("|one|two|three|", window, cx);
 7988    });
 7989    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7990    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7991        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7992            s.select_ranges(Some(60..70))
 7993        });
 7994        editor.insert("|four|five|six|", window, cx);
 7995    });
 7996    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7997
 7998    // First two buffers should be edited, but not the third one.
 7999    assert_eq!(
 8000        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8001        "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}",
 8002    );
 8003    buffer_1.update(cx, |buffer, _| {
 8004        assert!(buffer.is_dirty());
 8005        assert_eq!(
 8006            buffer.text(),
 8007            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8008        )
 8009    });
 8010    buffer_2.update(cx, |buffer, _| {
 8011        assert!(buffer.is_dirty());
 8012        assert_eq!(
 8013            buffer.text(),
 8014            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8015        )
 8016    });
 8017    buffer_3.update(cx, |buffer, _| {
 8018        assert!(!buffer.is_dirty());
 8019        assert_eq!(buffer.text(), sample_text_3,)
 8020    });
 8021    cx.executor().run_until_parked();
 8022
 8023    cx.executor().start_waiting();
 8024    let save = multi_buffer_editor
 8025        .update_in(cx, |editor, window, cx| {
 8026            editor.save(true, project.clone(), window, cx)
 8027        })
 8028        .unwrap();
 8029
 8030    let fake_server = fake_servers.next().await.unwrap();
 8031    fake_server
 8032        .server
 8033        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8034            Ok(Some(vec![lsp::TextEdit::new(
 8035                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8036                format!("[{} formatted]", params.text_document.uri),
 8037            )]))
 8038        })
 8039        .detach();
 8040    save.await;
 8041
 8042    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8043    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8044    assert_eq!(
 8045        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8046        uri!(
 8047            "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}"
 8048        ),
 8049    );
 8050    buffer_1.update(cx, |buffer, _| {
 8051        assert!(!buffer.is_dirty());
 8052        assert_eq!(
 8053            buffer.text(),
 8054            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8055        )
 8056    });
 8057    buffer_2.update(cx, |buffer, _| {
 8058        assert!(!buffer.is_dirty());
 8059        assert_eq!(
 8060            buffer.text(),
 8061            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8062        )
 8063    });
 8064    buffer_3.update(cx, |buffer, _| {
 8065        assert!(!buffer.is_dirty());
 8066        assert_eq!(buffer.text(), sample_text_3,)
 8067    });
 8068}
 8069
 8070#[gpui::test]
 8071async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8072    init_test(cx, |_| {});
 8073
 8074    let fs = FakeFs::new(cx.executor());
 8075    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8076
 8077    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8078
 8079    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8080    language_registry.add(rust_lang());
 8081    let mut fake_servers = language_registry.register_fake_lsp(
 8082        "Rust",
 8083        FakeLspAdapter {
 8084            capabilities: lsp::ServerCapabilities {
 8085                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8086                ..Default::default()
 8087            },
 8088            ..Default::default()
 8089        },
 8090    );
 8091
 8092    let buffer = project
 8093        .update(cx, |project, cx| {
 8094            project.open_local_buffer(path!("/file.rs"), cx)
 8095        })
 8096        .await
 8097        .unwrap();
 8098
 8099    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8100    let (editor, cx) = cx.add_window_view(|window, cx| {
 8101        build_editor_with_project(project.clone(), buffer, window, cx)
 8102    });
 8103    editor.update_in(cx, |editor, window, cx| {
 8104        editor.set_text("one\ntwo\nthree\n", window, cx)
 8105    });
 8106    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8107
 8108    cx.executor().start_waiting();
 8109    let fake_server = fake_servers.next().await.unwrap();
 8110
 8111    let save = editor
 8112        .update_in(cx, |editor, window, cx| {
 8113            editor.save(true, project.clone(), window, cx)
 8114        })
 8115        .unwrap();
 8116    fake_server
 8117        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8118            assert_eq!(
 8119                params.text_document.uri,
 8120                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8121            );
 8122            assert_eq!(params.options.tab_size, 4);
 8123            Ok(Some(vec![lsp::TextEdit::new(
 8124                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8125                ", ".to_string(),
 8126            )]))
 8127        })
 8128        .next()
 8129        .await;
 8130    cx.executor().start_waiting();
 8131    save.await;
 8132    assert_eq!(
 8133        editor.update(cx, |editor, cx| editor.text(cx)),
 8134        "one, two\nthree\n"
 8135    );
 8136    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8137
 8138    editor.update_in(cx, |editor, window, cx| {
 8139        editor.set_text("one\ntwo\nthree\n", window, cx)
 8140    });
 8141    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8142
 8143    // Ensure we can still save even if formatting hangs.
 8144    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8145        move |params, _| async move {
 8146            assert_eq!(
 8147                params.text_document.uri,
 8148                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8149            );
 8150            futures::future::pending::<()>().await;
 8151            unreachable!()
 8152        },
 8153    );
 8154    let save = editor
 8155        .update_in(cx, |editor, window, cx| {
 8156            editor.save(true, project.clone(), window, cx)
 8157        })
 8158        .unwrap();
 8159    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8160    cx.executor().start_waiting();
 8161    save.await;
 8162    assert_eq!(
 8163        editor.update(cx, |editor, cx| editor.text(cx)),
 8164        "one\ntwo\nthree\n"
 8165    );
 8166    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8167
 8168    // For non-dirty buffer, no formatting request should be sent
 8169    let save = editor
 8170        .update_in(cx, |editor, window, cx| {
 8171            editor.save(true, project.clone(), window, cx)
 8172        })
 8173        .unwrap();
 8174    let _pending_format_request = fake_server
 8175        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8176            panic!("Should not be invoked on non-dirty buffer");
 8177        })
 8178        .next();
 8179    cx.executor().start_waiting();
 8180    save.await;
 8181
 8182    // Set Rust language override and assert overridden tabsize is sent to language server
 8183    update_test_language_settings(cx, |settings| {
 8184        settings.languages.insert(
 8185            "Rust".into(),
 8186            LanguageSettingsContent {
 8187                tab_size: NonZeroU32::new(8),
 8188                ..Default::default()
 8189            },
 8190        );
 8191    });
 8192
 8193    editor.update_in(cx, |editor, window, cx| {
 8194        editor.set_text("somehting_new\n", window, cx)
 8195    });
 8196    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8197    let save = editor
 8198        .update_in(cx, |editor, window, cx| {
 8199            editor.save(true, project.clone(), window, cx)
 8200        })
 8201        .unwrap();
 8202    fake_server
 8203        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8204            assert_eq!(
 8205                params.text_document.uri,
 8206                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8207            );
 8208            assert_eq!(params.options.tab_size, 8);
 8209            Ok(Some(vec![]))
 8210        })
 8211        .next()
 8212        .await;
 8213    cx.executor().start_waiting();
 8214    save.await;
 8215}
 8216
 8217#[gpui::test]
 8218async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8219    init_test(cx, |settings| {
 8220        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8221            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8222        ))
 8223    });
 8224
 8225    let fs = FakeFs::new(cx.executor());
 8226    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8227
 8228    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8229
 8230    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8231    language_registry.add(Arc::new(Language::new(
 8232        LanguageConfig {
 8233            name: "Rust".into(),
 8234            matcher: LanguageMatcher {
 8235                path_suffixes: vec!["rs".to_string()],
 8236                ..Default::default()
 8237            },
 8238            ..LanguageConfig::default()
 8239        },
 8240        Some(tree_sitter_rust::LANGUAGE.into()),
 8241    )));
 8242    update_test_language_settings(cx, |settings| {
 8243        // Enable Prettier formatting for the same buffer, and ensure
 8244        // LSP is called instead of Prettier.
 8245        settings.defaults.prettier = Some(PrettierSettings {
 8246            allowed: true,
 8247            ..PrettierSettings::default()
 8248        });
 8249    });
 8250    let mut fake_servers = language_registry.register_fake_lsp(
 8251        "Rust",
 8252        FakeLspAdapter {
 8253            capabilities: lsp::ServerCapabilities {
 8254                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8255                ..Default::default()
 8256            },
 8257            ..Default::default()
 8258        },
 8259    );
 8260
 8261    let buffer = project
 8262        .update(cx, |project, cx| {
 8263            project.open_local_buffer(path!("/file.rs"), cx)
 8264        })
 8265        .await
 8266        .unwrap();
 8267
 8268    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8269    let (editor, cx) = cx.add_window_view(|window, cx| {
 8270        build_editor_with_project(project.clone(), buffer, window, cx)
 8271    });
 8272    editor.update_in(cx, |editor, window, cx| {
 8273        editor.set_text("one\ntwo\nthree\n", window, cx)
 8274    });
 8275
 8276    cx.executor().start_waiting();
 8277    let fake_server = fake_servers.next().await.unwrap();
 8278
 8279    let format = editor
 8280        .update_in(cx, |editor, window, cx| {
 8281            editor.perform_format(
 8282                project.clone(),
 8283                FormatTrigger::Manual,
 8284                FormatTarget::Buffers,
 8285                window,
 8286                cx,
 8287            )
 8288        })
 8289        .unwrap();
 8290    fake_server
 8291        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8292            assert_eq!(
 8293                params.text_document.uri,
 8294                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8295            );
 8296            assert_eq!(params.options.tab_size, 4);
 8297            Ok(Some(vec![lsp::TextEdit::new(
 8298                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8299                ", ".to_string(),
 8300            )]))
 8301        })
 8302        .next()
 8303        .await;
 8304    cx.executor().start_waiting();
 8305    format.await;
 8306    assert_eq!(
 8307        editor.update(cx, |editor, cx| editor.text(cx)),
 8308        "one, two\nthree\n"
 8309    );
 8310
 8311    editor.update_in(cx, |editor, window, cx| {
 8312        editor.set_text("one\ntwo\nthree\n", window, cx)
 8313    });
 8314    // Ensure we don't lock if formatting hangs.
 8315    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8316        move |params, _| async move {
 8317            assert_eq!(
 8318                params.text_document.uri,
 8319                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8320            );
 8321            futures::future::pending::<()>().await;
 8322            unreachable!()
 8323        },
 8324    );
 8325    let format = editor
 8326        .update_in(cx, |editor, window, cx| {
 8327            editor.perform_format(
 8328                project,
 8329                FormatTrigger::Manual,
 8330                FormatTarget::Buffers,
 8331                window,
 8332                cx,
 8333            )
 8334        })
 8335        .unwrap();
 8336    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8337    cx.executor().start_waiting();
 8338    format.await;
 8339    assert_eq!(
 8340        editor.update(cx, |editor, cx| editor.text(cx)),
 8341        "one\ntwo\nthree\n"
 8342    );
 8343}
 8344
 8345#[gpui::test]
 8346async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8347    init_test(cx, |settings| {
 8348        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8349            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8350        ))
 8351    });
 8352
 8353    let fs = FakeFs::new(cx.executor());
 8354    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8355
 8356    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8357
 8358    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8359    language_registry.add(Arc::new(Language::new(
 8360        LanguageConfig {
 8361            name: "TypeScript".into(),
 8362            matcher: LanguageMatcher {
 8363                path_suffixes: vec!["ts".to_string()],
 8364                ..Default::default()
 8365            },
 8366            ..LanguageConfig::default()
 8367        },
 8368        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8369    )));
 8370    update_test_language_settings(cx, |settings| {
 8371        settings.defaults.prettier = Some(PrettierSettings {
 8372            allowed: true,
 8373            ..PrettierSettings::default()
 8374        });
 8375    });
 8376    let mut fake_servers = language_registry.register_fake_lsp(
 8377        "TypeScript",
 8378        FakeLspAdapter {
 8379            capabilities: lsp::ServerCapabilities {
 8380                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8381                ..Default::default()
 8382            },
 8383            ..Default::default()
 8384        },
 8385    );
 8386
 8387    let buffer = project
 8388        .update(cx, |project, cx| {
 8389            project.open_local_buffer(path!("/file.ts"), cx)
 8390        })
 8391        .await
 8392        .unwrap();
 8393
 8394    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8395    let (editor, cx) = cx.add_window_view(|window, cx| {
 8396        build_editor_with_project(project.clone(), buffer, window, cx)
 8397    });
 8398    editor.update_in(cx, |editor, window, cx| {
 8399        editor.set_text(
 8400            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8401            window,
 8402            cx,
 8403        )
 8404    });
 8405
 8406    cx.executor().start_waiting();
 8407    let fake_server = fake_servers.next().await.unwrap();
 8408
 8409    let format = editor
 8410        .update_in(cx, |editor, window, cx| {
 8411            editor.perform_code_action_kind(
 8412                project.clone(),
 8413                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8414                window,
 8415                cx,
 8416            )
 8417        })
 8418        .unwrap();
 8419    fake_server
 8420        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8421            assert_eq!(
 8422                params.text_document.uri,
 8423                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8424            );
 8425            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8426                lsp::CodeAction {
 8427                    title: "Organize Imports".to_string(),
 8428                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8429                    edit: Some(lsp::WorkspaceEdit {
 8430                        changes: Some(
 8431                            [(
 8432                                params.text_document.uri.clone(),
 8433                                vec![lsp::TextEdit::new(
 8434                                    lsp::Range::new(
 8435                                        lsp::Position::new(1, 0),
 8436                                        lsp::Position::new(2, 0),
 8437                                    ),
 8438                                    "".to_string(),
 8439                                )],
 8440                            )]
 8441                            .into_iter()
 8442                            .collect(),
 8443                        ),
 8444                        ..Default::default()
 8445                    }),
 8446                    ..Default::default()
 8447                },
 8448            )]))
 8449        })
 8450        .next()
 8451        .await;
 8452    cx.executor().start_waiting();
 8453    format.await;
 8454    assert_eq!(
 8455        editor.update(cx, |editor, cx| editor.text(cx)),
 8456        "import { a } from 'module';\n\nconst x = a;\n"
 8457    );
 8458
 8459    editor.update_in(cx, |editor, window, cx| {
 8460        editor.set_text(
 8461            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8462            window,
 8463            cx,
 8464        )
 8465    });
 8466    // Ensure we don't lock if code action hangs.
 8467    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8468        move |params, _| async move {
 8469            assert_eq!(
 8470                params.text_document.uri,
 8471                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8472            );
 8473            futures::future::pending::<()>().await;
 8474            unreachable!()
 8475        },
 8476    );
 8477    let format = editor
 8478        .update_in(cx, |editor, window, cx| {
 8479            editor.perform_code_action_kind(
 8480                project,
 8481                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8482                window,
 8483                cx,
 8484            )
 8485        })
 8486        .unwrap();
 8487    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8488    cx.executor().start_waiting();
 8489    format.await;
 8490    assert_eq!(
 8491        editor.update(cx, |editor, cx| editor.text(cx)),
 8492        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8493    );
 8494}
 8495
 8496#[gpui::test]
 8497async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8498    init_test(cx, |_| {});
 8499
 8500    let mut cx = EditorLspTestContext::new_rust(
 8501        lsp::ServerCapabilities {
 8502            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8503            ..Default::default()
 8504        },
 8505        cx,
 8506    )
 8507    .await;
 8508
 8509    cx.set_state(indoc! {"
 8510        one.twoˇ
 8511    "});
 8512
 8513    // The format request takes a long time. When it completes, it inserts
 8514    // a newline and an indent before the `.`
 8515    cx.lsp
 8516        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8517            let executor = cx.background_executor().clone();
 8518            async move {
 8519                executor.timer(Duration::from_millis(100)).await;
 8520                Ok(Some(vec![lsp::TextEdit {
 8521                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8522                    new_text: "\n    ".into(),
 8523                }]))
 8524            }
 8525        });
 8526
 8527    // Submit a format request.
 8528    let format_1 = cx
 8529        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8530        .unwrap();
 8531    cx.executor().run_until_parked();
 8532
 8533    // Submit a second format request.
 8534    let format_2 = cx
 8535        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8536        .unwrap();
 8537    cx.executor().run_until_parked();
 8538
 8539    // Wait for both format requests to complete
 8540    cx.executor().advance_clock(Duration::from_millis(200));
 8541    cx.executor().start_waiting();
 8542    format_1.await.unwrap();
 8543    cx.executor().start_waiting();
 8544    format_2.await.unwrap();
 8545
 8546    // The formatting edits only happens once.
 8547    cx.assert_editor_state(indoc! {"
 8548        one
 8549            .twoˇ
 8550    "});
 8551}
 8552
 8553#[gpui::test]
 8554async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8555    init_test(cx, |settings| {
 8556        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8557    });
 8558
 8559    let mut cx = EditorLspTestContext::new_rust(
 8560        lsp::ServerCapabilities {
 8561            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8562            ..Default::default()
 8563        },
 8564        cx,
 8565    )
 8566    .await;
 8567
 8568    // Set up a buffer white some trailing whitespace and no trailing newline.
 8569    cx.set_state(
 8570        &[
 8571            "one ",   //
 8572            "twoˇ",   //
 8573            "three ", //
 8574            "four",   //
 8575        ]
 8576        .join("\n"),
 8577    );
 8578
 8579    // Submit a format request.
 8580    let format = cx
 8581        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8582        .unwrap();
 8583
 8584    // Record which buffer changes have been sent to the language server
 8585    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8586    cx.lsp
 8587        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8588            let buffer_changes = buffer_changes.clone();
 8589            move |params, _| {
 8590                buffer_changes.lock().extend(
 8591                    params
 8592                        .content_changes
 8593                        .into_iter()
 8594                        .map(|e| (e.range.unwrap(), e.text)),
 8595                );
 8596            }
 8597        });
 8598
 8599    // Handle formatting requests to the language server.
 8600    cx.lsp
 8601        .set_request_handler::<lsp::request::Formatting, _, _>({
 8602            let buffer_changes = buffer_changes.clone();
 8603            move |_, _| {
 8604                // When formatting is requested, trailing whitespace has already been stripped,
 8605                // and the trailing newline has already been added.
 8606                assert_eq!(
 8607                    &buffer_changes.lock()[1..],
 8608                    &[
 8609                        (
 8610                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8611                            "".into()
 8612                        ),
 8613                        (
 8614                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8615                            "".into()
 8616                        ),
 8617                        (
 8618                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8619                            "\n".into()
 8620                        ),
 8621                    ]
 8622                );
 8623
 8624                // Insert blank lines between each line of the buffer.
 8625                async move {
 8626                    Ok(Some(vec![
 8627                        lsp::TextEdit {
 8628                            range: lsp::Range::new(
 8629                                lsp::Position::new(1, 0),
 8630                                lsp::Position::new(1, 0),
 8631                            ),
 8632                            new_text: "\n".into(),
 8633                        },
 8634                        lsp::TextEdit {
 8635                            range: lsp::Range::new(
 8636                                lsp::Position::new(2, 0),
 8637                                lsp::Position::new(2, 0),
 8638                            ),
 8639                            new_text: "\n".into(),
 8640                        },
 8641                    ]))
 8642                }
 8643            }
 8644        });
 8645
 8646    // After formatting the buffer, the trailing whitespace is stripped,
 8647    // a newline is appended, and the edits provided by the language server
 8648    // have been applied.
 8649    format.await.unwrap();
 8650    cx.assert_editor_state(
 8651        &[
 8652            "one",   //
 8653            "",      //
 8654            "twoˇ",  //
 8655            "",      //
 8656            "three", //
 8657            "four",  //
 8658            "",      //
 8659        ]
 8660        .join("\n"),
 8661    );
 8662
 8663    // Undoing the formatting undoes the trailing whitespace removal, the
 8664    // trailing newline, and the LSP edits.
 8665    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8666    cx.assert_editor_state(
 8667        &[
 8668            "one ",   //
 8669            "twoˇ",   //
 8670            "three ", //
 8671            "four",   //
 8672        ]
 8673        .join("\n"),
 8674    );
 8675}
 8676
 8677#[gpui::test]
 8678async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8679    cx: &mut TestAppContext,
 8680) {
 8681    init_test(cx, |_| {});
 8682
 8683    cx.update(|cx| {
 8684        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8685            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8686                settings.auto_signature_help = Some(true);
 8687            });
 8688        });
 8689    });
 8690
 8691    let mut cx = EditorLspTestContext::new_rust(
 8692        lsp::ServerCapabilities {
 8693            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8694                ..Default::default()
 8695            }),
 8696            ..Default::default()
 8697        },
 8698        cx,
 8699    )
 8700    .await;
 8701
 8702    let language = Language::new(
 8703        LanguageConfig {
 8704            name: "Rust".into(),
 8705            brackets: BracketPairConfig {
 8706                pairs: vec![
 8707                    BracketPair {
 8708                        start: "{".to_string(),
 8709                        end: "}".to_string(),
 8710                        close: true,
 8711                        surround: true,
 8712                        newline: true,
 8713                    },
 8714                    BracketPair {
 8715                        start: "(".to_string(),
 8716                        end: ")".to_string(),
 8717                        close: true,
 8718                        surround: true,
 8719                        newline: true,
 8720                    },
 8721                    BracketPair {
 8722                        start: "/*".to_string(),
 8723                        end: " */".to_string(),
 8724                        close: true,
 8725                        surround: true,
 8726                        newline: true,
 8727                    },
 8728                    BracketPair {
 8729                        start: "[".to_string(),
 8730                        end: "]".to_string(),
 8731                        close: false,
 8732                        surround: false,
 8733                        newline: true,
 8734                    },
 8735                    BracketPair {
 8736                        start: "\"".to_string(),
 8737                        end: "\"".to_string(),
 8738                        close: true,
 8739                        surround: true,
 8740                        newline: false,
 8741                    },
 8742                    BracketPair {
 8743                        start: "<".to_string(),
 8744                        end: ">".to_string(),
 8745                        close: false,
 8746                        surround: true,
 8747                        newline: true,
 8748                    },
 8749                ],
 8750                ..Default::default()
 8751            },
 8752            autoclose_before: "})]".to_string(),
 8753            ..Default::default()
 8754        },
 8755        Some(tree_sitter_rust::LANGUAGE.into()),
 8756    );
 8757    let language = Arc::new(language);
 8758
 8759    cx.language_registry().add(language.clone());
 8760    cx.update_buffer(|buffer, cx| {
 8761        buffer.set_language(Some(language), cx);
 8762    });
 8763
 8764    cx.set_state(
 8765        &r#"
 8766            fn main() {
 8767                sampleˇ
 8768            }
 8769        "#
 8770        .unindent(),
 8771    );
 8772
 8773    cx.update_editor(|editor, window, cx| {
 8774        editor.handle_input("(", window, cx);
 8775    });
 8776    cx.assert_editor_state(
 8777        &"
 8778            fn main() {
 8779                sample(ˇ)
 8780            }
 8781        "
 8782        .unindent(),
 8783    );
 8784
 8785    let mocked_response = lsp::SignatureHelp {
 8786        signatures: vec![lsp::SignatureInformation {
 8787            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8788            documentation: None,
 8789            parameters: Some(vec![
 8790                lsp::ParameterInformation {
 8791                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8792                    documentation: None,
 8793                },
 8794                lsp::ParameterInformation {
 8795                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8796                    documentation: None,
 8797                },
 8798            ]),
 8799            active_parameter: None,
 8800        }],
 8801        active_signature: Some(0),
 8802        active_parameter: Some(0),
 8803    };
 8804    handle_signature_help_request(&mut cx, mocked_response).await;
 8805
 8806    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8807        .await;
 8808
 8809    cx.editor(|editor, _, _| {
 8810        let signature_help_state = editor.signature_help_state.popover().cloned();
 8811        assert_eq!(
 8812            signature_help_state.unwrap().label,
 8813            "param1: u8, param2: u8"
 8814        );
 8815    });
 8816}
 8817
 8818#[gpui::test]
 8819async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8820    init_test(cx, |_| {});
 8821
 8822    cx.update(|cx| {
 8823        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8824            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8825                settings.auto_signature_help = Some(false);
 8826                settings.show_signature_help_after_edits = Some(false);
 8827            });
 8828        });
 8829    });
 8830
 8831    let mut cx = EditorLspTestContext::new_rust(
 8832        lsp::ServerCapabilities {
 8833            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8834                ..Default::default()
 8835            }),
 8836            ..Default::default()
 8837        },
 8838        cx,
 8839    )
 8840    .await;
 8841
 8842    let language = Language::new(
 8843        LanguageConfig {
 8844            name: "Rust".into(),
 8845            brackets: BracketPairConfig {
 8846                pairs: vec![
 8847                    BracketPair {
 8848                        start: "{".to_string(),
 8849                        end: "}".to_string(),
 8850                        close: true,
 8851                        surround: true,
 8852                        newline: true,
 8853                    },
 8854                    BracketPair {
 8855                        start: "(".to_string(),
 8856                        end: ")".to_string(),
 8857                        close: true,
 8858                        surround: true,
 8859                        newline: true,
 8860                    },
 8861                    BracketPair {
 8862                        start: "/*".to_string(),
 8863                        end: " */".to_string(),
 8864                        close: true,
 8865                        surround: true,
 8866                        newline: true,
 8867                    },
 8868                    BracketPair {
 8869                        start: "[".to_string(),
 8870                        end: "]".to_string(),
 8871                        close: false,
 8872                        surround: false,
 8873                        newline: true,
 8874                    },
 8875                    BracketPair {
 8876                        start: "\"".to_string(),
 8877                        end: "\"".to_string(),
 8878                        close: true,
 8879                        surround: true,
 8880                        newline: false,
 8881                    },
 8882                    BracketPair {
 8883                        start: "<".to_string(),
 8884                        end: ">".to_string(),
 8885                        close: false,
 8886                        surround: true,
 8887                        newline: true,
 8888                    },
 8889                ],
 8890                ..Default::default()
 8891            },
 8892            autoclose_before: "})]".to_string(),
 8893            ..Default::default()
 8894        },
 8895        Some(tree_sitter_rust::LANGUAGE.into()),
 8896    );
 8897    let language = Arc::new(language);
 8898
 8899    cx.language_registry().add(language.clone());
 8900    cx.update_buffer(|buffer, cx| {
 8901        buffer.set_language(Some(language), cx);
 8902    });
 8903
 8904    // Ensure that signature_help is not called when no signature help is enabled.
 8905    cx.set_state(
 8906        &r#"
 8907            fn main() {
 8908                sampleˇ
 8909            }
 8910        "#
 8911        .unindent(),
 8912    );
 8913    cx.update_editor(|editor, window, cx| {
 8914        editor.handle_input("(", window, cx);
 8915    });
 8916    cx.assert_editor_state(
 8917        &"
 8918            fn main() {
 8919                sample(ˇ)
 8920            }
 8921        "
 8922        .unindent(),
 8923    );
 8924    cx.editor(|editor, _, _| {
 8925        assert!(editor.signature_help_state.task().is_none());
 8926    });
 8927
 8928    let mocked_response = lsp::SignatureHelp {
 8929        signatures: vec![lsp::SignatureInformation {
 8930            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8931            documentation: None,
 8932            parameters: Some(vec![
 8933                lsp::ParameterInformation {
 8934                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8935                    documentation: None,
 8936                },
 8937                lsp::ParameterInformation {
 8938                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8939                    documentation: None,
 8940                },
 8941            ]),
 8942            active_parameter: None,
 8943        }],
 8944        active_signature: Some(0),
 8945        active_parameter: Some(0),
 8946    };
 8947
 8948    // Ensure that signature_help is called when enabled afte edits
 8949    cx.update(|_, cx| {
 8950        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8951            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8952                settings.auto_signature_help = Some(false);
 8953                settings.show_signature_help_after_edits = Some(true);
 8954            });
 8955        });
 8956    });
 8957    cx.set_state(
 8958        &r#"
 8959            fn main() {
 8960                sampleˇ
 8961            }
 8962        "#
 8963        .unindent(),
 8964    );
 8965    cx.update_editor(|editor, window, cx| {
 8966        editor.handle_input("(", window, cx);
 8967    });
 8968    cx.assert_editor_state(
 8969        &"
 8970            fn main() {
 8971                sample(ˇ)
 8972            }
 8973        "
 8974        .unindent(),
 8975    );
 8976    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8977    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8978        .await;
 8979    cx.update_editor(|editor, _, _| {
 8980        let signature_help_state = editor.signature_help_state.popover().cloned();
 8981        assert!(signature_help_state.is_some());
 8982        assert_eq!(
 8983            signature_help_state.unwrap().label,
 8984            "param1: u8, param2: u8"
 8985        );
 8986        editor.signature_help_state = SignatureHelpState::default();
 8987    });
 8988
 8989    // Ensure that signature_help is called when auto signature help override is enabled
 8990    cx.update(|_, cx| {
 8991        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8992            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8993                settings.auto_signature_help = Some(true);
 8994                settings.show_signature_help_after_edits = Some(false);
 8995            });
 8996        });
 8997    });
 8998    cx.set_state(
 8999        &r#"
 9000            fn main() {
 9001                sampleˇ
 9002            }
 9003        "#
 9004        .unindent(),
 9005    );
 9006    cx.update_editor(|editor, window, cx| {
 9007        editor.handle_input("(", window, cx);
 9008    });
 9009    cx.assert_editor_state(
 9010        &"
 9011            fn main() {
 9012                sample(ˇ)
 9013            }
 9014        "
 9015        .unindent(),
 9016    );
 9017    handle_signature_help_request(&mut cx, mocked_response).await;
 9018    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9019        .await;
 9020    cx.editor(|editor, _, _| {
 9021        let signature_help_state = editor.signature_help_state.popover().cloned();
 9022        assert!(signature_help_state.is_some());
 9023        assert_eq!(
 9024            signature_help_state.unwrap().label,
 9025            "param1: u8, param2: u8"
 9026        );
 9027    });
 9028}
 9029
 9030#[gpui::test]
 9031async fn test_signature_help(cx: &mut TestAppContext) {
 9032    init_test(cx, |_| {});
 9033    cx.update(|cx| {
 9034        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9035            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9036                settings.auto_signature_help = Some(true);
 9037            });
 9038        });
 9039    });
 9040
 9041    let mut cx = EditorLspTestContext::new_rust(
 9042        lsp::ServerCapabilities {
 9043            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9044                ..Default::default()
 9045            }),
 9046            ..Default::default()
 9047        },
 9048        cx,
 9049    )
 9050    .await;
 9051
 9052    // A test that directly calls `show_signature_help`
 9053    cx.update_editor(|editor, window, cx| {
 9054        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9055    });
 9056
 9057    let mocked_response = lsp::SignatureHelp {
 9058        signatures: vec![lsp::SignatureInformation {
 9059            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9060            documentation: None,
 9061            parameters: Some(vec![
 9062                lsp::ParameterInformation {
 9063                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9064                    documentation: None,
 9065                },
 9066                lsp::ParameterInformation {
 9067                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9068                    documentation: None,
 9069                },
 9070            ]),
 9071            active_parameter: None,
 9072        }],
 9073        active_signature: Some(0),
 9074        active_parameter: Some(0),
 9075    };
 9076    handle_signature_help_request(&mut cx, mocked_response).await;
 9077
 9078    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9079        .await;
 9080
 9081    cx.editor(|editor, _, _| {
 9082        let signature_help_state = editor.signature_help_state.popover().cloned();
 9083        assert!(signature_help_state.is_some());
 9084        assert_eq!(
 9085            signature_help_state.unwrap().label,
 9086            "param1: u8, param2: u8"
 9087        );
 9088    });
 9089
 9090    // When exiting outside from inside the brackets, `signature_help` is closed.
 9091    cx.set_state(indoc! {"
 9092        fn main() {
 9093            sample(ˇ);
 9094        }
 9095
 9096        fn sample(param1: u8, param2: u8) {}
 9097    "});
 9098
 9099    cx.update_editor(|editor, window, cx| {
 9100        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9101    });
 9102
 9103    let mocked_response = lsp::SignatureHelp {
 9104        signatures: Vec::new(),
 9105        active_signature: None,
 9106        active_parameter: None,
 9107    };
 9108    handle_signature_help_request(&mut cx, mocked_response).await;
 9109
 9110    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9111        .await;
 9112
 9113    cx.editor(|editor, _, _| {
 9114        assert!(!editor.signature_help_state.is_shown());
 9115    });
 9116
 9117    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9118    cx.set_state(indoc! {"
 9119        fn main() {
 9120            sample(ˇ);
 9121        }
 9122
 9123        fn sample(param1: u8, param2: u8) {}
 9124    "});
 9125
 9126    let mocked_response = lsp::SignatureHelp {
 9127        signatures: vec![lsp::SignatureInformation {
 9128            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9129            documentation: None,
 9130            parameters: Some(vec![
 9131                lsp::ParameterInformation {
 9132                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9133                    documentation: None,
 9134                },
 9135                lsp::ParameterInformation {
 9136                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9137                    documentation: None,
 9138                },
 9139            ]),
 9140            active_parameter: None,
 9141        }],
 9142        active_signature: Some(0),
 9143        active_parameter: Some(0),
 9144    };
 9145    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9146    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9147        .await;
 9148    cx.editor(|editor, _, _| {
 9149        assert!(editor.signature_help_state.is_shown());
 9150    });
 9151
 9152    // Restore the popover with more parameter input
 9153    cx.set_state(indoc! {"
 9154        fn main() {
 9155            sample(param1, param2ˇ);
 9156        }
 9157
 9158        fn sample(param1: u8, param2: u8) {}
 9159    "});
 9160
 9161    let mocked_response = lsp::SignatureHelp {
 9162        signatures: vec![lsp::SignatureInformation {
 9163            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9164            documentation: None,
 9165            parameters: Some(vec![
 9166                lsp::ParameterInformation {
 9167                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9168                    documentation: None,
 9169                },
 9170                lsp::ParameterInformation {
 9171                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9172                    documentation: None,
 9173                },
 9174            ]),
 9175            active_parameter: None,
 9176        }],
 9177        active_signature: Some(0),
 9178        active_parameter: Some(1),
 9179    };
 9180    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9181    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9182        .await;
 9183
 9184    // When selecting a range, the popover is gone.
 9185    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9186    cx.update_editor(|editor, window, cx| {
 9187        editor.change_selections(None, window, cx, |s| {
 9188            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9189        })
 9190    });
 9191    cx.assert_editor_state(indoc! {"
 9192        fn main() {
 9193            sample(param1, «ˇparam2»);
 9194        }
 9195
 9196        fn sample(param1: u8, param2: u8) {}
 9197    "});
 9198    cx.editor(|editor, _, _| {
 9199        assert!(!editor.signature_help_state.is_shown());
 9200    });
 9201
 9202    // When unselecting again, the popover is back if within the brackets.
 9203    cx.update_editor(|editor, window, cx| {
 9204        editor.change_selections(None, window, cx, |s| {
 9205            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9206        })
 9207    });
 9208    cx.assert_editor_state(indoc! {"
 9209        fn main() {
 9210            sample(param1, ˇparam2);
 9211        }
 9212
 9213        fn sample(param1: u8, param2: u8) {}
 9214    "});
 9215    handle_signature_help_request(&mut cx, mocked_response).await;
 9216    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9217        .await;
 9218    cx.editor(|editor, _, _| {
 9219        assert!(editor.signature_help_state.is_shown());
 9220    });
 9221
 9222    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9223    cx.update_editor(|editor, window, cx| {
 9224        editor.change_selections(None, window, cx, |s| {
 9225            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9226            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9227        })
 9228    });
 9229    cx.assert_editor_state(indoc! {"
 9230        fn main() {
 9231            sample(param1, ˇparam2);
 9232        }
 9233
 9234        fn sample(param1: u8, param2: u8) {}
 9235    "});
 9236
 9237    let mocked_response = lsp::SignatureHelp {
 9238        signatures: vec![lsp::SignatureInformation {
 9239            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9240            documentation: None,
 9241            parameters: Some(vec![
 9242                lsp::ParameterInformation {
 9243                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9244                    documentation: None,
 9245                },
 9246                lsp::ParameterInformation {
 9247                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9248                    documentation: None,
 9249                },
 9250            ]),
 9251            active_parameter: None,
 9252        }],
 9253        active_signature: Some(0),
 9254        active_parameter: Some(1),
 9255    };
 9256    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9257    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9258        .await;
 9259    cx.update_editor(|editor, _, cx| {
 9260        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9261    });
 9262    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9263        .await;
 9264    cx.update_editor(|editor, window, cx| {
 9265        editor.change_selections(None, window, cx, |s| {
 9266            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9267        })
 9268    });
 9269    cx.assert_editor_state(indoc! {"
 9270        fn main() {
 9271            sample(param1, «ˇparam2»);
 9272        }
 9273
 9274        fn sample(param1: u8, param2: u8) {}
 9275    "});
 9276    cx.update_editor(|editor, window, cx| {
 9277        editor.change_selections(None, window, cx, |s| {
 9278            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9279        })
 9280    });
 9281    cx.assert_editor_state(indoc! {"
 9282        fn main() {
 9283            sample(param1, ˇparam2);
 9284        }
 9285
 9286        fn sample(param1: u8, param2: u8) {}
 9287    "});
 9288    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9289        .await;
 9290}
 9291
 9292#[gpui::test]
 9293async fn test_completion_mode(cx: &mut TestAppContext) {
 9294    init_test(cx, |_| {});
 9295    let mut cx = EditorLspTestContext::new_rust(
 9296        lsp::ServerCapabilities {
 9297            completion_provider: Some(lsp::CompletionOptions {
 9298                resolve_provider: Some(true),
 9299                ..Default::default()
 9300            }),
 9301            ..Default::default()
 9302        },
 9303        cx,
 9304    )
 9305    .await;
 9306
 9307    struct Run {
 9308        run_description: &'static str,
 9309        initial_state: String,
 9310        buffer_marked_text: String,
 9311        completion_text: &'static str,
 9312        expected_with_insert_mode: String,
 9313        expected_with_replace_mode: String,
 9314        expected_with_replace_subsequence_mode: String,
 9315        expected_with_replace_suffix_mode: String,
 9316    }
 9317
 9318    let runs = [
 9319        Run {
 9320            run_description: "Start of word matches completion text",
 9321            initial_state: "before ediˇ after".into(),
 9322            buffer_marked_text: "before <edi|> after".into(),
 9323            completion_text: "editor",
 9324            expected_with_insert_mode: "before editorˇ after".into(),
 9325            expected_with_replace_mode: "before editorˇ after".into(),
 9326            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9327            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9328        },
 9329        Run {
 9330            run_description: "Accept same text at the middle of the word",
 9331            initial_state: "before ediˇtor after".into(),
 9332            buffer_marked_text: "before <edi|tor> after".into(),
 9333            completion_text: "editor",
 9334            expected_with_insert_mode: "before editorˇtor after".into(),
 9335            expected_with_replace_mode: "before ediˇtor after".into(),
 9336            expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
 9337            expected_with_replace_suffix_mode: "before ediˇtor after".into(),
 9338        },
 9339        Run {
 9340            run_description: "End of word matches completion text -- cursor at end",
 9341            initial_state: "before torˇ after".into(),
 9342            buffer_marked_text: "before <tor|> after".into(),
 9343            completion_text: "editor",
 9344            expected_with_insert_mode: "before editorˇ after".into(),
 9345            expected_with_replace_mode: "before editorˇ after".into(),
 9346            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9347            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9348        },
 9349        Run {
 9350            run_description: "End of word matches completion text -- cursor at start",
 9351            initial_state: "before ˇtor after".into(),
 9352            buffer_marked_text: "before <|tor> after".into(),
 9353            completion_text: "editor",
 9354            expected_with_insert_mode: "before editorˇtor after".into(),
 9355            expected_with_replace_mode: "before editorˇ after".into(),
 9356            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9357            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9358        },
 9359        Run {
 9360            run_description: "Prepend text containing whitespace",
 9361            initial_state: "pˇfield: bool".into(),
 9362            buffer_marked_text: "<p|field>: bool".into(),
 9363            completion_text: "pub ",
 9364            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9365            expected_with_replace_mode: "pub ˇ: bool".into(),
 9366            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9367            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9368        },
 9369        Run {
 9370            run_description: "Add element to start of list",
 9371            initial_state: "[element_ˇelement_2]".into(),
 9372            buffer_marked_text: "[<element_|element_2>]".into(),
 9373            completion_text: "element_1",
 9374            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9375            expected_with_replace_mode: "[element_1ˇ]".into(),
 9376            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9377            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9378        },
 9379        Run {
 9380            run_description: "Add element to start of list -- first and second elements are equal",
 9381            initial_state: "[elˇelement]".into(),
 9382            buffer_marked_text: "[<el|element>]".into(),
 9383            completion_text: "element",
 9384            expected_with_insert_mode: "[elementˇelement]".into(),
 9385            expected_with_replace_mode: "[elˇement]".into(),
 9386            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9387            expected_with_replace_suffix_mode: "[elˇement]".into(),
 9388        },
 9389        Run {
 9390            run_description: "Ends with matching suffix",
 9391            initial_state: "SubˇError".into(),
 9392            buffer_marked_text: "<Sub|Error>".into(),
 9393            completion_text: "SubscriptionError",
 9394            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9395            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9396            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9397            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9398        },
 9399        Run {
 9400            run_description: "Suffix is a subsequence -- contiguous",
 9401            initial_state: "SubˇErr".into(),
 9402            buffer_marked_text: "<Sub|Err>".into(),
 9403            completion_text: "SubscriptionError",
 9404            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9405            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9406            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9407            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9408        },
 9409        Run {
 9410            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9411            initial_state: "Suˇscrirr".into(),
 9412            buffer_marked_text: "<Su|scrirr>".into(),
 9413            completion_text: "SubscriptionError",
 9414            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9415            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9416            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9417            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9418        },
 9419        Run {
 9420            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9421            initial_state: "foo(indˇix)".into(),
 9422            buffer_marked_text: "foo(<ind|ix>)".into(),
 9423            completion_text: "node_index",
 9424            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9425            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9426            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9427            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9428        },
 9429    ];
 9430
 9431    for run in runs {
 9432        let run_variations = [
 9433            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9434            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9435            (
 9436                LspInsertMode::ReplaceSubsequence,
 9437                run.expected_with_replace_subsequence_mode,
 9438            ),
 9439            (
 9440                LspInsertMode::ReplaceSuffix,
 9441                run.expected_with_replace_suffix_mode,
 9442            ),
 9443        ];
 9444
 9445        for (lsp_insert_mode, expected_text) in run_variations {
 9446            eprintln!(
 9447                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9448                run.run_description,
 9449            );
 9450
 9451            update_test_language_settings(&mut cx, |settings| {
 9452                settings.defaults.completions = Some(CompletionSettings {
 9453                    lsp_insert_mode,
 9454                    words: WordsCompletionMode::Disabled,
 9455                    lsp: true,
 9456                    lsp_fetch_timeout_ms: 0,
 9457                });
 9458            });
 9459
 9460            cx.set_state(&run.initial_state);
 9461            cx.update_editor(|editor, window, cx| {
 9462                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9463            });
 9464
 9465            let counter = Arc::new(AtomicUsize::new(0));
 9466            handle_completion_request_with_insert_and_replace(
 9467                &mut cx,
 9468                &run.buffer_marked_text,
 9469                vec![run.completion_text],
 9470                counter.clone(),
 9471            )
 9472            .await;
 9473            cx.condition(|editor, _| editor.context_menu_visible())
 9474                .await;
 9475            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9476
 9477            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9478                editor
 9479                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9480                    .unwrap()
 9481            });
 9482            cx.assert_editor_state(&expected_text);
 9483            handle_resolve_completion_request(&mut cx, None).await;
 9484            apply_additional_edits.await.unwrap();
 9485        }
 9486    }
 9487}
 9488
 9489#[gpui::test]
 9490async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9491    init_test(cx, |_| {});
 9492    let mut cx = EditorLspTestContext::new_rust(
 9493        lsp::ServerCapabilities {
 9494            completion_provider: Some(lsp::CompletionOptions {
 9495                resolve_provider: Some(true),
 9496                ..Default::default()
 9497            }),
 9498            ..Default::default()
 9499        },
 9500        cx,
 9501    )
 9502    .await;
 9503
 9504    let initial_state = "SubˇError";
 9505    let buffer_marked_text = "<Sub|Error>";
 9506    let completion_text = "SubscriptionError";
 9507    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9508    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9509
 9510    update_test_language_settings(&mut cx, |settings| {
 9511        settings.defaults.completions = Some(CompletionSettings {
 9512            words: WordsCompletionMode::Disabled,
 9513            // set the opposite here to ensure that the action is overriding the default behavior
 9514            lsp_insert_mode: LspInsertMode::Insert,
 9515            lsp: true,
 9516            lsp_fetch_timeout_ms: 0,
 9517        });
 9518    });
 9519
 9520    cx.set_state(initial_state);
 9521    cx.update_editor(|editor, window, cx| {
 9522        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9523    });
 9524
 9525    let counter = Arc::new(AtomicUsize::new(0));
 9526    handle_completion_request_with_insert_and_replace(
 9527        &mut cx,
 9528        &buffer_marked_text,
 9529        vec![completion_text],
 9530        counter.clone(),
 9531    )
 9532    .await;
 9533    cx.condition(|editor, _| editor.context_menu_visible())
 9534        .await;
 9535    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9536
 9537    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9538        editor
 9539            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9540            .unwrap()
 9541    });
 9542    cx.assert_editor_state(&expected_with_replace_mode);
 9543    handle_resolve_completion_request(&mut cx, None).await;
 9544    apply_additional_edits.await.unwrap();
 9545
 9546    update_test_language_settings(&mut cx, |settings| {
 9547        settings.defaults.completions = Some(CompletionSettings {
 9548            words: WordsCompletionMode::Disabled,
 9549            // set the opposite here to ensure that the action is overriding the default behavior
 9550            lsp_insert_mode: LspInsertMode::Replace,
 9551            lsp: true,
 9552            lsp_fetch_timeout_ms: 0,
 9553        });
 9554    });
 9555
 9556    cx.set_state(initial_state);
 9557    cx.update_editor(|editor, window, cx| {
 9558        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9559    });
 9560    handle_completion_request_with_insert_and_replace(
 9561        &mut cx,
 9562        &buffer_marked_text,
 9563        vec![completion_text],
 9564        counter.clone(),
 9565    )
 9566    .await;
 9567    cx.condition(|editor, _| editor.context_menu_visible())
 9568        .await;
 9569    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9570
 9571    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9572        editor
 9573            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9574            .unwrap()
 9575    });
 9576    cx.assert_editor_state(&expected_with_insert_mode);
 9577    handle_resolve_completion_request(&mut cx, None).await;
 9578    apply_additional_edits.await.unwrap();
 9579}
 9580
 9581#[gpui::test]
 9582async fn test_completion(cx: &mut TestAppContext) {
 9583    init_test(cx, |_| {});
 9584
 9585    let mut cx = EditorLspTestContext::new_rust(
 9586        lsp::ServerCapabilities {
 9587            completion_provider: Some(lsp::CompletionOptions {
 9588                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9589                resolve_provider: Some(true),
 9590                ..Default::default()
 9591            }),
 9592            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9593            ..Default::default()
 9594        },
 9595        cx,
 9596    )
 9597    .await;
 9598    let counter = Arc::new(AtomicUsize::new(0));
 9599
 9600    cx.set_state(indoc! {"
 9601        oneˇ
 9602        two
 9603        three
 9604    "});
 9605    cx.simulate_keystroke(".");
 9606    handle_completion_request(
 9607        &mut cx,
 9608        indoc! {"
 9609            one.|<>
 9610            two
 9611            three
 9612        "},
 9613        vec!["first_completion", "second_completion"],
 9614        counter.clone(),
 9615    )
 9616    .await;
 9617    cx.condition(|editor, _| editor.context_menu_visible())
 9618        .await;
 9619    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9620
 9621    let _handler = handle_signature_help_request(
 9622        &mut cx,
 9623        lsp::SignatureHelp {
 9624            signatures: vec![lsp::SignatureInformation {
 9625                label: "test signature".to_string(),
 9626                documentation: None,
 9627                parameters: Some(vec![lsp::ParameterInformation {
 9628                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9629                    documentation: None,
 9630                }]),
 9631                active_parameter: None,
 9632            }],
 9633            active_signature: None,
 9634            active_parameter: None,
 9635        },
 9636    );
 9637    cx.update_editor(|editor, window, cx| {
 9638        assert!(
 9639            !editor.signature_help_state.is_shown(),
 9640            "No signature help was called for"
 9641        );
 9642        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9643    });
 9644    cx.run_until_parked();
 9645    cx.update_editor(|editor, _, _| {
 9646        assert!(
 9647            !editor.signature_help_state.is_shown(),
 9648            "No signature help should be shown when completions menu is open"
 9649        );
 9650    });
 9651
 9652    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9653        editor.context_menu_next(&Default::default(), window, cx);
 9654        editor
 9655            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9656            .unwrap()
 9657    });
 9658    cx.assert_editor_state(indoc! {"
 9659        one.second_completionˇ
 9660        two
 9661        three
 9662    "});
 9663
 9664    handle_resolve_completion_request(
 9665        &mut cx,
 9666        Some(vec![
 9667            (
 9668                //This overlaps with the primary completion edit which is
 9669                //misbehavior from the LSP spec, test that we filter it out
 9670                indoc! {"
 9671                    one.second_ˇcompletion
 9672                    two
 9673                    threeˇ
 9674                "},
 9675                "overlapping additional edit",
 9676            ),
 9677            (
 9678                indoc! {"
 9679                    one.second_completion
 9680                    two
 9681                    threeˇ
 9682                "},
 9683                "\nadditional edit",
 9684            ),
 9685        ]),
 9686    )
 9687    .await;
 9688    apply_additional_edits.await.unwrap();
 9689    cx.assert_editor_state(indoc! {"
 9690        one.second_completionˇ
 9691        two
 9692        three
 9693        additional edit
 9694    "});
 9695
 9696    cx.set_state(indoc! {"
 9697        one.second_completion
 9698        twoˇ
 9699        threeˇ
 9700        additional edit
 9701    "});
 9702    cx.simulate_keystroke(" ");
 9703    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9704    cx.simulate_keystroke("s");
 9705    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9706
 9707    cx.assert_editor_state(indoc! {"
 9708        one.second_completion
 9709        two sˇ
 9710        three sˇ
 9711        additional edit
 9712    "});
 9713    handle_completion_request(
 9714        &mut cx,
 9715        indoc! {"
 9716            one.second_completion
 9717            two s
 9718            three <s|>
 9719            additional edit
 9720        "},
 9721        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9722        counter.clone(),
 9723    )
 9724    .await;
 9725    cx.condition(|editor, _| editor.context_menu_visible())
 9726        .await;
 9727    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9728
 9729    cx.simulate_keystroke("i");
 9730
 9731    handle_completion_request(
 9732        &mut cx,
 9733        indoc! {"
 9734            one.second_completion
 9735            two si
 9736            three <si|>
 9737            additional edit
 9738        "},
 9739        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9740        counter.clone(),
 9741    )
 9742    .await;
 9743    cx.condition(|editor, _| editor.context_menu_visible())
 9744        .await;
 9745    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9746
 9747    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9748        editor
 9749            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9750            .unwrap()
 9751    });
 9752    cx.assert_editor_state(indoc! {"
 9753        one.second_completion
 9754        two sixth_completionˇ
 9755        three sixth_completionˇ
 9756        additional edit
 9757    "});
 9758
 9759    apply_additional_edits.await.unwrap();
 9760
 9761    update_test_language_settings(&mut cx, |settings| {
 9762        settings.defaults.show_completions_on_input = Some(false);
 9763    });
 9764    cx.set_state("editorˇ");
 9765    cx.simulate_keystroke(".");
 9766    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9767    cx.simulate_keystrokes("c l o");
 9768    cx.assert_editor_state("editor.cloˇ");
 9769    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9770    cx.update_editor(|editor, window, cx| {
 9771        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9772    });
 9773    handle_completion_request(
 9774        &mut cx,
 9775        "editor.<clo|>",
 9776        vec!["close", "clobber"],
 9777        counter.clone(),
 9778    )
 9779    .await;
 9780    cx.condition(|editor, _| editor.context_menu_visible())
 9781        .await;
 9782    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9783
 9784    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9785        editor
 9786            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9787            .unwrap()
 9788    });
 9789    cx.assert_editor_state("editor.closeˇ");
 9790    handle_resolve_completion_request(&mut cx, None).await;
 9791    apply_additional_edits.await.unwrap();
 9792}
 9793
 9794#[gpui::test]
 9795async fn test_word_completion(cx: &mut TestAppContext) {
 9796    let lsp_fetch_timeout_ms = 10;
 9797    init_test(cx, |language_settings| {
 9798        language_settings.defaults.completions = Some(CompletionSettings {
 9799            words: WordsCompletionMode::Fallback,
 9800            lsp: true,
 9801            lsp_fetch_timeout_ms: 10,
 9802            lsp_insert_mode: LspInsertMode::Insert,
 9803        });
 9804    });
 9805
 9806    let mut cx = EditorLspTestContext::new_rust(
 9807        lsp::ServerCapabilities {
 9808            completion_provider: Some(lsp::CompletionOptions {
 9809                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9810                ..lsp::CompletionOptions::default()
 9811            }),
 9812            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9813            ..lsp::ServerCapabilities::default()
 9814        },
 9815        cx,
 9816    )
 9817    .await;
 9818
 9819    let throttle_completions = Arc::new(AtomicBool::new(false));
 9820
 9821    let lsp_throttle_completions = throttle_completions.clone();
 9822    let _completion_requests_handler =
 9823        cx.lsp
 9824            .server
 9825            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9826                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9827                let cx = cx.clone();
 9828                async move {
 9829                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9830                        cx.background_executor()
 9831                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9832                            .await;
 9833                    }
 9834                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9835                        lsp::CompletionItem {
 9836                            label: "first".into(),
 9837                            ..lsp::CompletionItem::default()
 9838                        },
 9839                        lsp::CompletionItem {
 9840                            label: "last".into(),
 9841                            ..lsp::CompletionItem::default()
 9842                        },
 9843                    ])))
 9844                }
 9845            });
 9846
 9847    cx.set_state(indoc! {"
 9848        oneˇ
 9849        two
 9850        three
 9851    "});
 9852    cx.simulate_keystroke(".");
 9853    cx.executor().run_until_parked();
 9854    cx.condition(|editor, _| editor.context_menu_visible())
 9855        .await;
 9856    cx.update_editor(|editor, window, cx| {
 9857        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9858        {
 9859            assert_eq!(
 9860                completion_menu_entries(&menu),
 9861                &["first", "last"],
 9862                "When LSP server is fast to reply, no fallback word completions are used"
 9863            );
 9864        } else {
 9865            panic!("expected completion menu to be open");
 9866        }
 9867        editor.cancel(&Cancel, window, cx);
 9868    });
 9869    cx.executor().run_until_parked();
 9870    cx.condition(|editor, _| !editor.context_menu_visible())
 9871        .await;
 9872
 9873    throttle_completions.store(true, atomic::Ordering::Release);
 9874    cx.simulate_keystroke(".");
 9875    cx.executor()
 9876        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9877    cx.executor().run_until_parked();
 9878    cx.condition(|editor, _| editor.context_menu_visible())
 9879        .await;
 9880    cx.update_editor(|editor, _, _| {
 9881        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9882        {
 9883            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9884                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9885        } else {
 9886            panic!("expected completion menu to be open");
 9887        }
 9888    });
 9889}
 9890
 9891#[gpui::test]
 9892async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9893    init_test(cx, |language_settings| {
 9894        language_settings.defaults.completions = Some(CompletionSettings {
 9895            words: WordsCompletionMode::Enabled,
 9896            lsp: true,
 9897            lsp_fetch_timeout_ms: 0,
 9898            lsp_insert_mode: LspInsertMode::Insert,
 9899        });
 9900    });
 9901
 9902    let mut cx = EditorLspTestContext::new_rust(
 9903        lsp::ServerCapabilities {
 9904            completion_provider: Some(lsp::CompletionOptions {
 9905                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9906                ..lsp::CompletionOptions::default()
 9907            }),
 9908            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9909            ..lsp::ServerCapabilities::default()
 9910        },
 9911        cx,
 9912    )
 9913    .await;
 9914
 9915    let _completion_requests_handler =
 9916        cx.lsp
 9917            .server
 9918            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9919                Ok(Some(lsp::CompletionResponse::Array(vec![
 9920                    lsp::CompletionItem {
 9921                        label: "first".into(),
 9922                        ..lsp::CompletionItem::default()
 9923                    },
 9924                    lsp::CompletionItem {
 9925                        label: "last".into(),
 9926                        ..lsp::CompletionItem::default()
 9927                    },
 9928                ])))
 9929            });
 9930
 9931    cx.set_state(indoc! {"ˇ
 9932        first
 9933        last
 9934        second
 9935    "});
 9936    cx.simulate_keystroke(".");
 9937    cx.executor().run_until_parked();
 9938    cx.condition(|editor, _| editor.context_menu_visible())
 9939        .await;
 9940    cx.update_editor(|editor, _, _| {
 9941        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9942        {
 9943            assert_eq!(
 9944                completion_menu_entries(&menu),
 9945                &["first", "last", "second"],
 9946                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9947            );
 9948        } else {
 9949            panic!("expected completion menu to be open");
 9950        }
 9951    });
 9952}
 9953
 9954#[gpui::test]
 9955async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9956    init_test(cx, |language_settings| {
 9957        language_settings.defaults.completions = Some(CompletionSettings {
 9958            words: WordsCompletionMode::Disabled,
 9959            lsp: true,
 9960            lsp_fetch_timeout_ms: 0,
 9961            lsp_insert_mode: LspInsertMode::Insert,
 9962        });
 9963    });
 9964
 9965    let mut cx = EditorLspTestContext::new_rust(
 9966        lsp::ServerCapabilities {
 9967            completion_provider: Some(lsp::CompletionOptions {
 9968                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9969                ..lsp::CompletionOptions::default()
 9970            }),
 9971            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9972            ..lsp::ServerCapabilities::default()
 9973        },
 9974        cx,
 9975    )
 9976    .await;
 9977
 9978    let _completion_requests_handler =
 9979        cx.lsp
 9980            .server
 9981            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9982                panic!("LSP completions should not be queried when dealing with word completions")
 9983            });
 9984
 9985    cx.set_state(indoc! {"ˇ
 9986        first
 9987        last
 9988        second
 9989    "});
 9990    cx.update_editor(|editor, window, cx| {
 9991        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9992    });
 9993    cx.executor().run_until_parked();
 9994    cx.condition(|editor, _| editor.context_menu_visible())
 9995        .await;
 9996    cx.update_editor(|editor, _, _| {
 9997        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9998        {
 9999            assert_eq!(
10000                completion_menu_entries(&menu),
10001                &["first", "last", "second"],
10002                "`ShowWordCompletions` action should show word completions"
10003            );
10004        } else {
10005            panic!("expected completion menu to be open");
10006        }
10007    });
10008
10009    cx.simulate_keystroke("l");
10010    cx.executor().run_until_parked();
10011    cx.condition(|editor, _| editor.context_menu_visible())
10012        .await;
10013    cx.update_editor(|editor, _, _| {
10014        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10015        {
10016            assert_eq!(
10017                completion_menu_entries(&menu),
10018                &["last"],
10019                "After showing word completions, further editing should filter them and not query the LSP"
10020            );
10021        } else {
10022            panic!("expected completion menu to be open");
10023        }
10024    });
10025}
10026
10027#[gpui::test]
10028async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10029    init_test(cx, |language_settings| {
10030        language_settings.defaults.completions = Some(CompletionSettings {
10031            words: WordsCompletionMode::Fallback,
10032            lsp: false,
10033            lsp_fetch_timeout_ms: 0,
10034            lsp_insert_mode: LspInsertMode::Insert,
10035        });
10036    });
10037
10038    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10039
10040    cx.set_state(indoc! {"ˇ
10041        0_usize
10042        let
10043        33
10044        4.5f32
10045    "});
10046    cx.update_editor(|editor, window, cx| {
10047        editor.show_completions(&ShowCompletions::default(), window, cx);
10048    });
10049    cx.executor().run_until_parked();
10050    cx.condition(|editor, _| editor.context_menu_visible())
10051        .await;
10052    cx.update_editor(|editor, window, cx| {
10053        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10054        {
10055            assert_eq!(
10056                completion_menu_entries(&menu),
10057                &["let"],
10058                "With no digits in the completion query, no digits should be in the word completions"
10059            );
10060        } else {
10061            panic!("expected completion menu to be open");
10062        }
10063        editor.cancel(&Cancel, window, cx);
10064    });
10065
10066    cx.set_state(indoc! {"10067        0_usize
10068        let
10069        3
10070        33.35f32
10071    "});
10072    cx.update_editor(|editor, window, cx| {
10073        editor.show_completions(&ShowCompletions::default(), window, cx);
10074    });
10075    cx.executor().run_until_parked();
10076    cx.condition(|editor, _| editor.context_menu_visible())
10077        .await;
10078    cx.update_editor(|editor, _, _| {
10079        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10080        {
10081            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10082                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10083        } else {
10084            panic!("expected completion menu to be open");
10085        }
10086    });
10087}
10088
10089fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10090    let position = || lsp::Position {
10091        line: params.text_document_position.position.line,
10092        character: params.text_document_position.position.character,
10093    };
10094    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10095        range: lsp::Range {
10096            start: position(),
10097            end: position(),
10098        },
10099        new_text: text.to_string(),
10100    }))
10101}
10102
10103#[gpui::test]
10104async fn test_multiline_completion(cx: &mut TestAppContext) {
10105    init_test(cx, |_| {});
10106
10107    let fs = FakeFs::new(cx.executor());
10108    fs.insert_tree(
10109        path!("/a"),
10110        json!({
10111            "main.ts": "a",
10112        }),
10113    )
10114    .await;
10115
10116    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10117    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10118    let typescript_language = Arc::new(Language::new(
10119        LanguageConfig {
10120            name: "TypeScript".into(),
10121            matcher: LanguageMatcher {
10122                path_suffixes: vec!["ts".to_string()],
10123                ..LanguageMatcher::default()
10124            },
10125            line_comments: vec!["// ".into()],
10126            ..LanguageConfig::default()
10127        },
10128        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10129    ));
10130    language_registry.add(typescript_language.clone());
10131    let mut fake_servers = language_registry.register_fake_lsp(
10132        "TypeScript",
10133        FakeLspAdapter {
10134            capabilities: lsp::ServerCapabilities {
10135                completion_provider: Some(lsp::CompletionOptions {
10136                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10137                    ..lsp::CompletionOptions::default()
10138                }),
10139                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10140                ..lsp::ServerCapabilities::default()
10141            },
10142            // Emulate vtsls label generation
10143            label_for_completion: Some(Box::new(|item, _| {
10144                let text = if let Some(description) = item
10145                    .label_details
10146                    .as_ref()
10147                    .and_then(|label_details| label_details.description.as_ref())
10148                {
10149                    format!("{} {}", item.label, description)
10150                } else if let Some(detail) = &item.detail {
10151                    format!("{} {}", item.label, detail)
10152                } else {
10153                    item.label.clone()
10154                };
10155                let len = text.len();
10156                Some(language::CodeLabel {
10157                    text,
10158                    runs: Vec::new(),
10159                    filter_range: 0..len,
10160                })
10161            })),
10162            ..FakeLspAdapter::default()
10163        },
10164    );
10165    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10166    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10167    let worktree_id = workspace
10168        .update(cx, |workspace, _window, cx| {
10169            workspace.project().update(cx, |project, cx| {
10170                project.worktrees(cx).next().unwrap().read(cx).id()
10171            })
10172        })
10173        .unwrap();
10174    let _buffer = project
10175        .update(cx, |project, cx| {
10176            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10177        })
10178        .await
10179        .unwrap();
10180    let editor = workspace
10181        .update(cx, |workspace, window, cx| {
10182            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10183        })
10184        .unwrap()
10185        .await
10186        .unwrap()
10187        .downcast::<Editor>()
10188        .unwrap();
10189    let fake_server = fake_servers.next().await.unwrap();
10190
10191    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10192    let multiline_label_2 = "a\nb\nc\n";
10193    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10194    let multiline_description = "d\ne\nf\n";
10195    let multiline_detail_2 = "g\nh\ni\n";
10196
10197    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10198        move |params, _| async move {
10199            Ok(Some(lsp::CompletionResponse::Array(vec![
10200                lsp::CompletionItem {
10201                    label: multiline_label.to_string(),
10202                    text_edit: gen_text_edit(&params, "new_text_1"),
10203                    ..lsp::CompletionItem::default()
10204                },
10205                lsp::CompletionItem {
10206                    label: "single line label 1".to_string(),
10207                    detail: Some(multiline_detail.to_string()),
10208                    text_edit: gen_text_edit(&params, "new_text_2"),
10209                    ..lsp::CompletionItem::default()
10210                },
10211                lsp::CompletionItem {
10212                    label: "single line label 2".to_string(),
10213                    label_details: Some(lsp::CompletionItemLabelDetails {
10214                        description: Some(multiline_description.to_string()),
10215                        detail: None,
10216                    }),
10217                    text_edit: gen_text_edit(&params, "new_text_2"),
10218                    ..lsp::CompletionItem::default()
10219                },
10220                lsp::CompletionItem {
10221                    label: multiline_label_2.to_string(),
10222                    detail: Some(multiline_detail_2.to_string()),
10223                    text_edit: gen_text_edit(&params, "new_text_3"),
10224                    ..lsp::CompletionItem::default()
10225                },
10226                lsp::CompletionItem {
10227                    label: "Label with many     spaces and \t but without newlines".to_string(),
10228                    detail: Some(
10229                        "Details with many     spaces and \t but without newlines".to_string(),
10230                    ),
10231                    text_edit: gen_text_edit(&params, "new_text_4"),
10232                    ..lsp::CompletionItem::default()
10233                },
10234            ])))
10235        },
10236    );
10237
10238    editor.update_in(cx, |editor, window, cx| {
10239        cx.focus_self(window);
10240        editor.move_to_end(&MoveToEnd, window, cx);
10241        editor.handle_input(".", window, cx);
10242    });
10243    cx.run_until_parked();
10244    completion_handle.next().await.unwrap();
10245
10246    editor.update(cx, |editor, _| {
10247        assert!(editor.context_menu_visible());
10248        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10249        {
10250            let completion_labels = menu
10251                .completions
10252                .borrow()
10253                .iter()
10254                .map(|c| c.label.text.clone())
10255                .collect::<Vec<_>>();
10256            assert_eq!(
10257                completion_labels,
10258                &[
10259                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10260                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10261                    "single line label 2 d e f ",
10262                    "a b c g h i ",
10263                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10264                ],
10265                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10266            );
10267
10268            for completion in menu
10269                .completions
10270                .borrow()
10271                .iter() {
10272                    assert_eq!(
10273                        completion.label.filter_range,
10274                        0..completion.label.text.len(),
10275                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10276                    );
10277                }
10278        } else {
10279            panic!("expected completion menu to be open");
10280        }
10281    });
10282}
10283
10284#[gpui::test]
10285async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10286    init_test(cx, |_| {});
10287    let mut cx = EditorLspTestContext::new_rust(
10288        lsp::ServerCapabilities {
10289            completion_provider: Some(lsp::CompletionOptions {
10290                trigger_characters: Some(vec![".".to_string()]),
10291                ..Default::default()
10292            }),
10293            ..Default::default()
10294        },
10295        cx,
10296    )
10297    .await;
10298    cx.lsp
10299        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10300            Ok(Some(lsp::CompletionResponse::Array(vec![
10301                lsp::CompletionItem {
10302                    label: "first".into(),
10303                    ..Default::default()
10304                },
10305                lsp::CompletionItem {
10306                    label: "last".into(),
10307                    ..Default::default()
10308                },
10309            ])))
10310        });
10311    cx.set_state("variableˇ");
10312    cx.simulate_keystroke(".");
10313    cx.executor().run_until_parked();
10314
10315    cx.update_editor(|editor, _, _| {
10316        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10317        {
10318            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
10319        } else {
10320            panic!("expected completion menu to be open");
10321        }
10322    });
10323
10324    cx.update_editor(|editor, window, cx| {
10325        editor.move_page_down(&MovePageDown::default(), window, cx);
10326        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10327        {
10328            assert!(
10329                menu.selected_item == 1,
10330                "expected PageDown to select the last item from the context menu"
10331            );
10332        } else {
10333            panic!("expected completion menu to stay open after PageDown");
10334        }
10335    });
10336
10337    cx.update_editor(|editor, window, cx| {
10338        editor.move_page_up(&MovePageUp::default(), window, cx);
10339        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10340        {
10341            assert!(
10342                menu.selected_item == 0,
10343                "expected PageUp to select the first item from the context menu"
10344            );
10345        } else {
10346            panic!("expected completion menu to stay open after PageUp");
10347        }
10348    });
10349}
10350
10351#[gpui::test]
10352async fn test_completion_sort(cx: &mut TestAppContext) {
10353    init_test(cx, |_| {});
10354    let mut cx = EditorLspTestContext::new_rust(
10355        lsp::ServerCapabilities {
10356            completion_provider: Some(lsp::CompletionOptions {
10357                trigger_characters: Some(vec![".".to_string()]),
10358                ..Default::default()
10359            }),
10360            ..Default::default()
10361        },
10362        cx,
10363    )
10364    .await;
10365    cx.lsp
10366        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10367            Ok(Some(lsp::CompletionResponse::Array(vec![
10368                lsp::CompletionItem {
10369                    label: "Range".into(),
10370                    sort_text: Some("a".into()),
10371                    ..Default::default()
10372                },
10373                lsp::CompletionItem {
10374                    label: "r".into(),
10375                    sort_text: Some("b".into()),
10376                    ..Default::default()
10377                },
10378                lsp::CompletionItem {
10379                    label: "ret".into(),
10380                    sort_text: Some("c".into()),
10381                    ..Default::default()
10382                },
10383                lsp::CompletionItem {
10384                    label: "return".into(),
10385                    sort_text: Some("d".into()),
10386                    ..Default::default()
10387                },
10388                lsp::CompletionItem {
10389                    label: "slice".into(),
10390                    sort_text: Some("d".into()),
10391                    ..Default::default()
10392                },
10393            ])))
10394        });
10395    cx.set_state("");
10396    cx.executor().run_until_parked();
10397    cx.update_editor(|editor, window, cx| {
10398        editor.show_completions(
10399            &ShowCompletions {
10400                trigger: Some("r".into()),
10401            },
10402            window,
10403            cx,
10404        );
10405    });
10406    cx.executor().run_until_parked();
10407
10408    cx.update_editor(|editor, _, _| {
10409        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10410        {
10411            assert_eq!(
10412                completion_menu_entries(&menu),
10413                &["r", "ret", "Range", "return"]
10414            );
10415        } else {
10416            panic!("expected completion menu to be open");
10417        }
10418    });
10419}
10420
10421#[gpui::test]
10422async fn test_as_is_completions(cx: &mut TestAppContext) {
10423    init_test(cx, |_| {});
10424    let mut cx = EditorLspTestContext::new_rust(
10425        lsp::ServerCapabilities {
10426            completion_provider: Some(lsp::CompletionOptions {
10427                ..Default::default()
10428            }),
10429            ..Default::default()
10430        },
10431        cx,
10432    )
10433    .await;
10434    cx.lsp
10435        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10436            Ok(Some(lsp::CompletionResponse::Array(vec![
10437                lsp::CompletionItem {
10438                    label: "unsafe".into(),
10439                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10440                        range: lsp::Range {
10441                            start: lsp::Position {
10442                                line: 1,
10443                                character: 2,
10444                            },
10445                            end: lsp::Position {
10446                                line: 1,
10447                                character: 3,
10448                            },
10449                        },
10450                        new_text: "unsafe".to_string(),
10451                    })),
10452                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
10453                    ..Default::default()
10454                },
10455            ])))
10456        });
10457    cx.set_state("fn a() {}\n");
10458    cx.executor().run_until_parked();
10459    cx.update_editor(|editor, window, cx| {
10460        editor.show_completions(
10461            &ShowCompletions {
10462                trigger: Some("\n".into()),
10463            },
10464            window,
10465            cx,
10466        );
10467    });
10468    cx.executor().run_until_parked();
10469
10470    cx.update_editor(|editor, window, cx| {
10471        editor.confirm_completion(&Default::default(), window, cx)
10472    });
10473    cx.executor().run_until_parked();
10474    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
10475}
10476
10477#[gpui::test]
10478async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10479    init_test(cx, |_| {});
10480
10481    let mut cx = EditorLspTestContext::new_rust(
10482        lsp::ServerCapabilities {
10483            completion_provider: Some(lsp::CompletionOptions {
10484                trigger_characters: Some(vec![".".to_string()]),
10485                resolve_provider: Some(true),
10486                ..Default::default()
10487            }),
10488            ..Default::default()
10489        },
10490        cx,
10491    )
10492    .await;
10493
10494    cx.set_state("fn main() { let a = 2ˇ; }");
10495    cx.simulate_keystroke(".");
10496    let completion_item = lsp::CompletionItem {
10497        label: "Some".into(),
10498        kind: Some(lsp::CompletionItemKind::SNIPPET),
10499        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10500        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10501            kind: lsp::MarkupKind::Markdown,
10502            value: "```rust\nSome(2)\n```".to_string(),
10503        })),
10504        deprecated: Some(false),
10505        sort_text: Some("Some".to_string()),
10506        filter_text: Some("Some".to_string()),
10507        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10508        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10509            range: lsp::Range {
10510                start: lsp::Position {
10511                    line: 0,
10512                    character: 22,
10513                },
10514                end: lsp::Position {
10515                    line: 0,
10516                    character: 22,
10517                },
10518            },
10519            new_text: "Some(2)".to_string(),
10520        })),
10521        additional_text_edits: Some(vec![lsp::TextEdit {
10522            range: lsp::Range {
10523                start: lsp::Position {
10524                    line: 0,
10525                    character: 20,
10526                },
10527                end: lsp::Position {
10528                    line: 0,
10529                    character: 22,
10530                },
10531            },
10532            new_text: "".to_string(),
10533        }]),
10534        ..Default::default()
10535    };
10536
10537    let closure_completion_item = completion_item.clone();
10538    let counter = Arc::new(AtomicUsize::new(0));
10539    let counter_clone = counter.clone();
10540    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10541        let task_completion_item = closure_completion_item.clone();
10542        counter_clone.fetch_add(1, atomic::Ordering::Release);
10543        async move {
10544            Ok(Some(lsp::CompletionResponse::Array(vec![
10545                task_completion_item,
10546            ])))
10547        }
10548    });
10549
10550    cx.condition(|editor, _| editor.context_menu_visible())
10551        .await;
10552    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10553    assert!(request.next().await.is_some());
10554    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10555
10556    cx.simulate_keystrokes("S o m");
10557    cx.condition(|editor, _| editor.context_menu_visible())
10558        .await;
10559    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10560    assert!(request.next().await.is_some());
10561    assert!(request.next().await.is_some());
10562    assert!(request.next().await.is_some());
10563    request.close();
10564    assert!(request.next().await.is_none());
10565    assert_eq!(
10566        counter.load(atomic::Ordering::Acquire),
10567        4,
10568        "With the completions menu open, only one LSP request should happen per input"
10569    );
10570}
10571
10572#[gpui::test]
10573async fn test_toggle_comment(cx: &mut TestAppContext) {
10574    init_test(cx, |_| {});
10575    let mut cx = EditorTestContext::new(cx).await;
10576    let language = Arc::new(Language::new(
10577        LanguageConfig {
10578            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10579            ..Default::default()
10580        },
10581        Some(tree_sitter_rust::LANGUAGE.into()),
10582    ));
10583    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10584
10585    // If multiple selections intersect a line, the line is only toggled once.
10586    cx.set_state(indoc! {"
10587        fn a() {
10588            «//b();
10589            ˇ»// «c();
10590            //ˇ»  d();
10591        }
10592    "});
10593
10594    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10595
10596    cx.assert_editor_state(indoc! {"
10597        fn a() {
10598            «b();
10599            c();
10600            ˇ» d();
10601        }
10602    "});
10603
10604    // The comment prefix is inserted at the same column for every line in a
10605    // selection.
10606    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10607
10608    cx.assert_editor_state(indoc! {"
10609        fn a() {
10610            // «b();
10611            // c();
10612            ˇ»//  d();
10613        }
10614    "});
10615
10616    // If a selection ends at the beginning of a line, that line is not toggled.
10617    cx.set_selections_state(indoc! {"
10618        fn a() {
10619            // b();
10620            «// c();
10621        ˇ»    //  d();
10622        }
10623    "});
10624
10625    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10626
10627    cx.assert_editor_state(indoc! {"
10628        fn a() {
10629            // b();
10630            «c();
10631        ˇ»    //  d();
10632        }
10633    "});
10634
10635    // If a selection span a single line and is empty, the line is toggled.
10636    cx.set_state(indoc! {"
10637        fn a() {
10638            a();
10639            b();
10640        ˇ
10641        }
10642    "});
10643
10644    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10645
10646    cx.assert_editor_state(indoc! {"
10647        fn a() {
10648            a();
10649            b();
10650        //•ˇ
10651        }
10652    "});
10653
10654    // If a selection span multiple lines, empty lines are not toggled.
10655    cx.set_state(indoc! {"
10656        fn a() {
10657            «a();
10658
10659            c();ˇ»
10660        }
10661    "});
10662
10663    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10664
10665    cx.assert_editor_state(indoc! {"
10666        fn a() {
10667            // «a();
10668
10669            // c();ˇ»
10670        }
10671    "});
10672
10673    // If a selection includes multiple comment prefixes, all lines are uncommented.
10674    cx.set_state(indoc! {"
10675        fn a() {
10676            «// a();
10677            /// b();
10678            //! c();ˇ»
10679        }
10680    "});
10681
10682    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10683
10684    cx.assert_editor_state(indoc! {"
10685        fn a() {
10686            «a();
10687            b();
10688            c();ˇ»
10689        }
10690    "});
10691}
10692
10693#[gpui::test]
10694async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10695    init_test(cx, |_| {});
10696    let mut cx = EditorTestContext::new(cx).await;
10697    let language = Arc::new(Language::new(
10698        LanguageConfig {
10699            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10700            ..Default::default()
10701        },
10702        Some(tree_sitter_rust::LANGUAGE.into()),
10703    ));
10704    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10705
10706    let toggle_comments = &ToggleComments {
10707        advance_downwards: false,
10708        ignore_indent: true,
10709    };
10710
10711    // If multiple selections intersect a line, the line is only toggled once.
10712    cx.set_state(indoc! {"
10713        fn a() {
10714        //    «b();
10715        //    c();
10716        //    ˇ» d();
10717        }
10718    "});
10719
10720    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10721
10722    cx.assert_editor_state(indoc! {"
10723        fn a() {
10724            «b();
10725            c();
10726            ˇ» d();
10727        }
10728    "});
10729
10730    // The comment prefix is inserted at the beginning of each line
10731    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10732
10733    cx.assert_editor_state(indoc! {"
10734        fn a() {
10735        //    «b();
10736        //    c();
10737        //    ˇ» d();
10738        }
10739    "});
10740
10741    // If a selection ends at the beginning of a line, that line is not toggled.
10742    cx.set_selections_state(indoc! {"
10743        fn a() {
10744        //    b();
10745        //    «c();
10746        ˇ»//     d();
10747        }
10748    "});
10749
10750    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10751
10752    cx.assert_editor_state(indoc! {"
10753        fn a() {
10754        //    b();
10755            «c();
10756        ˇ»//     d();
10757        }
10758    "});
10759
10760    // If a selection span a single line and is empty, the line is toggled.
10761    cx.set_state(indoc! {"
10762        fn a() {
10763            a();
10764            b();
10765        ˇ
10766        }
10767    "});
10768
10769    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10770
10771    cx.assert_editor_state(indoc! {"
10772        fn a() {
10773            a();
10774            b();
10775        //ˇ
10776        }
10777    "});
10778
10779    // If a selection span multiple lines, empty lines are not toggled.
10780    cx.set_state(indoc! {"
10781        fn a() {
10782            «a();
10783
10784            c();ˇ»
10785        }
10786    "});
10787
10788    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10789
10790    cx.assert_editor_state(indoc! {"
10791        fn a() {
10792        //    «a();
10793
10794        //    c();ˇ»
10795        }
10796    "});
10797
10798    // If a selection includes multiple comment prefixes, all lines are uncommented.
10799    cx.set_state(indoc! {"
10800        fn a() {
10801        //    «a();
10802        ///    b();
10803        //!    c();ˇ»
10804        }
10805    "});
10806
10807    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10808
10809    cx.assert_editor_state(indoc! {"
10810        fn a() {
10811            «a();
10812            b();
10813            c();ˇ»
10814        }
10815    "});
10816}
10817
10818#[gpui::test]
10819async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10820    init_test(cx, |_| {});
10821
10822    let language = Arc::new(Language::new(
10823        LanguageConfig {
10824            line_comments: vec!["// ".into()],
10825            ..Default::default()
10826        },
10827        Some(tree_sitter_rust::LANGUAGE.into()),
10828    ));
10829
10830    let mut cx = EditorTestContext::new(cx).await;
10831
10832    cx.language_registry().add(language.clone());
10833    cx.update_buffer(|buffer, cx| {
10834        buffer.set_language(Some(language), cx);
10835    });
10836
10837    let toggle_comments = &ToggleComments {
10838        advance_downwards: true,
10839        ignore_indent: false,
10840    };
10841
10842    // Single cursor on one line -> advance
10843    // Cursor moves horizontally 3 characters as well on non-blank line
10844    cx.set_state(indoc!(
10845        "fn a() {
10846             ˇdog();
10847             cat();
10848        }"
10849    ));
10850    cx.update_editor(|editor, window, cx| {
10851        editor.toggle_comments(toggle_comments, window, cx);
10852    });
10853    cx.assert_editor_state(indoc!(
10854        "fn a() {
10855             // dog();
10856             catˇ();
10857        }"
10858    ));
10859
10860    // Single selection on one line -> don't advance
10861    cx.set_state(indoc!(
10862        "fn a() {
10863             «dog()ˇ»;
10864             cat();
10865        }"
10866    ));
10867    cx.update_editor(|editor, window, cx| {
10868        editor.toggle_comments(toggle_comments, window, cx);
10869    });
10870    cx.assert_editor_state(indoc!(
10871        "fn a() {
10872             // «dog()ˇ»;
10873             cat();
10874        }"
10875    ));
10876
10877    // Multiple cursors on one line -> advance
10878    cx.set_state(indoc!(
10879        "fn a() {
10880             ˇdˇog();
10881             cat();
10882        }"
10883    ));
10884    cx.update_editor(|editor, window, cx| {
10885        editor.toggle_comments(toggle_comments, window, cx);
10886    });
10887    cx.assert_editor_state(indoc!(
10888        "fn a() {
10889             // dog();
10890             catˇ(ˇ);
10891        }"
10892    ));
10893
10894    // Multiple cursors on one line, with selection -> don't advance
10895    cx.set_state(indoc!(
10896        "fn a() {
10897             ˇdˇog«()ˇ»;
10898             cat();
10899        }"
10900    ));
10901    cx.update_editor(|editor, window, cx| {
10902        editor.toggle_comments(toggle_comments, window, cx);
10903    });
10904    cx.assert_editor_state(indoc!(
10905        "fn a() {
10906             // ˇdˇog«()ˇ»;
10907             cat();
10908        }"
10909    ));
10910
10911    // Single cursor on one line -> advance
10912    // Cursor moves to column 0 on blank line
10913    cx.set_state(indoc!(
10914        "fn a() {
10915             ˇdog();
10916
10917             cat();
10918        }"
10919    ));
10920    cx.update_editor(|editor, window, cx| {
10921        editor.toggle_comments(toggle_comments, window, cx);
10922    });
10923    cx.assert_editor_state(indoc!(
10924        "fn a() {
10925             // dog();
10926        ˇ
10927             cat();
10928        }"
10929    ));
10930
10931    // Single cursor on one line -> advance
10932    // Cursor starts and ends at column 0
10933    cx.set_state(indoc!(
10934        "fn a() {
10935         ˇ    dog();
10936             cat();
10937        }"
10938    ));
10939    cx.update_editor(|editor, window, cx| {
10940        editor.toggle_comments(toggle_comments, window, cx);
10941    });
10942    cx.assert_editor_state(indoc!(
10943        "fn a() {
10944             // dog();
10945         ˇ    cat();
10946        }"
10947    ));
10948}
10949
10950#[gpui::test]
10951async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10952    init_test(cx, |_| {});
10953
10954    let mut cx = EditorTestContext::new(cx).await;
10955
10956    let html_language = Arc::new(
10957        Language::new(
10958            LanguageConfig {
10959                name: "HTML".into(),
10960                block_comment: Some(("<!-- ".into(), " -->".into())),
10961                ..Default::default()
10962            },
10963            Some(tree_sitter_html::LANGUAGE.into()),
10964        )
10965        .with_injection_query(
10966            r#"
10967            (script_element
10968                (raw_text) @injection.content
10969                (#set! injection.language "javascript"))
10970            "#,
10971        )
10972        .unwrap(),
10973    );
10974
10975    let javascript_language = Arc::new(Language::new(
10976        LanguageConfig {
10977            name: "JavaScript".into(),
10978            line_comments: vec!["// ".into()],
10979            ..Default::default()
10980        },
10981        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10982    ));
10983
10984    cx.language_registry().add(html_language.clone());
10985    cx.language_registry().add(javascript_language.clone());
10986    cx.update_buffer(|buffer, cx| {
10987        buffer.set_language(Some(html_language), cx);
10988    });
10989
10990    // Toggle comments for empty selections
10991    cx.set_state(
10992        &r#"
10993            <p>A</p>ˇ
10994            <p>B</p>ˇ
10995            <p>C</p>ˇ
10996        "#
10997        .unindent(),
10998    );
10999    cx.update_editor(|editor, window, cx| {
11000        editor.toggle_comments(&ToggleComments::default(), window, cx)
11001    });
11002    cx.assert_editor_state(
11003        &r#"
11004            <!-- <p>A</p>ˇ -->
11005            <!-- <p>B</p>ˇ -->
11006            <!-- <p>C</p>ˇ -->
11007        "#
11008        .unindent(),
11009    );
11010    cx.update_editor(|editor, window, cx| {
11011        editor.toggle_comments(&ToggleComments::default(), window, cx)
11012    });
11013    cx.assert_editor_state(
11014        &r#"
11015            <p>A</p>ˇ
11016            <p>B</p>ˇ
11017            <p>C</p>ˇ
11018        "#
11019        .unindent(),
11020    );
11021
11022    // Toggle comments for mixture of empty and non-empty selections, where
11023    // multiple selections occupy a given line.
11024    cx.set_state(
11025        &r#"
11026            <p>A«</p>
11027            <p>ˇ»B</p>ˇ
11028            <p>C«</p>
11029            <p>ˇ»D</p>ˇ
11030        "#
11031        .unindent(),
11032    );
11033
11034    cx.update_editor(|editor, window, cx| {
11035        editor.toggle_comments(&ToggleComments::default(), window, cx)
11036    });
11037    cx.assert_editor_state(
11038        &r#"
11039            <!-- <p>A«</p>
11040            <p>ˇ»B</p>ˇ -->
11041            <!-- <p>C«</p>
11042            <p>ˇ»D</p>ˇ -->
11043        "#
11044        .unindent(),
11045    );
11046    cx.update_editor(|editor, window, cx| {
11047        editor.toggle_comments(&ToggleComments::default(), window, cx)
11048    });
11049    cx.assert_editor_state(
11050        &r#"
11051            <p>A«</p>
11052            <p>ˇ»B</p>ˇ
11053            <p>C«</p>
11054            <p>ˇ»D</p>ˇ
11055        "#
11056        .unindent(),
11057    );
11058
11059    // Toggle comments when different languages are active for different
11060    // selections.
11061    cx.set_state(
11062        &r#"
11063            ˇ<script>
11064                ˇvar x = new Y();
11065            ˇ</script>
11066        "#
11067        .unindent(),
11068    );
11069    cx.executor().run_until_parked();
11070    cx.update_editor(|editor, window, cx| {
11071        editor.toggle_comments(&ToggleComments::default(), window, cx)
11072    });
11073    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11074    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11075    cx.assert_editor_state(
11076        &r#"
11077            <!-- ˇ<script> -->
11078                // ˇvar x = new Y();
11079            <!-- ˇ</script> -->
11080        "#
11081        .unindent(),
11082    );
11083}
11084
11085#[gpui::test]
11086fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11087    init_test(cx, |_| {});
11088
11089    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11090    let multibuffer = cx.new(|cx| {
11091        let mut multibuffer = MultiBuffer::new(ReadWrite);
11092        multibuffer.push_excerpts(
11093            buffer.clone(),
11094            [
11095                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11096                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11097            ],
11098            cx,
11099        );
11100        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11101        multibuffer
11102    });
11103
11104    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11105    editor.update_in(cx, |editor, window, cx| {
11106        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11107        editor.change_selections(None, window, cx, |s| {
11108            s.select_ranges([
11109                Point::new(0, 0)..Point::new(0, 0),
11110                Point::new(1, 0)..Point::new(1, 0),
11111            ])
11112        });
11113
11114        editor.handle_input("X", window, cx);
11115        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11116        assert_eq!(
11117            editor.selections.ranges(cx),
11118            [
11119                Point::new(0, 1)..Point::new(0, 1),
11120                Point::new(1, 1)..Point::new(1, 1),
11121            ]
11122        );
11123
11124        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11125        editor.change_selections(None, window, cx, |s| {
11126            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11127        });
11128        editor.backspace(&Default::default(), window, cx);
11129        assert_eq!(editor.text(cx), "Xa\nbbb");
11130        assert_eq!(
11131            editor.selections.ranges(cx),
11132            [Point::new(1, 0)..Point::new(1, 0)]
11133        );
11134
11135        editor.change_selections(None, window, cx, |s| {
11136            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11137        });
11138        editor.backspace(&Default::default(), window, cx);
11139        assert_eq!(editor.text(cx), "X\nbb");
11140        assert_eq!(
11141            editor.selections.ranges(cx),
11142            [Point::new(0, 1)..Point::new(0, 1)]
11143        );
11144    });
11145}
11146
11147#[gpui::test]
11148fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11149    init_test(cx, |_| {});
11150
11151    let markers = vec![('[', ']').into(), ('(', ')').into()];
11152    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11153        indoc! {"
11154            [aaaa
11155            (bbbb]
11156            cccc)",
11157        },
11158        markers.clone(),
11159    );
11160    let excerpt_ranges = markers.into_iter().map(|marker| {
11161        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11162        ExcerptRange::new(context.clone())
11163    });
11164    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11165    let multibuffer = cx.new(|cx| {
11166        let mut multibuffer = MultiBuffer::new(ReadWrite);
11167        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11168        multibuffer
11169    });
11170
11171    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11172    editor.update_in(cx, |editor, window, cx| {
11173        let (expected_text, selection_ranges) = marked_text_ranges(
11174            indoc! {"
11175                aaaa
11176                bˇbbb
11177                bˇbbˇb
11178                cccc"
11179            },
11180            true,
11181        );
11182        assert_eq!(editor.text(cx), expected_text);
11183        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11184
11185        editor.handle_input("X", window, cx);
11186
11187        let (expected_text, expected_selections) = marked_text_ranges(
11188            indoc! {"
11189                aaaa
11190                bXˇbbXb
11191                bXˇbbXˇb
11192                cccc"
11193            },
11194            false,
11195        );
11196        assert_eq!(editor.text(cx), expected_text);
11197        assert_eq!(editor.selections.ranges(cx), expected_selections);
11198
11199        editor.newline(&Newline, window, cx);
11200        let (expected_text, expected_selections) = marked_text_ranges(
11201            indoc! {"
11202                aaaa
11203                bX
11204                ˇbbX
11205                b
11206                bX
11207                ˇbbX
11208                ˇb
11209                cccc"
11210            },
11211            false,
11212        );
11213        assert_eq!(editor.text(cx), expected_text);
11214        assert_eq!(editor.selections.ranges(cx), expected_selections);
11215    });
11216}
11217
11218#[gpui::test]
11219fn test_refresh_selections(cx: &mut TestAppContext) {
11220    init_test(cx, |_| {});
11221
11222    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11223    let mut excerpt1_id = None;
11224    let multibuffer = cx.new(|cx| {
11225        let mut multibuffer = MultiBuffer::new(ReadWrite);
11226        excerpt1_id = multibuffer
11227            .push_excerpts(
11228                buffer.clone(),
11229                [
11230                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11231                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11232                ],
11233                cx,
11234            )
11235            .into_iter()
11236            .next();
11237        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11238        multibuffer
11239    });
11240
11241    let editor = cx.add_window(|window, cx| {
11242        let mut editor = build_editor(multibuffer.clone(), window, cx);
11243        let snapshot = editor.snapshot(window, cx);
11244        editor.change_selections(None, window, cx, |s| {
11245            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11246        });
11247        editor.begin_selection(
11248            Point::new(2, 1).to_display_point(&snapshot),
11249            true,
11250            1,
11251            window,
11252            cx,
11253        );
11254        assert_eq!(
11255            editor.selections.ranges(cx),
11256            [
11257                Point::new(1, 3)..Point::new(1, 3),
11258                Point::new(2, 1)..Point::new(2, 1),
11259            ]
11260        );
11261        editor
11262    });
11263
11264    // Refreshing selections is a no-op when excerpts haven't changed.
11265    _ = editor.update(cx, |editor, window, cx| {
11266        editor.change_selections(None, window, cx, |s| s.refresh());
11267        assert_eq!(
11268            editor.selections.ranges(cx),
11269            [
11270                Point::new(1, 3)..Point::new(1, 3),
11271                Point::new(2, 1)..Point::new(2, 1),
11272            ]
11273        );
11274    });
11275
11276    multibuffer.update(cx, |multibuffer, cx| {
11277        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11278    });
11279    _ = editor.update(cx, |editor, window, cx| {
11280        // Removing an excerpt causes the first selection to become degenerate.
11281        assert_eq!(
11282            editor.selections.ranges(cx),
11283            [
11284                Point::new(0, 0)..Point::new(0, 0),
11285                Point::new(0, 1)..Point::new(0, 1)
11286            ]
11287        );
11288
11289        // Refreshing selections will relocate the first selection to the original buffer
11290        // location.
11291        editor.change_selections(None, window, cx, |s| s.refresh());
11292        assert_eq!(
11293            editor.selections.ranges(cx),
11294            [
11295                Point::new(0, 1)..Point::new(0, 1),
11296                Point::new(0, 3)..Point::new(0, 3)
11297            ]
11298        );
11299        assert!(editor.selections.pending_anchor().is_some());
11300    });
11301}
11302
11303#[gpui::test]
11304fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11305    init_test(cx, |_| {});
11306
11307    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11308    let mut excerpt1_id = None;
11309    let multibuffer = cx.new(|cx| {
11310        let mut multibuffer = MultiBuffer::new(ReadWrite);
11311        excerpt1_id = multibuffer
11312            .push_excerpts(
11313                buffer.clone(),
11314                [
11315                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11316                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11317                ],
11318                cx,
11319            )
11320            .into_iter()
11321            .next();
11322        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11323        multibuffer
11324    });
11325
11326    let editor = cx.add_window(|window, cx| {
11327        let mut editor = build_editor(multibuffer.clone(), window, cx);
11328        let snapshot = editor.snapshot(window, cx);
11329        editor.begin_selection(
11330            Point::new(1, 3).to_display_point(&snapshot),
11331            false,
11332            1,
11333            window,
11334            cx,
11335        );
11336        assert_eq!(
11337            editor.selections.ranges(cx),
11338            [Point::new(1, 3)..Point::new(1, 3)]
11339        );
11340        editor
11341    });
11342
11343    multibuffer.update(cx, |multibuffer, cx| {
11344        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11345    });
11346    _ = editor.update(cx, |editor, window, cx| {
11347        assert_eq!(
11348            editor.selections.ranges(cx),
11349            [Point::new(0, 0)..Point::new(0, 0)]
11350        );
11351
11352        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11353        editor.change_selections(None, window, cx, |s| s.refresh());
11354        assert_eq!(
11355            editor.selections.ranges(cx),
11356            [Point::new(0, 3)..Point::new(0, 3)]
11357        );
11358        assert!(editor.selections.pending_anchor().is_some());
11359    });
11360}
11361
11362#[gpui::test]
11363async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11364    init_test(cx, |_| {});
11365
11366    let language = Arc::new(
11367        Language::new(
11368            LanguageConfig {
11369                brackets: BracketPairConfig {
11370                    pairs: vec![
11371                        BracketPair {
11372                            start: "{".to_string(),
11373                            end: "}".to_string(),
11374                            close: true,
11375                            surround: true,
11376                            newline: true,
11377                        },
11378                        BracketPair {
11379                            start: "/* ".to_string(),
11380                            end: " */".to_string(),
11381                            close: true,
11382                            surround: true,
11383                            newline: true,
11384                        },
11385                    ],
11386                    ..Default::default()
11387                },
11388                ..Default::default()
11389            },
11390            Some(tree_sitter_rust::LANGUAGE.into()),
11391        )
11392        .with_indents_query("")
11393        .unwrap(),
11394    );
11395
11396    let text = concat!(
11397        "{   }\n",     //
11398        "  x\n",       //
11399        "  /*   */\n", //
11400        "x\n",         //
11401        "{{} }\n",     //
11402    );
11403
11404    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11405    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11406    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11407    editor
11408        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11409        .await;
11410
11411    editor.update_in(cx, |editor, window, cx| {
11412        editor.change_selections(None, window, cx, |s| {
11413            s.select_display_ranges([
11414                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11415                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11416                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11417            ])
11418        });
11419        editor.newline(&Newline, window, cx);
11420
11421        assert_eq!(
11422            editor.buffer().read(cx).read(cx).text(),
11423            concat!(
11424                "{ \n",    // Suppress rustfmt
11425                "\n",      //
11426                "}\n",     //
11427                "  x\n",   //
11428                "  /* \n", //
11429                "  \n",    //
11430                "  */\n",  //
11431                "x\n",     //
11432                "{{} \n",  //
11433                "}\n",     //
11434            )
11435        );
11436    });
11437}
11438
11439#[gpui::test]
11440fn test_highlighted_ranges(cx: &mut TestAppContext) {
11441    init_test(cx, |_| {});
11442
11443    let editor = cx.add_window(|window, cx| {
11444        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11445        build_editor(buffer.clone(), window, cx)
11446    });
11447
11448    _ = editor.update(cx, |editor, window, cx| {
11449        struct Type1;
11450        struct Type2;
11451
11452        let buffer = editor.buffer.read(cx).snapshot(cx);
11453
11454        let anchor_range =
11455            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11456
11457        editor.highlight_background::<Type1>(
11458            &[
11459                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11460                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11461                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11462                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11463            ],
11464            |_| Hsla::red(),
11465            cx,
11466        );
11467        editor.highlight_background::<Type2>(
11468            &[
11469                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11470                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11471                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11472                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11473            ],
11474            |_| Hsla::green(),
11475            cx,
11476        );
11477
11478        let snapshot = editor.snapshot(window, cx);
11479        let mut highlighted_ranges = editor.background_highlights_in_range(
11480            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11481            &snapshot,
11482            cx.theme().colors(),
11483        );
11484        // Enforce a consistent ordering based on color without relying on the ordering of the
11485        // highlight's `TypeId` which is non-executor.
11486        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11487        assert_eq!(
11488            highlighted_ranges,
11489            &[
11490                (
11491                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11492                    Hsla::red(),
11493                ),
11494                (
11495                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11496                    Hsla::red(),
11497                ),
11498                (
11499                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11500                    Hsla::green(),
11501                ),
11502                (
11503                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11504                    Hsla::green(),
11505                ),
11506            ]
11507        );
11508        assert_eq!(
11509            editor.background_highlights_in_range(
11510                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11511                &snapshot,
11512                cx.theme().colors(),
11513            ),
11514            &[(
11515                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11516                Hsla::red(),
11517            )]
11518        );
11519    });
11520}
11521
11522#[gpui::test]
11523async fn test_following(cx: &mut TestAppContext) {
11524    init_test(cx, |_| {});
11525
11526    let fs = FakeFs::new(cx.executor());
11527    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11528
11529    let buffer = project.update(cx, |project, cx| {
11530        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11531        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11532    });
11533    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11534    let follower = cx.update(|cx| {
11535        cx.open_window(
11536            WindowOptions {
11537                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11538                    gpui::Point::new(px(0.), px(0.)),
11539                    gpui::Point::new(px(10.), px(80.)),
11540                ))),
11541                ..Default::default()
11542            },
11543            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11544        )
11545        .unwrap()
11546    });
11547
11548    let is_still_following = Rc::new(RefCell::new(true));
11549    let follower_edit_event_count = Rc::new(RefCell::new(0));
11550    let pending_update = Rc::new(RefCell::new(None));
11551    let leader_entity = leader.root(cx).unwrap();
11552    let follower_entity = follower.root(cx).unwrap();
11553    _ = follower.update(cx, {
11554        let update = pending_update.clone();
11555        let is_still_following = is_still_following.clone();
11556        let follower_edit_event_count = follower_edit_event_count.clone();
11557        |_, window, cx| {
11558            cx.subscribe_in(
11559                &leader_entity,
11560                window,
11561                move |_, leader, event, window, cx| {
11562                    leader.read(cx).add_event_to_update_proto(
11563                        event,
11564                        &mut update.borrow_mut(),
11565                        window,
11566                        cx,
11567                    );
11568                },
11569            )
11570            .detach();
11571
11572            cx.subscribe_in(
11573                &follower_entity,
11574                window,
11575                move |_, _, event: &EditorEvent, _window, _cx| {
11576                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11577                        *is_still_following.borrow_mut() = false;
11578                    }
11579
11580                    if let EditorEvent::BufferEdited = event {
11581                        *follower_edit_event_count.borrow_mut() += 1;
11582                    }
11583                },
11584            )
11585            .detach();
11586        }
11587    });
11588
11589    // Update the selections only
11590    _ = leader.update(cx, |leader, window, cx| {
11591        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11592    });
11593    follower
11594        .update(cx, |follower, window, cx| {
11595            follower.apply_update_proto(
11596                &project,
11597                pending_update.borrow_mut().take().unwrap(),
11598                window,
11599                cx,
11600            )
11601        })
11602        .unwrap()
11603        .await
11604        .unwrap();
11605    _ = follower.update(cx, |follower, _, cx| {
11606        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11607    });
11608    assert!(*is_still_following.borrow());
11609    assert_eq!(*follower_edit_event_count.borrow(), 0);
11610
11611    // Update the scroll position only
11612    _ = leader.update(cx, |leader, window, cx| {
11613        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11614    });
11615    follower
11616        .update(cx, |follower, window, cx| {
11617            follower.apply_update_proto(
11618                &project,
11619                pending_update.borrow_mut().take().unwrap(),
11620                window,
11621                cx,
11622            )
11623        })
11624        .unwrap()
11625        .await
11626        .unwrap();
11627    assert_eq!(
11628        follower
11629            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11630            .unwrap(),
11631        gpui::Point::new(1.5, 3.5)
11632    );
11633    assert!(*is_still_following.borrow());
11634    assert_eq!(*follower_edit_event_count.borrow(), 0);
11635
11636    // Update the selections and scroll position. The follower's scroll position is updated
11637    // via autoscroll, not via the leader's exact scroll position.
11638    _ = leader.update(cx, |leader, window, cx| {
11639        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11640        leader.request_autoscroll(Autoscroll::newest(), cx);
11641        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11642    });
11643    follower
11644        .update(cx, |follower, window, cx| {
11645            follower.apply_update_proto(
11646                &project,
11647                pending_update.borrow_mut().take().unwrap(),
11648                window,
11649                cx,
11650            )
11651        })
11652        .unwrap()
11653        .await
11654        .unwrap();
11655    _ = follower.update(cx, |follower, _, cx| {
11656        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11657        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11658    });
11659    assert!(*is_still_following.borrow());
11660
11661    // Creating a pending selection that precedes another selection
11662    _ = leader.update(cx, |leader, window, cx| {
11663        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11664        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11665    });
11666    follower
11667        .update(cx, |follower, window, cx| {
11668            follower.apply_update_proto(
11669                &project,
11670                pending_update.borrow_mut().take().unwrap(),
11671                window,
11672                cx,
11673            )
11674        })
11675        .unwrap()
11676        .await
11677        .unwrap();
11678    _ = follower.update(cx, |follower, _, cx| {
11679        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11680    });
11681    assert!(*is_still_following.borrow());
11682
11683    // Extend the pending selection so that it surrounds another selection
11684    _ = leader.update(cx, |leader, window, cx| {
11685        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11686    });
11687    follower
11688        .update(cx, |follower, window, cx| {
11689            follower.apply_update_proto(
11690                &project,
11691                pending_update.borrow_mut().take().unwrap(),
11692                window,
11693                cx,
11694            )
11695        })
11696        .unwrap()
11697        .await
11698        .unwrap();
11699    _ = follower.update(cx, |follower, _, cx| {
11700        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11701    });
11702
11703    // Scrolling locally breaks the follow
11704    _ = follower.update(cx, |follower, window, cx| {
11705        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11706        follower.set_scroll_anchor(
11707            ScrollAnchor {
11708                anchor: top_anchor,
11709                offset: gpui::Point::new(0.0, 0.5),
11710            },
11711            window,
11712            cx,
11713        );
11714    });
11715    assert!(!(*is_still_following.borrow()));
11716}
11717
11718#[gpui::test]
11719async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11720    init_test(cx, |_| {});
11721
11722    let fs = FakeFs::new(cx.executor());
11723    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11724    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11725    let pane = workspace
11726        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11727        .unwrap();
11728
11729    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11730
11731    let leader = pane.update_in(cx, |_, window, cx| {
11732        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11733        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11734    });
11735
11736    // Start following the editor when it has no excerpts.
11737    let mut state_message =
11738        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11739    let workspace_entity = workspace.root(cx).unwrap();
11740    let follower_1 = cx
11741        .update_window(*workspace.deref(), |_, window, cx| {
11742            Editor::from_state_proto(
11743                workspace_entity,
11744                ViewId {
11745                    creator: Default::default(),
11746                    id: 0,
11747                },
11748                &mut state_message,
11749                window,
11750                cx,
11751            )
11752        })
11753        .unwrap()
11754        .unwrap()
11755        .await
11756        .unwrap();
11757
11758    let update_message = Rc::new(RefCell::new(None));
11759    follower_1.update_in(cx, {
11760        let update = update_message.clone();
11761        |_, window, cx| {
11762            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11763                leader.read(cx).add_event_to_update_proto(
11764                    event,
11765                    &mut update.borrow_mut(),
11766                    window,
11767                    cx,
11768                );
11769            })
11770            .detach();
11771        }
11772    });
11773
11774    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11775        (
11776            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11777            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11778        )
11779    });
11780
11781    // Insert some excerpts.
11782    leader.update(cx, |leader, cx| {
11783        leader.buffer.update(cx, |multibuffer, cx| {
11784            let excerpt_ids = multibuffer.push_excerpts(
11785                buffer_1.clone(),
11786                [
11787                    ExcerptRange::new(1..6),
11788                    ExcerptRange::new(12..15),
11789                    ExcerptRange::new(0..3),
11790                ],
11791                cx,
11792            );
11793            multibuffer.insert_excerpts_after(
11794                excerpt_ids[0],
11795                buffer_2.clone(),
11796                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11797                cx,
11798            );
11799        });
11800    });
11801
11802    // Apply the update of adding the excerpts.
11803    follower_1
11804        .update_in(cx, |follower, window, cx| {
11805            follower.apply_update_proto(
11806                &project,
11807                update_message.borrow().clone().unwrap(),
11808                window,
11809                cx,
11810            )
11811        })
11812        .await
11813        .unwrap();
11814    assert_eq!(
11815        follower_1.update(cx, |editor, cx| editor.text(cx)),
11816        leader.update(cx, |editor, cx| editor.text(cx))
11817    );
11818    update_message.borrow_mut().take();
11819
11820    // Start following separately after it already has excerpts.
11821    let mut state_message =
11822        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11823    let workspace_entity = workspace.root(cx).unwrap();
11824    let follower_2 = cx
11825        .update_window(*workspace.deref(), |_, window, cx| {
11826            Editor::from_state_proto(
11827                workspace_entity,
11828                ViewId {
11829                    creator: Default::default(),
11830                    id: 0,
11831                },
11832                &mut state_message,
11833                window,
11834                cx,
11835            )
11836        })
11837        .unwrap()
11838        .unwrap()
11839        .await
11840        .unwrap();
11841    assert_eq!(
11842        follower_2.update(cx, |editor, cx| editor.text(cx)),
11843        leader.update(cx, |editor, cx| editor.text(cx))
11844    );
11845
11846    // Remove some excerpts.
11847    leader.update(cx, |leader, cx| {
11848        leader.buffer.update(cx, |multibuffer, cx| {
11849            let excerpt_ids = multibuffer.excerpt_ids();
11850            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11851            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11852        });
11853    });
11854
11855    // Apply the update of removing the excerpts.
11856    follower_1
11857        .update_in(cx, |follower, window, cx| {
11858            follower.apply_update_proto(
11859                &project,
11860                update_message.borrow().clone().unwrap(),
11861                window,
11862                cx,
11863            )
11864        })
11865        .await
11866        .unwrap();
11867    follower_2
11868        .update_in(cx, |follower, window, cx| {
11869            follower.apply_update_proto(
11870                &project,
11871                update_message.borrow().clone().unwrap(),
11872                window,
11873                cx,
11874            )
11875        })
11876        .await
11877        .unwrap();
11878    update_message.borrow_mut().take();
11879    assert_eq!(
11880        follower_1.update(cx, |editor, cx| editor.text(cx)),
11881        leader.update(cx, |editor, cx| editor.text(cx))
11882    );
11883}
11884
11885#[gpui::test]
11886async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11887    init_test(cx, |_| {});
11888
11889    let mut cx = EditorTestContext::new(cx).await;
11890    let lsp_store =
11891        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11892
11893    cx.set_state(indoc! {"
11894        ˇfn func(abc def: i32) -> u32 {
11895        }
11896    "});
11897
11898    cx.update(|_, cx| {
11899        lsp_store.update(cx, |lsp_store, cx| {
11900            lsp_store
11901                .update_diagnostics(
11902                    LanguageServerId(0),
11903                    lsp::PublishDiagnosticsParams {
11904                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11905                        version: None,
11906                        diagnostics: vec![
11907                            lsp::Diagnostic {
11908                                range: lsp::Range::new(
11909                                    lsp::Position::new(0, 11),
11910                                    lsp::Position::new(0, 12),
11911                                ),
11912                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11913                                ..Default::default()
11914                            },
11915                            lsp::Diagnostic {
11916                                range: lsp::Range::new(
11917                                    lsp::Position::new(0, 12),
11918                                    lsp::Position::new(0, 15),
11919                                ),
11920                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11921                                ..Default::default()
11922                            },
11923                            lsp::Diagnostic {
11924                                range: lsp::Range::new(
11925                                    lsp::Position::new(0, 25),
11926                                    lsp::Position::new(0, 28),
11927                                ),
11928                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11929                                ..Default::default()
11930                            },
11931                        ],
11932                    },
11933                    &[],
11934                    cx,
11935                )
11936                .unwrap()
11937        });
11938    });
11939
11940    executor.run_until_parked();
11941
11942    cx.update_editor(|editor, window, cx| {
11943        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11944    });
11945
11946    cx.assert_editor_state(indoc! {"
11947        fn func(abc def: i32) -> ˇu32 {
11948        }
11949    "});
11950
11951    cx.update_editor(|editor, window, cx| {
11952        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11953    });
11954
11955    cx.assert_editor_state(indoc! {"
11956        fn func(abc ˇdef: i32) -> u32 {
11957        }
11958    "});
11959
11960    cx.update_editor(|editor, window, cx| {
11961        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11962    });
11963
11964    cx.assert_editor_state(indoc! {"
11965        fn func(abcˇ def: i32) -> u32 {
11966        }
11967    "});
11968
11969    cx.update_editor(|editor, window, cx| {
11970        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11971    });
11972
11973    cx.assert_editor_state(indoc! {"
11974        fn func(abc def: i32) -> ˇu32 {
11975        }
11976    "});
11977}
11978
11979#[gpui::test]
11980async fn cycle_through_same_place_diagnostics(
11981    executor: BackgroundExecutor,
11982    cx: &mut TestAppContext,
11983) {
11984    init_test(cx, |_| {});
11985
11986    let mut cx = EditorTestContext::new(cx).await;
11987    let lsp_store =
11988        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11989
11990    cx.set_state(indoc! {"
11991        ˇfn func(abc def: i32) -> u32 {
11992        }
11993    "});
11994
11995    cx.update(|_, cx| {
11996        lsp_store.update(cx, |lsp_store, cx| {
11997            lsp_store
11998                .update_diagnostics(
11999                    LanguageServerId(0),
12000                    lsp::PublishDiagnosticsParams {
12001                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12002                        version: None,
12003                        diagnostics: vec![
12004                            lsp::Diagnostic {
12005                                range: lsp::Range::new(
12006                                    lsp::Position::new(0, 11),
12007                                    lsp::Position::new(0, 12),
12008                                ),
12009                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12010                                ..Default::default()
12011                            },
12012                            lsp::Diagnostic {
12013                                range: lsp::Range::new(
12014                                    lsp::Position::new(0, 12),
12015                                    lsp::Position::new(0, 15),
12016                                ),
12017                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12018                                ..Default::default()
12019                            },
12020                            lsp::Diagnostic {
12021                                range: lsp::Range::new(
12022                                    lsp::Position::new(0, 12),
12023                                    lsp::Position::new(0, 15),
12024                                ),
12025                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12026                                ..Default::default()
12027                            },
12028                            lsp::Diagnostic {
12029                                range: lsp::Range::new(
12030                                    lsp::Position::new(0, 25),
12031                                    lsp::Position::new(0, 28),
12032                                ),
12033                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12034                                ..Default::default()
12035                            },
12036                        ],
12037                    },
12038                    &[],
12039                    cx,
12040                )
12041                .unwrap()
12042        });
12043    });
12044    executor.run_until_parked();
12045
12046    //// Backward
12047
12048    // Fourth diagnostic
12049    cx.update_editor(|editor, window, cx| {
12050        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12051    });
12052    cx.assert_editor_state(indoc! {"
12053        fn func(abc def: i32) -> ˇu32 {
12054        }
12055    "});
12056
12057    // Third diagnostic
12058    cx.update_editor(|editor, window, cx| {
12059        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12060    });
12061    cx.assert_editor_state(indoc! {"
12062        fn func(abc ˇdef: i32) -> u32 {
12063        }
12064    "});
12065
12066    // Second diagnostic, same place
12067    cx.update_editor(|editor, window, cx| {
12068        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12069    });
12070    cx.assert_editor_state(indoc! {"
12071        fn func(abc ˇdef: i32) -> u32 {
12072        }
12073    "});
12074
12075    // First diagnostic
12076    cx.update_editor(|editor, window, cx| {
12077        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12078    });
12079    cx.assert_editor_state(indoc! {"
12080        fn func(abcˇ def: i32) -> u32 {
12081        }
12082    "});
12083
12084    // Wrapped over, fourth diagnostic
12085    cx.update_editor(|editor, window, cx| {
12086        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12087    });
12088    cx.assert_editor_state(indoc! {"
12089        fn func(abc def: i32) -> ˇu32 {
12090        }
12091    "});
12092
12093    cx.update_editor(|editor, window, cx| {
12094        editor.move_to_beginning(&MoveToBeginning, window, cx);
12095    });
12096    cx.assert_editor_state(indoc! {"
12097        ˇfn func(abc def: i32) -> u32 {
12098        }
12099    "});
12100
12101    //// Forward
12102
12103    // First diagnostic
12104    cx.update_editor(|editor, window, cx| {
12105        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12106    });
12107    cx.assert_editor_state(indoc! {"
12108        fn func(abcˇ def: i32) -> u32 {
12109        }
12110    "});
12111
12112    // Second diagnostic
12113    cx.update_editor(|editor, window, cx| {
12114        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12115    });
12116    cx.assert_editor_state(indoc! {"
12117        fn func(abc ˇdef: i32) -> u32 {
12118        }
12119    "});
12120
12121    // Third diagnostic, same place
12122    cx.update_editor(|editor, window, cx| {
12123        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12124    });
12125    cx.assert_editor_state(indoc! {"
12126        fn func(abc ˇdef: i32) -> u32 {
12127        }
12128    "});
12129
12130    // Fourth diagnostic
12131    cx.update_editor(|editor, window, cx| {
12132        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12133    });
12134    cx.assert_editor_state(indoc! {"
12135        fn func(abc def: i32) -> ˇu32 {
12136        }
12137    "});
12138
12139    // Wrapped around, first diagnostic
12140    cx.update_editor(|editor, window, cx| {
12141        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12142    });
12143    cx.assert_editor_state(indoc! {"
12144        fn func(abcˇ def: i32) -> u32 {
12145        }
12146    "});
12147}
12148
12149#[gpui::test]
12150async fn active_diagnostics_dismiss_after_invalidation(
12151    executor: BackgroundExecutor,
12152    cx: &mut TestAppContext,
12153) {
12154    init_test(cx, |_| {});
12155
12156    let mut cx = EditorTestContext::new(cx).await;
12157    let lsp_store =
12158        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12159
12160    cx.set_state(indoc! {"
12161        ˇfn func(abc def: i32) -> u32 {
12162        }
12163    "});
12164
12165    let message = "Something's wrong!";
12166    cx.update(|_, cx| {
12167        lsp_store.update(cx, |lsp_store, cx| {
12168            lsp_store
12169                .update_diagnostics(
12170                    LanguageServerId(0),
12171                    lsp::PublishDiagnosticsParams {
12172                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12173                        version: None,
12174                        diagnostics: vec![lsp::Diagnostic {
12175                            range: lsp::Range::new(
12176                                lsp::Position::new(0, 11),
12177                                lsp::Position::new(0, 12),
12178                            ),
12179                            severity: Some(lsp::DiagnosticSeverity::ERROR),
12180                            message: message.to_string(),
12181                            ..Default::default()
12182                        }],
12183                    },
12184                    &[],
12185                    cx,
12186                )
12187                .unwrap()
12188        });
12189    });
12190    executor.run_until_parked();
12191
12192    cx.update_editor(|editor, window, cx| {
12193        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12194        assert_eq!(
12195            editor
12196                .active_diagnostics
12197                .as_ref()
12198                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
12199            Some(message),
12200            "Should have a diagnostics group activated"
12201        );
12202    });
12203    cx.assert_editor_state(indoc! {"
12204        fn func(abcˇ def: i32) -> u32 {
12205        }
12206    "});
12207
12208    cx.update(|_, cx| {
12209        lsp_store.update(cx, |lsp_store, cx| {
12210            lsp_store
12211                .update_diagnostics(
12212                    LanguageServerId(0),
12213                    lsp::PublishDiagnosticsParams {
12214                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12215                        version: None,
12216                        diagnostics: Vec::new(),
12217                    },
12218                    &[],
12219                    cx,
12220                )
12221                .unwrap()
12222        });
12223    });
12224    executor.run_until_parked();
12225    cx.update_editor(|editor, _, _| {
12226        assert_eq!(
12227            editor.active_diagnostics, None,
12228            "After no diagnostics set to the editor, no diagnostics should be active"
12229        );
12230    });
12231    cx.assert_editor_state(indoc! {"
12232        fn func(abcˇ def: i32) -> u32 {
12233        }
12234    "});
12235
12236    cx.update_editor(|editor, window, cx| {
12237        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12238        assert_eq!(
12239            editor.active_diagnostics, None,
12240            "Should be no diagnostics to go to and activate"
12241        );
12242    });
12243    cx.assert_editor_state(indoc! {"
12244        fn func(abcˇ def: i32) -> u32 {
12245        }
12246    "});
12247}
12248
12249#[gpui::test]
12250async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12251    init_test(cx, |_| {});
12252
12253    let mut cx = EditorTestContext::new(cx).await;
12254
12255    cx.set_state(indoc! {"
12256        fn func(abˇc def: i32) -> u32 {
12257        }
12258    "});
12259    let lsp_store =
12260        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12261
12262    cx.update(|_, cx| {
12263        lsp_store.update(cx, |lsp_store, cx| {
12264            lsp_store.update_diagnostics(
12265                LanguageServerId(0),
12266                lsp::PublishDiagnosticsParams {
12267                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12268                    version: None,
12269                    diagnostics: vec![lsp::Diagnostic {
12270                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12271                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12272                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12273                        ..Default::default()
12274                    }],
12275                },
12276                &[],
12277                cx,
12278            )
12279        })
12280    }).unwrap();
12281    cx.run_until_parked();
12282    cx.update_editor(|editor, window, cx| {
12283        hover_popover::hover(editor, &Default::default(), window, cx)
12284    });
12285    cx.run_until_parked();
12286    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12287}
12288
12289#[gpui::test]
12290async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12291    init_test(cx, |_| {});
12292
12293    let mut cx = EditorTestContext::new(cx).await;
12294
12295    let diff_base = r#"
12296        use some::mod;
12297
12298        const A: u32 = 42;
12299
12300        fn main() {
12301            println!("hello");
12302
12303            println!("world");
12304        }
12305        "#
12306    .unindent();
12307
12308    // Edits are modified, removed, modified, added
12309    cx.set_state(
12310        &r#"
12311        use some::modified;
12312
12313        ˇ
12314        fn main() {
12315            println!("hello there");
12316
12317            println!("around the");
12318            println!("world");
12319        }
12320        "#
12321        .unindent(),
12322    );
12323
12324    cx.set_head_text(&diff_base);
12325    executor.run_until_parked();
12326
12327    cx.update_editor(|editor, window, cx| {
12328        //Wrap around the bottom of the buffer
12329        for _ in 0..3 {
12330            editor.go_to_next_hunk(&GoToHunk, window, cx);
12331        }
12332    });
12333
12334    cx.assert_editor_state(
12335        &r#"
12336        ˇuse some::modified;
12337
12338
12339        fn main() {
12340            println!("hello there");
12341
12342            println!("around the");
12343            println!("world");
12344        }
12345        "#
12346        .unindent(),
12347    );
12348
12349    cx.update_editor(|editor, window, cx| {
12350        //Wrap around the top of the buffer
12351        for _ in 0..2 {
12352            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12353        }
12354    });
12355
12356    cx.assert_editor_state(
12357        &r#"
12358        use some::modified;
12359
12360
12361        fn main() {
12362        ˇ    println!("hello there");
12363
12364            println!("around the");
12365            println!("world");
12366        }
12367        "#
12368        .unindent(),
12369    );
12370
12371    cx.update_editor(|editor, window, cx| {
12372        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12373    });
12374
12375    cx.assert_editor_state(
12376        &r#"
12377        use some::modified;
12378
12379        ˇ
12380        fn main() {
12381            println!("hello there");
12382
12383            println!("around the");
12384            println!("world");
12385        }
12386        "#
12387        .unindent(),
12388    );
12389
12390    cx.update_editor(|editor, window, cx| {
12391        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12392    });
12393
12394    cx.assert_editor_state(
12395        &r#"
12396        ˇuse some::modified;
12397
12398
12399        fn main() {
12400            println!("hello there");
12401
12402            println!("around the");
12403            println!("world");
12404        }
12405        "#
12406        .unindent(),
12407    );
12408
12409    cx.update_editor(|editor, window, cx| {
12410        for _ in 0..2 {
12411            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12412        }
12413    });
12414
12415    cx.assert_editor_state(
12416        &r#"
12417        use some::modified;
12418
12419
12420        fn main() {
12421        ˇ    println!("hello there");
12422
12423            println!("around the");
12424            println!("world");
12425        }
12426        "#
12427        .unindent(),
12428    );
12429
12430    cx.update_editor(|editor, window, cx| {
12431        editor.fold(&Fold, window, cx);
12432    });
12433
12434    cx.update_editor(|editor, window, cx| {
12435        editor.go_to_next_hunk(&GoToHunk, window, cx);
12436    });
12437
12438    cx.assert_editor_state(
12439        &r#"
12440        ˇuse some::modified;
12441
12442
12443        fn main() {
12444            println!("hello there");
12445
12446            println!("around the");
12447            println!("world");
12448        }
12449        "#
12450        .unindent(),
12451    );
12452}
12453
12454#[test]
12455fn test_split_words() {
12456    fn split(text: &str) -> Vec<&str> {
12457        split_words(text).collect()
12458    }
12459
12460    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12461    assert_eq!(split("hello_world"), &["hello_", "world"]);
12462    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12463    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12464    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12465    assert_eq!(split("helloworld"), &["helloworld"]);
12466
12467    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12468}
12469
12470#[gpui::test]
12471async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12472    init_test(cx, |_| {});
12473
12474    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12475    let mut assert = |before, after| {
12476        let _state_context = cx.set_state(before);
12477        cx.run_until_parked();
12478        cx.update_editor(|editor, window, cx| {
12479            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12480        });
12481        cx.run_until_parked();
12482        cx.assert_editor_state(after);
12483    };
12484
12485    // Outside bracket jumps to outside of matching bracket
12486    assert("console.logˇ(var);", "console.log(var)ˇ;");
12487    assert("console.log(var)ˇ;", "console.logˇ(var);");
12488
12489    // Inside bracket jumps to inside of matching bracket
12490    assert("console.log(ˇvar);", "console.log(varˇ);");
12491    assert("console.log(varˇ);", "console.log(ˇvar);");
12492
12493    // When outside a bracket and inside, favor jumping to the inside bracket
12494    assert(
12495        "console.log('foo', [1, 2, 3]ˇ);",
12496        "console.log(ˇ'foo', [1, 2, 3]);",
12497    );
12498    assert(
12499        "console.log(ˇ'foo', [1, 2, 3]);",
12500        "console.log('foo', [1, 2, 3]ˇ);",
12501    );
12502
12503    // Bias forward if two options are equally likely
12504    assert(
12505        "let result = curried_fun()ˇ();",
12506        "let result = curried_fun()()ˇ;",
12507    );
12508
12509    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12510    assert(
12511        indoc! {"
12512            function test() {
12513                console.log('test')ˇ
12514            }"},
12515        indoc! {"
12516            function test() {
12517                console.logˇ('test')
12518            }"},
12519    );
12520}
12521
12522#[gpui::test]
12523async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12524    init_test(cx, |_| {});
12525
12526    let fs = FakeFs::new(cx.executor());
12527    fs.insert_tree(
12528        path!("/a"),
12529        json!({
12530            "main.rs": "fn main() { let a = 5; }",
12531            "other.rs": "// Test file",
12532        }),
12533    )
12534    .await;
12535    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12536
12537    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12538    language_registry.add(Arc::new(Language::new(
12539        LanguageConfig {
12540            name: "Rust".into(),
12541            matcher: LanguageMatcher {
12542                path_suffixes: vec!["rs".to_string()],
12543                ..Default::default()
12544            },
12545            brackets: BracketPairConfig {
12546                pairs: vec![BracketPair {
12547                    start: "{".to_string(),
12548                    end: "}".to_string(),
12549                    close: true,
12550                    surround: true,
12551                    newline: true,
12552                }],
12553                disabled_scopes_by_bracket_ix: Vec::new(),
12554            },
12555            ..Default::default()
12556        },
12557        Some(tree_sitter_rust::LANGUAGE.into()),
12558    )));
12559    let mut fake_servers = language_registry.register_fake_lsp(
12560        "Rust",
12561        FakeLspAdapter {
12562            capabilities: lsp::ServerCapabilities {
12563                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12564                    first_trigger_character: "{".to_string(),
12565                    more_trigger_character: None,
12566                }),
12567                ..Default::default()
12568            },
12569            ..Default::default()
12570        },
12571    );
12572
12573    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12574
12575    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12576
12577    let worktree_id = workspace
12578        .update(cx, |workspace, _, cx| {
12579            workspace.project().update(cx, |project, cx| {
12580                project.worktrees(cx).next().unwrap().read(cx).id()
12581            })
12582        })
12583        .unwrap();
12584
12585    let buffer = project
12586        .update(cx, |project, cx| {
12587            project.open_local_buffer(path!("/a/main.rs"), cx)
12588        })
12589        .await
12590        .unwrap();
12591    let editor_handle = workspace
12592        .update(cx, |workspace, window, cx| {
12593            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12594        })
12595        .unwrap()
12596        .await
12597        .unwrap()
12598        .downcast::<Editor>()
12599        .unwrap();
12600
12601    cx.executor().start_waiting();
12602    let fake_server = fake_servers.next().await.unwrap();
12603
12604    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12605        |params, _| async move {
12606            assert_eq!(
12607                params.text_document_position.text_document.uri,
12608                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12609            );
12610            assert_eq!(
12611                params.text_document_position.position,
12612                lsp::Position::new(0, 21),
12613            );
12614
12615            Ok(Some(vec![lsp::TextEdit {
12616                new_text: "]".to_string(),
12617                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12618            }]))
12619        },
12620    );
12621
12622    editor_handle.update_in(cx, |editor, window, cx| {
12623        window.focus(&editor.focus_handle(cx));
12624        editor.change_selections(None, window, cx, |s| {
12625            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12626        });
12627        editor.handle_input("{", window, cx);
12628    });
12629
12630    cx.executor().run_until_parked();
12631
12632    buffer.update(cx, |buffer, _| {
12633        assert_eq!(
12634            buffer.text(),
12635            "fn main() { let a = {5}; }",
12636            "No extra braces from on type formatting should appear in the buffer"
12637        )
12638    });
12639}
12640
12641#[gpui::test]
12642async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12643    init_test(cx, |_| {});
12644
12645    let fs = FakeFs::new(cx.executor());
12646    fs.insert_tree(
12647        path!("/a"),
12648        json!({
12649            "main.rs": "fn main() { let a = 5; }",
12650            "other.rs": "// Test file",
12651        }),
12652    )
12653    .await;
12654
12655    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12656
12657    let server_restarts = Arc::new(AtomicUsize::new(0));
12658    let closure_restarts = Arc::clone(&server_restarts);
12659    let language_server_name = "test language server";
12660    let language_name: LanguageName = "Rust".into();
12661
12662    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12663    language_registry.add(Arc::new(Language::new(
12664        LanguageConfig {
12665            name: language_name.clone(),
12666            matcher: LanguageMatcher {
12667                path_suffixes: vec!["rs".to_string()],
12668                ..Default::default()
12669            },
12670            ..Default::default()
12671        },
12672        Some(tree_sitter_rust::LANGUAGE.into()),
12673    )));
12674    let mut fake_servers = language_registry.register_fake_lsp(
12675        "Rust",
12676        FakeLspAdapter {
12677            name: language_server_name,
12678            initialization_options: Some(json!({
12679                "testOptionValue": true
12680            })),
12681            initializer: Some(Box::new(move |fake_server| {
12682                let task_restarts = Arc::clone(&closure_restarts);
12683                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12684                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12685                    futures::future::ready(Ok(()))
12686                });
12687            })),
12688            ..Default::default()
12689        },
12690    );
12691
12692    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12693    let _buffer = project
12694        .update(cx, |project, cx| {
12695            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12696        })
12697        .await
12698        .unwrap();
12699    let _fake_server = fake_servers.next().await.unwrap();
12700    update_test_language_settings(cx, |language_settings| {
12701        language_settings.languages.insert(
12702            language_name.clone(),
12703            LanguageSettingsContent {
12704                tab_size: NonZeroU32::new(8),
12705                ..Default::default()
12706            },
12707        );
12708    });
12709    cx.executor().run_until_parked();
12710    assert_eq!(
12711        server_restarts.load(atomic::Ordering::Acquire),
12712        0,
12713        "Should not restart LSP server on an unrelated change"
12714    );
12715
12716    update_test_project_settings(cx, |project_settings| {
12717        project_settings.lsp.insert(
12718            "Some other server name".into(),
12719            LspSettings {
12720                binary: None,
12721                settings: None,
12722                initialization_options: Some(json!({
12723                    "some other init value": false
12724                })),
12725                enable_lsp_tasks: false,
12726            },
12727        );
12728    });
12729    cx.executor().run_until_parked();
12730    assert_eq!(
12731        server_restarts.load(atomic::Ordering::Acquire),
12732        0,
12733        "Should not restart LSP server on an unrelated LSP settings change"
12734    );
12735
12736    update_test_project_settings(cx, |project_settings| {
12737        project_settings.lsp.insert(
12738            language_server_name.into(),
12739            LspSettings {
12740                binary: None,
12741                settings: None,
12742                initialization_options: Some(json!({
12743                    "anotherInitValue": false
12744                })),
12745                enable_lsp_tasks: false,
12746            },
12747        );
12748    });
12749    cx.executor().run_until_parked();
12750    assert_eq!(
12751        server_restarts.load(atomic::Ordering::Acquire),
12752        1,
12753        "Should restart LSP server on a related LSP settings change"
12754    );
12755
12756    update_test_project_settings(cx, |project_settings| {
12757        project_settings.lsp.insert(
12758            language_server_name.into(),
12759            LspSettings {
12760                binary: None,
12761                settings: None,
12762                initialization_options: Some(json!({
12763                    "anotherInitValue": false
12764                })),
12765                enable_lsp_tasks: false,
12766            },
12767        );
12768    });
12769    cx.executor().run_until_parked();
12770    assert_eq!(
12771        server_restarts.load(atomic::Ordering::Acquire),
12772        1,
12773        "Should not restart LSP server on a related LSP settings change that is the same"
12774    );
12775
12776    update_test_project_settings(cx, |project_settings| {
12777        project_settings.lsp.insert(
12778            language_server_name.into(),
12779            LspSettings {
12780                binary: None,
12781                settings: None,
12782                initialization_options: None,
12783                enable_lsp_tasks: false,
12784            },
12785        );
12786    });
12787    cx.executor().run_until_parked();
12788    assert_eq!(
12789        server_restarts.load(atomic::Ordering::Acquire),
12790        2,
12791        "Should restart LSP server on another related LSP settings change"
12792    );
12793}
12794
12795#[gpui::test]
12796async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12797    init_test(cx, |_| {});
12798
12799    let mut cx = EditorLspTestContext::new_rust(
12800        lsp::ServerCapabilities {
12801            completion_provider: Some(lsp::CompletionOptions {
12802                trigger_characters: Some(vec![".".to_string()]),
12803                resolve_provider: Some(true),
12804                ..Default::default()
12805            }),
12806            ..Default::default()
12807        },
12808        cx,
12809    )
12810    .await;
12811
12812    cx.set_state("fn main() { let a = 2ˇ; }");
12813    cx.simulate_keystroke(".");
12814    let completion_item = lsp::CompletionItem {
12815        label: "some".into(),
12816        kind: Some(lsp::CompletionItemKind::SNIPPET),
12817        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12818        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12819            kind: lsp::MarkupKind::Markdown,
12820            value: "```rust\nSome(2)\n```".to_string(),
12821        })),
12822        deprecated: Some(false),
12823        sort_text: Some("fffffff2".to_string()),
12824        filter_text: Some("some".to_string()),
12825        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12826        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12827            range: lsp::Range {
12828                start: lsp::Position {
12829                    line: 0,
12830                    character: 22,
12831                },
12832                end: lsp::Position {
12833                    line: 0,
12834                    character: 22,
12835                },
12836            },
12837            new_text: "Some(2)".to_string(),
12838        })),
12839        additional_text_edits: Some(vec![lsp::TextEdit {
12840            range: lsp::Range {
12841                start: lsp::Position {
12842                    line: 0,
12843                    character: 20,
12844                },
12845                end: lsp::Position {
12846                    line: 0,
12847                    character: 22,
12848                },
12849            },
12850            new_text: "".to_string(),
12851        }]),
12852        ..Default::default()
12853    };
12854
12855    let closure_completion_item = completion_item.clone();
12856    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12857        let task_completion_item = closure_completion_item.clone();
12858        async move {
12859            Ok(Some(lsp::CompletionResponse::Array(vec![
12860                task_completion_item,
12861            ])))
12862        }
12863    });
12864
12865    request.next().await;
12866
12867    cx.condition(|editor, _| editor.context_menu_visible())
12868        .await;
12869    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12870        editor
12871            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12872            .unwrap()
12873    });
12874    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12875
12876    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12877        let task_completion_item = completion_item.clone();
12878        async move { Ok(task_completion_item) }
12879    })
12880    .next()
12881    .await
12882    .unwrap();
12883    apply_additional_edits.await.unwrap();
12884    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12885}
12886
12887#[gpui::test]
12888async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12889    init_test(cx, |_| {});
12890
12891    let mut cx = EditorLspTestContext::new_rust(
12892        lsp::ServerCapabilities {
12893            completion_provider: Some(lsp::CompletionOptions {
12894                trigger_characters: Some(vec![".".to_string()]),
12895                resolve_provider: Some(true),
12896                ..Default::default()
12897            }),
12898            ..Default::default()
12899        },
12900        cx,
12901    )
12902    .await;
12903
12904    cx.set_state("fn main() { let a = 2ˇ; }");
12905    cx.simulate_keystroke(".");
12906
12907    let item1 = lsp::CompletionItem {
12908        label: "method id()".to_string(),
12909        filter_text: Some("id".to_string()),
12910        detail: None,
12911        documentation: None,
12912        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12913            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12914            new_text: ".id".to_string(),
12915        })),
12916        ..lsp::CompletionItem::default()
12917    };
12918
12919    let item2 = lsp::CompletionItem {
12920        label: "other".to_string(),
12921        filter_text: Some("other".to_string()),
12922        detail: None,
12923        documentation: None,
12924        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12925            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12926            new_text: ".other".to_string(),
12927        })),
12928        ..lsp::CompletionItem::default()
12929    };
12930
12931    let item1 = item1.clone();
12932    cx.set_request_handler::<lsp::request::Completion, _, _>({
12933        let item1 = item1.clone();
12934        move |_, _, _| {
12935            let item1 = item1.clone();
12936            let item2 = item2.clone();
12937            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12938        }
12939    })
12940    .next()
12941    .await;
12942
12943    cx.condition(|editor, _| editor.context_menu_visible())
12944        .await;
12945    cx.update_editor(|editor, _, _| {
12946        let context_menu = editor.context_menu.borrow_mut();
12947        let context_menu = context_menu
12948            .as_ref()
12949            .expect("Should have the context menu deployed");
12950        match context_menu {
12951            CodeContextMenu::Completions(completions_menu) => {
12952                let completions = completions_menu.completions.borrow_mut();
12953                assert_eq!(
12954                    completions
12955                        .iter()
12956                        .map(|completion| &completion.label.text)
12957                        .collect::<Vec<_>>(),
12958                    vec!["method id()", "other"]
12959                )
12960            }
12961            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12962        }
12963    });
12964
12965    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12966        let item1 = item1.clone();
12967        move |_, item_to_resolve, _| {
12968            let item1 = item1.clone();
12969            async move {
12970                if item1 == item_to_resolve {
12971                    Ok(lsp::CompletionItem {
12972                        label: "method id()".to_string(),
12973                        filter_text: Some("id".to_string()),
12974                        detail: Some("Now resolved!".to_string()),
12975                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12976                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12977                            range: lsp::Range::new(
12978                                lsp::Position::new(0, 22),
12979                                lsp::Position::new(0, 22),
12980                            ),
12981                            new_text: ".id".to_string(),
12982                        })),
12983                        ..lsp::CompletionItem::default()
12984                    })
12985                } else {
12986                    Ok(item_to_resolve)
12987                }
12988            }
12989        }
12990    })
12991    .next()
12992    .await
12993    .unwrap();
12994    cx.run_until_parked();
12995
12996    cx.update_editor(|editor, window, cx| {
12997        editor.context_menu_next(&Default::default(), window, cx);
12998    });
12999
13000    cx.update_editor(|editor, _, _| {
13001        let context_menu = editor.context_menu.borrow_mut();
13002        let context_menu = context_menu
13003            .as_ref()
13004            .expect("Should have the context menu deployed");
13005        match context_menu {
13006            CodeContextMenu::Completions(completions_menu) => {
13007                let completions = completions_menu.completions.borrow_mut();
13008                assert_eq!(
13009                    completions
13010                        .iter()
13011                        .map(|completion| &completion.label.text)
13012                        .collect::<Vec<_>>(),
13013                    vec!["method id() Now resolved!", "other"],
13014                    "Should update first completion label, but not second as the filter text did not match."
13015                );
13016            }
13017            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13018        }
13019    });
13020}
13021
13022#[gpui::test]
13023async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13024    init_test(cx, |_| {});
13025
13026    let mut cx = EditorLspTestContext::new_rust(
13027        lsp::ServerCapabilities {
13028            completion_provider: Some(lsp::CompletionOptions {
13029                trigger_characters: Some(vec![".".to_string()]),
13030                resolve_provider: Some(true),
13031                ..Default::default()
13032            }),
13033            ..Default::default()
13034        },
13035        cx,
13036    )
13037    .await;
13038
13039    cx.set_state("fn main() { let a = 2ˇ; }");
13040    cx.simulate_keystroke(".");
13041
13042    let unresolved_item_1 = lsp::CompletionItem {
13043        label: "id".to_string(),
13044        filter_text: Some("id".to_string()),
13045        detail: None,
13046        documentation: None,
13047        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13048            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13049            new_text: ".id".to_string(),
13050        })),
13051        ..lsp::CompletionItem::default()
13052    };
13053    let resolved_item_1 = lsp::CompletionItem {
13054        additional_text_edits: Some(vec![lsp::TextEdit {
13055            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13056            new_text: "!!".to_string(),
13057        }]),
13058        ..unresolved_item_1.clone()
13059    };
13060    let unresolved_item_2 = lsp::CompletionItem {
13061        label: "other".to_string(),
13062        filter_text: Some("other".to_string()),
13063        detail: None,
13064        documentation: None,
13065        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13066            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13067            new_text: ".other".to_string(),
13068        })),
13069        ..lsp::CompletionItem::default()
13070    };
13071    let resolved_item_2 = lsp::CompletionItem {
13072        additional_text_edits: Some(vec![lsp::TextEdit {
13073            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13074            new_text: "??".to_string(),
13075        }]),
13076        ..unresolved_item_2.clone()
13077    };
13078
13079    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13080    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13081    cx.lsp
13082        .server
13083        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13084            let unresolved_item_1 = unresolved_item_1.clone();
13085            let resolved_item_1 = resolved_item_1.clone();
13086            let unresolved_item_2 = unresolved_item_2.clone();
13087            let resolved_item_2 = resolved_item_2.clone();
13088            let resolve_requests_1 = resolve_requests_1.clone();
13089            let resolve_requests_2 = resolve_requests_2.clone();
13090            move |unresolved_request, _| {
13091                let unresolved_item_1 = unresolved_item_1.clone();
13092                let resolved_item_1 = resolved_item_1.clone();
13093                let unresolved_item_2 = unresolved_item_2.clone();
13094                let resolved_item_2 = resolved_item_2.clone();
13095                let resolve_requests_1 = resolve_requests_1.clone();
13096                let resolve_requests_2 = resolve_requests_2.clone();
13097                async move {
13098                    if unresolved_request == unresolved_item_1 {
13099                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13100                        Ok(resolved_item_1.clone())
13101                    } else if unresolved_request == unresolved_item_2 {
13102                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13103                        Ok(resolved_item_2.clone())
13104                    } else {
13105                        panic!("Unexpected completion item {unresolved_request:?}")
13106                    }
13107                }
13108            }
13109        })
13110        .detach();
13111
13112    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13113        let unresolved_item_1 = unresolved_item_1.clone();
13114        let unresolved_item_2 = unresolved_item_2.clone();
13115        async move {
13116            Ok(Some(lsp::CompletionResponse::Array(vec![
13117                unresolved_item_1,
13118                unresolved_item_2,
13119            ])))
13120        }
13121    })
13122    .next()
13123    .await;
13124
13125    cx.condition(|editor, _| editor.context_menu_visible())
13126        .await;
13127    cx.update_editor(|editor, _, _| {
13128        let context_menu = editor.context_menu.borrow_mut();
13129        let context_menu = context_menu
13130            .as_ref()
13131            .expect("Should have the context menu deployed");
13132        match context_menu {
13133            CodeContextMenu::Completions(completions_menu) => {
13134                let completions = completions_menu.completions.borrow_mut();
13135                assert_eq!(
13136                    completions
13137                        .iter()
13138                        .map(|completion| &completion.label.text)
13139                        .collect::<Vec<_>>(),
13140                    vec!["id", "other"]
13141                )
13142            }
13143            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13144        }
13145    });
13146    cx.run_until_parked();
13147
13148    cx.update_editor(|editor, window, cx| {
13149        editor.context_menu_next(&ContextMenuNext, window, cx);
13150    });
13151    cx.run_until_parked();
13152    cx.update_editor(|editor, window, cx| {
13153        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13154    });
13155    cx.run_until_parked();
13156    cx.update_editor(|editor, window, cx| {
13157        editor.context_menu_next(&ContextMenuNext, window, cx);
13158    });
13159    cx.run_until_parked();
13160    cx.update_editor(|editor, window, cx| {
13161        editor
13162            .compose_completion(&ComposeCompletion::default(), window, cx)
13163            .expect("No task returned")
13164    })
13165    .await
13166    .expect("Completion failed");
13167    cx.run_until_parked();
13168
13169    cx.update_editor(|editor, _, cx| {
13170        assert_eq!(
13171            resolve_requests_1.load(atomic::Ordering::Acquire),
13172            1,
13173            "Should always resolve once despite multiple selections"
13174        );
13175        assert_eq!(
13176            resolve_requests_2.load(atomic::Ordering::Acquire),
13177            1,
13178            "Should always resolve once after multiple selections and applying the completion"
13179        );
13180        assert_eq!(
13181            editor.text(cx),
13182            "fn main() { let a = ??.other; }",
13183            "Should use resolved data when applying the completion"
13184        );
13185    });
13186}
13187
13188#[gpui::test]
13189async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13190    init_test(cx, |_| {});
13191
13192    let item_0 = lsp::CompletionItem {
13193        label: "abs".into(),
13194        insert_text: Some("abs".into()),
13195        data: Some(json!({ "very": "special"})),
13196        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13197        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13198            lsp::InsertReplaceEdit {
13199                new_text: "abs".to_string(),
13200                insert: lsp::Range::default(),
13201                replace: lsp::Range::default(),
13202            },
13203        )),
13204        ..lsp::CompletionItem::default()
13205    };
13206    let items = iter::once(item_0.clone())
13207        .chain((11..51).map(|i| lsp::CompletionItem {
13208            label: format!("item_{}", i),
13209            insert_text: Some(format!("item_{}", i)),
13210            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13211            ..lsp::CompletionItem::default()
13212        }))
13213        .collect::<Vec<_>>();
13214
13215    let default_commit_characters = vec!["?".to_string()];
13216    let default_data = json!({ "default": "data"});
13217    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13218    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13219    let default_edit_range = lsp::Range {
13220        start: lsp::Position {
13221            line: 0,
13222            character: 5,
13223        },
13224        end: lsp::Position {
13225            line: 0,
13226            character: 5,
13227        },
13228    };
13229
13230    let mut cx = EditorLspTestContext::new_rust(
13231        lsp::ServerCapabilities {
13232            completion_provider: Some(lsp::CompletionOptions {
13233                trigger_characters: Some(vec![".".to_string()]),
13234                resolve_provider: Some(true),
13235                ..Default::default()
13236            }),
13237            ..Default::default()
13238        },
13239        cx,
13240    )
13241    .await;
13242
13243    cx.set_state("fn main() { let a = 2ˇ; }");
13244    cx.simulate_keystroke(".");
13245
13246    let completion_data = default_data.clone();
13247    let completion_characters = default_commit_characters.clone();
13248    let completion_items = items.clone();
13249    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13250        let default_data = completion_data.clone();
13251        let default_commit_characters = completion_characters.clone();
13252        let items = completion_items.clone();
13253        async move {
13254            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13255                items,
13256                item_defaults: Some(lsp::CompletionListItemDefaults {
13257                    data: Some(default_data.clone()),
13258                    commit_characters: Some(default_commit_characters.clone()),
13259                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13260                        default_edit_range,
13261                    )),
13262                    insert_text_format: Some(default_insert_text_format),
13263                    insert_text_mode: Some(default_insert_text_mode),
13264                }),
13265                ..lsp::CompletionList::default()
13266            })))
13267        }
13268    })
13269    .next()
13270    .await;
13271
13272    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13273    cx.lsp
13274        .server
13275        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13276            let closure_resolved_items = resolved_items.clone();
13277            move |item_to_resolve, _| {
13278                let closure_resolved_items = closure_resolved_items.clone();
13279                async move {
13280                    closure_resolved_items.lock().push(item_to_resolve.clone());
13281                    Ok(item_to_resolve)
13282                }
13283            }
13284        })
13285        .detach();
13286
13287    cx.condition(|editor, _| editor.context_menu_visible())
13288        .await;
13289    cx.run_until_parked();
13290    cx.update_editor(|editor, _, _| {
13291        let menu = editor.context_menu.borrow_mut();
13292        match menu.as_ref().expect("should have the completions menu") {
13293            CodeContextMenu::Completions(completions_menu) => {
13294                assert_eq!(
13295                    completions_menu
13296                        .entries
13297                        .borrow()
13298                        .iter()
13299                        .map(|mat| mat.string.clone())
13300                        .collect::<Vec<String>>(),
13301                    items
13302                        .iter()
13303                        .map(|completion| completion.label.clone())
13304                        .collect::<Vec<String>>()
13305                );
13306            }
13307            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13308        }
13309    });
13310    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13311    // with 4 from the end.
13312    assert_eq!(
13313        *resolved_items.lock(),
13314        [&items[0..16], &items[items.len() - 4..items.len()]]
13315            .concat()
13316            .iter()
13317            .cloned()
13318            .map(|mut item| {
13319                if item.data.is_none() {
13320                    item.data = Some(default_data.clone());
13321                }
13322                item
13323            })
13324            .collect::<Vec<lsp::CompletionItem>>(),
13325        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13326    );
13327    resolved_items.lock().clear();
13328
13329    cx.update_editor(|editor, window, cx| {
13330        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13331    });
13332    cx.run_until_parked();
13333    // Completions that have already been resolved are skipped.
13334    assert_eq!(
13335        *resolved_items.lock(),
13336        items[items.len() - 16..items.len() - 4]
13337            .iter()
13338            .cloned()
13339            .map(|mut item| {
13340                if item.data.is_none() {
13341                    item.data = Some(default_data.clone());
13342                }
13343                item
13344            })
13345            .collect::<Vec<lsp::CompletionItem>>()
13346    );
13347    resolved_items.lock().clear();
13348}
13349
13350#[gpui::test]
13351async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13352    init_test(cx, |_| {});
13353
13354    let mut cx = EditorLspTestContext::new(
13355        Language::new(
13356            LanguageConfig {
13357                matcher: LanguageMatcher {
13358                    path_suffixes: vec!["jsx".into()],
13359                    ..Default::default()
13360                },
13361                overrides: [(
13362                    "element".into(),
13363                    LanguageConfigOverride {
13364                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13365                        ..Default::default()
13366                    },
13367                )]
13368                .into_iter()
13369                .collect(),
13370                ..Default::default()
13371            },
13372            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13373        )
13374        .with_override_query("(jsx_self_closing_element) @element")
13375        .unwrap(),
13376        lsp::ServerCapabilities {
13377            completion_provider: Some(lsp::CompletionOptions {
13378                trigger_characters: Some(vec![":".to_string()]),
13379                ..Default::default()
13380            }),
13381            ..Default::default()
13382        },
13383        cx,
13384    )
13385    .await;
13386
13387    cx.lsp
13388        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13389            Ok(Some(lsp::CompletionResponse::Array(vec![
13390                lsp::CompletionItem {
13391                    label: "bg-blue".into(),
13392                    ..Default::default()
13393                },
13394                lsp::CompletionItem {
13395                    label: "bg-red".into(),
13396                    ..Default::default()
13397                },
13398                lsp::CompletionItem {
13399                    label: "bg-yellow".into(),
13400                    ..Default::default()
13401                },
13402            ])))
13403        });
13404
13405    cx.set_state(r#"<p class="bgˇ" />"#);
13406
13407    // Trigger completion when typing a dash, because the dash is an extra
13408    // word character in the 'element' scope, which contains the cursor.
13409    cx.simulate_keystroke("-");
13410    cx.executor().run_until_parked();
13411    cx.update_editor(|editor, _, _| {
13412        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13413        {
13414            assert_eq!(
13415                completion_menu_entries(&menu),
13416                &["bg-red", "bg-blue", "bg-yellow"]
13417            );
13418        } else {
13419            panic!("expected completion menu to be open");
13420        }
13421    });
13422
13423    cx.simulate_keystroke("l");
13424    cx.executor().run_until_parked();
13425    cx.update_editor(|editor, _, _| {
13426        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13427        {
13428            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13429        } else {
13430            panic!("expected completion menu to be open");
13431        }
13432    });
13433
13434    // When filtering completions, consider the character after the '-' to
13435    // be the start of a subword.
13436    cx.set_state(r#"<p class="yelˇ" />"#);
13437    cx.simulate_keystroke("l");
13438    cx.executor().run_until_parked();
13439    cx.update_editor(|editor, _, _| {
13440        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13441        {
13442            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13443        } else {
13444            panic!("expected completion menu to be open");
13445        }
13446    });
13447}
13448
13449fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13450    let entries = menu.entries.borrow();
13451    entries.iter().map(|mat| mat.string.clone()).collect()
13452}
13453
13454#[gpui::test]
13455async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13456    init_test(cx, |settings| {
13457        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13458            FormatterList(vec![Formatter::Prettier].into()),
13459        ))
13460    });
13461
13462    let fs = FakeFs::new(cx.executor());
13463    fs.insert_file(path!("/file.ts"), Default::default()).await;
13464
13465    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13466    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13467
13468    language_registry.add(Arc::new(Language::new(
13469        LanguageConfig {
13470            name: "TypeScript".into(),
13471            matcher: LanguageMatcher {
13472                path_suffixes: vec!["ts".to_string()],
13473                ..Default::default()
13474            },
13475            ..Default::default()
13476        },
13477        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13478    )));
13479    update_test_language_settings(cx, |settings| {
13480        settings.defaults.prettier = Some(PrettierSettings {
13481            allowed: true,
13482            ..PrettierSettings::default()
13483        });
13484    });
13485
13486    let test_plugin = "test_plugin";
13487    let _ = language_registry.register_fake_lsp(
13488        "TypeScript",
13489        FakeLspAdapter {
13490            prettier_plugins: vec![test_plugin],
13491            ..Default::default()
13492        },
13493    );
13494
13495    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13496    let buffer = project
13497        .update(cx, |project, cx| {
13498            project.open_local_buffer(path!("/file.ts"), cx)
13499        })
13500        .await
13501        .unwrap();
13502
13503    let buffer_text = "one\ntwo\nthree\n";
13504    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13505    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13506    editor.update_in(cx, |editor, window, cx| {
13507        editor.set_text(buffer_text, window, cx)
13508    });
13509
13510    editor
13511        .update_in(cx, |editor, window, cx| {
13512            editor.perform_format(
13513                project.clone(),
13514                FormatTrigger::Manual,
13515                FormatTarget::Buffers,
13516                window,
13517                cx,
13518            )
13519        })
13520        .unwrap()
13521        .await;
13522    assert_eq!(
13523        editor.update(cx, |editor, cx| editor.text(cx)),
13524        buffer_text.to_string() + prettier_format_suffix,
13525        "Test prettier formatting was not applied to the original buffer text",
13526    );
13527
13528    update_test_language_settings(cx, |settings| {
13529        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13530    });
13531    let format = editor.update_in(cx, |editor, window, cx| {
13532        editor.perform_format(
13533            project.clone(),
13534            FormatTrigger::Manual,
13535            FormatTarget::Buffers,
13536            window,
13537            cx,
13538        )
13539    });
13540    format.await.unwrap();
13541    assert_eq!(
13542        editor.update(cx, |editor, cx| editor.text(cx)),
13543        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13544        "Autoformatting (via test prettier) was not applied to the original buffer text",
13545    );
13546}
13547
13548#[gpui::test]
13549async fn test_addition_reverts(cx: &mut TestAppContext) {
13550    init_test(cx, |_| {});
13551    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13552    let base_text = indoc! {r#"
13553        struct Row;
13554        struct Row1;
13555        struct Row2;
13556
13557        struct Row4;
13558        struct Row5;
13559        struct Row6;
13560
13561        struct Row8;
13562        struct Row9;
13563        struct Row10;"#};
13564
13565    // When addition hunks are not adjacent to carets, no hunk revert is performed
13566    assert_hunk_revert(
13567        indoc! {r#"struct Row;
13568                   struct Row1;
13569                   struct Row1.1;
13570                   struct Row1.2;
13571                   struct Row2;ˇ
13572
13573                   struct Row4;
13574                   struct Row5;
13575                   struct Row6;
13576
13577                   struct Row8;
13578                   ˇstruct Row9;
13579                   struct Row9.1;
13580                   struct Row9.2;
13581                   struct Row9.3;
13582                   struct Row10;"#},
13583        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13584        indoc! {r#"struct Row;
13585                   struct Row1;
13586                   struct Row1.1;
13587                   struct Row1.2;
13588                   struct Row2;ˇ
13589
13590                   struct Row4;
13591                   struct Row5;
13592                   struct Row6;
13593
13594                   struct Row8;
13595                   ˇstruct Row9;
13596                   struct Row9.1;
13597                   struct Row9.2;
13598                   struct Row9.3;
13599                   struct Row10;"#},
13600        base_text,
13601        &mut cx,
13602    );
13603    // Same for selections
13604    assert_hunk_revert(
13605        indoc! {r#"struct Row;
13606                   struct Row1;
13607                   struct Row2;
13608                   struct Row2.1;
13609                   struct Row2.2;
13610                   «ˇ
13611                   struct Row4;
13612                   struct» Row5;
13613                   «struct Row6;
13614                   ˇ»
13615                   struct Row9.1;
13616                   struct Row9.2;
13617                   struct Row9.3;
13618                   struct Row8;
13619                   struct Row9;
13620                   struct Row10;"#},
13621        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13622        indoc! {r#"struct Row;
13623                   struct Row1;
13624                   struct Row2;
13625                   struct Row2.1;
13626                   struct Row2.2;
13627                   «ˇ
13628                   struct Row4;
13629                   struct» Row5;
13630                   «struct Row6;
13631                   ˇ»
13632                   struct Row9.1;
13633                   struct Row9.2;
13634                   struct Row9.3;
13635                   struct Row8;
13636                   struct Row9;
13637                   struct Row10;"#},
13638        base_text,
13639        &mut cx,
13640    );
13641
13642    // When carets and selections intersect the addition hunks, those are reverted.
13643    // Adjacent carets got merged.
13644    assert_hunk_revert(
13645        indoc! {r#"struct Row;
13646                   ˇ// something on the top
13647                   struct Row1;
13648                   struct Row2;
13649                   struct Roˇw3.1;
13650                   struct Row2.2;
13651                   struct Row2.3;ˇ
13652
13653                   struct Row4;
13654                   struct ˇRow5.1;
13655                   struct Row5.2;
13656                   struct «Rowˇ»5.3;
13657                   struct Row5;
13658                   struct Row6;
13659                   ˇ
13660                   struct Row9.1;
13661                   struct «Rowˇ»9.2;
13662                   struct «ˇRow»9.3;
13663                   struct Row8;
13664                   struct Row9;
13665                   «ˇ// something on bottom»
13666                   struct Row10;"#},
13667        vec![
13668            DiffHunkStatusKind::Added,
13669            DiffHunkStatusKind::Added,
13670            DiffHunkStatusKind::Added,
13671            DiffHunkStatusKind::Added,
13672            DiffHunkStatusKind::Added,
13673        ],
13674        indoc! {r#"struct Row;
13675                   ˇstruct Row1;
13676                   struct Row2;
13677                   ˇ
13678                   struct Row4;
13679                   ˇstruct Row5;
13680                   struct Row6;
13681                   ˇ
13682                   ˇstruct Row8;
13683                   struct Row9;
13684                   ˇstruct Row10;"#},
13685        base_text,
13686        &mut cx,
13687    );
13688}
13689
13690#[gpui::test]
13691async fn test_modification_reverts(cx: &mut TestAppContext) {
13692    init_test(cx, |_| {});
13693    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13694    let base_text = indoc! {r#"
13695        struct Row;
13696        struct Row1;
13697        struct Row2;
13698
13699        struct Row4;
13700        struct Row5;
13701        struct Row6;
13702
13703        struct Row8;
13704        struct Row9;
13705        struct Row10;"#};
13706
13707    // Modification hunks behave the same as the addition ones.
13708    assert_hunk_revert(
13709        indoc! {r#"struct Row;
13710                   struct Row1;
13711                   struct Row33;
13712                   ˇ
13713                   struct Row4;
13714                   struct Row5;
13715                   struct Row6;
13716                   ˇ
13717                   struct Row99;
13718                   struct Row9;
13719                   struct Row10;"#},
13720        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13721        indoc! {r#"struct Row;
13722                   struct Row1;
13723                   struct Row33;
13724                   ˇ
13725                   struct Row4;
13726                   struct Row5;
13727                   struct Row6;
13728                   ˇ
13729                   struct Row99;
13730                   struct Row9;
13731                   struct Row10;"#},
13732        base_text,
13733        &mut cx,
13734    );
13735    assert_hunk_revert(
13736        indoc! {r#"struct Row;
13737                   struct Row1;
13738                   struct Row33;
13739                   «ˇ
13740                   struct Row4;
13741                   struct» Row5;
13742                   «struct Row6;
13743                   ˇ»
13744                   struct Row99;
13745                   struct Row9;
13746                   struct Row10;"#},
13747        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13748        indoc! {r#"struct Row;
13749                   struct Row1;
13750                   struct Row33;
13751                   «ˇ
13752                   struct Row4;
13753                   struct» Row5;
13754                   «struct Row6;
13755                   ˇ»
13756                   struct Row99;
13757                   struct Row9;
13758                   struct Row10;"#},
13759        base_text,
13760        &mut cx,
13761    );
13762
13763    assert_hunk_revert(
13764        indoc! {r#"ˇstruct Row1.1;
13765                   struct Row1;
13766                   «ˇstr»uct Row22;
13767
13768                   struct ˇRow44;
13769                   struct Row5;
13770                   struct «Rˇ»ow66;ˇ
13771
13772                   «struˇ»ct Row88;
13773                   struct Row9;
13774                   struct Row1011;ˇ"#},
13775        vec![
13776            DiffHunkStatusKind::Modified,
13777            DiffHunkStatusKind::Modified,
13778            DiffHunkStatusKind::Modified,
13779            DiffHunkStatusKind::Modified,
13780            DiffHunkStatusKind::Modified,
13781            DiffHunkStatusKind::Modified,
13782        ],
13783        indoc! {r#"struct Row;
13784                   ˇstruct Row1;
13785                   struct Row2;
13786                   ˇ
13787                   struct Row4;
13788                   ˇstruct Row5;
13789                   struct Row6;
13790                   ˇ
13791                   struct Row8;
13792                   ˇstruct Row9;
13793                   struct Row10;ˇ"#},
13794        base_text,
13795        &mut cx,
13796    );
13797}
13798
13799#[gpui::test]
13800async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13801    init_test(cx, |_| {});
13802    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13803    let base_text = indoc! {r#"
13804        one
13805
13806        two
13807        three
13808        "#};
13809
13810    cx.set_head_text(base_text);
13811    cx.set_state("\nˇ\n");
13812    cx.executor().run_until_parked();
13813    cx.update_editor(|editor, _window, cx| {
13814        editor.expand_selected_diff_hunks(cx);
13815    });
13816    cx.executor().run_until_parked();
13817    cx.update_editor(|editor, window, cx| {
13818        editor.backspace(&Default::default(), window, cx);
13819    });
13820    cx.run_until_parked();
13821    cx.assert_state_with_diff(
13822        indoc! {r#"
13823
13824        - two
13825        - threeˇ
13826        +
13827        "#}
13828        .to_string(),
13829    );
13830}
13831
13832#[gpui::test]
13833async fn test_deletion_reverts(cx: &mut TestAppContext) {
13834    init_test(cx, |_| {});
13835    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13836    let base_text = indoc! {r#"struct Row;
13837struct Row1;
13838struct Row2;
13839
13840struct Row4;
13841struct Row5;
13842struct Row6;
13843
13844struct Row8;
13845struct Row9;
13846struct Row10;"#};
13847
13848    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13849    assert_hunk_revert(
13850        indoc! {r#"struct Row;
13851                   struct Row2;
13852
13853                   ˇstruct Row4;
13854                   struct Row5;
13855                   struct Row6;
13856                   ˇ
13857                   struct Row8;
13858                   struct Row10;"#},
13859        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13860        indoc! {r#"struct Row;
13861                   struct Row2;
13862
13863                   ˇstruct Row4;
13864                   struct Row5;
13865                   struct Row6;
13866                   ˇ
13867                   struct Row8;
13868                   struct Row10;"#},
13869        base_text,
13870        &mut cx,
13871    );
13872    assert_hunk_revert(
13873        indoc! {r#"struct Row;
13874                   struct Row2;
13875
13876                   «ˇstruct Row4;
13877                   struct» Row5;
13878                   «struct Row6;
13879                   ˇ»
13880                   struct Row8;
13881                   struct Row10;"#},
13882        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13883        indoc! {r#"struct Row;
13884                   struct Row2;
13885
13886                   «ˇstruct Row4;
13887                   struct» Row5;
13888                   «struct Row6;
13889                   ˇ»
13890                   struct Row8;
13891                   struct Row10;"#},
13892        base_text,
13893        &mut cx,
13894    );
13895
13896    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13897    assert_hunk_revert(
13898        indoc! {r#"struct Row;
13899                   ˇstruct Row2;
13900
13901                   struct Row4;
13902                   struct Row5;
13903                   struct Row6;
13904
13905                   struct Row8;ˇ
13906                   struct Row10;"#},
13907        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13908        indoc! {r#"struct Row;
13909                   struct Row1;
13910                   ˇstruct Row2;
13911
13912                   struct Row4;
13913                   struct Row5;
13914                   struct Row6;
13915
13916                   struct Row8;ˇ
13917                   struct Row9;
13918                   struct Row10;"#},
13919        base_text,
13920        &mut cx,
13921    );
13922    assert_hunk_revert(
13923        indoc! {r#"struct Row;
13924                   struct Row2«ˇ;
13925                   struct Row4;
13926                   struct» Row5;
13927                   «struct Row6;
13928
13929                   struct Row8;ˇ»
13930                   struct Row10;"#},
13931        vec![
13932            DiffHunkStatusKind::Deleted,
13933            DiffHunkStatusKind::Deleted,
13934            DiffHunkStatusKind::Deleted,
13935        ],
13936        indoc! {r#"struct Row;
13937                   struct Row1;
13938                   struct Row2«ˇ;
13939
13940                   struct Row4;
13941                   struct» Row5;
13942                   «struct Row6;
13943
13944                   struct Row8;ˇ»
13945                   struct Row9;
13946                   struct Row10;"#},
13947        base_text,
13948        &mut cx,
13949    );
13950}
13951
13952#[gpui::test]
13953async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13954    init_test(cx, |_| {});
13955
13956    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13957    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13958    let base_text_3 =
13959        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13960
13961    let text_1 = edit_first_char_of_every_line(base_text_1);
13962    let text_2 = edit_first_char_of_every_line(base_text_2);
13963    let text_3 = edit_first_char_of_every_line(base_text_3);
13964
13965    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13966    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13967    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13968
13969    let multibuffer = cx.new(|cx| {
13970        let mut multibuffer = MultiBuffer::new(ReadWrite);
13971        multibuffer.push_excerpts(
13972            buffer_1.clone(),
13973            [
13974                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13975                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13976                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13977            ],
13978            cx,
13979        );
13980        multibuffer.push_excerpts(
13981            buffer_2.clone(),
13982            [
13983                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13984                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13985                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13986            ],
13987            cx,
13988        );
13989        multibuffer.push_excerpts(
13990            buffer_3.clone(),
13991            [
13992                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13993                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13994                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13995            ],
13996            cx,
13997        );
13998        multibuffer
13999    });
14000
14001    let fs = FakeFs::new(cx.executor());
14002    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14003    let (editor, cx) = cx
14004        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14005    editor.update_in(cx, |editor, _window, cx| {
14006        for (buffer, diff_base) in [
14007            (buffer_1.clone(), base_text_1),
14008            (buffer_2.clone(), base_text_2),
14009            (buffer_3.clone(), base_text_3),
14010        ] {
14011            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14012            editor
14013                .buffer
14014                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14015        }
14016    });
14017    cx.executor().run_until_parked();
14018
14019    editor.update_in(cx, |editor, window, cx| {
14020        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}");
14021        editor.select_all(&SelectAll, window, cx);
14022        editor.git_restore(&Default::default(), window, cx);
14023    });
14024    cx.executor().run_until_parked();
14025
14026    // When all ranges are selected, all buffer hunks are reverted.
14027    editor.update(cx, |editor, cx| {
14028        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");
14029    });
14030    buffer_1.update(cx, |buffer, _| {
14031        assert_eq!(buffer.text(), base_text_1);
14032    });
14033    buffer_2.update(cx, |buffer, _| {
14034        assert_eq!(buffer.text(), base_text_2);
14035    });
14036    buffer_3.update(cx, |buffer, _| {
14037        assert_eq!(buffer.text(), base_text_3);
14038    });
14039
14040    editor.update_in(cx, |editor, window, cx| {
14041        editor.undo(&Default::default(), window, cx);
14042    });
14043
14044    editor.update_in(cx, |editor, window, cx| {
14045        editor.change_selections(None, window, cx, |s| {
14046            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14047        });
14048        editor.git_restore(&Default::default(), window, cx);
14049    });
14050
14051    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14052    // but not affect buffer_2 and its related excerpts.
14053    editor.update(cx, |editor, cx| {
14054        assert_eq!(
14055            editor.text(cx),
14056            "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}"
14057        );
14058    });
14059    buffer_1.update(cx, |buffer, _| {
14060        assert_eq!(buffer.text(), base_text_1);
14061    });
14062    buffer_2.update(cx, |buffer, _| {
14063        assert_eq!(
14064            buffer.text(),
14065            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14066        );
14067    });
14068    buffer_3.update(cx, |buffer, _| {
14069        assert_eq!(
14070            buffer.text(),
14071            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14072        );
14073    });
14074
14075    fn edit_first_char_of_every_line(text: &str) -> String {
14076        text.split('\n')
14077            .map(|line| format!("X{}", &line[1..]))
14078            .collect::<Vec<_>>()
14079            .join("\n")
14080    }
14081}
14082
14083#[gpui::test]
14084async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14085    init_test(cx, |_| {});
14086
14087    let cols = 4;
14088    let rows = 10;
14089    let sample_text_1 = sample_text(rows, cols, 'a');
14090    assert_eq!(
14091        sample_text_1,
14092        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14093    );
14094    let sample_text_2 = sample_text(rows, cols, 'l');
14095    assert_eq!(
14096        sample_text_2,
14097        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14098    );
14099    let sample_text_3 = sample_text(rows, cols, 'v');
14100    assert_eq!(
14101        sample_text_3,
14102        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14103    );
14104
14105    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14106    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14107    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14108
14109    let multi_buffer = cx.new(|cx| {
14110        let mut multibuffer = MultiBuffer::new(ReadWrite);
14111        multibuffer.push_excerpts(
14112            buffer_1.clone(),
14113            [
14114                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14115                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14116                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14117            ],
14118            cx,
14119        );
14120        multibuffer.push_excerpts(
14121            buffer_2.clone(),
14122            [
14123                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14124                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14125                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14126            ],
14127            cx,
14128        );
14129        multibuffer.push_excerpts(
14130            buffer_3.clone(),
14131            [
14132                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14133                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14134                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14135            ],
14136            cx,
14137        );
14138        multibuffer
14139    });
14140
14141    let fs = FakeFs::new(cx.executor());
14142    fs.insert_tree(
14143        "/a",
14144        json!({
14145            "main.rs": sample_text_1,
14146            "other.rs": sample_text_2,
14147            "lib.rs": sample_text_3,
14148        }),
14149    )
14150    .await;
14151    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14152    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14153    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14154    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14155        Editor::new(
14156            EditorMode::Full,
14157            multi_buffer,
14158            Some(project.clone()),
14159            window,
14160            cx,
14161        )
14162    });
14163    let multibuffer_item_id = workspace
14164        .update(cx, |workspace, window, cx| {
14165            assert!(
14166                workspace.active_item(cx).is_none(),
14167                "active item should be None before the first item is added"
14168            );
14169            workspace.add_item_to_active_pane(
14170                Box::new(multi_buffer_editor.clone()),
14171                None,
14172                true,
14173                window,
14174                cx,
14175            );
14176            let active_item = workspace
14177                .active_item(cx)
14178                .expect("should have an active item after adding the multi buffer");
14179            assert!(
14180                !active_item.is_singleton(cx),
14181                "A multi buffer was expected to active after adding"
14182            );
14183            active_item.item_id()
14184        })
14185        .unwrap();
14186    cx.executor().run_until_parked();
14187
14188    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14189        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14190            s.select_ranges(Some(1..2))
14191        });
14192        editor.open_excerpts(&OpenExcerpts, window, cx);
14193    });
14194    cx.executor().run_until_parked();
14195    let first_item_id = workspace
14196        .update(cx, |workspace, window, cx| {
14197            let active_item = workspace
14198                .active_item(cx)
14199                .expect("should have an active item after navigating into the 1st buffer");
14200            let first_item_id = active_item.item_id();
14201            assert_ne!(
14202                first_item_id, multibuffer_item_id,
14203                "Should navigate into the 1st buffer and activate it"
14204            );
14205            assert!(
14206                active_item.is_singleton(cx),
14207                "New active item should be a singleton buffer"
14208            );
14209            assert_eq!(
14210                active_item
14211                    .act_as::<Editor>(cx)
14212                    .expect("should have navigated into an editor for the 1st buffer")
14213                    .read(cx)
14214                    .text(cx),
14215                sample_text_1
14216            );
14217
14218            workspace
14219                .go_back(workspace.active_pane().downgrade(), window, cx)
14220                .detach_and_log_err(cx);
14221
14222            first_item_id
14223        })
14224        .unwrap();
14225    cx.executor().run_until_parked();
14226    workspace
14227        .update(cx, |workspace, _, cx| {
14228            let active_item = workspace
14229                .active_item(cx)
14230                .expect("should have an active item after navigating back");
14231            assert_eq!(
14232                active_item.item_id(),
14233                multibuffer_item_id,
14234                "Should navigate back to the multi buffer"
14235            );
14236            assert!(!active_item.is_singleton(cx));
14237        })
14238        .unwrap();
14239
14240    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14241        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14242            s.select_ranges(Some(39..40))
14243        });
14244        editor.open_excerpts(&OpenExcerpts, window, cx);
14245    });
14246    cx.executor().run_until_parked();
14247    let second_item_id = workspace
14248        .update(cx, |workspace, window, cx| {
14249            let active_item = workspace
14250                .active_item(cx)
14251                .expect("should have an active item after navigating into the 2nd buffer");
14252            let second_item_id = active_item.item_id();
14253            assert_ne!(
14254                second_item_id, multibuffer_item_id,
14255                "Should navigate away from the multibuffer"
14256            );
14257            assert_ne!(
14258                second_item_id, first_item_id,
14259                "Should navigate into the 2nd buffer and activate it"
14260            );
14261            assert!(
14262                active_item.is_singleton(cx),
14263                "New active item should be a singleton buffer"
14264            );
14265            assert_eq!(
14266                active_item
14267                    .act_as::<Editor>(cx)
14268                    .expect("should have navigated into an editor")
14269                    .read(cx)
14270                    .text(cx),
14271                sample_text_2
14272            );
14273
14274            workspace
14275                .go_back(workspace.active_pane().downgrade(), window, cx)
14276                .detach_and_log_err(cx);
14277
14278            second_item_id
14279        })
14280        .unwrap();
14281    cx.executor().run_until_parked();
14282    workspace
14283        .update(cx, |workspace, _, cx| {
14284            let active_item = workspace
14285                .active_item(cx)
14286                .expect("should have an active item after navigating back from the 2nd buffer");
14287            assert_eq!(
14288                active_item.item_id(),
14289                multibuffer_item_id,
14290                "Should navigate back from the 2nd buffer to the multi buffer"
14291            );
14292            assert!(!active_item.is_singleton(cx));
14293        })
14294        .unwrap();
14295
14296    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14297        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14298            s.select_ranges(Some(70..70))
14299        });
14300        editor.open_excerpts(&OpenExcerpts, window, cx);
14301    });
14302    cx.executor().run_until_parked();
14303    workspace
14304        .update(cx, |workspace, window, cx| {
14305            let active_item = workspace
14306                .active_item(cx)
14307                .expect("should have an active item after navigating into the 3rd buffer");
14308            let third_item_id = active_item.item_id();
14309            assert_ne!(
14310                third_item_id, multibuffer_item_id,
14311                "Should navigate into the 3rd buffer and activate it"
14312            );
14313            assert_ne!(third_item_id, first_item_id);
14314            assert_ne!(third_item_id, second_item_id);
14315            assert!(
14316                active_item.is_singleton(cx),
14317                "New active item should be a singleton buffer"
14318            );
14319            assert_eq!(
14320                active_item
14321                    .act_as::<Editor>(cx)
14322                    .expect("should have navigated into an editor")
14323                    .read(cx)
14324                    .text(cx),
14325                sample_text_3
14326            );
14327
14328            workspace
14329                .go_back(workspace.active_pane().downgrade(), window, cx)
14330                .detach_and_log_err(cx);
14331        })
14332        .unwrap();
14333    cx.executor().run_until_parked();
14334    workspace
14335        .update(cx, |workspace, _, cx| {
14336            let active_item = workspace
14337                .active_item(cx)
14338                .expect("should have an active item after navigating back from the 3rd buffer");
14339            assert_eq!(
14340                active_item.item_id(),
14341                multibuffer_item_id,
14342                "Should navigate back from the 3rd buffer to the multi buffer"
14343            );
14344            assert!(!active_item.is_singleton(cx));
14345        })
14346        .unwrap();
14347}
14348
14349#[gpui::test]
14350async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14351    init_test(cx, |_| {});
14352
14353    let mut cx = EditorTestContext::new(cx).await;
14354
14355    let diff_base = r#"
14356        use some::mod;
14357
14358        const A: u32 = 42;
14359
14360        fn main() {
14361            println!("hello");
14362
14363            println!("world");
14364        }
14365        "#
14366    .unindent();
14367
14368    cx.set_state(
14369        &r#"
14370        use some::modified;
14371
14372        ˇ
14373        fn main() {
14374            println!("hello there");
14375
14376            println!("around the");
14377            println!("world");
14378        }
14379        "#
14380        .unindent(),
14381    );
14382
14383    cx.set_head_text(&diff_base);
14384    executor.run_until_parked();
14385
14386    cx.update_editor(|editor, window, cx| {
14387        editor.go_to_next_hunk(&GoToHunk, window, cx);
14388        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14389    });
14390    executor.run_until_parked();
14391    cx.assert_state_with_diff(
14392        r#"
14393          use some::modified;
14394
14395
14396          fn main() {
14397        -     println!("hello");
14398        + ˇ    println!("hello there");
14399
14400              println!("around the");
14401              println!("world");
14402          }
14403        "#
14404        .unindent(),
14405    );
14406
14407    cx.update_editor(|editor, window, cx| {
14408        for _ in 0..2 {
14409            editor.go_to_next_hunk(&GoToHunk, window, cx);
14410            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14411        }
14412    });
14413    executor.run_until_parked();
14414    cx.assert_state_with_diff(
14415        r#"
14416        - use some::mod;
14417        + ˇuse some::modified;
14418
14419
14420          fn main() {
14421        -     println!("hello");
14422        +     println!("hello there");
14423
14424        +     println!("around the");
14425              println!("world");
14426          }
14427        "#
14428        .unindent(),
14429    );
14430
14431    cx.update_editor(|editor, window, cx| {
14432        editor.go_to_next_hunk(&GoToHunk, window, cx);
14433        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14434    });
14435    executor.run_until_parked();
14436    cx.assert_state_with_diff(
14437        r#"
14438        - use some::mod;
14439        + use some::modified;
14440
14441        - const A: u32 = 42;
14442          ˇ
14443          fn main() {
14444        -     println!("hello");
14445        +     println!("hello there");
14446
14447        +     println!("around the");
14448              println!("world");
14449          }
14450        "#
14451        .unindent(),
14452    );
14453
14454    cx.update_editor(|editor, window, cx| {
14455        editor.cancel(&Cancel, window, cx);
14456    });
14457
14458    cx.assert_state_with_diff(
14459        r#"
14460          use some::modified;
14461
14462          ˇ
14463          fn main() {
14464              println!("hello there");
14465
14466              println!("around the");
14467              println!("world");
14468          }
14469        "#
14470        .unindent(),
14471    );
14472}
14473
14474#[gpui::test]
14475async fn test_diff_base_change_with_expanded_diff_hunks(
14476    executor: BackgroundExecutor,
14477    cx: &mut TestAppContext,
14478) {
14479    init_test(cx, |_| {});
14480
14481    let mut cx = EditorTestContext::new(cx).await;
14482
14483    let diff_base = r#"
14484        use some::mod1;
14485        use some::mod2;
14486
14487        const A: u32 = 42;
14488        const B: u32 = 42;
14489        const C: u32 = 42;
14490
14491        fn main() {
14492            println!("hello");
14493
14494            println!("world");
14495        }
14496        "#
14497    .unindent();
14498
14499    cx.set_state(
14500        &r#"
14501        use some::mod2;
14502
14503        const A: u32 = 42;
14504        const C: u32 = 42;
14505
14506        fn main(ˇ) {
14507            //println!("hello");
14508
14509            println!("world");
14510            //
14511            //
14512        }
14513        "#
14514        .unindent(),
14515    );
14516
14517    cx.set_head_text(&diff_base);
14518    executor.run_until_parked();
14519
14520    cx.update_editor(|editor, window, cx| {
14521        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14522    });
14523    executor.run_until_parked();
14524    cx.assert_state_with_diff(
14525        r#"
14526        - use some::mod1;
14527          use some::mod2;
14528
14529          const A: u32 = 42;
14530        - const B: u32 = 42;
14531          const C: u32 = 42;
14532
14533          fn main(ˇ) {
14534        -     println!("hello");
14535        +     //println!("hello");
14536
14537              println!("world");
14538        +     //
14539        +     //
14540          }
14541        "#
14542        .unindent(),
14543    );
14544
14545    cx.set_head_text("new diff base!");
14546    executor.run_until_parked();
14547    cx.assert_state_with_diff(
14548        r#"
14549        - new diff base!
14550        + use some::mod2;
14551        +
14552        + const A: u32 = 42;
14553        + const C: u32 = 42;
14554        +
14555        + fn main(ˇ) {
14556        +     //println!("hello");
14557        +
14558        +     println!("world");
14559        +     //
14560        +     //
14561        + }
14562        "#
14563        .unindent(),
14564    );
14565}
14566
14567#[gpui::test]
14568async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14569    init_test(cx, |_| {});
14570
14571    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14572    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14573    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14574    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14575    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14576    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14577
14578    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14579    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14580    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14581
14582    let multi_buffer = cx.new(|cx| {
14583        let mut multibuffer = MultiBuffer::new(ReadWrite);
14584        multibuffer.push_excerpts(
14585            buffer_1.clone(),
14586            [
14587                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14588                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14589                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14590            ],
14591            cx,
14592        );
14593        multibuffer.push_excerpts(
14594            buffer_2.clone(),
14595            [
14596                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14597                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14598                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14599            ],
14600            cx,
14601        );
14602        multibuffer.push_excerpts(
14603            buffer_3.clone(),
14604            [
14605                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14606                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14607                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14608            ],
14609            cx,
14610        );
14611        multibuffer
14612    });
14613
14614    let editor =
14615        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14616    editor
14617        .update(cx, |editor, _window, cx| {
14618            for (buffer, diff_base) in [
14619                (buffer_1.clone(), file_1_old),
14620                (buffer_2.clone(), file_2_old),
14621                (buffer_3.clone(), file_3_old),
14622            ] {
14623                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14624                editor
14625                    .buffer
14626                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14627            }
14628        })
14629        .unwrap();
14630
14631    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14632    cx.run_until_parked();
14633
14634    cx.assert_editor_state(
14635        &"
14636            ˇaaa
14637            ccc
14638            ddd
14639
14640            ggg
14641            hhh
14642
14643
14644            lll
14645            mmm
14646            NNN
14647
14648            qqq
14649            rrr
14650
14651            uuu
14652            111
14653            222
14654            333
14655
14656            666
14657            777
14658
14659            000
14660            !!!"
14661        .unindent(),
14662    );
14663
14664    cx.update_editor(|editor, window, cx| {
14665        editor.select_all(&SelectAll, window, cx);
14666        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14667    });
14668    cx.executor().run_until_parked();
14669
14670    cx.assert_state_with_diff(
14671        "
14672            «aaa
14673          - bbb
14674            ccc
14675            ddd
14676
14677            ggg
14678            hhh
14679
14680
14681            lll
14682            mmm
14683          - nnn
14684          + NNN
14685
14686            qqq
14687            rrr
14688
14689            uuu
14690            111
14691            222
14692            333
14693
14694          + 666
14695            777
14696
14697            000
14698            !!!ˇ»"
14699            .unindent(),
14700    );
14701}
14702
14703#[gpui::test]
14704async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14705    init_test(cx, |_| {});
14706
14707    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14708    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14709
14710    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14711    let multi_buffer = cx.new(|cx| {
14712        let mut multibuffer = MultiBuffer::new(ReadWrite);
14713        multibuffer.push_excerpts(
14714            buffer.clone(),
14715            [
14716                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14717                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14718                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14719            ],
14720            cx,
14721        );
14722        multibuffer
14723    });
14724
14725    let editor =
14726        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14727    editor
14728        .update(cx, |editor, _window, cx| {
14729            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14730            editor
14731                .buffer
14732                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14733        })
14734        .unwrap();
14735
14736    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14737    cx.run_until_parked();
14738
14739    cx.update_editor(|editor, window, cx| {
14740        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14741    });
14742    cx.executor().run_until_parked();
14743
14744    // When the start of a hunk coincides with the start of its excerpt,
14745    // the hunk is expanded. When the start of a a hunk is earlier than
14746    // the start of its excerpt, the hunk is not expanded.
14747    cx.assert_state_with_diff(
14748        "
14749            ˇaaa
14750          - bbb
14751          + BBB
14752
14753          - ddd
14754          - eee
14755          + DDD
14756          + EEE
14757            fff
14758
14759            iii
14760        "
14761        .unindent(),
14762    );
14763}
14764
14765#[gpui::test]
14766async fn test_edits_around_expanded_insertion_hunks(
14767    executor: BackgroundExecutor,
14768    cx: &mut TestAppContext,
14769) {
14770    init_test(cx, |_| {});
14771
14772    let mut cx = EditorTestContext::new(cx).await;
14773
14774    let diff_base = r#"
14775        use some::mod1;
14776        use some::mod2;
14777
14778        const A: u32 = 42;
14779
14780        fn main() {
14781            println!("hello");
14782
14783            println!("world");
14784        }
14785        "#
14786    .unindent();
14787    executor.run_until_parked();
14788    cx.set_state(
14789        &r#"
14790        use some::mod1;
14791        use some::mod2;
14792
14793        const A: u32 = 42;
14794        const B: u32 = 42;
14795        const C: u32 = 42;
14796        ˇ
14797
14798        fn main() {
14799            println!("hello");
14800
14801            println!("world");
14802        }
14803        "#
14804        .unindent(),
14805    );
14806
14807    cx.set_head_text(&diff_base);
14808    executor.run_until_parked();
14809
14810    cx.update_editor(|editor, window, cx| {
14811        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14812    });
14813    executor.run_until_parked();
14814
14815    cx.assert_state_with_diff(
14816        r#"
14817        use some::mod1;
14818        use some::mod2;
14819
14820        const A: u32 = 42;
14821      + const B: u32 = 42;
14822      + const C: u32 = 42;
14823      + ˇ
14824
14825        fn main() {
14826            println!("hello");
14827
14828            println!("world");
14829        }
14830      "#
14831        .unindent(),
14832    );
14833
14834    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14835    executor.run_until_parked();
14836
14837    cx.assert_state_with_diff(
14838        r#"
14839        use some::mod1;
14840        use some::mod2;
14841
14842        const A: u32 = 42;
14843      + const B: u32 = 42;
14844      + const C: u32 = 42;
14845      + const D: u32 = 42;
14846      + ˇ
14847
14848        fn main() {
14849            println!("hello");
14850
14851            println!("world");
14852        }
14853      "#
14854        .unindent(),
14855    );
14856
14857    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14858    executor.run_until_parked();
14859
14860    cx.assert_state_with_diff(
14861        r#"
14862        use some::mod1;
14863        use some::mod2;
14864
14865        const A: u32 = 42;
14866      + const B: u32 = 42;
14867      + const C: u32 = 42;
14868      + const D: u32 = 42;
14869      + const E: u32 = 42;
14870      + ˇ
14871
14872        fn main() {
14873            println!("hello");
14874
14875            println!("world");
14876        }
14877      "#
14878        .unindent(),
14879    );
14880
14881    cx.update_editor(|editor, window, cx| {
14882        editor.delete_line(&DeleteLine, window, cx);
14883    });
14884    executor.run_until_parked();
14885
14886    cx.assert_state_with_diff(
14887        r#"
14888        use some::mod1;
14889        use some::mod2;
14890
14891        const A: u32 = 42;
14892      + const B: u32 = 42;
14893      + const C: u32 = 42;
14894      + const D: u32 = 42;
14895      + const E: u32 = 42;
14896        ˇ
14897        fn main() {
14898            println!("hello");
14899
14900            println!("world");
14901        }
14902      "#
14903        .unindent(),
14904    );
14905
14906    cx.update_editor(|editor, window, cx| {
14907        editor.move_up(&MoveUp, window, cx);
14908        editor.delete_line(&DeleteLine, window, cx);
14909        editor.move_up(&MoveUp, window, cx);
14910        editor.delete_line(&DeleteLine, window, cx);
14911        editor.move_up(&MoveUp, window, cx);
14912        editor.delete_line(&DeleteLine, window, cx);
14913    });
14914    executor.run_until_parked();
14915    cx.assert_state_with_diff(
14916        r#"
14917        use some::mod1;
14918        use some::mod2;
14919
14920        const A: u32 = 42;
14921      + const B: u32 = 42;
14922        ˇ
14923        fn main() {
14924            println!("hello");
14925
14926            println!("world");
14927        }
14928      "#
14929        .unindent(),
14930    );
14931
14932    cx.update_editor(|editor, window, cx| {
14933        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14934        editor.delete_line(&DeleteLine, window, cx);
14935    });
14936    executor.run_until_parked();
14937    cx.assert_state_with_diff(
14938        r#"
14939        ˇ
14940        fn main() {
14941            println!("hello");
14942
14943            println!("world");
14944        }
14945      "#
14946        .unindent(),
14947    );
14948}
14949
14950#[gpui::test]
14951async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14952    init_test(cx, |_| {});
14953
14954    let mut cx = EditorTestContext::new(cx).await;
14955    cx.set_head_text(indoc! { "
14956        one
14957        two
14958        three
14959        four
14960        five
14961        "
14962    });
14963    cx.set_state(indoc! { "
14964        one
14965        ˇthree
14966        five
14967    "});
14968    cx.run_until_parked();
14969    cx.update_editor(|editor, window, cx| {
14970        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14971    });
14972    cx.assert_state_with_diff(
14973        indoc! { "
14974        one
14975      - two
14976        ˇthree
14977      - four
14978        five
14979    "}
14980        .to_string(),
14981    );
14982    cx.update_editor(|editor, window, cx| {
14983        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14984    });
14985
14986    cx.assert_state_with_diff(
14987        indoc! { "
14988        one
14989        ˇthree
14990        five
14991    "}
14992        .to_string(),
14993    );
14994
14995    cx.set_state(indoc! { "
14996        one
14997        ˇTWO
14998        three
14999        four
15000        five
15001    "});
15002    cx.run_until_parked();
15003    cx.update_editor(|editor, window, cx| {
15004        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15005    });
15006
15007    cx.assert_state_with_diff(
15008        indoc! { "
15009            one
15010          - two
15011          + ˇTWO
15012            three
15013            four
15014            five
15015        "}
15016        .to_string(),
15017    );
15018    cx.update_editor(|editor, window, cx| {
15019        editor.move_up(&Default::default(), window, cx);
15020        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15021    });
15022    cx.assert_state_with_diff(
15023        indoc! { "
15024            one
15025            ˇTWO
15026            three
15027            four
15028            five
15029        "}
15030        .to_string(),
15031    );
15032}
15033
15034#[gpui::test]
15035async fn test_edits_around_expanded_deletion_hunks(
15036    executor: BackgroundExecutor,
15037    cx: &mut TestAppContext,
15038) {
15039    init_test(cx, |_| {});
15040
15041    let mut cx = EditorTestContext::new(cx).await;
15042
15043    let diff_base = r#"
15044        use some::mod1;
15045        use some::mod2;
15046
15047        const A: u32 = 42;
15048        const B: u32 = 42;
15049        const C: u32 = 42;
15050
15051
15052        fn main() {
15053            println!("hello");
15054
15055            println!("world");
15056        }
15057    "#
15058    .unindent();
15059    executor.run_until_parked();
15060    cx.set_state(
15061        &r#"
15062        use some::mod1;
15063        use some::mod2;
15064
15065        ˇconst B: u32 = 42;
15066        const C: u32 = 42;
15067
15068
15069        fn main() {
15070            println!("hello");
15071
15072            println!("world");
15073        }
15074        "#
15075        .unindent(),
15076    );
15077
15078    cx.set_head_text(&diff_base);
15079    executor.run_until_parked();
15080
15081    cx.update_editor(|editor, window, cx| {
15082        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15083    });
15084    executor.run_until_parked();
15085
15086    cx.assert_state_with_diff(
15087        r#"
15088        use some::mod1;
15089        use some::mod2;
15090
15091      - const A: u32 = 42;
15092        ˇconst B: u32 = 42;
15093        const C: u32 = 42;
15094
15095
15096        fn main() {
15097            println!("hello");
15098
15099            println!("world");
15100        }
15101      "#
15102        .unindent(),
15103    );
15104
15105    cx.update_editor(|editor, window, cx| {
15106        editor.delete_line(&DeleteLine, window, cx);
15107    });
15108    executor.run_until_parked();
15109    cx.assert_state_with_diff(
15110        r#"
15111        use some::mod1;
15112        use some::mod2;
15113
15114      - const A: u32 = 42;
15115      - const B: u32 = 42;
15116        ˇconst C: u32 = 42;
15117
15118
15119        fn main() {
15120            println!("hello");
15121
15122            println!("world");
15123        }
15124      "#
15125        .unindent(),
15126    );
15127
15128    cx.update_editor(|editor, window, cx| {
15129        editor.delete_line(&DeleteLine, window, cx);
15130    });
15131    executor.run_until_parked();
15132    cx.assert_state_with_diff(
15133        r#"
15134        use some::mod1;
15135        use some::mod2;
15136
15137      - const A: u32 = 42;
15138      - const B: u32 = 42;
15139      - const C: u32 = 42;
15140        ˇ
15141
15142        fn main() {
15143            println!("hello");
15144
15145            println!("world");
15146        }
15147      "#
15148        .unindent(),
15149    );
15150
15151    cx.update_editor(|editor, window, cx| {
15152        editor.handle_input("replacement", window, cx);
15153    });
15154    executor.run_until_parked();
15155    cx.assert_state_with_diff(
15156        r#"
15157        use some::mod1;
15158        use some::mod2;
15159
15160      - const A: u32 = 42;
15161      - const B: u32 = 42;
15162      - const C: u32 = 42;
15163      -
15164      + replacementˇ
15165
15166        fn main() {
15167            println!("hello");
15168
15169            println!("world");
15170        }
15171      "#
15172        .unindent(),
15173    );
15174}
15175
15176#[gpui::test]
15177async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15178    init_test(cx, |_| {});
15179
15180    let mut cx = EditorTestContext::new(cx).await;
15181
15182    let base_text = r#"
15183        one
15184        two
15185        three
15186        four
15187        five
15188    "#
15189    .unindent();
15190    executor.run_until_parked();
15191    cx.set_state(
15192        &r#"
15193        one
15194        two
15195        fˇour
15196        five
15197        "#
15198        .unindent(),
15199    );
15200
15201    cx.set_head_text(&base_text);
15202    executor.run_until_parked();
15203
15204    cx.update_editor(|editor, window, cx| {
15205        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15206    });
15207    executor.run_until_parked();
15208
15209    cx.assert_state_with_diff(
15210        r#"
15211          one
15212          two
15213        - three
15214          fˇour
15215          five
15216        "#
15217        .unindent(),
15218    );
15219
15220    cx.update_editor(|editor, window, cx| {
15221        editor.backspace(&Backspace, window, cx);
15222        editor.backspace(&Backspace, window, cx);
15223    });
15224    executor.run_until_parked();
15225    cx.assert_state_with_diff(
15226        r#"
15227          one
15228          two
15229        - threeˇ
15230        - four
15231        + our
15232          five
15233        "#
15234        .unindent(),
15235    );
15236}
15237
15238#[gpui::test]
15239async fn test_edit_after_expanded_modification_hunk(
15240    executor: BackgroundExecutor,
15241    cx: &mut TestAppContext,
15242) {
15243    init_test(cx, |_| {});
15244
15245    let mut cx = EditorTestContext::new(cx).await;
15246
15247    let diff_base = r#"
15248        use some::mod1;
15249        use some::mod2;
15250
15251        const A: u32 = 42;
15252        const B: u32 = 42;
15253        const C: u32 = 42;
15254        const D: u32 = 42;
15255
15256
15257        fn main() {
15258            println!("hello");
15259
15260            println!("world");
15261        }"#
15262    .unindent();
15263
15264    cx.set_state(
15265        &r#"
15266        use some::mod1;
15267        use some::mod2;
15268
15269        const A: u32 = 42;
15270        const B: u32 = 42;
15271        const C: u32 = 43ˇ
15272        const D: u32 = 42;
15273
15274
15275        fn main() {
15276            println!("hello");
15277
15278            println!("world");
15279        }"#
15280        .unindent(),
15281    );
15282
15283    cx.set_head_text(&diff_base);
15284    executor.run_until_parked();
15285    cx.update_editor(|editor, window, cx| {
15286        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15287    });
15288    executor.run_until_parked();
15289
15290    cx.assert_state_with_diff(
15291        r#"
15292        use some::mod1;
15293        use some::mod2;
15294
15295        const A: u32 = 42;
15296        const B: u32 = 42;
15297      - const C: u32 = 42;
15298      + const C: u32 = 43ˇ
15299        const D: u32 = 42;
15300
15301
15302        fn main() {
15303            println!("hello");
15304
15305            println!("world");
15306        }"#
15307        .unindent(),
15308    );
15309
15310    cx.update_editor(|editor, window, cx| {
15311        editor.handle_input("\nnew_line\n", window, cx);
15312    });
15313    executor.run_until_parked();
15314
15315    cx.assert_state_with_diff(
15316        r#"
15317        use some::mod1;
15318        use some::mod2;
15319
15320        const A: u32 = 42;
15321        const B: u32 = 42;
15322      - const C: u32 = 42;
15323      + const C: u32 = 43
15324      + new_line
15325      + ˇ
15326        const D: u32 = 42;
15327
15328
15329        fn main() {
15330            println!("hello");
15331
15332            println!("world");
15333        }"#
15334        .unindent(),
15335    );
15336}
15337
15338#[gpui::test]
15339async fn test_stage_and_unstage_added_file_hunk(
15340    executor: BackgroundExecutor,
15341    cx: &mut TestAppContext,
15342) {
15343    init_test(cx, |_| {});
15344
15345    let mut cx = EditorTestContext::new(cx).await;
15346    cx.update_editor(|editor, _, cx| {
15347        editor.set_expand_all_diff_hunks(cx);
15348    });
15349
15350    let working_copy = r#"
15351            ˇfn main() {
15352                println!("hello, world!");
15353            }
15354        "#
15355    .unindent();
15356
15357    cx.set_state(&working_copy);
15358    executor.run_until_parked();
15359
15360    cx.assert_state_with_diff(
15361        r#"
15362            + ˇfn main() {
15363            +     println!("hello, world!");
15364            + }
15365        "#
15366        .unindent(),
15367    );
15368    cx.assert_index_text(None);
15369
15370    cx.update_editor(|editor, window, cx| {
15371        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15372    });
15373    executor.run_until_parked();
15374    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15375    cx.assert_state_with_diff(
15376        r#"
15377            + ˇfn main() {
15378            +     println!("hello, world!");
15379            + }
15380        "#
15381        .unindent(),
15382    );
15383
15384    cx.update_editor(|editor, window, cx| {
15385        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15386    });
15387    executor.run_until_parked();
15388    cx.assert_index_text(None);
15389}
15390
15391async fn setup_indent_guides_editor(
15392    text: &str,
15393    cx: &mut TestAppContext,
15394) -> (BufferId, EditorTestContext) {
15395    init_test(cx, |_| {});
15396
15397    let mut cx = EditorTestContext::new(cx).await;
15398
15399    let buffer_id = cx.update_editor(|editor, window, cx| {
15400        editor.set_text(text, window, cx);
15401        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15402
15403        buffer_ids[0]
15404    });
15405
15406    (buffer_id, cx)
15407}
15408
15409fn assert_indent_guides(
15410    range: Range<u32>,
15411    expected: Vec<IndentGuide>,
15412    active_indices: Option<Vec<usize>>,
15413    cx: &mut EditorTestContext,
15414) {
15415    let indent_guides = cx.update_editor(|editor, window, cx| {
15416        let snapshot = editor.snapshot(window, cx).display_snapshot;
15417        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15418            editor,
15419            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15420            true,
15421            &snapshot,
15422            cx,
15423        );
15424
15425        indent_guides.sort_by(|a, b| {
15426            a.depth.cmp(&b.depth).then(
15427                a.start_row
15428                    .cmp(&b.start_row)
15429                    .then(a.end_row.cmp(&b.end_row)),
15430            )
15431        });
15432        indent_guides
15433    });
15434
15435    if let Some(expected) = active_indices {
15436        let active_indices = cx.update_editor(|editor, window, cx| {
15437            let snapshot = editor.snapshot(window, cx).display_snapshot;
15438            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15439        });
15440
15441        assert_eq!(
15442            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15443            expected,
15444            "Active indent guide indices do not match"
15445        );
15446    }
15447
15448    assert_eq!(indent_guides, expected, "Indent guides do not match");
15449}
15450
15451fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15452    IndentGuide {
15453        buffer_id,
15454        start_row: MultiBufferRow(start_row),
15455        end_row: MultiBufferRow(end_row),
15456        depth,
15457        tab_size: 4,
15458        settings: IndentGuideSettings {
15459            enabled: true,
15460            line_width: 1,
15461            active_line_width: 1,
15462            ..Default::default()
15463        },
15464    }
15465}
15466
15467#[gpui::test]
15468async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15469    let (buffer_id, mut cx) = setup_indent_guides_editor(
15470        &"
15471    fn main() {
15472        let a = 1;
15473    }"
15474        .unindent(),
15475        cx,
15476    )
15477    .await;
15478
15479    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15480}
15481
15482#[gpui::test]
15483async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15484    let (buffer_id, mut cx) = setup_indent_guides_editor(
15485        &"
15486    fn main() {
15487        let a = 1;
15488        let b = 2;
15489    }"
15490        .unindent(),
15491        cx,
15492    )
15493    .await;
15494
15495    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15496}
15497
15498#[gpui::test]
15499async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15500    let (buffer_id, mut cx) = setup_indent_guides_editor(
15501        &"
15502    fn main() {
15503        let a = 1;
15504        if a == 3 {
15505            let b = 2;
15506        } else {
15507            let c = 3;
15508        }
15509    }"
15510        .unindent(),
15511        cx,
15512    )
15513    .await;
15514
15515    assert_indent_guides(
15516        0..8,
15517        vec![
15518            indent_guide(buffer_id, 1, 6, 0),
15519            indent_guide(buffer_id, 3, 3, 1),
15520            indent_guide(buffer_id, 5, 5, 1),
15521        ],
15522        None,
15523        &mut cx,
15524    );
15525}
15526
15527#[gpui::test]
15528async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15529    let (buffer_id, mut cx) = setup_indent_guides_editor(
15530        &"
15531    fn main() {
15532        let a = 1;
15533            let b = 2;
15534        let c = 3;
15535    }"
15536        .unindent(),
15537        cx,
15538    )
15539    .await;
15540
15541    assert_indent_guides(
15542        0..5,
15543        vec![
15544            indent_guide(buffer_id, 1, 3, 0),
15545            indent_guide(buffer_id, 2, 2, 1),
15546        ],
15547        None,
15548        &mut cx,
15549    );
15550}
15551
15552#[gpui::test]
15553async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15554    let (buffer_id, mut cx) = setup_indent_guides_editor(
15555        &"
15556        fn main() {
15557            let a = 1;
15558
15559            let c = 3;
15560        }"
15561        .unindent(),
15562        cx,
15563    )
15564    .await;
15565
15566    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15567}
15568
15569#[gpui::test]
15570async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15571    let (buffer_id, mut cx) = setup_indent_guides_editor(
15572        &"
15573        fn main() {
15574            let a = 1;
15575
15576            let c = 3;
15577
15578            if a == 3 {
15579                let b = 2;
15580            } else {
15581                let c = 3;
15582            }
15583        }"
15584        .unindent(),
15585        cx,
15586    )
15587    .await;
15588
15589    assert_indent_guides(
15590        0..11,
15591        vec![
15592            indent_guide(buffer_id, 1, 9, 0),
15593            indent_guide(buffer_id, 6, 6, 1),
15594            indent_guide(buffer_id, 8, 8, 1),
15595        ],
15596        None,
15597        &mut cx,
15598    );
15599}
15600
15601#[gpui::test]
15602async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15603    let (buffer_id, mut cx) = setup_indent_guides_editor(
15604        &"
15605        fn main() {
15606            let a = 1;
15607
15608            let c = 3;
15609
15610            if a == 3 {
15611                let b = 2;
15612            } else {
15613                let c = 3;
15614            }
15615        }"
15616        .unindent(),
15617        cx,
15618    )
15619    .await;
15620
15621    assert_indent_guides(
15622        1..11,
15623        vec![
15624            indent_guide(buffer_id, 1, 9, 0),
15625            indent_guide(buffer_id, 6, 6, 1),
15626            indent_guide(buffer_id, 8, 8, 1),
15627        ],
15628        None,
15629        &mut cx,
15630    );
15631}
15632
15633#[gpui::test]
15634async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15635    let (buffer_id, mut cx) = setup_indent_guides_editor(
15636        &"
15637        fn main() {
15638            let a = 1;
15639
15640            let c = 3;
15641
15642            if a == 3 {
15643                let b = 2;
15644            } else {
15645                let c = 3;
15646            }
15647        }"
15648        .unindent(),
15649        cx,
15650    )
15651    .await;
15652
15653    assert_indent_guides(
15654        1..10,
15655        vec![
15656            indent_guide(buffer_id, 1, 9, 0),
15657            indent_guide(buffer_id, 6, 6, 1),
15658            indent_guide(buffer_id, 8, 8, 1),
15659        ],
15660        None,
15661        &mut cx,
15662    );
15663}
15664
15665#[gpui::test]
15666async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15667    let (buffer_id, mut cx) = setup_indent_guides_editor(
15668        &"
15669        block1
15670            block2
15671                block3
15672                    block4
15673            block2
15674        block1
15675        block1"
15676            .unindent(),
15677        cx,
15678    )
15679    .await;
15680
15681    assert_indent_guides(
15682        1..10,
15683        vec![
15684            indent_guide(buffer_id, 1, 4, 0),
15685            indent_guide(buffer_id, 2, 3, 1),
15686            indent_guide(buffer_id, 3, 3, 2),
15687        ],
15688        None,
15689        &mut cx,
15690    );
15691}
15692
15693#[gpui::test]
15694async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15695    let (buffer_id, mut cx) = setup_indent_guides_editor(
15696        &"
15697        block1
15698            block2
15699                block3
15700
15701        block1
15702        block1"
15703            .unindent(),
15704        cx,
15705    )
15706    .await;
15707
15708    assert_indent_guides(
15709        0..6,
15710        vec![
15711            indent_guide(buffer_id, 1, 2, 0),
15712            indent_guide(buffer_id, 2, 2, 1),
15713        ],
15714        None,
15715        &mut cx,
15716    );
15717}
15718
15719#[gpui::test]
15720async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15721    let (buffer_id, mut cx) = setup_indent_guides_editor(
15722        &"
15723        block1
15724
15725
15726
15727            block2
15728        "
15729        .unindent(),
15730        cx,
15731    )
15732    .await;
15733
15734    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15735}
15736
15737#[gpui::test]
15738async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15739    let (buffer_id, mut cx) = setup_indent_guides_editor(
15740        &"
15741        def a:
15742        \tb = 3
15743        \tif True:
15744        \t\tc = 4
15745        \t\td = 5
15746        \tprint(b)
15747        "
15748        .unindent(),
15749        cx,
15750    )
15751    .await;
15752
15753    assert_indent_guides(
15754        0..6,
15755        vec![
15756            indent_guide(buffer_id, 1, 6, 0),
15757            indent_guide(buffer_id, 3, 4, 1),
15758        ],
15759        None,
15760        &mut cx,
15761    );
15762}
15763
15764#[gpui::test]
15765async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15766    let (buffer_id, mut cx) = setup_indent_guides_editor(
15767        &"
15768    fn main() {
15769        let a = 1;
15770    }"
15771        .unindent(),
15772        cx,
15773    )
15774    .await;
15775
15776    cx.update_editor(|editor, window, cx| {
15777        editor.change_selections(None, window, cx, |s| {
15778            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15779        });
15780    });
15781
15782    assert_indent_guides(
15783        0..3,
15784        vec![indent_guide(buffer_id, 1, 1, 0)],
15785        Some(vec![0]),
15786        &mut cx,
15787    );
15788}
15789
15790#[gpui::test]
15791async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15792    let (buffer_id, mut cx) = setup_indent_guides_editor(
15793        &"
15794    fn main() {
15795        if 1 == 2 {
15796            let a = 1;
15797        }
15798    }"
15799        .unindent(),
15800        cx,
15801    )
15802    .await;
15803
15804    cx.update_editor(|editor, window, cx| {
15805        editor.change_selections(None, window, cx, |s| {
15806            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15807        });
15808    });
15809
15810    assert_indent_guides(
15811        0..4,
15812        vec![
15813            indent_guide(buffer_id, 1, 3, 0),
15814            indent_guide(buffer_id, 2, 2, 1),
15815        ],
15816        Some(vec![1]),
15817        &mut cx,
15818    );
15819
15820    cx.update_editor(|editor, window, cx| {
15821        editor.change_selections(None, window, cx, |s| {
15822            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15823        });
15824    });
15825
15826    assert_indent_guides(
15827        0..4,
15828        vec![
15829            indent_guide(buffer_id, 1, 3, 0),
15830            indent_guide(buffer_id, 2, 2, 1),
15831        ],
15832        Some(vec![1]),
15833        &mut cx,
15834    );
15835
15836    cx.update_editor(|editor, window, cx| {
15837        editor.change_selections(None, window, cx, |s| {
15838            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15839        });
15840    });
15841
15842    assert_indent_guides(
15843        0..4,
15844        vec![
15845            indent_guide(buffer_id, 1, 3, 0),
15846            indent_guide(buffer_id, 2, 2, 1),
15847        ],
15848        Some(vec![0]),
15849        &mut cx,
15850    );
15851}
15852
15853#[gpui::test]
15854async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15855    let (buffer_id, mut cx) = setup_indent_guides_editor(
15856        &"
15857    fn main() {
15858        let a = 1;
15859
15860        let b = 2;
15861    }"
15862        .unindent(),
15863        cx,
15864    )
15865    .await;
15866
15867    cx.update_editor(|editor, window, cx| {
15868        editor.change_selections(None, window, cx, |s| {
15869            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15870        });
15871    });
15872
15873    assert_indent_guides(
15874        0..5,
15875        vec![indent_guide(buffer_id, 1, 3, 0)],
15876        Some(vec![0]),
15877        &mut cx,
15878    );
15879}
15880
15881#[gpui::test]
15882async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15883    let (buffer_id, mut cx) = setup_indent_guides_editor(
15884        &"
15885    def m:
15886        a = 1
15887        pass"
15888            .unindent(),
15889        cx,
15890    )
15891    .await;
15892
15893    cx.update_editor(|editor, window, cx| {
15894        editor.change_selections(None, window, cx, |s| {
15895            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15896        });
15897    });
15898
15899    assert_indent_guides(
15900        0..3,
15901        vec![indent_guide(buffer_id, 1, 2, 0)],
15902        Some(vec![0]),
15903        &mut cx,
15904    );
15905}
15906
15907#[gpui::test]
15908async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15909    init_test(cx, |_| {});
15910    let mut cx = EditorTestContext::new(cx).await;
15911    let text = indoc! {
15912        "
15913        impl A {
15914            fn b() {
15915                0;
15916                3;
15917                5;
15918                6;
15919                7;
15920            }
15921        }
15922        "
15923    };
15924    let base_text = indoc! {
15925        "
15926        impl A {
15927            fn b() {
15928                0;
15929                1;
15930                2;
15931                3;
15932                4;
15933            }
15934            fn c() {
15935                5;
15936                6;
15937                7;
15938            }
15939        }
15940        "
15941    };
15942
15943    cx.update_editor(|editor, window, cx| {
15944        editor.set_text(text, window, cx);
15945
15946        editor.buffer().update(cx, |multibuffer, cx| {
15947            let buffer = multibuffer.as_singleton().unwrap();
15948            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15949
15950            multibuffer.set_all_diff_hunks_expanded(cx);
15951            multibuffer.add_diff(diff, cx);
15952
15953            buffer.read(cx).remote_id()
15954        })
15955    });
15956    cx.run_until_parked();
15957
15958    cx.assert_state_with_diff(
15959        indoc! { "
15960          impl A {
15961              fn b() {
15962                  0;
15963        -         1;
15964        -         2;
15965                  3;
15966        -         4;
15967        -     }
15968        -     fn c() {
15969                  5;
15970                  6;
15971                  7;
15972              }
15973          }
15974          ˇ"
15975        }
15976        .to_string(),
15977    );
15978
15979    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15980        editor
15981            .snapshot(window, cx)
15982            .buffer_snapshot
15983            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15984            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15985            .collect::<Vec<_>>()
15986    });
15987    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15988    assert_eq!(
15989        actual_guides,
15990        vec![
15991            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15992            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15993            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15994        ]
15995    );
15996}
15997
15998#[gpui::test]
15999async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16000    init_test(cx, |_| {});
16001    let mut cx = EditorTestContext::new(cx).await;
16002
16003    let diff_base = r#"
16004        a
16005        b
16006        c
16007        "#
16008    .unindent();
16009
16010    cx.set_state(
16011        &r#"
16012        ˇA
16013        b
16014        C
16015        "#
16016        .unindent(),
16017    );
16018    cx.set_head_text(&diff_base);
16019    cx.update_editor(|editor, window, cx| {
16020        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16021    });
16022    executor.run_until_parked();
16023
16024    let both_hunks_expanded = r#"
16025        - a
16026        + ˇA
16027          b
16028        - c
16029        + C
16030        "#
16031    .unindent();
16032
16033    cx.assert_state_with_diff(both_hunks_expanded.clone());
16034
16035    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16036        let snapshot = editor.snapshot(window, cx);
16037        let hunks = editor
16038            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16039            .collect::<Vec<_>>();
16040        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16041        let buffer_id = hunks[0].buffer_id;
16042        hunks
16043            .into_iter()
16044            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16045            .collect::<Vec<_>>()
16046    });
16047    assert_eq!(hunk_ranges.len(), 2);
16048
16049    cx.update_editor(|editor, _, cx| {
16050        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16051    });
16052    executor.run_until_parked();
16053
16054    let second_hunk_expanded = r#"
16055          ˇA
16056          b
16057        - c
16058        + C
16059        "#
16060    .unindent();
16061
16062    cx.assert_state_with_diff(second_hunk_expanded);
16063
16064    cx.update_editor(|editor, _, cx| {
16065        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16066    });
16067    executor.run_until_parked();
16068
16069    cx.assert_state_with_diff(both_hunks_expanded.clone());
16070
16071    cx.update_editor(|editor, _, cx| {
16072        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16073    });
16074    executor.run_until_parked();
16075
16076    let first_hunk_expanded = r#"
16077        - a
16078        + ˇA
16079          b
16080          C
16081        "#
16082    .unindent();
16083
16084    cx.assert_state_with_diff(first_hunk_expanded);
16085
16086    cx.update_editor(|editor, _, cx| {
16087        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16088    });
16089    executor.run_until_parked();
16090
16091    cx.assert_state_with_diff(both_hunks_expanded);
16092
16093    cx.set_state(
16094        &r#"
16095        ˇA
16096        b
16097        "#
16098        .unindent(),
16099    );
16100    cx.run_until_parked();
16101
16102    // TODO this cursor position seems bad
16103    cx.assert_state_with_diff(
16104        r#"
16105        - ˇa
16106        + A
16107          b
16108        "#
16109        .unindent(),
16110    );
16111
16112    cx.update_editor(|editor, window, cx| {
16113        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16114    });
16115
16116    cx.assert_state_with_diff(
16117        r#"
16118            - ˇa
16119            + A
16120              b
16121            - c
16122            "#
16123        .unindent(),
16124    );
16125
16126    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16127        let snapshot = editor.snapshot(window, cx);
16128        let hunks = editor
16129            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16130            .collect::<Vec<_>>();
16131        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16132        let buffer_id = hunks[0].buffer_id;
16133        hunks
16134            .into_iter()
16135            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16136            .collect::<Vec<_>>()
16137    });
16138    assert_eq!(hunk_ranges.len(), 2);
16139
16140    cx.update_editor(|editor, _, cx| {
16141        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16142    });
16143    executor.run_until_parked();
16144
16145    cx.assert_state_with_diff(
16146        r#"
16147        - ˇa
16148        + A
16149          b
16150        "#
16151        .unindent(),
16152    );
16153}
16154
16155#[gpui::test]
16156async fn test_toggle_deletion_hunk_at_start_of_file(
16157    executor: BackgroundExecutor,
16158    cx: &mut TestAppContext,
16159) {
16160    init_test(cx, |_| {});
16161    let mut cx = EditorTestContext::new(cx).await;
16162
16163    let diff_base = r#"
16164        a
16165        b
16166        c
16167        "#
16168    .unindent();
16169
16170    cx.set_state(
16171        &r#"
16172        ˇb
16173        c
16174        "#
16175        .unindent(),
16176    );
16177    cx.set_head_text(&diff_base);
16178    cx.update_editor(|editor, window, cx| {
16179        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16180    });
16181    executor.run_until_parked();
16182
16183    let hunk_expanded = r#"
16184        - a
16185          ˇb
16186          c
16187        "#
16188    .unindent();
16189
16190    cx.assert_state_with_diff(hunk_expanded.clone());
16191
16192    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16193        let snapshot = editor.snapshot(window, cx);
16194        let hunks = editor
16195            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16196            .collect::<Vec<_>>();
16197        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16198        let buffer_id = hunks[0].buffer_id;
16199        hunks
16200            .into_iter()
16201            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16202            .collect::<Vec<_>>()
16203    });
16204    assert_eq!(hunk_ranges.len(), 1);
16205
16206    cx.update_editor(|editor, _, cx| {
16207        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16208    });
16209    executor.run_until_parked();
16210
16211    let hunk_collapsed = r#"
16212          ˇb
16213          c
16214        "#
16215    .unindent();
16216
16217    cx.assert_state_with_diff(hunk_collapsed);
16218
16219    cx.update_editor(|editor, _, cx| {
16220        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16221    });
16222    executor.run_until_parked();
16223
16224    cx.assert_state_with_diff(hunk_expanded.clone());
16225}
16226
16227#[gpui::test]
16228async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16229    init_test(cx, |_| {});
16230
16231    let fs = FakeFs::new(cx.executor());
16232    fs.insert_tree(
16233        path!("/test"),
16234        json!({
16235            ".git": {},
16236            "file-1": "ONE\n",
16237            "file-2": "TWO\n",
16238            "file-3": "THREE\n",
16239        }),
16240    )
16241    .await;
16242
16243    fs.set_head_for_repo(
16244        path!("/test/.git").as_ref(),
16245        &[
16246            ("file-1".into(), "one\n".into()),
16247            ("file-2".into(), "two\n".into()),
16248            ("file-3".into(), "three\n".into()),
16249        ],
16250    );
16251
16252    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16253    let mut buffers = vec![];
16254    for i in 1..=3 {
16255        let buffer = project
16256            .update(cx, |project, cx| {
16257                let path = format!(path!("/test/file-{}"), i);
16258                project.open_local_buffer(path, cx)
16259            })
16260            .await
16261            .unwrap();
16262        buffers.push(buffer);
16263    }
16264
16265    let multibuffer = cx.new(|cx| {
16266        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16267        multibuffer.set_all_diff_hunks_expanded(cx);
16268        for buffer in &buffers {
16269            let snapshot = buffer.read(cx).snapshot();
16270            multibuffer.set_excerpts_for_path(
16271                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16272                buffer.clone(),
16273                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16274                DEFAULT_MULTIBUFFER_CONTEXT,
16275                cx,
16276            );
16277        }
16278        multibuffer
16279    });
16280
16281    let editor = cx.add_window(|window, cx| {
16282        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16283    });
16284    cx.run_until_parked();
16285
16286    let snapshot = editor
16287        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16288        .unwrap();
16289    let hunks = snapshot
16290        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16291        .map(|hunk| match hunk {
16292            DisplayDiffHunk::Unfolded {
16293                display_row_range, ..
16294            } => display_row_range,
16295            DisplayDiffHunk::Folded { .. } => unreachable!(),
16296        })
16297        .collect::<Vec<_>>();
16298    assert_eq!(
16299        hunks,
16300        [
16301            DisplayRow(2)..DisplayRow(4),
16302            DisplayRow(7)..DisplayRow(9),
16303            DisplayRow(12)..DisplayRow(14),
16304        ]
16305    );
16306}
16307
16308#[gpui::test]
16309async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16310    init_test(cx, |_| {});
16311
16312    let mut cx = EditorTestContext::new(cx).await;
16313    cx.set_head_text(indoc! { "
16314        one
16315        two
16316        three
16317        four
16318        five
16319        "
16320    });
16321    cx.set_index_text(indoc! { "
16322        one
16323        two
16324        three
16325        four
16326        five
16327        "
16328    });
16329    cx.set_state(indoc! {"
16330        one
16331        TWO
16332        ˇTHREE
16333        FOUR
16334        five
16335    "});
16336    cx.run_until_parked();
16337    cx.update_editor(|editor, window, cx| {
16338        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16339    });
16340    cx.run_until_parked();
16341    cx.assert_index_text(Some(indoc! {"
16342        one
16343        TWO
16344        THREE
16345        FOUR
16346        five
16347    "}));
16348    cx.set_state(indoc! { "
16349        one
16350        TWO
16351        ˇTHREE-HUNDRED
16352        FOUR
16353        five
16354    "});
16355    cx.run_until_parked();
16356    cx.update_editor(|editor, window, cx| {
16357        let snapshot = editor.snapshot(window, cx);
16358        let hunks = editor
16359            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16360            .collect::<Vec<_>>();
16361        assert_eq!(hunks.len(), 1);
16362        assert_eq!(
16363            hunks[0].status(),
16364            DiffHunkStatus {
16365                kind: DiffHunkStatusKind::Modified,
16366                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16367            }
16368        );
16369
16370        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16371    });
16372    cx.run_until_parked();
16373    cx.assert_index_text(Some(indoc! {"
16374        one
16375        TWO
16376        THREE-HUNDRED
16377        FOUR
16378        five
16379    "}));
16380}
16381
16382#[gpui::test]
16383fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16384    init_test(cx, |_| {});
16385
16386    let editor = cx.add_window(|window, cx| {
16387        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16388        build_editor(buffer, window, cx)
16389    });
16390
16391    let render_args = Arc::new(Mutex::new(None));
16392    let snapshot = editor
16393        .update(cx, |editor, window, cx| {
16394            let snapshot = editor.buffer().read(cx).snapshot(cx);
16395            let range =
16396                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16397
16398            struct RenderArgs {
16399                row: MultiBufferRow,
16400                folded: bool,
16401                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16402            }
16403
16404            let crease = Crease::inline(
16405                range,
16406                FoldPlaceholder::test(),
16407                {
16408                    let toggle_callback = render_args.clone();
16409                    move |row, folded, callback, _window, _cx| {
16410                        *toggle_callback.lock() = Some(RenderArgs {
16411                            row,
16412                            folded,
16413                            callback,
16414                        });
16415                        div()
16416                    }
16417                },
16418                |_row, _folded, _window, _cx| div(),
16419            );
16420
16421            editor.insert_creases(Some(crease), cx);
16422            let snapshot = editor.snapshot(window, cx);
16423            let _div = snapshot.render_crease_toggle(
16424                MultiBufferRow(1),
16425                false,
16426                cx.entity().clone(),
16427                window,
16428                cx,
16429            );
16430            snapshot
16431        })
16432        .unwrap();
16433
16434    let render_args = render_args.lock().take().unwrap();
16435    assert_eq!(render_args.row, MultiBufferRow(1));
16436    assert!(!render_args.folded);
16437    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16438
16439    cx.update_window(*editor, |_, window, cx| {
16440        (render_args.callback)(true, window, cx)
16441    })
16442    .unwrap();
16443    let snapshot = editor
16444        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16445        .unwrap();
16446    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16447
16448    cx.update_window(*editor, |_, window, cx| {
16449        (render_args.callback)(false, window, cx)
16450    })
16451    .unwrap();
16452    let snapshot = editor
16453        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16454        .unwrap();
16455    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16456}
16457
16458#[gpui::test]
16459async fn test_input_text(cx: &mut TestAppContext) {
16460    init_test(cx, |_| {});
16461    let mut cx = EditorTestContext::new(cx).await;
16462
16463    cx.set_state(
16464        &r#"ˇone
16465        two
16466
16467        three
16468        fourˇ
16469        five
16470
16471        siˇx"#
16472            .unindent(),
16473    );
16474
16475    cx.dispatch_action(HandleInput(String::new()));
16476    cx.assert_editor_state(
16477        &r#"ˇone
16478        two
16479
16480        three
16481        fourˇ
16482        five
16483
16484        siˇx"#
16485            .unindent(),
16486    );
16487
16488    cx.dispatch_action(HandleInput("AAAA".to_string()));
16489    cx.assert_editor_state(
16490        &r#"AAAAˇone
16491        two
16492
16493        three
16494        fourAAAAˇ
16495        five
16496
16497        siAAAAˇx"#
16498            .unindent(),
16499    );
16500}
16501
16502#[gpui::test]
16503async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16504    init_test(cx, |_| {});
16505
16506    let mut cx = EditorTestContext::new(cx).await;
16507    cx.set_state(
16508        r#"let foo = 1;
16509let foo = 2;
16510let foo = 3;
16511let fooˇ = 4;
16512let foo = 5;
16513let foo = 6;
16514let foo = 7;
16515let foo = 8;
16516let foo = 9;
16517let foo = 10;
16518let foo = 11;
16519let foo = 12;
16520let foo = 13;
16521let foo = 14;
16522let foo = 15;"#,
16523    );
16524
16525    cx.update_editor(|e, window, cx| {
16526        assert_eq!(
16527            e.next_scroll_position,
16528            NextScrollCursorCenterTopBottom::Center,
16529            "Default next scroll direction is center",
16530        );
16531
16532        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16533        assert_eq!(
16534            e.next_scroll_position,
16535            NextScrollCursorCenterTopBottom::Top,
16536            "After center, next scroll direction should be top",
16537        );
16538
16539        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16540        assert_eq!(
16541            e.next_scroll_position,
16542            NextScrollCursorCenterTopBottom::Bottom,
16543            "After top, next scroll direction should be bottom",
16544        );
16545
16546        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16547        assert_eq!(
16548            e.next_scroll_position,
16549            NextScrollCursorCenterTopBottom::Center,
16550            "After bottom, scrolling should start over",
16551        );
16552
16553        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16554        assert_eq!(
16555            e.next_scroll_position,
16556            NextScrollCursorCenterTopBottom::Top,
16557            "Scrolling continues if retriggered fast enough"
16558        );
16559    });
16560
16561    cx.executor()
16562        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16563    cx.executor().run_until_parked();
16564    cx.update_editor(|e, _, _| {
16565        assert_eq!(
16566            e.next_scroll_position,
16567            NextScrollCursorCenterTopBottom::Center,
16568            "If scrolling is not triggered fast enough, it should reset"
16569        );
16570    });
16571}
16572
16573#[gpui::test]
16574async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16575    init_test(cx, |_| {});
16576    let mut cx = EditorLspTestContext::new_rust(
16577        lsp::ServerCapabilities {
16578            definition_provider: Some(lsp::OneOf::Left(true)),
16579            references_provider: Some(lsp::OneOf::Left(true)),
16580            ..lsp::ServerCapabilities::default()
16581        },
16582        cx,
16583    )
16584    .await;
16585
16586    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16587        let go_to_definition = cx
16588            .lsp
16589            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16590                move |params, _| async move {
16591                    if empty_go_to_definition {
16592                        Ok(None)
16593                    } else {
16594                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16595                            uri: params.text_document_position_params.text_document.uri,
16596                            range: lsp::Range::new(
16597                                lsp::Position::new(4, 3),
16598                                lsp::Position::new(4, 6),
16599                            ),
16600                        })))
16601                    }
16602                },
16603            );
16604        let references = cx
16605            .lsp
16606            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16607                Ok(Some(vec![lsp::Location {
16608                    uri: params.text_document_position.text_document.uri,
16609                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16610                }]))
16611            });
16612        (go_to_definition, references)
16613    };
16614
16615    cx.set_state(
16616        &r#"fn one() {
16617            let mut a = ˇtwo();
16618        }
16619
16620        fn two() {}"#
16621            .unindent(),
16622    );
16623    set_up_lsp_handlers(false, &mut cx);
16624    let navigated = cx
16625        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16626        .await
16627        .expect("Failed to navigate to definition");
16628    assert_eq!(
16629        navigated,
16630        Navigated::Yes,
16631        "Should have navigated to definition from the GetDefinition response"
16632    );
16633    cx.assert_editor_state(
16634        &r#"fn one() {
16635            let mut a = two();
16636        }
16637
16638        fn «twoˇ»() {}"#
16639            .unindent(),
16640    );
16641
16642    let editors = cx.update_workspace(|workspace, _, cx| {
16643        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16644    });
16645    cx.update_editor(|_, _, test_editor_cx| {
16646        assert_eq!(
16647            editors.len(),
16648            1,
16649            "Initially, only one, test, editor should be open in the workspace"
16650        );
16651        assert_eq!(
16652            test_editor_cx.entity(),
16653            editors.last().expect("Asserted len is 1").clone()
16654        );
16655    });
16656
16657    set_up_lsp_handlers(true, &mut cx);
16658    let navigated = cx
16659        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16660        .await
16661        .expect("Failed to navigate to lookup references");
16662    assert_eq!(
16663        navigated,
16664        Navigated::Yes,
16665        "Should have navigated to references as a fallback after empty GoToDefinition response"
16666    );
16667    // We should not change the selections in the existing file,
16668    // if opening another milti buffer with the references
16669    cx.assert_editor_state(
16670        &r#"fn one() {
16671            let mut a = two();
16672        }
16673
16674        fn «twoˇ»() {}"#
16675            .unindent(),
16676    );
16677    let editors = cx.update_workspace(|workspace, _, cx| {
16678        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16679    });
16680    cx.update_editor(|_, _, test_editor_cx| {
16681        assert_eq!(
16682            editors.len(),
16683            2,
16684            "After falling back to references search, we open a new editor with the results"
16685        );
16686        let references_fallback_text = editors
16687            .into_iter()
16688            .find(|new_editor| *new_editor != test_editor_cx.entity())
16689            .expect("Should have one non-test editor now")
16690            .read(test_editor_cx)
16691            .text(test_editor_cx);
16692        assert_eq!(
16693            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16694            "Should use the range from the references response and not the GoToDefinition one"
16695        );
16696    });
16697}
16698
16699#[gpui::test]
16700async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16701    init_test(cx, |_| {});
16702    cx.update(|cx| {
16703        let mut editor_settings = EditorSettings::get_global(cx).clone();
16704        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16705        EditorSettings::override_global(editor_settings, cx);
16706    });
16707    let mut cx = EditorLspTestContext::new_rust(
16708        lsp::ServerCapabilities {
16709            definition_provider: Some(lsp::OneOf::Left(true)),
16710            references_provider: Some(lsp::OneOf::Left(true)),
16711            ..lsp::ServerCapabilities::default()
16712        },
16713        cx,
16714    )
16715    .await;
16716    let original_state = r#"fn one() {
16717        let mut a = ˇtwo();
16718    }
16719
16720    fn two() {}"#
16721        .unindent();
16722    cx.set_state(&original_state);
16723
16724    let mut go_to_definition = cx
16725        .lsp
16726        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16727            move |_, _| async move { Ok(None) },
16728        );
16729    let _references = cx
16730        .lsp
16731        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16732            panic!("Should not call for references with no go to definition fallback")
16733        });
16734
16735    let navigated = cx
16736        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16737        .await
16738        .expect("Failed to navigate to lookup references");
16739    go_to_definition
16740        .next()
16741        .await
16742        .expect("Should have called the go_to_definition handler");
16743
16744    assert_eq!(
16745        navigated,
16746        Navigated::No,
16747        "Should have navigated to references as a fallback after empty GoToDefinition response"
16748    );
16749    cx.assert_editor_state(&original_state);
16750    let editors = cx.update_workspace(|workspace, _, cx| {
16751        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16752    });
16753    cx.update_editor(|_, _, _| {
16754        assert_eq!(
16755            editors.len(),
16756            1,
16757            "After unsuccessful fallback, no other editor should have been opened"
16758        );
16759    });
16760}
16761
16762#[gpui::test]
16763async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16764    init_test(cx, |_| {});
16765
16766    let language = Arc::new(Language::new(
16767        LanguageConfig::default(),
16768        Some(tree_sitter_rust::LANGUAGE.into()),
16769    ));
16770
16771    let text = r#"
16772        #[cfg(test)]
16773        mod tests() {
16774            #[test]
16775            fn runnable_1() {
16776                let a = 1;
16777            }
16778
16779            #[test]
16780            fn runnable_2() {
16781                let a = 1;
16782                let b = 2;
16783            }
16784        }
16785    "#
16786    .unindent();
16787
16788    let fs = FakeFs::new(cx.executor());
16789    fs.insert_file("/file.rs", Default::default()).await;
16790
16791    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16792    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16793    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16794    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16795    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16796
16797    let editor = cx.new_window_entity(|window, cx| {
16798        Editor::new(
16799            EditorMode::Full,
16800            multi_buffer,
16801            Some(project.clone()),
16802            window,
16803            cx,
16804        )
16805    });
16806
16807    editor.update_in(cx, |editor, window, cx| {
16808        let snapshot = editor.buffer().read(cx).snapshot(cx);
16809        editor.tasks.insert(
16810            (buffer.read(cx).remote_id(), 3),
16811            RunnableTasks {
16812                templates: vec![],
16813                offset: snapshot.anchor_before(43),
16814                column: 0,
16815                extra_variables: HashMap::default(),
16816                context_range: BufferOffset(43)..BufferOffset(85),
16817            },
16818        );
16819        editor.tasks.insert(
16820            (buffer.read(cx).remote_id(), 8),
16821            RunnableTasks {
16822                templates: vec![],
16823                offset: snapshot.anchor_before(86),
16824                column: 0,
16825                extra_variables: HashMap::default(),
16826                context_range: BufferOffset(86)..BufferOffset(191),
16827            },
16828        );
16829
16830        // Test finding task when cursor is inside function body
16831        editor.change_selections(None, window, cx, |s| {
16832            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16833        });
16834        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16835        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16836
16837        // Test finding task when cursor is on function name
16838        editor.change_selections(None, window, cx, |s| {
16839            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16840        });
16841        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16842        assert_eq!(row, 8, "Should find task when cursor is on function name");
16843    });
16844}
16845
16846#[gpui::test]
16847async fn test_folding_buffers(cx: &mut TestAppContext) {
16848    init_test(cx, |_| {});
16849
16850    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16851    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16852    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16853
16854    let fs = FakeFs::new(cx.executor());
16855    fs.insert_tree(
16856        path!("/a"),
16857        json!({
16858            "first.rs": sample_text_1,
16859            "second.rs": sample_text_2,
16860            "third.rs": sample_text_3,
16861        }),
16862    )
16863    .await;
16864    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16865    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16866    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16867    let worktree = project.update(cx, |project, cx| {
16868        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16869        assert_eq!(worktrees.len(), 1);
16870        worktrees.pop().unwrap()
16871    });
16872    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16873
16874    let buffer_1 = project
16875        .update(cx, |project, cx| {
16876            project.open_buffer((worktree_id, "first.rs"), cx)
16877        })
16878        .await
16879        .unwrap();
16880    let buffer_2 = project
16881        .update(cx, |project, cx| {
16882            project.open_buffer((worktree_id, "second.rs"), cx)
16883        })
16884        .await
16885        .unwrap();
16886    let buffer_3 = project
16887        .update(cx, |project, cx| {
16888            project.open_buffer((worktree_id, "third.rs"), cx)
16889        })
16890        .await
16891        .unwrap();
16892
16893    let multi_buffer = cx.new(|cx| {
16894        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16895        multi_buffer.push_excerpts(
16896            buffer_1.clone(),
16897            [
16898                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16899                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16900                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16901            ],
16902            cx,
16903        );
16904        multi_buffer.push_excerpts(
16905            buffer_2.clone(),
16906            [
16907                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16908                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16909                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16910            ],
16911            cx,
16912        );
16913        multi_buffer.push_excerpts(
16914            buffer_3.clone(),
16915            [
16916                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16917                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16918                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16919            ],
16920            cx,
16921        );
16922        multi_buffer
16923    });
16924    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16925        Editor::new(
16926            EditorMode::Full,
16927            multi_buffer.clone(),
16928            Some(project.clone()),
16929            window,
16930            cx,
16931        )
16932    });
16933
16934    assert_eq!(
16935        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16936        "\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",
16937    );
16938
16939    multi_buffer_editor.update(cx, |editor, cx| {
16940        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16941    });
16942    assert_eq!(
16943        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16944        "\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",
16945        "After folding the first buffer, its text should not be displayed"
16946    );
16947
16948    multi_buffer_editor.update(cx, |editor, cx| {
16949        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16950    });
16951    assert_eq!(
16952        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16953        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16954        "After folding the second buffer, its text should not be displayed"
16955    );
16956
16957    multi_buffer_editor.update(cx, |editor, cx| {
16958        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16959    });
16960    assert_eq!(
16961        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16962        "\n\n\n\n\n",
16963        "After folding the third buffer, its text should not be displayed"
16964    );
16965
16966    // Emulate selection inside the fold logic, that should work
16967    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16968        editor
16969            .snapshot(window, cx)
16970            .next_line_boundary(Point::new(0, 4));
16971    });
16972
16973    multi_buffer_editor.update(cx, |editor, cx| {
16974        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16975    });
16976    assert_eq!(
16977        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16978        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16979        "After unfolding the second buffer, its text should be displayed"
16980    );
16981
16982    // Typing inside of buffer 1 causes that buffer to be unfolded.
16983    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16984        assert_eq!(
16985            multi_buffer
16986                .read(cx)
16987                .snapshot(cx)
16988                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16989                .collect::<String>(),
16990            "bbbb"
16991        );
16992        editor.change_selections(None, window, cx, |selections| {
16993            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16994        });
16995        editor.handle_input("B", window, cx);
16996    });
16997
16998    assert_eq!(
16999        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17000        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17001        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17002    );
17003
17004    multi_buffer_editor.update(cx, |editor, cx| {
17005        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17006    });
17007    assert_eq!(
17008        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17009        "\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",
17010        "After unfolding the all buffers, all original text should be displayed"
17011    );
17012}
17013
17014#[gpui::test]
17015async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17016    init_test(cx, |_| {});
17017
17018    let sample_text_1 = "1111\n2222\n3333".to_string();
17019    let sample_text_2 = "4444\n5555\n6666".to_string();
17020    let sample_text_3 = "7777\n8888\n9999".to_string();
17021
17022    let fs = FakeFs::new(cx.executor());
17023    fs.insert_tree(
17024        path!("/a"),
17025        json!({
17026            "first.rs": sample_text_1,
17027            "second.rs": sample_text_2,
17028            "third.rs": sample_text_3,
17029        }),
17030    )
17031    .await;
17032    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17033    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17034    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17035    let worktree = project.update(cx, |project, cx| {
17036        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17037        assert_eq!(worktrees.len(), 1);
17038        worktrees.pop().unwrap()
17039    });
17040    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17041
17042    let buffer_1 = project
17043        .update(cx, |project, cx| {
17044            project.open_buffer((worktree_id, "first.rs"), cx)
17045        })
17046        .await
17047        .unwrap();
17048    let buffer_2 = project
17049        .update(cx, |project, cx| {
17050            project.open_buffer((worktree_id, "second.rs"), cx)
17051        })
17052        .await
17053        .unwrap();
17054    let buffer_3 = project
17055        .update(cx, |project, cx| {
17056            project.open_buffer((worktree_id, "third.rs"), cx)
17057        })
17058        .await
17059        .unwrap();
17060
17061    let multi_buffer = cx.new(|cx| {
17062        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17063        multi_buffer.push_excerpts(
17064            buffer_1.clone(),
17065            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17066            cx,
17067        );
17068        multi_buffer.push_excerpts(
17069            buffer_2.clone(),
17070            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17071            cx,
17072        );
17073        multi_buffer.push_excerpts(
17074            buffer_3.clone(),
17075            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17076            cx,
17077        );
17078        multi_buffer
17079    });
17080
17081    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17082        Editor::new(
17083            EditorMode::Full,
17084            multi_buffer,
17085            Some(project.clone()),
17086            window,
17087            cx,
17088        )
17089    });
17090
17091    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17092    assert_eq!(
17093        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17094        full_text,
17095    );
17096
17097    multi_buffer_editor.update(cx, |editor, cx| {
17098        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17099    });
17100    assert_eq!(
17101        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17102        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17103        "After folding the first buffer, its text should not be displayed"
17104    );
17105
17106    multi_buffer_editor.update(cx, |editor, cx| {
17107        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17108    });
17109
17110    assert_eq!(
17111        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17112        "\n\n\n\n\n\n7777\n8888\n9999",
17113        "After folding the second buffer, its text should not be displayed"
17114    );
17115
17116    multi_buffer_editor.update(cx, |editor, cx| {
17117        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17118    });
17119    assert_eq!(
17120        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17121        "\n\n\n\n\n",
17122        "After folding the third buffer, its text should not be displayed"
17123    );
17124
17125    multi_buffer_editor.update(cx, |editor, cx| {
17126        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17127    });
17128    assert_eq!(
17129        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17130        "\n\n\n\n4444\n5555\n6666\n\n",
17131        "After unfolding the second buffer, its text should be displayed"
17132    );
17133
17134    multi_buffer_editor.update(cx, |editor, cx| {
17135        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17136    });
17137    assert_eq!(
17138        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17139        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17140        "After unfolding the first buffer, its text should be displayed"
17141    );
17142
17143    multi_buffer_editor.update(cx, |editor, cx| {
17144        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17145    });
17146    assert_eq!(
17147        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17148        full_text,
17149        "After unfolding all buffers, all original text should be displayed"
17150    );
17151}
17152
17153#[gpui::test]
17154async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17155    init_test(cx, |_| {});
17156
17157    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17158
17159    let fs = FakeFs::new(cx.executor());
17160    fs.insert_tree(
17161        path!("/a"),
17162        json!({
17163            "main.rs": sample_text,
17164        }),
17165    )
17166    .await;
17167    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17168    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17169    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17170    let worktree = project.update(cx, |project, cx| {
17171        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17172        assert_eq!(worktrees.len(), 1);
17173        worktrees.pop().unwrap()
17174    });
17175    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17176
17177    let buffer_1 = project
17178        .update(cx, |project, cx| {
17179            project.open_buffer((worktree_id, "main.rs"), cx)
17180        })
17181        .await
17182        .unwrap();
17183
17184    let multi_buffer = cx.new(|cx| {
17185        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17186        multi_buffer.push_excerpts(
17187            buffer_1.clone(),
17188            [ExcerptRange::new(
17189                Point::new(0, 0)
17190                    ..Point::new(
17191                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17192                        0,
17193                    ),
17194            )],
17195            cx,
17196        );
17197        multi_buffer
17198    });
17199    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17200        Editor::new(
17201            EditorMode::Full,
17202            multi_buffer,
17203            Some(project.clone()),
17204            window,
17205            cx,
17206        )
17207    });
17208
17209    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17210    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17211        enum TestHighlight {}
17212        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17213        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17214        editor.highlight_text::<TestHighlight>(
17215            vec![highlight_range.clone()],
17216            HighlightStyle::color(Hsla::green()),
17217            cx,
17218        );
17219        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17220    });
17221
17222    let full_text = format!("\n\n{sample_text}");
17223    assert_eq!(
17224        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17225        full_text,
17226    );
17227}
17228
17229#[gpui::test]
17230async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17231    init_test(cx, |_| {});
17232    cx.update(|cx| {
17233        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17234            "keymaps/default-linux.json",
17235            cx,
17236        )
17237        .unwrap();
17238        cx.bind_keys(default_key_bindings);
17239    });
17240
17241    let (editor, cx) = cx.add_window_view(|window, cx| {
17242        let multi_buffer = MultiBuffer::build_multi(
17243            [
17244                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17245                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17246                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17247                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17248            ],
17249            cx,
17250        );
17251        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17252
17253        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17254        // fold all but the second buffer, so that we test navigating between two
17255        // adjacent folded buffers, as well as folded buffers at the start and
17256        // end the multibuffer
17257        editor.fold_buffer(buffer_ids[0], cx);
17258        editor.fold_buffer(buffer_ids[2], cx);
17259        editor.fold_buffer(buffer_ids[3], cx);
17260
17261        editor
17262    });
17263    cx.simulate_resize(size(px(1000.), px(1000.)));
17264
17265    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17266    cx.assert_excerpts_with_selections(indoc! {"
17267        [EXCERPT]
17268        ˇ[FOLDED]
17269        [EXCERPT]
17270        a1
17271        b1
17272        [EXCERPT]
17273        [FOLDED]
17274        [EXCERPT]
17275        [FOLDED]
17276        "
17277    });
17278    cx.simulate_keystroke("down");
17279    cx.assert_excerpts_with_selections(indoc! {"
17280        [EXCERPT]
17281        [FOLDED]
17282        [EXCERPT]
17283        ˇa1
17284        b1
17285        [EXCERPT]
17286        [FOLDED]
17287        [EXCERPT]
17288        [FOLDED]
17289        "
17290    });
17291    cx.simulate_keystroke("down");
17292    cx.assert_excerpts_with_selections(indoc! {"
17293        [EXCERPT]
17294        [FOLDED]
17295        [EXCERPT]
17296        a1
17297        ˇb1
17298        [EXCERPT]
17299        [FOLDED]
17300        [EXCERPT]
17301        [FOLDED]
17302        "
17303    });
17304    cx.simulate_keystroke("down");
17305    cx.assert_excerpts_with_selections(indoc! {"
17306        [EXCERPT]
17307        [FOLDED]
17308        [EXCERPT]
17309        a1
17310        b1
17311        ˇ[EXCERPT]
17312        [FOLDED]
17313        [EXCERPT]
17314        [FOLDED]
17315        "
17316    });
17317    cx.simulate_keystroke("down");
17318    cx.assert_excerpts_with_selections(indoc! {"
17319        [EXCERPT]
17320        [FOLDED]
17321        [EXCERPT]
17322        a1
17323        b1
17324        [EXCERPT]
17325        ˇ[FOLDED]
17326        [EXCERPT]
17327        [FOLDED]
17328        "
17329    });
17330    for _ in 0..5 {
17331        cx.simulate_keystroke("down");
17332        cx.assert_excerpts_with_selections(indoc! {"
17333            [EXCERPT]
17334            [FOLDED]
17335            [EXCERPT]
17336            a1
17337            b1
17338            [EXCERPT]
17339            [FOLDED]
17340            [EXCERPT]
17341            ˇ[FOLDED]
17342            "
17343        });
17344    }
17345
17346    cx.simulate_keystroke("up");
17347    cx.assert_excerpts_with_selections(indoc! {"
17348        [EXCERPT]
17349        [FOLDED]
17350        [EXCERPT]
17351        a1
17352        b1
17353        [EXCERPT]
17354        ˇ[FOLDED]
17355        [EXCERPT]
17356        [FOLDED]
17357        "
17358    });
17359    cx.simulate_keystroke("up");
17360    cx.assert_excerpts_with_selections(indoc! {"
17361        [EXCERPT]
17362        [FOLDED]
17363        [EXCERPT]
17364        a1
17365        b1
17366        ˇ[EXCERPT]
17367        [FOLDED]
17368        [EXCERPT]
17369        [FOLDED]
17370        "
17371    });
17372    cx.simulate_keystroke("up");
17373    cx.assert_excerpts_with_selections(indoc! {"
17374        [EXCERPT]
17375        [FOLDED]
17376        [EXCERPT]
17377        a1
17378        ˇb1
17379        [EXCERPT]
17380        [FOLDED]
17381        [EXCERPT]
17382        [FOLDED]
17383        "
17384    });
17385    cx.simulate_keystroke("up");
17386    cx.assert_excerpts_with_selections(indoc! {"
17387        [EXCERPT]
17388        [FOLDED]
17389        [EXCERPT]
17390        ˇa1
17391        b1
17392        [EXCERPT]
17393        [FOLDED]
17394        [EXCERPT]
17395        [FOLDED]
17396        "
17397    });
17398    for _ in 0..5 {
17399        cx.simulate_keystroke("up");
17400        cx.assert_excerpts_with_selections(indoc! {"
17401            [EXCERPT]
17402            ˇ[FOLDED]
17403            [EXCERPT]
17404            a1
17405            b1
17406            [EXCERPT]
17407            [FOLDED]
17408            [EXCERPT]
17409            [FOLDED]
17410            "
17411        });
17412    }
17413}
17414
17415#[gpui::test]
17416async fn test_inline_completion_text(cx: &mut TestAppContext) {
17417    init_test(cx, |_| {});
17418
17419    // Simple insertion
17420    assert_highlighted_edits(
17421        "Hello, world!",
17422        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17423        true,
17424        cx,
17425        |highlighted_edits, cx| {
17426            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17427            assert_eq!(highlighted_edits.highlights.len(), 1);
17428            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17429            assert_eq!(
17430                highlighted_edits.highlights[0].1.background_color,
17431                Some(cx.theme().status().created_background)
17432            );
17433        },
17434    )
17435    .await;
17436
17437    // Replacement
17438    assert_highlighted_edits(
17439        "This is a test.",
17440        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17441        false,
17442        cx,
17443        |highlighted_edits, cx| {
17444            assert_eq!(highlighted_edits.text, "That is a test.");
17445            assert_eq!(highlighted_edits.highlights.len(), 1);
17446            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17447            assert_eq!(
17448                highlighted_edits.highlights[0].1.background_color,
17449                Some(cx.theme().status().created_background)
17450            );
17451        },
17452    )
17453    .await;
17454
17455    // Multiple edits
17456    assert_highlighted_edits(
17457        "Hello, world!",
17458        vec![
17459            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17460            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17461        ],
17462        false,
17463        cx,
17464        |highlighted_edits, cx| {
17465            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17466            assert_eq!(highlighted_edits.highlights.len(), 2);
17467            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17468            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17469            assert_eq!(
17470                highlighted_edits.highlights[0].1.background_color,
17471                Some(cx.theme().status().created_background)
17472            );
17473            assert_eq!(
17474                highlighted_edits.highlights[1].1.background_color,
17475                Some(cx.theme().status().created_background)
17476            );
17477        },
17478    )
17479    .await;
17480
17481    // Multiple lines with edits
17482    assert_highlighted_edits(
17483        "First line\nSecond line\nThird line\nFourth line",
17484        vec![
17485            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17486            (
17487                Point::new(2, 0)..Point::new(2, 10),
17488                "New third line".to_string(),
17489            ),
17490            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17491        ],
17492        false,
17493        cx,
17494        |highlighted_edits, cx| {
17495            assert_eq!(
17496                highlighted_edits.text,
17497                "Second modified\nNew third line\nFourth updated line"
17498            );
17499            assert_eq!(highlighted_edits.highlights.len(), 3);
17500            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17501            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17502            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17503            for highlight in &highlighted_edits.highlights {
17504                assert_eq!(
17505                    highlight.1.background_color,
17506                    Some(cx.theme().status().created_background)
17507                );
17508            }
17509        },
17510    )
17511    .await;
17512}
17513
17514#[gpui::test]
17515async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17516    init_test(cx, |_| {});
17517
17518    // Deletion
17519    assert_highlighted_edits(
17520        "Hello, world!",
17521        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17522        true,
17523        cx,
17524        |highlighted_edits, cx| {
17525            assert_eq!(highlighted_edits.text, "Hello, world!");
17526            assert_eq!(highlighted_edits.highlights.len(), 1);
17527            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17528            assert_eq!(
17529                highlighted_edits.highlights[0].1.background_color,
17530                Some(cx.theme().status().deleted_background)
17531            );
17532        },
17533    )
17534    .await;
17535
17536    // Insertion
17537    assert_highlighted_edits(
17538        "Hello, world!",
17539        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17540        true,
17541        cx,
17542        |highlighted_edits, cx| {
17543            assert_eq!(highlighted_edits.highlights.len(), 1);
17544            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17545            assert_eq!(
17546                highlighted_edits.highlights[0].1.background_color,
17547                Some(cx.theme().status().created_background)
17548            );
17549        },
17550    )
17551    .await;
17552}
17553
17554async fn assert_highlighted_edits(
17555    text: &str,
17556    edits: Vec<(Range<Point>, String)>,
17557    include_deletions: bool,
17558    cx: &mut TestAppContext,
17559    assertion_fn: impl Fn(HighlightedText, &App),
17560) {
17561    let window = cx.add_window(|window, cx| {
17562        let buffer = MultiBuffer::build_simple(text, cx);
17563        Editor::new(EditorMode::Full, buffer, None, window, cx)
17564    });
17565    let cx = &mut VisualTestContext::from_window(*window, cx);
17566
17567    let (buffer, snapshot) = window
17568        .update(cx, |editor, _window, cx| {
17569            (
17570                editor.buffer().clone(),
17571                editor.buffer().read(cx).snapshot(cx),
17572            )
17573        })
17574        .unwrap();
17575
17576    let edits = edits
17577        .into_iter()
17578        .map(|(range, edit)| {
17579            (
17580                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17581                edit,
17582            )
17583        })
17584        .collect::<Vec<_>>();
17585
17586    let text_anchor_edits = edits
17587        .clone()
17588        .into_iter()
17589        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17590        .collect::<Vec<_>>();
17591
17592    let edit_preview = window
17593        .update(cx, |_, _window, cx| {
17594            buffer
17595                .read(cx)
17596                .as_singleton()
17597                .unwrap()
17598                .read(cx)
17599                .preview_edits(text_anchor_edits.into(), cx)
17600        })
17601        .unwrap()
17602        .await;
17603
17604    cx.update(|_window, cx| {
17605        let highlighted_edits = inline_completion_edit_text(
17606            &snapshot.as_singleton().unwrap().2,
17607            &edits,
17608            &edit_preview,
17609            include_deletions,
17610            cx,
17611        );
17612        assertion_fn(highlighted_edits, cx)
17613    });
17614}
17615
17616#[track_caller]
17617fn assert_breakpoint(
17618    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17619    path: &Arc<Path>,
17620    expected: Vec<(u32, Breakpoint)>,
17621) {
17622    if expected.len() == 0usize {
17623        assert!(!breakpoints.contains_key(path), "{}", path.display());
17624    } else {
17625        let mut breakpoint = breakpoints
17626            .get(path)
17627            .unwrap()
17628            .into_iter()
17629            .map(|breakpoint| {
17630                (
17631                    breakpoint.row,
17632                    Breakpoint {
17633                        message: breakpoint.message.clone(),
17634                        state: breakpoint.state,
17635                        condition: breakpoint.condition.clone(),
17636                        hit_condition: breakpoint.hit_condition.clone(),
17637                    },
17638                )
17639            })
17640            .collect::<Vec<_>>();
17641
17642        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17643
17644        assert_eq!(expected, breakpoint);
17645    }
17646}
17647
17648fn add_log_breakpoint_at_cursor(
17649    editor: &mut Editor,
17650    log_message: &str,
17651    window: &mut Window,
17652    cx: &mut Context<Editor>,
17653) {
17654    let (anchor, bp) = editor
17655        .breakpoint_at_cursor_head(window, cx)
17656        .unwrap_or_else(|| {
17657            let cursor_position: Point = editor.selections.newest(cx).head();
17658
17659            let breakpoint_position = editor
17660                .snapshot(window, cx)
17661                .display_snapshot
17662                .buffer_snapshot
17663                .anchor_before(Point::new(cursor_position.row, 0));
17664
17665            (breakpoint_position, Breakpoint::new_log(&log_message))
17666        });
17667
17668    editor.edit_breakpoint_at_anchor(
17669        anchor,
17670        bp,
17671        BreakpointEditAction::EditLogMessage(log_message.into()),
17672        cx,
17673    );
17674}
17675
17676#[gpui::test]
17677async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17678    init_test(cx, |_| {});
17679
17680    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17681    let fs = FakeFs::new(cx.executor());
17682    fs.insert_tree(
17683        path!("/a"),
17684        json!({
17685            "main.rs": sample_text,
17686        }),
17687    )
17688    .await;
17689    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17691    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17692
17693    let fs = FakeFs::new(cx.executor());
17694    fs.insert_tree(
17695        path!("/a"),
17696        json!({
17697            "main.rs": sample_text,
17698        }),
17699    )
17700    .await;
17701    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17702    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17703    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17704    let worktree_id = workspace
17705        .update(cx, |workspace, _window, cx| {
17706            workspace.project().update(cx, |project, cx| {
17707                project.worktrees(cx).next().unwrap().read(cx).id()
17708            })
17709        })
17710        .unwrap();
17711
17712    let buffer = project
17713        .update(cx, |project, cx| {
17714            project.open_buffer((worktree_id, "main.rs"), cx)
17715        })
17716        .await
17717        .unwrap();
17718
17719    let (editor, cx) = cx.add_window_view(|window, cx| {
17720        Editor::new(
17721            EditorMode::Full,
17722            MultiBuffer::build_from_buffer(buffer, cx),
17723            Some(project.clone()),
17724            window,
17725            cx,
17726        )
17727    });
17728
17729    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17730    let abs_path = project.read_with(cx, |project, cx| {
17731        project
17732            .absolute_path(&project_path, cx)
17733            .map(|path_buf| Arc::from(path_buf.to_owned()))
17734            .unwrap()
17735    });
17736
17737    // assert we can add breakpoint on the first line
17738    editor.update_in(cx, |editor, window, cx| {
17739        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17740        editor.move_to_end(&MoveToEnd, window, cx);
17741        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17742    });
17743
17744    let breakpoints = editor.update(cx, |editor, cx| {
17745        editor
17746            .breakpoint_store()
17747            .as_ref()
17748            .unwrap()
17749            .read(cx)
17750            .all_breakpoints(cx)
17751            .clone()
17752    });
17753
17754    assert_eq!(1, breakpoints.len());
17755    assert_breakpoint(
17756        &breakpoints,
17757        &abs_path,
17758        vec![
17759            (0, Breakpoint::new_standard()),
17760            (3, Breakpoint::new_standard()),
17761        ],
17762    );
17763
17764    editor.update_in(cx, |editor, window, cx| {
17765        editor.move_to_beginning(&MoveToBeginning, window, cx);
17766        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17767    });
17768
17769    let breakpoints = editor.update(cx, |editor, cx| {
17770        editor
17771            .breakpoint_store()
17772            .as_ref()
17773            .unwrap()
17774            .read(cx)
17775            .all_breakpoints(cx)
17776            .clone()
17777    });
17778
17779    assert_eq!(1, breakpoints.len());
17780    assert_breakpoint(
17781        &breakpoints,
17782        &abs_path,
17783        vec![(3, Breakpoint::new_standard())],
17784    );
17785
17786    editor.update_in(cx, |editor, window, cx| {
17787        editor.move_to_end(&MoveToEnd, window, cx);
17788        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17789    });
17790
17791    let breakpoints = editor.update(cx, |editor, cx| {
17792        editor
17793            .breakpoint_store()
17794            .as_ref()
17795            .unwrap()
17796            .read(cx)
17797            .all_breakpoints(cx)
17798            .clone()
17799    });
17800
17801    assert_eq!(0, breakpoints.len());
17802    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17803}
17804
17805#[gpui::test]
17806async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17807    init_test(cx, |_| {});
17808
17809    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17810
17811    let fs = FakeFs::new(cx.executor());
17812    fs.insert_tree(
17813        path!("/a"),
17814        json!({
17815            "main.rs": sample_text,
17816        }),
17817    )
17818    .await;
17819    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17820    let (workspace, cx) =
17821        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17822
17823    let worktree_id = workspace.update(cx, |workspace, cx| {
17824        workspace.project().update(cx, |project, cx| {
17825            project.worktrees(cx).next().unwrap().read(cx).id()
17826        })
17827    });
17828
17829    let buffer = project
17830        .update(cx, |project, cx| {
17831            project.open_buffer((worktree_id, "main.rs"), cx)
17832        })
17833        .await
17834        .unwrap();
17835
17836    let (editor, cx) = cx.add_window_view(|window, cx| {
17837        Editor::new(
17838            EditorMode::Full,
17839            MultiBuffer::build_from_buffer(buffer, cx),
17840            Some(project.clone()),
17841            window,
17842            cx,
17843        )
17844    });
17845
17846    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17847    let abs_path = project.read_with(cx, |project, cx| {
17848        project
17849            .absolute_path(&project_path, cx)
17850            .map(|path_buf| Arc::from(path_buf.to_owned()))
17851            .unwrap()
17852    });
17853
17854    editor.update_in(cx, |editor, window, cx| {
17855        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17856    });
17857
17858    let breakpoints = editor.update(cx, |editor, cx| {
17859        editor
17860            .breakpoint_store()
17861            .as_ref()
17862            .unwrap()
17863            .read(cx)
17864            .all_breakpoints(cx)
17865            .clone()
17866    });
17867
17868    assert_breakpoint(
17869        &breakpoints,
17870        &abs_path,
17871        vec![(0, Breakpoint::new_log("hello world"))],
17872    );
17873
17874    // Removing a log message from a log breakpoint should remove it
17875    editor.update_in(cx, |editor, window, cx| {
17876        add_log_breakpoint_at_cursor(editor, "", window, cx);
17877    });
17878
17879    let breakpoints = editor.update(cx, |editor, cx| {
17880        editor
17881            .breakpoint_store()
17882            .as_ref()
17883            .unwrap()
17884            .read(cx)
17885            .all_breakpoints(cx)
17886            .clone()
17887    });
17888
17889    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17890
17891    editor.update_in(cx, |editor, window, cx| {
17892        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17893        editor.move_to_end(&MoveToEnd, window, cx);
17894        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17895        // Not adding a log message to a standard breakpoint shouldn't remove it
17896        add_log_breakpoint_at_cursor(editor, "", window, cx);
17897    });
17898
17899    let breakpoints = editor.update(cx, |editor, cx| {
17900        editor
17901            .breakpoint_store()
17902            .as_ref()
17903            .unwrap()
17904            .read(cx)
17905            .all_breakpoints(cx)
17906            .clone()
17907    });
17908
17909    assert_breakpoint(
17910        &breakpoints,
17911        &abs_path,
17912        vec![
17913            (0, Breakpoint::new_standard()),
17914            (3, Breakpoint::new_standard()),
17915        ],
17916    );
17917
17918    editor.update_in(cx, |editor, window, cx| {
17919        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17920    });
17921
17922    let breakpoints = editor.update(cx, |editor, cx| {
17923        editor
17924            .breakpoint_store()
17925            .as_ref()
17926            .unwrap()
17927            .read(cx)
17928            .all_breakpoints(cx)
17929            .clone()
17930    });
17931
17932    assert_breakpoint(
17933        &breakpoints,
17934        &abs_path,
17935        vec![
17936            (0, Breakpoint::new_standard()),
17937            (3, Breakpoint::new_log("hello world")),
17938        ],
17939    );
17940
17941    editor.update_in(cx, |editor, window, cx| {
17942        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17943    });
17944
17945    let breakpoints = editor.update(cx, |editor, cx| {
17946        editor
17947            .breakpoint_store()
17948            .as_ref()
17949            .unwrap()
17950            .read(cx)
17951            .all_breakpoints(cx)
17952            .clone()
17953    });
17954
17955    assert_breakpoint(
17956        &breakpoints,
17957        &abs_path,
17958        vec![
17959            (0, Breakpoint::new_standard()),
17960            (3, Breakpoint::new_log("hello Earth!!")),
17961        ],
17962    );
17963}
17964
17965/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17966/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17967/// or when breakpoints were placed out of order. This tests for a regression too
17968#[gpui::test]
17969async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17970    init_test(cx, |_| {});
17971
17972    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17973    let fs = FakeFs::new(cx.executor());
17974    fs.insert_tree(
17975        path!("/a"),
17976        json!({
17977            "main.rs": sample_text,
17978        }),
17979    )
17980    .await;
17981    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17982    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17983    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17984
17985    let fs = FakeFs::new(cx.executor());
17986    fs.insert_tree(
17987        path!("/a"),
17988        json!({
17989            "main.rs": sample_text,
17990        }),
17991    )
17992    .await;
17993    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17994    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17995    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17996    let worktree_id = workspace
17997        .update(cx, |workspace, _window, cx| {
17998            workspace.project().update(cx, |project, cx| {
17999                project.worktrees(cx).next().unwrap().read(cx).id()
18000            })
18001        })
18002        .unwrap();
18003
18004    let buffer = project
18005        .update(cx, |project, cx| {
18006            project.open_buffer((worktree_id, "main.rs"), cx)
18007        })
18008        .await
18009        .unwrap();
18010
18011    let (editor, cx) = cx.add_window_view(|window, cx| {
18012        Editor::new(
18013            EditorMode::Full,
18014            MultiBuffer::build_from_buffer(buffer, cx),
18015            Some(project.clone()),
18016            window,
18017            cx,
18018        )
18019    });
18020
18021    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18022    let abs_path = project.read_with(cx, |project, cx| {
18023        project
18024            .absolute_path(&project_path, cx)
18025            .map(|path_buf| Arc::from(path_buf.to_owned()))
18026            .unwrap()
18027    });
18028
18029    // assert we can add breakpoint on the first line
18030    editor.update_in(cx, |editor, window, cx| {
18031        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18032        editor.move_to_end(&MoveToEnd, window, cx);
18033        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18034        editor.move_up(&MoveUp, window, cx);
18035        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18036    });
18037
18038    let breakpoints = editor.update(cx, |editor, cx| {
18039        editor
18040            .breakpoint_store()
18041            .as_ref()
18042            .unwrap()
18043            .read(cx)
18044            .all_breakpoints(cx)
18045            .clone()
18046    });
18047
18048    assert_eq!(1, breakpoints.len());
18049    assert_breakpoint(
18050        &breakpoints,
18051        &abs_path,
18052        vec![
18053            (0, Breakpoint::new_standard()),
18054            (2, Breakpoint::new_standard()),
18055            (3, Breakpoint::new_standard()),
18056        ],
18057    );
18058
18059    editor.update_in(cx, |editor, window, cx| {
18060        editor.move_to_beginning(&MoveToBeginning, window, cx);
18061        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18062        editor.move_to_end(&MoveToEnd, window, cx);
18063        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18064        // Disabling a breakpoint that doesn't exist should do nothing
18065        editor.move_up(&MoveUp, window, cx);
18066        editor.move_up(&MoveUp, window, cx);
18067        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18068    });
18069
18070    let breakpoints = editor.update(cx, |editor, cx| {
18071        editor
18072            .breakpoint_store()
18073            .as_ref()
18074            .unwrap()
18075            .read(cx)
18076            .all_breakpoints(cx)
18077            .clone()
18078    });
18079
18080    let disable_breakpoint = {
18081        let mut bp = Breakpoint::new_standard();
18082        bp.state = BreakpointState::Disabled;
18083        bp
18084    };
18085
18086    assert_eq!(1, breakpoints.len());
18087    assert_breakpoint(
18088        &breakpoints,
18089        &abs_path,
18090        vec![
18091            (0, disable_breakpoint.clone()),
18092            (2, Breakpoint::new_standard()),
18093            (3, disable_breakpoint.clone()),
18094        ],
18095    );
18096
18097    editor.update_in(cx, |editor, window, cx| {
18098        editor.move_to_beginning(&MoveToBeginning, window, cx);
18099        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18100        editor.move_to_end(&MoveToEnd, window, cx);
18101        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18102        editor.move_up(&MoveUp, window, cx);
18103        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18104    });
18105
18106    let breakpoints = editor.update(cx, |editor, cx| {
18107        editor
18108            .breakpoint_store()
18109            .as_ref()
18110            .unwrap()
18111            .read(cx)
18112            .all_breakpoints(cx)
18113            .clone()
18114    });
18115
18116    assert_eq!(1, breakpoints.len());
18117    assert_breakpoint(
18118        &breakpoints,
18119        &abs_path,
18120        vec![
18121            (0, Breakpoint::new_standard()),
18122            (2, disable_breakpoint),
18123            (3, Breakpoint::new_standard()),
18124        ],
18125    );
18126}
18127
18128#[gpui::test]
18129async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18130    init_test(cx, |_| {});
18131    let capabilities = lsp::ServerCapabilities {
18132        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18133            prepare_provider: Some(true),
18134            work_done_progress_options: Default::default(),
18135        })),
18136        ..Default::default()
18137    };
18138    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18139
18140    cx.set_state(indoc! {"
18141        struct Fˇoo {}
18142    "});
18143
18144    cx.update_editor(|editor, _, cx| {
18145        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18146        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18147        editor.highlight_background::<DocumentHighlightRead>(
18148            &[highlight_range],
18149            |c| c.editor_document_highlight_read_background,
18150            cx,
18151        );
18152    });
18153
18154    let mut prepare_rename_handler = cx
18155        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18156            move |_, _, _| async move {
18157                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18158                    start: lsp::Position {
18159                        line: 0,
18160                        character: 7,
18161                    },
18162                    end: lsp::Position {
18163                        line: 0,
18164                        character: 10,
18165                    },
18166                })))
18167            },
18168        );
18169    let prepare_rename_task = cx
18170        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18171        .expect("Prepare rename was not started");
18172    prepare_rename_handler.next().await.unwrap();
18173    prepare_rename_task.await.expect("Prepare rename failed");
18174
18175    let mut rename_handler =
18176        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18177            let edit = lsp::TextEdit {
18178                range: lsp::Range {
18179                    start: lsp::Position {
18180                        line: 0,
18181                        character: 7,
18182                    },
18183                    end: lsp::Position {
18184                        line: 0,
18185                        character: 10,
18186                    },
18187                },
18188                new_text: "FooRenamed".to_string(),
18189            };
18190            Ok(Some(lsp::WorkspaceEdit::new(
18191                // Specify the same edit twice
18192                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18193            )))
18194        });
18195    let rename_task = cx
18196        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18197        .expect("Confirm rename was not started");
18198    rename_handler.next().await.unwrap();
18199    rename_task.await.expect("Confirm rename failed");
18200    cx.run_until_parked();
18201
18202    // Despite two edits, only one is actually applied as those are identical
18203    cx.assert_editor_state(indoc! {"
18204        struct FooRenamedˇ {}
18205    "});
18206}
18207
18208#[gpui::test]
18209async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18210    init_test(cx, |_| {});
18211    // These capabilities indicate that the server does not support prepare rename.
18212    let capabilities = lsp::ServerCapabilities {
18213        rename_provider: Some(lsp::OneOf::Left(true)),
18214        ..Default::default()
18215    };
18216    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18217
18218    cx.set_state(indoc! {"
18219        struct Fˇoo {}
18220    "});
18221
18222    cx.update_editor(|editor, _window, cx| {
18223        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18224        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18225        editor.highlight_background::<DocumentHighlightRead>(
18226            &[highlight_range],
18227            |c| c.editor_document_highlight_read_background,
18228            cx,
18229        );
18230    });
18231
18232    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18233        .expect("Prepare rename was not started")
18234        .await
18235        .expect("Prepare rename failed");
18236
18237    let mut rename_handler =
18238        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18239            let edit = lsp::TextEdit {
18240                range: lsp::Range {
18241                    start: lsp::Position {
18242                        line: 0,
18243                        character: 7,
18244                    },
18245                    end: lsp::Position {
18246                        line: 0,
18247                        character: 10,
18248                    },
18249                },
18250                new_text: "FooRenamed".to_string(),
18251            };
18252            Ok(Some(lsp::WorkspaceEdit::new(
18253                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18254            )))
18255        });
18256    let rename_task = cx
18257        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18258        .expect("Confirm rename was not started");
18259    rename_handler.next().await.unwrap();
18260    rename_task.await.expect("Confirm rename failed");
18261    cx.run_until_parked();
18262
18263    // Correct range is renamed, as `surrounding_word` is used to find it.
18264    cx.assert_editor_state(indoc! {"
18265        struct FooRenamedˇ {}
18266    "});
18267}
18268
18269#[gpui::test]
18270async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18271    init_test(cx, |_| {});
18272    let mut cx = EditorTestContext::new(cx).await;
18273
18274    let language = Arc::new(
18275        Language::new(
18276            LanguageConfig::default(),
18277            Some(tree_sitter_html::LANGUAGE.into()),
18278        )
18279        .with_brackets_query(
18280            r#"
18281            ("<" @open "/>" @close)
18282            ("</" @open ">" @close)
18283            ("<" @open ">" @close)
18284            ("\"" @open "\"" @close)
18285            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18286        "#,
18287        )
18288        .unwrap(),
18289    );
18290    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18291
18292    cx.set_state(indoc! {"
18293        <span>ˇ</span>
18294    "});
18295    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18296    cx.assert_editor_state(indoc! {"
18297        <span>
18298        ˇ
18299        </span>
18300    "});
18301
18302    cx.set_state(indoc! {"
18303        <span><span></span>ˇ</span>
18304    "});
18305    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18306    cx.assert_editor_state(indoc! {"
18307        <span><span></span>
18308        ˇ</span>
18309    "});
18310
18311    cx.set_state(indoc! {"
18312        <span>ˇ
18313        </span>
18314    "});
18315    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18316    cx.assert_editor_state(indoc! {"
18317        <span>
18318        ˇ
18319        </span>
18320    "});
18321}
18322
18323#[gpui::test(iterations = 10)]
18324async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18325    init_test(cx, |_| {});
18326
18327    let fs = FakeFs::new(cx.executor());
18328    fs.insert_tree(
18329        path!("/dir"),
18330        json!({
18331            "a.ts": "a",
18332        }),
18333    )
18334    .await;
18335
18336    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18337    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18338    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18339
18340    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18341    language_registry.add(Arc::new(Language::new(
18342        LanguageConfig {
18343            name: "TypeScript".into(),
18344            matcher: LanguageMatcher {
18345                path_suffixes: vec!["ts".to_string()],
18346                ..Default::default()
18347            },
18348            ..Default::default()
18349        },
18350        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18351    )));
18352    let mut fake_language_servers = language_registry.register_fake_lsp(
18353        "TypeScript",
18354        FakeLspAdapter {
18355            capabilities: lsp::ServerCapabilities {
18356                code_lens_provider: Some(lsp::CodeLensOptions {
18357                    resolve_provider: Some(true),
18358                }),
18359                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18360                    commands: vec!["_the/command".to_string()],
18361                    ..lsp::ExecuteCommandOptions::default()
18362                }),
18363                ..lsp::ServerCapabilities::default()
18364            },
18365            ..FakeLspAdapter::default()
18366        },
18367    );
18368
18369    let (buffer, _handle) = project
18370        .update(cx, |p, cx| {
18371            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18372        })
18373        .await
18374        .unwrap();
18375    cx.executor().run_until_parked();
18376
18377    let fake_server = fake_language_servers.next().await.unwrap();
18378
18379    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18380    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18381    drop(buffer_snapshot);
18382    let actions = cx
18383        .update_window(*workspace, |_, window, cx| {
18384            project.code_actions(&buffer, anchor..anchor, window, cx)
18385        })
18386        .unwrap();
18387
18388    fake_server
18389        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18390            Ok(Some(vec![
18391                lsp::CodeLens {
18392                    range: lsp::Range::default(),
18393                    command: Some(lsp::Command {
18394                        title: "Code lens command".to_owned(),
18395                        command: "_the/command".to_owned(),
18396                        arguments: None,
18397                    }),
18398                    data: None,
18399                },
18400                lsp::CodeLens {
18401                    range: lsp::Range::default(),
18402                    command: Some(lsp::Command {
18403                        title: "Command not in capabilities".to_owned(),
18404                        command: "not in capabilities".to_owned(),
18405                        arguments: None,
18406                    }),
18407                    data: None,
18408                },
18409                lsp::CodeLens {
18410                    range: lsp::Range {
18411                        start: lsp::Position {
18412                            line: 1,
18413                            character: 1,
18414                        },
18415                        end: lsp::Position {
18416                            line: 1,
18417                            character: 1,
18418                        },
18419                    },
18420                    command: Some(lsp::Command {
18421                        title: "Command not in range".to_owned(),
18422                        command: "_the/command".to_owned(),
18423                        arguments: None,
18424                    }),
18425                    data: None,
18426                },
18427            ]))
18428        })
18429        .next()
18430        .await;
18431
18432    let actions = actions.await.unwrap();
18433    assert_eq!(
18434        actions.len(),
18435        1,
18436        "Should have only one valid action for the 0..0 range"
18437    );
18438    let action = actions[0].clone();
18439    let apply = project.update(cx, |project, cx| {
18440        project.apply_code_action(buffer.clone(), action, true, cx)
18441    });
18442
18443    // Resolving the code action does not populate its edits. In absence of
18444    // edits, we must execute the given command.
18445    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18446        |mut lens, _| async move {
18447            let lens_command = lens.command.as_mut().expect("should have a command");
18448            assert_eq!(lens_command.title, "Code lens command");
18449            lens_command.arguments = Some(vec![json!("the-argument")]);
18450            Ok(lens)
18451        },
18452    );
18453
18454    // While executing the command, the language server sends the editor
18455    // a `workspaceEdit` request.
18456    fake_server
18457        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18458            let fake = fake_server.clone();
18459            move |params, _| {
18460                assert_eq!(params.command, "_the/command");
18461                let fake = fake.clone();
18462                async move {
18463                    fake.server
18464                        .request::<lsp::request::ApplyWorkspaceEdit>(
18465                            lsp::ApplyWorkspaceEditParams {
18466                                label: None,
18467                                edit: lsp::WorkspaceEdit {
18468                                    changes: Some(
18469                                        [(
18470                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18471                                            vec![lsp::TextEdit {
18472                                                range: lsp::Range::new(
18473                                                    lsp::Position::new(0, 0),
18474                                                    lsp::Position::new(0, 0),
18475                                                ),
18476                                                new_text: "X".into(),
18477                                            }],
18478                                        )]
18479                                        .into_iter()
18480                                        .collect(),
18481                                    ),
18482                                    ..Default::default()
18483                                },
18484                            },
18485                        )
18486                        .await
18487                        .unwrap();
18488                    Ok(Some(json!(null)))
18489                }
18490            }
18491        })
18492        .next()
18493        .await;
18494
18495    // Applying the code lens command returns a project transaction containing the edits
18496    // sent by the language server in its `workspaceEdit` request.
18497    let transaction = apply.await.unwrap();
18498    assert!(transaction.0.contains_key(&buffer));
18499    buffer.update(cx, |buffer, cx| {
18500        assert_eq!(buffer.text(), "Xa");
18501        buffer.undo(cx);
18502        assert_eq!(buffer.text(), "a");
18503    });
18504}
18505
18506#[gpui::test]
18507async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18508    init_test(cx, |_| {});
18509
18510    let fs = FakeFs::new(cx.executor());
18511    let main_text = r#"fn main() {
18512println!("1");
18513println!("2");
18514println!("3");
18515println!("4");
18516println!("5");
18517}"#;
18518    let lib_text = "mod foo {}";
18519    fs.insert_tree(
18520        path!("/a"),
18521        json!({
18522            "lib.rs": lib_text,
18523            "main.rs": main_text,
18524        }),
18525    )
18526    .await;
18527
18528    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18529    let (workspace, cx) =
18530        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18531    let worktree_id = workspace.update(cx, |workspace, cx| {
18532        workspace.project().update(cx, |project, cx| {
18533            project.worktrees(cx).next().unwrap().read(cx).id()
18534        })
18535    });
18536
18537    let expected_ranges = vec![
18538        Point::new(0, 0)..Point::new(0, 0),
18539        Point::new(1, 0)..Point::new(1, 1),
18540        Point::new(2, 0)..Point::new(2, 2),
18541        Point::new(3, 0)..Point::new(3, 3),
18542    ];
18543
18544    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18545    let editor_1 = workspace
18546        .update_in(cx, |workspace, window, cx| {
18547            workspace.open_path(
18548                (worktree_id, "main.rs"),
18549                Some(pane_1.downgrade()),
18550                true,
18551                window,
18552                cx,
18553            )
18554        })
18555        .unwrap()
18556        .await
18557        .downcast::<Editor>()
18558        .unwrap();
18559    pane_1.update(cx, |pane, cx| {
18560        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18561        open_editor.update(cx, |editor, cx| {
18562            assert_eq!(
18563                editor.display_text(cx),
18564                main_text,
18565                "Original main.rs text on initial open",
18566            );
18567            assert_eq!(
18568                editor
18569                    .selections
18570                    .all::<Point>(cx)
18571                    .into_iter()
18572                    .map(|s| s.range())
18573                    .collect::<Vec<_>>(),
18574                vec![Point::zero()..Point::zero()],
18575                "Default selections on initial open",
18576            );
18577        })
18578    });
18579    editor_1.update_in(cx, |editor, window, cx| {
18580        editor.change_selections(None, window, cx, |s| {
18581            s.select_ranges(expected_ranges.clone());
18582        });
18583    });
18584
18585    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18586        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18587    });
18588    let editor_2 = workspace
18589        .update_in(cx, |workspace, window, cx| {
18590            workspace.open_path(
18591                (worktree_id, "main.rs"),
18592                Some(pane_2.downgrade()),
18593                true,
18594                window,
18595                cx,
18596            )
18597        })
18598        .unwrap()
18599        .await
18600        .downcast::<Editor>()
18601        .unwrap();
18602    pane_2.update(cx, |pane, cx| {
18603        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18604        open_editor.update(cx, |editor, cx| {
18605            assert_eq!(
18606                editor.display_text(cx),
18607                main_text,
18608                "Original main.rs text on initial open in another panel",
18609            );
18610            assert_eq!(
18611                editor
18612                    .selections
18613                    .all::<Point>(cx)
18614                    .into_iter()
18615                    .map(|s| s.range())
18616                    .collect::<Vec<_>>(),
18617                vec![Point::zero()..Point::zero()],
18618                "Default selections on initial open in another panel",
18619            );
18620        })
18621    });
18622
18623    editor_2.update_in(cx, |editor, window, cx| {
18624        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18625    });
18626
18627    let _other_editor_1 = workspace
18628        .update_in(cx, |workspace, window, cx| {
18629            workspace.open_path(
18630                (worktree_id, "lib.rs"),
18631                Some(pane_1.downgrade()),
18632                true,
18633                window,
18634                cx,
18635            )
18636        })
18637        .unwrap()
18638        .await
18639        .downcast::<Editor>()
18640        .unwrap();
18641    pane_1
18642        .update_in(cx, |pane, window, cx| {
18643            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18644                .unwrap()
18645        })
18646        .await
18647        .unwrap();
18648    drop(editor_1);
18649    pane_1.update(cx, |pane, cx| {
18650        pane.active_item()
18651            .unwrap()
18652            .downcast::<Editor>()
18653            .unwrap()
18654            .update(cx, |editor, cx| {
18655                assert_eq!(
18656                    editor.display_text(cx),
18657                    lib_text,
18658                    "Other file should be open and active",
18659                );
18660            });
18661        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18662    });
18663
18664    let _other_editor_2 = workspace
18665        .update_in(cx, |workspace, window, cx| {
18666            workspace.open_path(
18667                (worktree_id, "lib.rs"),
18668                Some(pane_2.downgrade()),
18669                true,
18670                window,
18671                cx,
18672            )
18673        })
18674        .unwrap()
18675        .await
18676        .downcast::<Editor>()
18677        .unwrap();
18678    pane_2
18679        .update_in(cx, |pane, window, cx| {
18680            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18681                .unwrap()
18682        })
18683        .await
18684        .unwrap();
18685    drop(editor_2);
18686    pane_2.update(cx, |pane, cx| {
18687        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18688        open_editor.update(cx, |editor, cx| {
18689            assert_eq!(
18690                editor.display_text(cx),
18691                lib_text,
18692                "Other file should be open and active in another panel too",
18693            );
18694        });
18695        assert_eq!(
18696            pane.items().count(),
18697            1,
18698            "No other editors should be open in another pane",
18699        );
18700    });
18701
18702    let _editor_1_reopened = workspace
18703        .update_in(cx, |workspace, window, cx| {
18704            workspace.open_path(
18705                (worktree_id, "main.rs"),
18706                Some(pane_1.downgrade()),
18707                true,
18708                window,
18709                cx,
18710            )
18711        })
18712        .unwrap()
18713        .await
18714        .downcast::<Editor>()
18715        .unwrap();
18716    let _editor_2_reopened = workspace
18717        .update_in(cx, |workspace, window, cx| {
18718            workspace.open_path(
18719                (worktree_id, "main.rs"),
18720                Some(pane_2.downgrade()),
18721                true,
18722                window,
18723                cx,
18724            )
18725        })
18726        .unwrap()
18727        .await
18728        .downcast::<Editor>()
18729        .unwrap();
18730    pane_1.update(cx, |pane, cx| {
18731        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18732        open_editor.update(cx, |editor, cx| {
18733            assert_eq!(
18734                editor.display_text(cx),
18735                main_text,
18736                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18737            );
18738            assert_eq!(
18739                editor
18740                    .selections
18741                    .all::<Point>(cx)
18742                    .into_iter()
18743                    .map(|s| s.range())
18744                    .collect::<Vec<_>>(),
18745                expected_ranges,
18746                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18747            );
18748        })
18749    });
18750    pane_2.update(cx, |pane, cx| {
18751        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18752        open_editor.update(cx, |editor, cx| {
18753            assert_eq!(
18754                editor.display_text(cx),
18755                r#"fn main() {
18756⋯rintln!("1");
18757⋯intln!("2");
18758⋯ntln!("3");
18759println!("4");
18760println!("5");
18761}"#,
18762                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18763            );
18764            assert_eq!(
18765                editor
18766                    .selections
18767                    .all::<Point>(cx)
18768                    .into_iter()
18769                    .map(|s| s.range())
18770                    .collect::<Vec<_>>(),
18771                vec![Point::zero()..Point::zero()],
18772                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18773            );
18774        })
18775    });
18776}
18777
18778#[gpui::test]
18779async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18780    init_test(cx, |_| {});
18781
18782    let fs = FakeFs::new(cx.executor());
18783    let main_text = r#"fn main() {
18784println!("1");
18785println!("2");
18786println!("3");
18787println!("4");
18788println!("5");
18789}"#;
18790    let lib_text = "mod foo {}";
18791    fs.insert_tree(
18792        path!("/a"),
18793        json!({
18794            "lib.rs": lib_text,
18795            "main.rs": main_text,
18796        }),
18797    )
18798    .await;
18799
18800    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18801    let (workspace, cx) =
18802        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18803    let worktree_id = workspace.update(cx, |workspace, cx| {
18804        workspace.project().update(cx, |project, cx| {
18805            project.worktrees(cx).next().unwrap().read(cx).id()
18806        })
18807    });
18808
18809    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18810    let editor = workspace
18811        .update_in(cx, |workspace, window, cx| {
18812            workspace.open_path(
18813                (worktree_id, "main.rs"),
18814                Some(pane.downgrade()),
18815                true,
18816                window,
18817                cx,
18818            )
18819        })
18820        .unwrap()
18821        .await
18822        .downcast::<Editor>()
18823        .unwrap();
18824    pane.update(cx, |pane, cx| {
18825        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18826        open_editor.update(cx, |editor, cx| {
18827            assert_eq!(
18828                editor.display_text(cx),
18829                main_text,
18830                "Original main.rs text on initial open",
18831            );
18832        })
18833    });
18834    editor.update_in(cx, |editor, window, cx| {
18835        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18836    });
18837
18838    cx.update_global(|store: &mut SettingsStore, cx| {
18839        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18840            s.restore_on_file_reopen = Some(false);
18841        });
18842    });
18843    editor.update_in(cx, |editor, window, cx| {
18844        editor.fold_ranges(
18845            vec![
18846                Point::new(1, 0)..Point::new(1, 1),
18847                Point::new(2, 0)..Point::new(2, 2),
18848                Point::new(3, 0)..Point::new(3, 3),
18849            ],
18850            false,
18851            window,
18852            cx,
18853        );
18854    });
18855    pane.update_in(cx, |pane, window, cx| {
18856        pane.close_all_items(&CloseAllItems::default(), window, cx)
18857            .unwrap()
18858    })
18859    .await
18860    .unwrap();
18861    pane.update(cx, |pane, _| {
18862        assert!(pane.active_item().is_none());
18863    });
18864    cx.update_global(|store: &mut SettingsStore, cx| {
18865        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18866            s.restore_on_file_reopen = Some(true);
18867        });
18868    });
18869
18870    let _editor_reopened = workspace
18871        .update_in(cx, |workspace, window, cx| {
18872            workspace.open_path(
18873                (worktree_id, "main.rs"),
18874                Some(pane.downgrade()),
18875                true,
18876                window,
18877                cx,
18878            )
18879        })
18880        .unwrap()
18881        .await
18882        .downcast::<Editor>()
18883        .unwrap();
18884    pane.update(cx, |pane, cx| {
18885        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18886        open_editor.update(cx, |editor, cx| {
18887            assert_eq!(
18888                editor.display_text(cx),
18889                main_text,
18890                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18891            );
18892        })
18893    });
18894}
18895
18896fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18897    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18898    point..point
18899}
18900
18901fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18902    let (text, ranges) = marked_text_ranges(marked_text, true);
18903    assert_eq!(editor.text(cx), text);
18904    assert_eq!(
18905        editor.selections.ranges(cx),
18906        ranges,
18907        "Assert selections are {}",
18908        marked_text
18909    );
18910}
18911
18912pub fn handle_signature_help_request(
18913    cx: &mut EditorLspTestContext,
18914    mocked_response: lsp::SignatureHelp,
18915) -> impl Future<Output = ()> + use<> {
18916    let mut request =
18917        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18918            let mocked_response = mocked_response.clone();
18919            async move { Ok(Some(mocked_response)) }
18920        });
18921
18922    async move {
18923        request.next().await;
18924    }
18925}
18926
18927/// Handle completion request passing a marked string specifying where the completion
18928/// should be triggered from using '|' character, what range should be replaced, and what completions
18929/// should be returned using '<' and '>' to delimit the range.
18930///
18931/// Also see `handle_completion_request_with_insert_and_replace`.
18932#[track_caller]
18933pub fn handle_completion_request(
18934    cx: &mut EditorLspTestContext,
18935    marked_string: &str,
18936    completions: Vec<&'static str>,
18937    counter: Arc<AtomicUsize>,
18938) -> impl Future<Output = ()> {
18939    let complete_from_marker: TextRangeMarker = '|'.into();
18940    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18941    let (_, mut marked_ranges) = marked_text_ranges_by(
18942        marked_string,
18943        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18944    );
18945
18946    let complete_from_position =
18947        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18948    let replace_range =
18949        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18950
18951    let mut request =
18952        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18953            let completions = completions.clone();
18954            counter.fetch_add(1, atomic::Ordering::Release);
18955            async move {
18956                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18957                assert_eq!(
18958                    params.text_document_position.position,
18959                    complete_from_position
18960                );
18961                Ok(Some(lsp::CompletionResponse::Array(
18962                    completions
18963                        .iter()
18964                        .map(|completion_text| lsp::CompletionItem {
18965                            label: completion_text.to_string(),
18966                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18967                                range: replace_range,
18968                                new_text: completion_text.to_string(),
18969                            })),
18970                            ..Default::default()
18971                        })
18972                        .collect(),
18973                )))
18974            }
18975        });
18976
18977    async move {
18978        request.next().await;
18979    }
18980}
18981
18982/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18983/// given instead, which also contains an `insert` range.
18984///
18985/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18986/// that is, `replace_range.start..cursor_pos`.
18987pub fn handle_completion_request_with_insert_and_replace(
18988    cx: &mut EditorLspTestContext,
18989    marked_string: &str,
18990    completions: Vec<&'static str>,
18991    counter: Arc<AtomicUsize>,
18992) -> impl Future<Output = ()> {
18993    let complete_from_marker: TextRangeMarker = '|'.into();
18994    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18995    let (_, mut marked_ranges) = marked_text_ranges_by(
18996        marked_string,
18997        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18998    );
18999
19000    let complete_from_position =
19001        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19002    let replace_range =
19003        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19004
19005    let mut request =
19006        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19007            let completions = completions.clone();
19008            counter.fetch_add(1, atomic::Ordering::Release);
19009            async move {
19010                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19011                assert_eq!(
19012                    params.text_document_position.position, complete_from_position,
19013                    "marker `|` position doesn't match",
19014                );
19015                Ok(Some(lsp::CompletionResponse::Array(
19016                    completions
19017                        .iter()
19018                        .map(|completion_text| lsp::CompletionItem {
19019                            label: completion_text.to_string(),
19020                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19021                                lsp::InsertReplaceEdit {
19022                                    insert: lsp::Range {
19023                                        start: replace_range.start,
19024                                        end: complete_from_position,
19025                                    },
19026                                    replace: replace_range,
19027                                    new_text: completion_text.to_string(),
19028                                },
19029                            )),
19030                            ..Default::default()
19031                        })
19032                        .collect(),
19033                )))
19034            }
19035        });
19036
19037    async move {
19038        request.next().await;
19039    }
19040}
19041
19042fn handle_resolve_completion_request(
19043    cx: &mut EditorLspTestContext,
19044    edits: Option<Vec<(&'static str, &'static str)>>,
19045) -> impl Future<Output = ()> {
19046    let edits = edits.map(|edits| {
19047        edits
19048            .iter()
19049            .map(|(marked_string, new_text)| {
19050                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19051                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19052                lsp::TextEdit::new(replace_range, new_text.to_string())
19053            })
19054            .collect::<Vec<_>>()
19055    });
19056
19057    let mut request =
19058        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19059            let edits = edits.clone();
19060            async move {
19061                Ok(lsp::CompletionItem {
19062                    additional_text_edits: edits,
19063                    ..Default::default()
19064                })
19065            }
19066        });
19067
19068    async move {
19069        request.next().await;
19070    }
19071}
19072
19073pub(crate) fn update_test_language_settings(
19074    cx: &mut TestAppContext,
19075    f: impl Fn(&mut AllLanguageSettingsContent),
19076) {
19077    cx.update(|cx| {
19078        SettingsStore::update_global(cx, |store, cx| {
19079            store.update_user_settings::<AllLanguageSettings>(cx, f);
19080        });
19081    });
19082}
19083
19084pub(crate) fn update_test_project_settings(
19085    cx: &mut TestAppContext,
19086    f: impl Fn(&mut ProjectSettings),
19087) {
19088    cx.update(|cx| {
19089        SettingsStore::update_global(cx, |store, cx| {
19090            store.update_user_settings::<ProjectSettings>(cx, f);
19091        });
19092    });
19093}
19094
19095pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19096    cx.update(|cx| {
19097        assets::Assets.load_test_fonts(cx);
19098        let store = SettingsStore::test(cx);
19099        cx.set_global(store);
19100        theme::init(theme::LoadThemes::JustBase, cx);
19101        release_channel::init(SemanticVersion::default(), cx);
19102        client::init_settings(cx);
19103        language::init(cx);
19104        Project::init_settings(cx);
19105        workspace::init_settings(cx);
19106        crate::init(cx);
19107    });
19108
19109    update_test_language_settings(cx, f);
19110}
19111
19112#[track_caller]
19113fn assert_hunk_revert(
19114    not_reverted_text_with_selections: &str,
19115    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19116    expected_reverted_text_with_selections: &str,
19117    base_text: &str,
19118    cx: &mut EditorLspTestContext,
19119) {
19120    cx.set_state(not_reverted_text_with_selections);
19121    cx.set_head_text(base_text);
19122    cx.executor().run_until_parked();
19123
19124    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19125        let snapshot = editor.snapshot(window, cx);
19126        let reverted_hunk_statuses = snapshot
19127            .buffer_snapshot
19128            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19129            .map(|hunk| hunk.status().kind)
19130            .collect::<Vec<_>>();
19131
19132        editor.git_restore(&Default::default(), window, cx);
19133        reverted_hunk_statuses
19134    });
19135    cx.executor().run_until_parked();
19136    cx.assert_editor_state(expected_reverted_text_with_selections);
19137    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19138}