editor_tests.rs

    1use super::*;
    2use crate::{
    3    scroll::scroll_amount::ScrollAmount,
    4    test::{
    5        assert_text_with_selections, build_editor,
    6        editor_lsp_test_context::{git_commit_lang, EditorLspTestContext},
    7        editor_test_context::EditorTestContext,
    8        select_ranges,
    9    },
   10    JoinLines,
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    div, BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions,
   17};
   18use indoc::indoc;
   19use language::{
   20    language_settings::{
   21        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   22        LanguageSettingsContent, PrettierSettings,
   23    },
   24    BracketPairConfig,
   25    Capability::ReadWrite,
   26    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   27    Override, Point,
   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    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   36    project_settings::{LspSettings, ProjectSettings},
   37    FakeFs,
   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::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
   51    uri,
   52};
   53use workspace::{
   54    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   55    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   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(cx: &mut TestAppContext) {
 2922    init_test(cx, |settings| {
 2923        settings.defaults.tab_size = NonZeroU32::new(4)
 2924    });
 2925
 2926    let language = Arc::new(
 2927        Language::new(
 2928            LanguageConfig::default(),
 2929            Some(tree_sitter_rust::LANGUAGE.into()),
 2930        )
 2931        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2932        .unwrap(),
 2933    );
 2934
 2935    let mut cx = EditorTestContext::new(cx).await;
 2936    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2937    cx.set_state(indoc! {"
 2938        fn a() {
 2939            if b {
 2940        \t ˇc
 2941            }
 2942        }
 2943    "});
 2944
 2945    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2946    cx.assert_editor_state(indoc! {"
 2947        fn a() {
 2948            if b {
 2949                ˇc
 2950            }
 2951        }
 2952    "});
 2953}
 2954
 2955#[gpui::test]
 2956async fn test_indent_outdent(cx: &mut TestAppContext) {
 2957    init_test(cx, |settings| {
 2958        settings.defaults.tab_size = NonZeroU32::new(4);
 2959    });
 2960
 2961    let mut cx = EditorTestContext::new(cx).await;
 2962
 2963    cx.set_state(indoc! {"
 2964          «oneˇ» «twoˇ»
 2965        three
 2966         four
 2967    "});
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970            «oneˇ» «twoˇ»
 2971        three
 2972         four
 2973    "});
 2974
 2975    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2976    cx.assert_editor_state(indoc! {"
 2977        «oneˇ» «twoˇ»
 2978        three
 2979         four
 2980    "});
 2981
 2982    // select across line ending
 2983    cx.set_state(indoc! {"
 2984        one two
 2985        t«hree
 2986        ˇ» four
 2987    "});
 2988    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2989    cx.assert_editor_state(indoc! {"
 2990        one two
 2991            t«hree
 2992        ˇ» four
 2993    "});
 2994
 2995    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2996    cx.assert_editor_state(indoc! {"
 2997        one two
 2998        t«hree
 2999        ˇ» four
 3000    "});
 3001
 3002    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3003    cx.set_state(indoc! {"
 3004        one two
 3005        ˇthree
 3006            four
 3007    "});
 3008    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3009    cx.assert_editor_state(indoc! {"
 3010        one two
 3011            ˇthree
 3012            four
 3013    "});
 3014
 3015    cx.set_state(indoc! {"
 3016        one two
 3017        ˇ    three
 3018            four
 3019    "});
 3020    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3021    cx.assert_editor_state(indoc! {"
 3022        one two
 3023        ˇthree
 3024            four
 3025    "});
 3026}
 3027
 3028#[gpui::test]
 3029async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3030    init_test(cx, |settings| {
 3031        settings.defaults.hard_tabs = Some(true);
 3032    });
 3033
 3034    let mut cx = EditorTestContext::new(cx).await;
 3035
 3036    // select two ranges on one line
 3037    cx.set_state(indoc! {"
 3038        «oneˇ» «twoˇ»
 3039        three
 3040        four
 3041    "});
 3042    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3043    cx.assert_editor_state(indoc! {"
 3044        \t«oneˇ» «twoˇ»
 3045        three
 3046        four
 3047    "});
 3048    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3049    cx.assert_editor_state(indoc! {"
 3050        \t\t«oneˇ» «twoˇ»
 3051        three
 3052        four
 3053    "});
 3054    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3055    cx.assert_editor_state(indoc! {"
 3056        \t«oneˇ» «twoˇ»
 3057        three
 3058        four
 3059    "});
 3060    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3061    cx.assert_editor_state(indoc! {"
 3062        «oneˇ» «twoˇ»
 3063        three
 3064        four
 3065    "});
 3066
 3067    // select across a line ending
 3068    cx.set_state(indoc! {"
 3069        one two
 3070        t«hree
 3071        ˇ»four
 3072    "});
 3073    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3074    cx.assert_editor_state(indoc! {"
 3075        one two
 3076        \tt«hree
 3077        ˇ»four
 3078    "});
 3079    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3080    cx.assert_editor_state(indoc! {"
 3081        one two
 3082        \t\tt«hree
 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        \tt«hree
 3089        ˇ»four
 3090    "});
 3091    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3092    cx.assert_editor_state(indoc! {"
 3093        one two
 3094        t«hree
 3095        ˇ»four
 3096    "});
 3097
 3098    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3099    cx.set_state(indoc! {"
 3100        one two
 3101        ˇthree
 3102        four
 3103    "});
 3104    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3105    cx.assert_editor_state(indoc! {"
 3106        one two
 3107        ˇthree
 3108        four
 3109    "});
 3110    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3111    cx.assert_editor_state(indoc! {"
 3112        one two
 3113        \tˇthree
 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        ˇthree
 3120        four
 3121    "});
 3122}
 3123
 3124#[gpui::test]
 3125fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3126    init_test(cx, |settings| {
 3127        settings.languages.extend([
 3128            (
 3129                "TOML".into(),
 3130                LanguageSettingsContent {
 3131                    tab_size: NonZeroU32::new(2),
 3132                    ..Default::default()
 3133                },
 3134            ),
 3135            (
 3136                "Rust".into(),
 3137                LanguageSettingsContent {
 3138                    tab_size: NonZeroU32::new(4),
 3139                    ..Default::default()
 3140                },
 3141            ),
 3142        ]);
 3143    });
 3144
 3145    let toml_language = Arc::new(Language::new(
 3146        LanguageConfig {
 3147            name: "TOML".into(),
 3148            ..Default::default()
 3149        },
 3150        None,
 3151    ));
 3152    let rust_language = Arc::new(Language::new(
 3153        LanguageConfig {
 3154            name: "Rust".into(),
 3155            ..Default::default()
 3156        },
 3157        None,
 3158    ));
 3159
 3160    let toml_buffer =
 3161        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3162    let rust_buffer =
 3163        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3164    let multibuffer = cx.new(|cx| {
 3165        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3166        multibuffer.push_excerpts(
 3167            toml_buffer.clone(),
 3168            [ExcerptRange {
 3169                context: Point::new(0, 0)..Point::new(2, 0),
 3170                primary: None,
 3171            }],
 3172            cx,
 3173        );
 3174        multibuffer.push_excerpts(
 3175            rust_buffer.clone(),
 3176            [ExcerptRange {
 3177                context: Point::new(0, 0)..Point::new(1, 0),
 3178                primary: None,
 3179            }],
 3180            cx,
 3181        );
 3182        multibuffer
 3183    });
 3184
 3185    cx.add_window(|window, cx| {
 3186        let mut editor = build_editor(multibuffer, window, cx);
 3187
 3188        assert_eq!(
 3189            editor.text(cx),
 3190            indoc! {"
 3191                a = 1
 3192                b = 2
 3193
 3194                const c: usize = 3;
 3195            "}
 3196        );
 3197
 3198        select_ranges(
 3199            &mut editor,
 3200            indoc! {"
 3201                «aˇ» = 1
 3202                b = 2
 3203
 3204                «const c:ˇ» usize = 3;
 3205            "},
 3206            window,
 3207            cx,
 3208        );
 3209
 3210        editor.tab(&Tab, window, cx);
 3211        assert_text_with_selections(
 3212            &mut editor,
 3213            indoc! {"
 3214                  «aˇ» = 1
 3215                b = 2
 3216
 3217                    «const c:ˇ» usize = 3;
 3218            "},
 3219            cx,
 3220        );
 3221        editor.backtab(&Backtab, window, cx);
 3222        assert_text_with_selections(
 3223            &mut editor,
 3224            indoc! {"
 3225                «aˇ» = 1
 3226                b = 2
 3227
 3228                «const c:ˇ» usize = 3;
 3229            "},
 3230            cx,
 3231        );
 3232
 3233        editor
 3234    });
 3235}
 3236
 3237#[gpui::test]
 3238async fn test_backspace(cx: &mut TestAppContext) {
 3239    init_test(cx, |_| {});
 3240
 3241    let mut cx = EditorTestContext::new(cx).await;
 3242
 3243    // Basic backspace
 3244    cx.set_state(indoc! {"
 3245        onˇe two three
 3246        fou«rˇ» five six
 3247        seven «ˇeight nine
 3248        »ten
 3249    "});
 3250    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3251    cx.assert_editor_state(indoc! {"
 3252        oˇe two three
 3253        fouˇ five six
 3254        seven ˇten
 3255    "});
 3256
 3257    // Test backspace inside and around indents
 3258    cx.set_state(indoc! {"
 3259        zero
 3260            ˇone
 3261                ˇtwo
 3262            ˇ ˇ ˇ  three
 3263        ˇ  ˇ  four
 3264    "});
 3265    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3266    cx.assert_editor_state(indoc! {"
 3267        zero
 3268        ˇone
 3269            ˇtwo
 3270        ˇ  threeˇ  four
 3271    "});
 3272}
 3273
 3274#[gpui::test]
 3275async fn test_delete(cx: &mut TestAppContext) {
 3276    init_test(cx, |_| {});
 3277
 3278    let mut cx = EditorTestContext::new(cx).await;
 3279    cx.set_state(indoc! {"
 3280        onˇe two three
 3281        fou«rˇ» five six
 3282        seven «ˇeight nine
 3283        »ten
 3284    "});
 3285    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3286    cx.assert_editor_state(indoc! {"
 3287        onˇ two three
 3288        fouˇ five six
 3289        seven ˇten
 3290    "});
 3291}
 3292
 3293#[gpui::test]
 3294fn test_delete_line(cx: &mut TestAppContext) {
 3295    init_test(cx, |_| {});
 3296
 3297    let editor = cx.add_window(|window, cx| {
 3298        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3299        build_editor(buffer, window, cx)
 3300    });
 3301    _ = editor.update(cx, |editor, window, cx| {
 3302        editor.change_selections(None, window, cx, |s| {
 3303            s.select_display_ranges([
 3304                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3305                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3306                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3307            ])
 3308        });
 3309        editor.delete_line(&DeleteLine, window, cx);
 3310        assert_eq!(editor.display_text(cx), "ghi");
 3311        assert_eq!(
 3312            editor.selections.display_ranges(cx),
 3313            vec![
 3314                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3315                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3316            ]
 3317        );
 3318    });
 3319
 3320    let editor = cx.add_window(|window, cx| {
 3321        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3322        build_editor(buffer, window, cx)
 3323    });
 3324    _ = editor.update(cx, |editor, window, cx| {
 3325        editor.change_selections(None, window, cx, |s| {
 3326            s.select_display_ranges([
 3327                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3328            ])
 3329        });
 3330        editor.delete_line(&DeleteLine, window, cx);
 3331        assert_eq!(editor.display_text(cx), "ghi\n");
 3332        assert_eq!(
 3333            editor.selections.display_ranges(cx),
 3334            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3335        );
 3336    });
 3337}
 3338
 3339#[gpui::test]
 3340fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3341    init_test(cx, |_| {});
 3342
 3343    cx.add_window(|window, cx| {
 3344        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3345        let mut editor = build_editor(buffer.clone(), window, cx);
 3346        let buffer = buffer.read(cx).as_singleton().unwrap();
 3347
 3348        assert_eq!(
 3349            editor.selections.ranges::<Point>(cx),
 3350            &[Point::new(0, 0)..Point::new(0, 0)]
 3351        );
 3352
 3353        // When on single line, replace newline at end by space
 3354        editor.join_lines(&JoinLines, window, cx);
 3355        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3356        assert_eq!(
 3357            editor.selections.ranges::<Point>(cx),
 3358            &[Point::new(0, 3)..Point::new(0, 3)]
 3359        );
 3360
 3361        // When multiple lines are selected, remove newlines that are spanned by the selection
 3362        editor.change_selections(None, window, cx, |s| {
 3363            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3364        });
 3365        editor.join_lines(&JoinLines, window, cx);
 3366        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3367        assert_eq!(
 3368            editor.selections.ranges::<Point>(cx),
 3369            &[Point::new(0, 11)..Point::new(0, 11)]
 3370        );
 3371
 3372        // Undo should be transactional
 3373        editor.undo(&Undo, 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, 5)..Point::new(2, 2)]
 3378        );
 3379
 3380        // When joining an empty line don't insert a space
 3381        editor.change_selections(None, window, cx, |s| {
 3382            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3383        });
 3384        editor.join_lines(&JoinLines, window, cx);
 3385        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3386        assert_eq!(
 3387            editor.selections.ranges::<Point>(cx),
 3388            [Point::new(2, 3)..Point::new(2, 3)]
 3389        );
 3390
 3391        // We can remove trailing newlines
 3392        editor.join_lines(&JoinLines, window, cx);
 3393        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3394        assert_eq!(
 3395            editor.selections.ranges::<Point>(cx),
 3396            [Point::new(2, 3)..Point::new(2, 3)]
 3397        );
 3398
 3399        // We don't blow up on the last line
 3400        editor.join_lines(&JoinLines, window, cx);
 3401        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3402        assert_eq!(
 3403            editor.selections.ranges::<Point>(cx),
 3404            [Point::new(2, 3)..Point::new(2, 3)]
 3405        );
 3406
 3407        // reset to test indentation
 3408        editor.buffer.update(cx, |buffer, cx| {
 3409            buffer.edit(
 3410                [
 3411                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3412                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3413                ],
 3414                None,
 3415                cx,
 3416            )
 3417        });
 3418
 3419        // We remove any leading spaces
 3420        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3421        editor.change_selections(None, window, cx, |s| {
 3422            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3423        });
 3424        editor.join_lines(&JoinLines, window, cx);
 3425        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3426
 3427        // We don't insert a space for a line containing only spaces
 3428        editor.join_lines(&JoinLines, window, cx);
 3429        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3430
 3431        // We ignore any leading tabs
 3432        editor.join_lines(&JoinLines, window, cx);
 3433        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3434
 3435        editor
 3436    });
 3437}
 3438
 3439#[gpui::test]
 3440fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3441    init_test(cx, |_| {});
 3442
 3443    cx.add_window(|window, cx| {
 3444        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3445        let mut editor = build_editor(buffer.clone(), window, cx);
 3446        let buffer = buffer.read(cx).as_singleton().unwrap();
 3447
 3448        editor.change_selections(None, window, cx, |s| {
 3449            s.select_ranges([
 3450                Point::new(0, 2)..Point::new(1, 1),
 3451                Point::new(1, 2)..Point::new(1, 2),
 3452                Point::new(3, 1)..Point::new(3, 2),
 3453            ])
 3454        });
 3455
 3456        editor.join_lines(&JoinLines, window, cx);
 3457        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3458
 3459        assert_eq!(
 3460            editor.selections.ranges::<Point>(cx),
 3461            [
 3462                Point::new(0, 7)..Point::new(0, 7),
 3463                Point::new(1, 3)..Point::new(1, 3)
 3464            ]
 3465        );
 3466        editor
 3467    });
 3468}
 3469
 3470#[gpui::test]
 3471async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3472    init_test(cx, |_| {});
 3473
 3474    let mut cx = EditorTestContext::new(cx).await;
 3475
 3476    let diff_base = r#"
 3477        Line 0
 3478        Line 1
 3479        Line 2
 3480        Line 3
 3481        "#
 3482    .unindent();
 3483
 3484    cx.set_state(
 3485        &r#"
 3486        ˇLine 0
 3487        Line 1
 3488        Line 2
 3489        Line 3
 3490        "#
 3491        .unindent(),
 3492    );
 3493
 3494    cx.set_head_text(&diff_base);
 3495    executor.run_until_parked();
 3496
 3497    // Join lines
 3498    cx.update_editor(|editor, window, cx| {
 3499        editor.join_lines(&JoinLines, window, cx);
 3500    });
 3501    executor.run_until_parked();
 3502
 3503    cx.assert_editor_state(
 3504        &r#"
 3505        Line 0ˇ Line 1
 3506        Line 2
 3507        Line 3
 3508        "#
 3509        .unindent(),
 3510    );
 3511    // Join again
 3512    cx.update_editor(|editor, window, cx| {
 3513        editor.join_lines(&JoinLines, window, cx);
 3514    });
 3515    executor.run_until_parked();
 3516
 3517    cx.assert_editor_state(
 3518        &r#"
 3519        Line 0 Line 1ˇ Line 2
 3520        Line 3
 3521        "#
 3522        .unindent(),
 3523    );
 3524}
 3525
 3526#[gpui::test]
 3527async fn test_custom_newlines_cause_no_false_positive_diffs(
 3528    executor: BackgroundExecutor,
 3529    cx: &mut TestAppContext,
 3530) {
 3531    init_test(cx, |_| {});
 3532    let mut cx = EditorTestContext::new(cx).await;
 3533    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3534    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3535    executor.run_until_parked();
 3536
 3537    cx.update_editor(|editor, window, cx| {
 3538        let snapshot = editor.snapshot(window, cx);
 3539        assert_eq!(
 3540            snapshot
 3541                .buffer_snapshot
 3542                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3543                .collect::<Vec<_>>(),
 3544            Vec::new(),
 3545            "Should not have any diffs for files with custom newlines"
 3546        );
 3547    });
 3548}
 3549
 3550#[gpui::test]
 3551async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3552    init_test(cx, |_| {});
 3553
 3554    let mut cx = EditorTestContext::new(cx).await;
 3555
 3556    // Test sort_lines_case_insensitive()
 3557    cx.set_state(indoc! {"
 3558        «z
 3559        y
 3560        x
 3561        Z
 3562        Y
 3563        Xˇ»
 3564    "});
 3565    cx.update_editor(|e, window, cx| {
 3566        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3567    });
 3568    cx.assert_editor_state(indoc! {"
 3569        «x
 3570        X
 3571        y
 3572        Y
 3573        z
 3574        Zˇ»
 3575    "});
 3576
 3577    // Test reverse_lines()
 3578    cx.set_state(indoc! {"
 3579        «5
 3580        4
 3581        3
 3582        2
 3583        1ˇ»
 3584    "});
 3585    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3586    cx.assert_editor_state(indoc! {"
 3587        «1
 3588        2
 3589        3
 3590        4
 3591        5ˇ»
 3592    "});
 3593
 3594    // Skip testing shuffle_line()
 3595
 3596    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3597    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3598
 3599    // Don't manipulate when cursor is on single line, but expand the selection
 3600    cx.set_state(indoc! {"
 3601        ddˇdd
 3602        ccc
 3603        bb
 3604        a
 3605    "});
 3606    cx.update_editor(|e, window, cx| {
 3607        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3608    });
 3609    cx.assert_editor_state(indoc! {"
 3610        «ddddˇ»
 3611        ccc
 3612        bb
 3613        a
 3614    "});
 3615
 3616    // Basic manipulate case
 3617    // Start selection moves to column 0
 3618    // End of selection shrinks to fit shorter line
 3619    cx.set_state(indoc! {"
 3620        dd«d
 3621        ccc
 3622        bb
 3623        aaaaaˇ»
 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        «aaaaa
 3630        bb
 3631        ccc
 3632        dddˇ»
 3633    "});
 3634
 3635    // Manipulate case with newlines
 3636    cx.set_state(indoc! {"
 3637        dd«d
 3638        ccc
 3639
 3640        bb
 3641        aaaaa
 3642
 3643        ˇ»
 3644    "});
 3645    cx.update_editor(|e, window, cx| {
 3646        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3647    });
 3648    cx.assert_editor_state(indoc! {"
 3649        «
 3650
 3651        aaaaa
 3652        bb
 3653        ccc
 3654        dddˇ»
 3655
 3656    "});
 3657
 3658    // Adding new line
 3659    cx.set_state(indoc! {"
 3660        aa«a
 3661        bbˇ»b
 3662    "});
 3663    cx.update_editor(|e, window, cx| {
 3664        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3665    });
 3666    cx.assert_editor_state(indoc! {"
 3667        «aaa
 3668        bbb
 3669        added_lineˇ»
 3670    "});
 3671
 3672    // Removing line
 3673    cx.set_state(indoc! {"
 3674        aa«a
 3675        bbbˇ»
 3676    "});
 3677    cx.update_editor(|e, window, cx| {
 3678        e.manipulate_lines(window, cx, |lines| {
 3679            lines.pop();
 3680        })
 3681    });
 3682    cx.assert_editor_state(indoc! {"
 3683        «aaaˇ»
 3684    "});
 3685
 3686    // Removing all lines
 3687    cx.set_state(indoc! {"
 3688        aa«a
 3689        bbbˇ»
 3690    "});
 3691    cx.update_editor(|e, window, cx| {
 3692        e.manipulate_lines(window, cx, |lines| {
 3693            lines.drain(..);
 3694        })
 3695    });
 3696    cx.assert_editor_state(indoc! {"
 3697        ˇ
 3698    "});
 3699}
 3700
 3701#[gpui::test]
 3702async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3703    init_test(cx, |_| {});
 3704
 3705    let mut cx = EditorTestContext::new(cx).await;
 3706
 3707    // Consider continuous selection as single selection
 3708    cx.set_state(indoc! {"
 3709        Aaa«aa
 3710        cˇ»c«c
 3711        bb
 3712        aaaˇ»aa
 3713    "});
 3714    cx.update_editor(|e, window, cx| {
 3715        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3716    });
 3717    cx.assert_editor_state(indoc! {"
 3718        «Aaaaa
 3719        ccc
 3720        bb
 3721        aaaaaˇ»
 3722    "});
 3723
 3724    cx.set_state(indoc! {"
 3725        Aaa«aa
 3726        cˇ»c«c
 3727        bb
 3728        aaaˇ»aa
 3729    "});
 3730    cx.update_editor(|e, window, cx| {
 3731        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3732    });
 3733    cx.assert_editor_state(indoc! {"
 3734        «Aaaaa
 3735        ccc
 3736        bbˇ»
 3737    "});
 3738
 3739    // Consider non continuous selection as distinct dedup operations
 3740    cx.set_state(indoc! {"
 3741        «aaaaa
 3742        bb
 3743        aaaaa
 3744        aaaaaˇ»
 3745
 3746        aaa«aaˇ»
 3747    "});
 3748    cx.update_editor(|e, window, cx| {
 3749        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3750    });
 3751    cx.assert_editor_state(indoc! {"
 3752        «aaaaa
 3753        bbˇ»
 3754
 3755        «aaaaaˇ»
 3756    "});
 3757}
 3758
 3759#[gpui::test]
 3760async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3761    init_test(cx, |_| {});
 3762
 3763    let mut cx = EditorTestContext::new(cx).await;
 3764
 3765    cx.set_state(indoc! {"
 3766        «Aaa
 3767        aAa
 3768        Aaaˇ»
 3769    "});
 3770    cx.update_editor(|e, window, cx| {
 3771        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3772    });
 3773    cx.assert_editor_state(indoc! {"
 3774        «Aaa
 3775        aAaˇ»
 3776    "});
 3777
 3778    cx.set_state(indoc! {"
 3779        «Aaa
 3780        aAa
 3781        aaAˇ»
 3782    "});
 3783    cx.update_editor(|e, window, cx| {
 3784        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3785    });
 3786    cx.assert_editor_state(indoc! {"
 3787        «Aaaˇ»
 3788    "});
 3789}
 3790
 3791#[gpui::test]
 3792async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3793    init_test(cx, |_| {});
 3794
 3795    let mut cx = EditorTestContext::new(cx).await;
 3796
 3797    // Manipulate with multiple selections on a single line
 3798    cx.set_state(indoc! {"
 3799        dd«dd
 3800        cˇ»c«c
 3801        bb
 3802        aaaˇ»aa
 3803    "});
 3804    cx.update_editor(|e, window, cx| {
 3805        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3806    });
 3807    cx.assert_editor_state(indoc! {"
 3808        «aaaaa
 3809        bb
 3810        ccc
 3811        ddddˇ»
 3812    "});
 3813
 3814    // Manipulate with multiple disjoin selections
 3815    cx.set_state(indoc! {"
 3816 3817        4
 3818        3
 3819        2
 3820        1ˇ»
 3821
 3822        dd«dd
 3823        ccc
 3824        bb
 3825        aaaˇ»aa
 3826    "});
 3827    cx.update_editor(|e, window, cx| {
 3828        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3829    });
 3830    cx.assert_editor_state(indoc! {"
 3831        «1
 3832        2
 3833        3
 3834        4
 3835        5ˇ»
 3836
 3837        «aaaaa
 3838        bb
 3839        ccc
 3840        ddddˇ»
 3841    "});
 3842
 3843    // Adding lines on each selection
 3844    cx.set_state(indoc! {"
 3845 3846        1ˇ»
 3847
 3848        bb«bb
 3849        aaaˇ»aa
 3850    "});
 3851    cx.update_editor(|e, window, cx| {
 3852        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3853    });
 3854    cx.assert_editor_state(indoc! {"
 3855        «2
 3856        1
 3857        added lineˇ»
 3858
 3859        «bbbb
 3860        aaaaa
 3861        added lineˇ»
 3862    "});
 3863
 3864    // Removing lines on each selection
 3865    cx.set_state(indoc! {"
 3866 3867        1ˇ»
 3868
 3869        bb«bb
 3870        aaaˇ»aa
 3871    "});
 3872    cx.update_editor(|e, window, cx| {
 3873        e.manipulate_lines(window, cx, |lines| {
 3874            lines.pop();
 3875        })
 3876    });
 3877    cx.assert_editor_state(indoc! {"
 3878        «2ˇ»
 3879
 3880        «bbbbˇ»
 3881    "});
 3882}
 3883
 3884#[gpui::test]
 3885async fn test_manipulate_text(cx: &mut TestAppContext) {
 3886    init_test(cx, |_| {});
 3887
 3888    let mut cx = EditorTestContext::new(cx).await;
 3889
 3890    // Test convert_to_upper_case()
 3891    cx.set_state(indoc! {"
 3892        «hello worldˇ»
 3893    "});
 3894    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3895    cx.assert_editor_state(indoc! {"
 3896        «HELLO WORLDˇ»
 3897    "});
 3898
 3899    // Test convert_to_lower_case()
 3900    cx.set_state(indoc! {"
 3901        «HELLO WORLDˇ»
 3902    "});
 3903    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3904    cx.assert_editor_state(indoc! {"
 3905        «hello worldˇ»
 3906    "});
 3907
 3908    // Test multiple line, single selection case
 3909    cx.set_state(indoc! {"
 3910        «The quick brown
 3911        fox jumps over
 3912        the lazy dogˇ»
 3913    "});
 3914    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3915    cx.assert_editor_state(indoc! {"
 3916        «The Quick Brown
 3917        Fox Jumps Over
 3918        The Lazy Dogˇ»
 3919    "});
 3920
 3921    // Test multiple line, single selection case
 3922    cx.set_state(indoc! {"
 3923        «The quick brown
 3924        fox jumps over
 3925        the lazy dogˇ»
 3926    "});
 3927    cx.update_editor(|e, window, cx| {
 3928        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3929    });
 3930    cx.assert_editor_state(indoc! {"
 3931        «TheQuickBrown
 3932        FoxJumpsOver
 3933        TheLazyDogˇ»
 3934    "});
 3935
 3936    // From here on out, test more complex cases of manipulate_text()
 3937
 3938    // Test no selection case - should affect words cursors are in
 3939    // Cursor at beginning, middle, and end of word
 3940    cx.set_state(indoc! {"
 3941        ˇhello big beauˇtiful worldˇ
 3942    "});
 3943    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3944    cx.assert_editor_state(indoc! {"
 3945        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3946    "});
 3947
 3948    // Test multiple selections on a single line and across multiple lines
 3949    cx.set_state(indoc! {"
 3950        «Theˇ» quick «brown
 3951        foxˇ» jumps «overˇ»
 3952        the «lazyˇ» dog
 3953    "});
 3954    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3955    cx.assert_editor_state(indoc! {"
 3956        «THEˇ» quick «BROWN
 3957        FOXˇ» jumps «OVERˇ»
 3958        the «LAZYˇ» dog
 3959    "});
 3960
 3961    // Test case where text length grows
 3962    cx.set_state(indoc! {"
 3963        «tschüߡ»
 3964    "});
 3965    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3966    cx.assert_editor_state(indoc! {"
 3967        «TSCHÜSSˇ»
 3968    "});
 3969
 3970    // Test to make sure we don't crash when text shrinks
 3971    cx.set_state(indoc! {"
 3972        aaa_bbbˇ
 3973    "});
 3974    cx.update_editor(|e, window, cx| {
 3975        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3976    });
 3977    cx.assert_editor_state(indoc! {"
 3978        «aaaBbbˇ»
 3979    "});
 3980
 3981    // Test to make sure we all aware of the fact that each word can grow and shrink
 3982    // Final selections should be aware of this fact
 3983    cx.set_state(indoc! {"
 3984        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3985    "});
 3986    cx.update_editor(|e, window, cx| {
 3987        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3988    });
 3989    cx.assert_editor_state(indoc! {"
 3990        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3991    "});
 3992
 3993    cx.set_state(indoc! {"
 3994        «hElLo, WoRld!ˇ»
 3995    "});
 3996    cx.update_editor(|e, window, cx| {
 3997        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3998    });
 3999    cx.assert_editor_state(indoc! {"
 4000        «HeLlO, wOrLD!ˇ»
 4001    "});
 4002}
 4003
 4004#[gpui::test]
 4005fn test_duplicate_line(cx: &mut TestAppContext) {
 4006    init_test(cx, |_| {});
 4007
 4008    let editor = cx.add_window(|window, cx| {
 4009        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4010        build_editor(buffer, window, cx)
 4011    });
 4012    _ = editor.update(cx, |editor, window, cx| {
 4013        editor.change_selections(None, window, cx, |s| {
 4014            s.select_display_ranges([
 4015                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4016                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4017                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4018                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4019            ])
 4020        });
 4021        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4022        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4023        assert_eq!(
 4024            editor.selections.display_ranges(cx),
 4025            vec![
 4026                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4027                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4028                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4029                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4030            ]
 4031        );
 4032    });
 4033
 4034    let editor = cx.add_window(|window, cx| {
 4035        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4036        build_editor(buffer, window, cx)
 4037    });
 4038    _ = editor.update(cx, |editor, window, cx| {
 4039        editor.change_selections(None, window, cx, |s| {
 4040            s.select_display_ranges([
 4041                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4042                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4043            ])
 4044        });
 4045        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4046        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4047        assert_eq!(
 4048            editor.selections.display_ranges(cx),
 4049            vec![
 4050                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4051                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4052            ]
 4053        );
 4054    });
 4055
 4056    // With `move_upwards` the selections stay in place, except for
 4057    // the lines inserted above them
 4058    let editor = cx.add_window(|window, cx| {
 4059        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4060        build_editor(buffer, window, cx)
 4061    });
 4062    _ = editor.update(cx, |editor, window, cx| {
 4063        editor.change_selections(None, window, cx, |s| {
 4064            s.select_display_ranges([
 4065                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4066                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4067                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4068                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4069            ])
 4070        });
 4071        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4072        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4073        assert_eq!(
 4074            editor.selections.display_ranges(cx),
 4075            vec![
 4076                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4077                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4078                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4079                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4080            ]
 4081        );
 4082    });
 4083
 4084    let editor = cx.add_window(|window, cx| {
 4085        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4086        build_editor(buffer, window, cx)
 4087    });
 4088    _ = editor.update(cx, |editor, window, cx| {
 4089        editor.change_selections(None, window, cx, |s| {
 4090            s.select_display_ranges([
 4091                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4092                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4093            ])
 4094        });
 4095        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4096        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4097        assert_eq!(
 4098            editor.selections.display_ranges(cx),
 4099            vec![
 4100                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4101                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4102            ]
 4103        );
 4104    });
 4105
 4106    let editor = cx.add_window(|window, cx| {
 4107        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4108        build_editor(buffer, window, cx)
 4109    });
 4110    _ = editor.update(cx, |editor, window, cx| {
 4111        editor.change_selections(None, window, cx, |s| {
 4112            s.select_display_ranges([
 4113                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4114                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4115            ])
 4116        });
 4117        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4118        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4119        assert_eq!(
 4120            editor.selections.display_ranges(cx),
 4121            vec![
 4122                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4123                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4124            ]
 4125        );
 4126    });
 4127}
 4128
 4129#[gpui::test]
 4130fn test_move_line_up_down(cx: &mut TestAppContext) {
 4131    init_test(cx, |_| {});
 4132
 4133    let editor = cx.add_window(|window, cx| {
 4134        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4135        build_editor(buffer, window, cx)
 4136    });
 4137    _ = editor.update(cx, |editor, window, cx| {
 4138        editor.fold_creases(
 4139            vec![
 4140                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4141                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4142                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4143            ],
 4144            true,
 4145            window,
 4146            cx,
 4147        );
 4148        editor.change_selections(None, window, cx, |s| {
 4149            s.select_display_ranges([
 4150                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4151                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4152                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4153                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4154            ])
 4155        });
 4156        assert_eq!(
 4157            editor.display_text(cx),
 4158            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4159        );
 4160
 4161        editor.move_line_up(&MoveLineUp, window, cx);
 4162        assert_eq!(
 4163            editor.display_text(cx),
 4164            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4165        );
 4166        assert_eq!(
 4167            editor.selections.display_ranges(cx),
 4168            vec![
 4169                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4170                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4171                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4172                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4173            ]
 4174        );
 4175    });
 4176
 4177    _ = editor.update(cx, |editor, window, cx| {
 4178        editor.move_line_down(&MoveLineDown, window, cx);
 4179        assert_eq!(
 4180            editor.display_text(cx),
 4181            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4182        );
 4183        assert_eq!(
 4184            editor.selections.display_ranges(cx),
 4185            vec![
 4186                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4187                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4188                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4189                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4190            ]
 4191        );
 4192    });
 4193
 4194    _ = editor.update(cx, |editor, window, cx| {
 4195        editor.move_line_down(&MoveLineDown, window, cx);
 4196        assert_eq!(
 4197            editor.display_text(cx),
 4198            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4199        );
 4200        assert_eq!(
 4201            editor.selections.display_ranges(cx),
 4202            vec![
 4203                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4204                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4205                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4206                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4207            ]
 4208        );
 4209    });
 4210
 4211    _ = editor.update(cx, |editor, window, cx| {
 4212        editor.move_line_up(&MoveLineUp, window, cx);
 4213        assert_eq!(
 4214            editor.display_text(cx),
 4215            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4216        );
 4217        assert_eq!(
 4218            editor.selections.display_ranges(cx),
 4219            vec![
 4220                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4221                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4222                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4223                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4224            ]
 4225        );
 4226    });
 4227}
 4228
 4229#[gpui::test]
 4230fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4231    init_test(cx, |_| {});
 4232
 4233    let editor = cx.add_window(|window, cx| {
 4234        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4235        build_editor(buffer, window, cx)
 4236    });
 4237    _ = editor.update(cx, |editor, window, cx| {
 4238        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4239        editor.insert_blocks(
 4240            [BlockProperties {
 4241                style: BlockStyle::Fixed,
 4242                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4243                height: 1,
 4244                render: Arc::new(|_| div().into_any()),
 4245                priority: 0,
 4246            }],
 4247            Some(Autoscroll::fit()),
 4248            cx,
 4249        );
 4250        editor.change_selections(None, window, cx, |s| {
 4251            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4252        });
 4253        editor.move_line_down(&MoveLineDown, window, cx);
 4254    });
 4255}
 4256
 4257#[gpui::test]
 4258async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4259    init_test(cx, |_| {});
 4260
 4261    let mut cx = EditorTestContext::new(cx).await;
 4262    cx.set_state(
 4263        &"
 4264            ˇzero
 4265            one
 4266            two
 4267            three
 4268            four
 4269            five
 4270        "
 4271        .unindent(),
 4272    );
 4273
 4274    // Create a four-line block that replaces three lines of text.
 4275    cx.update_editor(|editor, window, cx| {
 4276        let snapshot = editor.snapshot(window, cx);
 4277        let snapshot = &snapshot.buffer_snapshot;
 4278        let placement = BlockPlacement::Replace(
 4279            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4280        );
 4281        editor.insert_blocks(
 4282            [BlockProperties {
 4283                placement,
 4284                height: 4,
 4285                style: BlockStyle::Sticky,
 4286                render: Arc::new(|_| gpui::div().into_any_element()),
 4287                priority: 0,
 4288            }],
 4289            None,
 4290            cx,
 4291        );
 4292    });
 4293
 4294    // Move down so that the cursor touches the block.
 4295    cx.update_editor(|editor, window, cx| {
 4296        editor.move_down(&Default::default(), window, cx);
 4297    });
 4298    cx.assert_editor_state(
 4299        &"
 4300            zero
 4301            «one
 4302            two
 4303            threeˇ»
 4304            four
 4305            five
 4306        "
 4307        .unindent(),
 4308    );
 4309
 4310    // Move down past the block.
 4311    cx.update_editor(|editor, window, cx| {
 4312        editor.move_down(&Default::default(), window, cx);
 4313    });
 4314    cx.assert_editor_state(
 4315        &"
 4316            zero
 4317            one
 4318            two
 4319            three
 4320            ˇfour
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325}
 4326
 4327#[gpui::test]
 4328fn test_transpose(cx: &mut TestAppContext) {
 4329    init_test(cx, |_| {});
 4330
 4331    _ = cx.add_window(|window, cx| {
 4332        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4333        editor.set_style(EditorStyle::default(), window, cx);
 4334        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4335        editor.transpose(&Default::default(), window, cx);
 4336        assert_eq!(editor.text(cx), "bac");
 4337        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4338
 4339        editor.transpose(&Default::default(), window, cx);
 4340        assert_eq!(editor.text(cx), "bca");
 4341        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4342
 4343        editor.transpose(&Default::default(), window, cx);
 4344        assert_eq!(editor.text(cx), "bac");
 4345        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4346
 4347        editor
 4348    });
 4349
 4350    _ = cx.add_window(|window, cx| {
 4351        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4352        editor.set_style(EditorStyle::default(), window, cx);
 4353        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4354        editor.transpose(&Default::default(), window, cx);
 4355        assert_eq!(editor.text(cx), "acb\nde");
 4356        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4357
 4358        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4359        editor.transpose(&Default::default(), window, cx);
 4360        assert_eq!(editor.text(cx), "acbd\ne");
 4361        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4362
 4363        editor.transpose(&Default::default(), window, cx);
 4364        assert_eq!(editor.text(cx), "acbde\n");
 4365        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4366
 4367        editor.transpose(&Default::default(), window, cx);
 4368        assert_eq!(editor.text(cx), "acbd\ne");
 4369        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4370
 4371        editor
 4372    });
 4373
 4374    _ = cx.add_window(|window, cx| {
 4375        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4376        editor.set_style(EditorStyle::default(), window, cx);
 4377        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4378        editor.transpose(&Default::default(), window, cx);
 4379        assert_eq!(editor.text(cx), "bacd\ne");
 4380        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4381
 4382        editor.transpose(&Default::default(), window, cx);
 4383        assert_eq!(editor.text(cx), "bcade\n");
 4384        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4385
 4386        editor.transpose(&Default::default(), window, cx);
 4387        assert_eq!(editor.text(cx), "bcda\ne");
 4388        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4389
 4390        editor.transpose(&Default::default(), window, cx);
 4391        assert_eq!(editor.text(cx), "bcade\n");
 4392        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4393
 4394        editor.transpose(&Default::default(), window, cx);
 4395        assert_eq!(editor.text(cx), "bcaed\n");
 4396        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4397
 4398        editor
 4399    });
 4400
 4401    _ = cx.add_window(|window, cx| {
 4402        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4403        editor.set_style(EditorStyle::default(), window, cx);
 4404        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4405        editor.transpose(&Default::default(), window, cx);
 4406        assert_eq!(editor.text(cx), "🏀🍐✋");
 4407        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4408
 4409        editor.transpose(&Default::default(), window, cx);
 4410        assert_eq!(editor.text(cx), "🏀✋🍐");
 4411        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4412
 4413        editor.transpose(&Default::default(), window, cx);
 4414        assert_eq!(editor.text(cx), "🏀🍐✋");
 4415        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4416
 4417        editor
 4418    });
 4419}
 4420
 4421#[gpui::test]
 4422async fn test_rewrap(cx: &mut TestAppContext) {
 4423    init_test(cx, |settings| {
 4424        settings.languages.extend([
 4425            (
 4426                "Markdown".into(),
 4427                LanguageSettingsContent {
 4428                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4429                    ..Default::default()
 4430                },
 4431            ),
 4432            (
 4433                "Plain Text".into(),
 4434                LanguageSettingsContent {
 4435                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4436                    ..Default::default()
 4437                },
 4438            ),
 4439        ])
 4440    });
 4441
 4442    let mut cx = EditorTestContext::new(cx).await;
 4443
 4444    let language_with_c_comments = Arc::new(Language::new(
 4445        LanguageConfig {
 4446            line_comments: vec!["// ".into()],
 4447            ..LanguageConfig::default()
 4448        },
 4449        None,
 4450    ));
 4451    let language_with_pound_comments = Arc::new(Language::new(
 4452        LanguageConfig {
 4453            line_comments: vec!["# ".into()],
 4454            ..LanguageConfig::default()
 4455        },
 4456        None,
 4457    ));
 4458    let markdown_language = Arc::new(Language::new(
 4459        LanguageConfig {
 4460            name: "Markdown".into(),
 4461            ..LanguageConfig::default()
 4462        },
 4463        None,
 4464    ));
 4465    let language_with_doc_comments = Arc::new(Language::new(
 4466        LanguageConfig {
 4467            line_comments: vec!["// ".into(), "/// ".into()],
 4468            ..LanguageConfig::default()
 4469        },
 4470        Some(tree_sitter_rust::LANGUAGE.into()),
 4471    ));
 4472
 4473    let plaintext_language = Arc::new(Language::new(
 4474        LanguageConfig {
 4475            name: "Plain Text".into(),
 4476            ..LanguageConfig::default()
 4477        },
 4478        None,
 4479    ));
 4480
 4481    assert_rewrap(
 4482        indoc! {"
 4483            // ˇ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.
 4484        "},
 4485        indoc! {"
 4486            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4487            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4488            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4489            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4490            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4491            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4492            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4493            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4494            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4495            // porttitor id. Aliquam id accumsan eros.
 4496        "},
 4497        language_with_c_comments.clone(),
 4498        &mut cx,
 4499    );
 4500
 4501    // Test that rewrapping works inside of a selection
 4502    assert_rewrap(
 4503        indoc! {"
 4504            «// 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.ˇ»
 4505        "},
 4506        indoc! {"
 4507            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4508            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4509            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4510            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4511            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4512            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4513            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4514            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4515            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4516            // porttitor id. Aliquam id accumsan eros.ˇ»
 4517        "},
 4518        language_with_c_comments.clone(),
 4519        &mut cx,
 4520    );
 4521
 4522    // Test that cursors that expand to the same region are collapsed.
 4523    assert_rewrap(
 4524        indoc! {"
 4525            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4526            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4527            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4528            // ˇ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.
 4529        "},
 4530        indoc! {"
 4531            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4532            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4533            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4534            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4535            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4536            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4537            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4538            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4539            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4540            // porttitor id. Aliquam id accumsan eros.
 4541        "},
 4542        language_with_c_comments.clone(),
 4543        &mut cx,
 4544    );
 4545
 4546    // Test that non-contiguous selections are treated separately.
 4547    assert_rewrap(
 4548        indoc! {"
 4549            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4550            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4551            //
 4552            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4553            // ˇ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.
 4554        "},
 4555        indoc! {"
 4556            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4557            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4558            // auctor, eu lacinia sapien scelerisque.
 4559            //
 4560            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4561            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4562            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4563            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4564            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4565            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4566            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4567        "},
 4568        language_with_c_comments.clone(),
 4569        &mut cx,
 4570    );
 4571
 4572    // Test that different comment prefixes are supported.
 4573    assert_rewrap(
 4574        indoc! {"
 4575            # ˇ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.
 4576        "},
 4577        indoc! {"
 4578            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4579            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4580            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4581            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4582            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4583            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4584            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4585            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4586            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4587            # accumsan eros.
 4588        "},
 4589        language_with_pound_comments.clone(),
 4590        &mut cx,
 4591    );
 4592
 4593    // Test that rewrapping is ignored outside of comments in most languages.
 4594    assert_rewrap(
 4595        indoc! {"
 4596            /// Adds two numbers.
 4597            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4598            fn add(a: u32, b: u32) -> u32 {
 4599                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ˇ
 4600            }
 4601        "},
 4602        indoc! {"
 4603            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4604            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4605            fn add(a: u32, b: u32) -> u32 {
 4606                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ˇ
 4607            }
 4608        "},
 4609        language_with_doc_comments.clone(),
 4610        &mut cx,
 4611    );
 4612
 4613    // Test that rewrapping works in Markdown and Plain Text languages.
 4614    assert_rewrap(
 4615        indoc! {"
 4616            # Hello
 4617
 4618            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.
 4619        "},
 4620        indoc! {"
 4621            # Hello
 4622
 4623            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4624            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4625            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4626            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4627            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4628            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4629            Integer sit amet scelerisque nisi.
 4630        "},
 4631        markdown_language,
 4632        &mut cx,
 4633    );
 4634
 4635    assert_rewrap(
 4636        indoc! {"
 4637            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.
 4638        "},
 4639        indoc! {"
 4640            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4641            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4642            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4643            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4644            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4645            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4646            Integer sit amet scelerisque nisi.
 4647        "},
 4648        plaintext_language,
 4649        &mut cx,
 4650    );
 4651
 4652    // Test rewrapping unaligned comments in a selection.
 4653    assert_rewrap(
 4654        indoc! {"
 4655            fn foo() {
 4656                if true {
 4657            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4658            // Praesent semper egestas tellus id dignissim.ˇ»
 4659                    do_something();
 4660                } else {
 4661                    //
 4662                }
 4663            }
 4664        "},
 4665        indoc! {"
 4666            fn foo() {
 4667                if true {
 4668            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4669                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4670                    // egestas tellus id dignissim.ˇ»
 4671                    do_something();
 4672                } else {
 4673                    //
 4674                }
 4675            }
 4676        "},
 4677        language_with_doc_comments.clone(),
 4678        &mut cx,
 4679    );
 4680
 4681    assert_rewrap(
 4682        indoc! {"
 4683            fn foo() {
 4684                if true {
 4685            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4686            // Praesent semper egestas tellus id dignissim.»
 4687                    do_something();
 4688                } else {
 4689                    //
 4690                }
 4691
 4692            }
 4693        "},
 4694        indoc! {"
 4695            fn foo() {
 4696                if true {
 4697            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4698                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4699                    // egestas tellus id dignissim.»
 4700                    do_something();
 4701                } else {
 4702                    //
 4703                }
 4704
 4705            }
 4706        "},
 4707        language_with_doc_comments.clone(),
 4708        &mut cx,
 4709    );
 4710
 4711    #[track_caller]
 4712    fn assert_rewrap(
 4713        unwrapped_text: &str,
 4714        wrapped_text: &str,
 4715        language: Arc<Language>,
 4716        cx: &mut EditorTestContext,
 4717    ) {
 4718        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4719        cx.set_state(unwrapped_text);
 4720        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4721        cx.assert_editor_state(wrapped_text);
 4722    }
 4723}
 4724
 4725#[gpui::test]
 4726async fn test_hard_wrap(cx: &mut TestAppContext) {
 4727    init_test(cx, |_| {});
 4728    let mut cx = EditorTestContext::new(cx).await;
 4729
 4730    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4731    cx.update_editor(|editor, _, cx| {
 4732        editor.set_hard_wrap(Some(14), cx);
 4733    });
 4734
 4735    cx.set_state(indoc!(
 4736        "
 4737        one two three ˇ
 4738        "
 4739    ));
 4740    cx.simulate_input("four");
 4741    cx.run_until_parked();
 4742
 4743    cx.assert_editor_state(indoc!(
 4744        "
 4745        one two three
 4746        fourˇ
 4747        "
 4748    ));
 4749
 4750    cx.update_editor(|editor, window, cx| {
 4751        editor.newline(&Default::default(), window, cx);
 4752    });
 4753    cx.run_until_parked();
 4754    cx.assert_editor_state(indoc!(
 4755        "
 4756        one two three
 4757        four
 4758        ˇ
 4759        "
 4760    ));
 4761
 4762    cx.simulate_input("five");
 4763    cx.run_until_parked();
 4764    cx.assert_editor_state(indoc!(
 4765        "
 4766        one two three
 4767        four
 4768        fiveˇ
 4769        "
 4770    ));
 4771
 4772    cx.update_editor(|editor, window, cx| {
 4773        editor.newline(&Default::default(), window, cx);
 4774    });
 4775    cx.run_until_parked();
 4776    cx.simulate_input("# ");
 4777    cx.run_until_parked();
 4778    cx.assert_editor_state(indoc!(
 4779        "
 4780        one two three
 4781        four
 4782        five
 4783        # ˇ
 4784        "
 4785    ));
 4786
 4787    cx.update_editor(|editor, window, cx| {
 4788        editor.newline(&Default::default(), window, cx);
 4789    });
 4790    cx.run_until_parked();
 4791    cx.assert_editor_state(indoc!(
 4792        "
 4793        one two three
 4794        four
 4795        five
 4796        #\x20
 4797 4798        "
 4799    ));
 4800
 4801    cx.simulate_input(" 6");
 4802    cx.run_until_parked();
 4803    cx.assert_editor_state(indoc!(
 4804        "
 4805        one two three
 4806        four
 4807        five
 4808        #
 4809        # 6ˇ
 4810        "
 4811    ));
 4812}
 4813
 4814#[gpui::test]
 4815async fn test_clipboard(cx: &mut TestAppContext) {
 4816    init_test(cx, |_| {});
 4817
 4818    let mut cx = EditorTestContext::new(cx).await;
 4819
 4820    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4821    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4822    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4823
 4824    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4825    cx.set_state("two ˇfour ˇsix ˇ");
 4826    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4827    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4828
 4829    // Paste again but with only two cursors. Since the number of cursors doesn't
 4830    // match the number of slices in the clipboard, the entire clipboard text
 4831    // is pasted at each cursor.
 4832    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4833    cx.update_editor(|e, window, cx| {
 4834        e.handle_input("( ", window, cx);
 4835        e.paste(&Paste, window, cx);
 4836        e.handle_input(") ", window, cx);
 4837    });
 4838    cx.assert_editor_state(
 4839        &([
 4840            "( one✅ ",
 4841            "three ",
 4842            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4843            "three ",
 4844            "five ) ˇ",
 4845        ]
 4846        .join("\n")),
 4847    );
 4848
 4849    // Cut with three selections, one of which is full-line.
 4850    cx.set_state(indoc! {"
 4851        1«2ˇ»3
 4852        4ˇ567
 4853        «8ˇ»9"});
 4854    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4855    cx.assert_editor_state(indoc! {"
 4856        1ˇ3
 4857        ˇ9"});
 4858
 4859    // Paste with three selections, noticing how the copied selection that was full-line
 4860    // gets inserted before the second cursor.
 4861    cx.set_state(indoc! {"
 4862        1ˇ3
 4863 4864        «oˇ»ne"});
 4865    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4866    cx.assert_editor_state(indoc! {"
 4867        12ˇ3
 4868        4567
 4869 4870        8ˇne"});
 4871
 4872    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4873    cx.set_state(indoc! {"
 4874        The quick brown
 4875        fox juˇmps over
 4876        the lazy dog"});
 4877    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4878    assert_eq!(
 4879        cx.read_from_clipboard()
 4880            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4881        Some("fox jumps over\n".to_string())
 4882    );
 4883
 4884    // Paste with three selections, noticing how the copied full-line selection is inserted
 4885    // before the empty selections but replaces the selection that is non-empty.
 4886    cx.set_state(indoc! {"
 4887        Tˇhe quick brown
 4888        «foˇ»x jumps over
 4889        tˇhe lazy dog"});
 4890    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4891    cx.assert_editor_state(indoc! {"
 4892        fox jumps over
 4893        Tˇhe quick brown
 4894        fox jumps over
 4895        ˇx jumps over
 4896        fox jumps over
 4897        tˇhe lazy dog"});
 4898}
 4899
 4900#[gpui::test]
 4901async fn test_copy_trim(cx: &mut TestAppContext) {
 4902    init_test(cx, |_| {});
 4903
 4904    let mut cx = EditorTestContext::new(cx).await;
 4905    cx.set_state(
 4906        r#"            «for selection in selections.iter() {
 4907            let mut start = selection.start;
 4908            let mut end = selection.end;
 4909            let is_entire_line = selection.is_empty();
 4910            if is_entire_line {
 4911                start = Point::new(start.row, 0);ˇ»
 4912                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4913            }
 4914        "#,
 4915    );
 4916    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4917    assert_eq!(
 4918        cx.read_from_clipboard()
 4919            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4920        Some(
 4921            "for selection in selections.iter() {
 4922            let mut start = selection.start;
 4923            let mut end = selection.end;
 4924            let is_entire_line = selection.is_empty();
 4925            if is_entire_line {
 4926                start = Point::new(start.row, 0);"
 4927                .to_string()
 4928        ),
 4929        "Regular copying preserves all indentation selected",
 4930    );
 4931    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4932    assert_eq!(
 4933        cx.read_from_clipboard()
 4934            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4935        Some(
 4936            "for selection in selections.iter() {
 4937let mut start = selection.start;
 4938let mut end = selection.end;
 4939let is_entire_line = selection.is_empty();
 4940if is_entire_line {
 4941    start = Point::new(start.row, 0);"
 4942                .to_string()
 4943        ),
 4944        "Copying with stripping should strip all leading whitespaces"
 4945    );
 4946
 4947    cx.set_state(
 4948        r#"       «     for selection in selections.iter() {
 4949            let mut start = selection.start;
 4950            let mut end = selection.end;
 4951            let is_entire_line = selection.is_empty();
 4952            if is_entire_line {
 4953                start = Point::new(start.row, 0);ˇ»
 4954                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4955            }
 4956        "#,
 4957    );
 4958    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4959    assert_eq!(
 4960        cx.read_from_clipboard()
 4961            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4962        Some(
 4963            "     for selection in selections.iter() {
 4964            let mut start = selection.start;
 4965            let mut end = selection.end;
 4966            let is_entire_line = selection.is_empty();
 4967            if is_entire_line {
 4968                start = Point::new(start.row, 0);"
 4969                .to_string()
 4970        ),
 4971        "Regular copying preserves all indentation selected",
 4972    );
 4973    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4974    assert_eq!(
 4975        cx.read_from_clipboard()
 4976            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4977        Some(
 4978            "for selection in selections.iter() {
 4979let mut start = selection.start;
 4980let mut end = selection.end;
 4981let is_entire_line = selection.is_empty();
 4982if is_entire_line {
 4983    start = Point::new(start.row, 0);"
 4984                .to_string()
 4985        ),
 4986        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 4987    );
 4988
 4989    cx.set_state(
 4990        r#"       «ˇ     for selection in selections.iter() {
 4991            let mut start = selection.start;
 4992            let mut end = selection.end;
 4993            let is_entire_line = selection.is_empty();
 4994            if is_entire_line {
 4995                start = Point::new(start.row, 0);»
 4996                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4997            }
 4998        "#,
 4999    );
 5000    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5001    assert_eq!(
 5002        cx.read_from_clipboard()
 5003            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5004        Some(
 5005            "     for selection in selections.iter() {
 5006            let mut start = selection.start;
 5007            let mut end = selection.end;
 5008            let is_entire_line = selection.is_empty();
 5009            if is_entire_line {
 5010                start = Point::new(start.row, 0);"
 5011                .to_string()
 5012        ),
 5013        "Regular copying for reverse selection works the same",
 5014    );
 5015    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5016    assert_eq!(
 5017        cx.read_from_clipboard()
 5018            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5019        Some(
 5020            "for selection in selections.iter() {
 5021let mut start = selection.start;
 5022let mut end = selection.end;
 5023let is_entire_line = selection.is_empty();
 5024if is_entire_line {
 5025    start = Point::new(start.row, 0);"
 5026                .to_string()
 5027        ),
 5028        "Copying with stripping for reverse selection works the same"
 5029    );
 5030
 5031    cx.set_state(
 5032        r#"            for selection «in selections.iter() {
 5033            let mut start = selection.start;
 5034            let mut end = selection.end;
 5035            let is_entire_line = selection.is_empty();
 5036            if is_entire_line {
 5037                start = Point::new(start.row, 0);ˇ»
 5038                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5039            }
 5040        "#,
 5041    );
 5042    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5043    assert_eq!(
 5044        cx.read_from_clipboard()
 5045            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5046        Some(
 5047            "in selections.iter() {
 5048            let mut start = selection.start;
 5049            let mut end = selection.end;
 5050            let is_entire_line = selection.is_empty();
 5051            if is_entire_line {
 5052                start = Point::new(start.row, 0);"
 5053                .to_string()
 5054        ),
 5055        "When selecting past the indent, the copying works as usual",
 5056    );
 5057    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5058    assert_eq!(
 5059        cx.read_from_clipboard()
 5060            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5061        Some(
 5062            "in selections.iter() {
 5063            let mut start = selection.start;
 5064            let mut end = selection.end;
 5065            let is_entire_line = selection.is_empty();
 5066            if is_entire_line {
 5067                start = Point::new(start.row, 0);"
 5068                .to_string()
 5069        ),
 5070        "When selecting past the indent, nothing is trimmed"
 5071    );
 5072}
 5073
 5074#[gpui::test]
 5075async fn test_paste_multiline(cx: &mut TestAppContext) {
 5076    init_test(cx, |_| {});
 5077
 5078    let mut cx = EditorTestContext::new(cx).await;
 5079    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5080
 5081    // Cut an indented block, without the leading whitespace.
 5082    cx.set_state(indoc! {"
 5083        const a: B = (
 5084            c(),
 5085            «d(
 5086                e,
 5087                f
 5088            )ˇ»
 5089        );
 5090    "});
 5091    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5092    cx.assert_editor_state(indoc! {"
 5093        const a: B = (
 5094            c(),
 5095            ˇ
 5096        );
 5097    "});
 5098
 5099    // Paste it at the same position.
 5100    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5101    cx.assert_editor_state(indoc! {"
 5102        const a: B = (
 5103            c(),
 5104            d(
 5105                e,
 5106                f
 5107 5108        );
 5109    "});
 5110
 5111    // Paste it at a line with a lower indent level.
 5112    cx.set_state(indoc! {"
 5113        ˇ
 5114        const a: B = (
 5115            c(),
 5116        );
 5117    "});
 5118    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5119    cx.assert_editor_state(indoc! {"
 5120        d(
 5121            e,
 5122            f
 5123 5124        const a: B = (
 5125            c(),
 5126        );
 5127    "});
 5128
 5129    // Cut an indented block, with the leading whitespace.
 5130    cx.set_state(indoc! {"
 5131        const a: B = (
 5132            c(),
 5133        «    d(
 5134                e,
 5135                f
 5136            )
 5137        ˇ»);
 5138    "});
 5139    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5140    cx.assert_editor_state(indoc! {"
 5141        const a: B = (
 5142            c(),
 5143        ˇ);
 5144    "});
 5145
 5146    // Paste it at the same position.
 5147    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5148    cx.assert_editor_state(indoc! {"
 5149        const a: B = (
 5150            c(),
 5151            d(
 5152                e,
 5153                f
 5154            )
 5155        ˇ);
 5156    "});
 5157
 5158    // Paste it at a line with a higher indent level.
 5159    cx.set_state(indoc! {"
 5160        const a: B = (
 5161            c(),
 5162            d(
 5163                e,
 5164 5165            )
 5166        );
 5167    "});
 5168    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5169    cx.assert_editor_state(indoc! {"
 5170        const a: B = (
 5171            c(),
 5172            d(
 5173                e,
 5174                f    d(
 5175                    e,
 5176                    f
 5177                )
 5178        ˇ
 5179            )
 5180        );
 5181    "});
 5182
 5183    // Copy an indented block, starting mid-line
 5184    cx.set_state(indoc! {"
 5185        const a: B = (
 5186            c(),
 5187            somethin«g(
 5188                e,
 5189                f
 5190            )ˇ»
 5191        );
 5192    "});
 5193    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5194
 5195    // Paste it on a line with a lower indent level
 5196    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5197    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5198    cx.assert_editor_state(indoc! {"
 5199        const a: B = (
 5200            c(),
 5201            something(
 5202                e,
 5203                f
 5204            )
 5205        );
 5206        g(
 5207            e,
 5208            f
 5209"});
 5210}
 5211
 5212#[gpui::test]
 5213async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5214    init_test(cx, |_| {});
 5215
 5216    cx.write_to_clipboard(ClipboardItem::new_string(
 5217        "    d(\n        e\n    );\n".into(),
 5218    ));
 5219
 5220    let mut cx = EditorTestContext::new(cx).await;
 5221    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5222
 5223    cx.set_state(indoc! {"
 5224        fn a() {
 5225            b();
 5226            if c() {
 5227                ˇ
 5228            }
 5229        }
 5230    "});
 5231
 5232    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5233    cx.assert_editor_state(indoc! {"
 5234        fn a() {
 5235            b();
 5236            if c() {
 5237                d(
 5238                    e
 5239                );
 5240        ˇ
 5241            }
 5242        }
 5243    "});
 5244
 5245    cx.set_state(indoc! {"
 5246        fn a() {
 5247            b();
 5248            ˇ
 5249        }
 5250    "});
 5251
 5252    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5253    cx.assert_editor_state(indoc! {"
 5254        fn a() {
 5255            b();
 5256            d(
 5257                e
 5258            );
 5259        ˇ
 5260        }
 5261    "});
 5262}
 5263
 5264#[gpui::test]
 5265fn test_select_all(cx: &mut TestAppContext) {
 5266    init_test(cx, |_| {});
 5267
 5268    let editor = cx.add_window(|window, cx| {
 5269        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5270        build_editor(buffer, window, cx)
 5271    });
 5272    _ = editor.update(cx, |editor, window, cx| {
 5273        editor.select_all(&SelectAll, window, cx);
 5274        assert_eq!(
 5275            editor.selections.display_ranges(cx),
 5276            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5277        );
 5278    });
 5279}
 5280
 5281#[gpui::test]
 5282fn test_select_line(cx: &mut TestAppContext) {
 5283    init_test(cx, |_| {});
 5284
 5285    let editor = cx.add_window(|window, cx| {
 5286        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5287        build_editor(buffer, window, cx)
 5288    });
 5289    _ = editor.update(cx, |editor, window, cx| {
 5290        editor.change_selections(None, window, cx, |s| {
 5291            s.select_display_ranges([
 5292                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5293                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5294                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5295                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5296            ])
 5297        });
 5298        editor.select_line(&SelectLine, window, cx);
 5299        assert_eq!(
 5300            editor.selections.display_ranges(cx),
 5301            vec![
 5302                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5303                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5304            ]
 5305        );
 5306    });
 5307
 5308    _ = editor.update(cx, |editor, window, cx| {
 5309        editor.select_line(&SelectLine, window, cx);
 5310        assert_eq!(
 5311            editor.selections.display_ranges(cx),
 5312            vec![
 5313                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5314                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5315            ]
 5316        );
 5317    });
 5318
 5319    _ = editor.update(cx, |editor, window, cx| {
 5320        editor.select_line(&SelectLine, window, cx);
 5321        assert_eq!(
 5322            editor.selections.display_ranges(cx),
 5323            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5324        );
 5325    });
 5326}
 5327
 5328#[gpui::test]
 5329async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5330    init_test(cx, |_| {});
 5331    let mut cx = EditorTestContext::new(cx).await;
 5332
 5333    #[track_caller]
 5334    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5335        cx.set_state(initial_state);
 5336        cx.update_editor(|e, window, cx| {
 5337            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5338        });
 5339        cx.assert_editor_state(expected_state);
 5340    }
 5341
 5342    // Selection starts and ends at the middle of lines, left-to-right
 5343    test(
 5344        &mut cx,
 5345        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5346        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5347    );
 5348    // Same thing, right-to-left
 5349    test(
 5350        &mut cx,
 5351        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5352        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5353    );
 5354
 5355    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5356    test(
 5357        &mut cx,
 5358        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5359        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5360    );
 5361    // Same thing, right-to-left
 5362    test(
 5363        &mut cx,
 5364        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5365        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5366    );
 5367
 5368    // Whole buffer, left-to-right, last line ends with newline
 5369    test(
 5370        &mut cx,
 5371        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5372        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5373    );
 5374    // Same thing, right-to-left
 5375    test(
 5376        &mut cx,
 5377        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5378        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5379    );
 5380
 5381    // Starts at the end of a line, ends at the start of another
 5382    test(
 5383        &mut cx,
 5384        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5385        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5386    );
 5387}
 5388
 5389#[gpui::test]
 5390async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5391    init_test(cx, |_| {});
 5392
 5393    let editor = cx.add_window(|window, cx| {
 5394        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5395        build_editor(buffer, window, cx)
 5396    });
 5397
 5398    // setup
 5399    _ = editor.update(cx, |editor, window, cx| {
 5400        editor.fold_creases(
 5401            vec![
 5402                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5403                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5404                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5405            ],
 5406            true,
 5407            window,
 5408            cx,
 5409        );
 5410        assert_eq!(
 5411            editor.display_text(cx),
 5412            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5413        );
 5414    });
 5415
 5416    _ = editor.update(cx, |editor, window, cx| {
 5417        editor.change_selections(None, window, cx, |s| {
 5418            s.select_display_ranges([
 5419                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5420                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5421                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5422                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5423            ])
 5424        });
 5425        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5426        assert_eq!(
 5427            editor.display_text(cx),
 5428            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5429        );
 5430    });
 5431    EditorTestContext::for_editor(editor, cx)
 5432        .await
 5433        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5434
 5435    _ = editor.update(cx, |editor, window, cx| {
 5436        editor.change_selections(None, window, cx, |s| {
 5437            s.select_display_ranges([
 5438                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5439            ])
 5440        });
 5441        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5442        assert_eq!(
 5443            editor.display_text(cx),
 5444            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5445        );
 5446        assert_eq!(
 5447            editor.selections.display_ranges(cx),
 5448            [
 5449                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5450                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5451                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5452                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5453                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5454                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5455                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5456            ]
 5457        );
 5458    });
 5459    EditorTestContext::for_editor(editor, cx)
 5460        .await
 5461        .assert_editor_state(
 5462            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5463        );
 5464}
 5465
 5466#[gpui::test]
 5467async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5468    init_test(cx, |_| {});
 5469
 5470    let mut cx = EditorTestContext::new(cx).await;
 5471
 5472    cx.set_state(indoc!(
 5473        r#"abc
 5474           defˇghi
 5475
 5476           jk
 5477           nlmo
 5478           "#
 5479    ));
 5480
 5481    cx.update_editor(|editor, window, cx| {
 5482        editor.add_selection_above(&Default::default(), window, cx);
 5483    });
 5484
 5485    cx.assert_editor_state(indoc!(
 5486        r#"abcˇ
 5487           defˇghi
 5488
 5489           jk
 5490           nlmo
 5491           "#
 5492    ));
 5493
 5494    cx.update_editor(|editor, window, cx| {
 5495        editor.add_selection_above(&Default::default(), window, cx);
 5496    });
 5497
 5498    cx.assert_editor_state(indoc!(
 5499        r#"abcˇ
 5500            defˇghi
 5501
 5502            jk
 5503            nlmo
 5504            "#
 5505    ));
 5506
 5507    cx.update_editor(|editor, window, cx| {
 5508        editor.add_selection_below(&Default::default(), window, cx);
 5509    });
 5510
 5511    cx.assert_editor_state(indoc!(
 5512        r#"abc
 5513           defˇghi
 5514
 5515           jk
 5516           nlmo
 5517           "#
 5518    ));
 5519
 5520    cx.update_editor(|editor, window, cx| {
 5521        editor.undo_selection(&Default::default(), window, cx);
 5522    });
 5523
 5524    cx.assert_editor_state(indoc!(
 5525        r#"abcˇ
 5526           defˇghi
 5527
 5528           jk
 5529           nlmo
 5530           "#
 5531    ));
 5532
 5533    cx.update_editor(|editor, window, cx| {
 5534        editor.redo_selection(&Default::default(), window, cx);
 5535    });
 5536
 5537    cx.assert_editor_state(indoc!(
 5538        r#"abc
 5539           defˇghi
 5540
 5541           jk
 5542           nlmo
 5543           "#
 5544    ));
 5545
 5546    cx.update_editor(|editor, window, cx| {
 5547        editor.add_selection_below(&Default::default(), window, cx);
 5548    });
 5549
 5550    cx.assert_editor_state(indoc!(
 5551        r#"abc
 5552           defˇghi
 5553
 5554           jk
 5555           nlmˇo
 5556           "#
 5557    ));
 5558
 5559    cx.update_editor(|editor, window, cx| {
 5560        editor.add_selection_below(&Default::default(), window, cx);
 5561    });
 5562
 5563    cx.assert_editor_state(indoc!(
 5564        r#"abc
 5565           defˇghi
 5566
 5567           jk
 5568           nlmˇo
 5569           "#
 5570    ));
 5571
 5572    // change selections
 5573    cx.set_state(indoc!(
 5574        r#"abc
 5575           def«ˇg»hi
 5576
 5577           jk
 5578           nlmo
 5579           "#
 5580    ));
 5581
 5582    cx.update_editor(|editor, window, cx| {
 5583        editor.add_selection_below(&Default::default(), window, cx);
 5584    });
 5585
 5586    cx.assert_editor_state(indoc!(
 5587        r#"abc
 5588           def«ˇg»hi
 5589
 5590           jk
 5591           nlm«ˇo»
 5592           "#
 5593    ));
 5594
 5595    cx.update_editor(|editor, window, cx| {
 5596        editor.add_selection_below(&Default::default(), window, cx);
 5597    });
 5598
 5599    cx.assert_editor_state(indoc!(
 5600        r#"abc
 5601           def«ˇg»hi
 5602
 5603           jk
 5604           nlm«ˇo»
 5605           "#
 5606    ));
 5607
 5608    cx.update_editor(|editor, window, cx| {
 5609        editor.add_selection_above(&Default::default(), window, cx);
 5610    });
 5611
 5612    cx.assert_editor_state(indoc!(
 5613        r#"abc
 5614           def«ˇg»hi
 5615
 5616           jk
 5617           nlmo
 5618           "#
 5619    ));
 5620
 5621    cx.update_editor(|editor, window, cx| {
 5622        editor.add_selection_above(&Default::default(), window, cx);
 5623    });
 5624
 5625    cx.assert_editor_state(indoc!(
 5626        r#"abc
 5627           def«ˇg»hi
 5628
 5629           jk
 5630           nlmo
 5631           "#
 5632    ));
 5633
 5634    // Change selections again
 5635    cx.set_state(indoc!(
 5636        r#"a«bc
 5637           defgˇ»hi
 5638
 5639           jk
 5640           nlmo
 5641           "#
 5642    ));
 5643
 5644    cx.update_editor(|editor, window, cx| {
 5645        editor.add_selection_below(&Default::default(), window, cx);
 5646    });
 5647
 5648    cx.assert_editor_state(indoc!(
 5649        r#"a«bcˇ»
 5650           d«efgˇ»hi
 5651
 5652           j«kˇ»
 5653           nlmo
 5654           "#
 5655    ));
 5656
 5657    cx.update_editor(|editor, window, cx| {
 5658        editor.add_selection_below(&Default::default(), window, cx);
 5659    });
 5660    cx.assert_editor_state(indoc!(
 5661        r#"a«bcˇ»
 5662           d«efgˇ»hi
 5663
 5664           j«kˇ»
 5665           n«lmoˇ»
 5666           "#
 5667    ));
 5668    cx.update_editor(|editor, window, cx| {
 5669        editor.add_selection_above(&Default::default(), window, cx);
 5670    });
 5671
 5672    cx.assert_editor_state(indoc!(
 5673        r#"a«bcˇ»
 5674           d«efgˇ»hi
 5675
 5676           j«kˇ»
 5677           nlmo
 5678           "#
 5679    ));
 5680
 5681    // Change selections again
 5682    cx.set_state(indoc!(
 5683        r#"abc
 5684           d«ˇefghi
 5685
 5686           jk
 5687           nlm»o
 5688           "#
 5689    ));
 5690
 5691    cx.update_editor(|editor, window, cx| {
 5692        editor.add_selection_above(&Default::default(), window, cx);
 5693    });
 5694
 5695    cx.assert_editor_state(indoc!(
 5696        r#"a«ˇbc»
 5697           d«ˇef»ghi
 5698
 5699           j«ˇk»
 5700           n«ˇlm»o
 5701           "#
 5702    ));
 5703
 5704    cx.update_editor(|editor, window, cx| {
 5705        editor.add_selection_below(&Default::default(), window, cx);
 5706    });
 5707
 5708    cx.assert_editor_state(indoc!(
 5709        r#"abc
 5710           d«ˇef»ghi
 5711
 5712           j«ˇk»
 5713           n«ˇlm»o
 5714           "#
 5715    ));
 5716}
 5717
 5718#[gpui::test]
 5719async fn test_select_next(cx: &mut TestAppContext) {
 5720    init_test(cx, |_| {});
 5721
 5722    let mut cx = EditorTestContext::new(cx).await;
 5723    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5724
 5725    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5726        .unwrap();
 5727    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5728
 5729    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5730        .unwrap();
 5731    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5732
 5733    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5734    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5735
 5736    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5737    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5738
 5739    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5740        .unwrap();
 5741    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5742
 5743    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5744        .unwrap();
 5745    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5746}
 5747
 5748#[gpui::test]
 5749async fn test_select_all_matches(cx: &mut TestAppContext) {
 5750    init_test(cx, |_| {});
 5751
 5752    let mut cx = EditorTestContext::new(cx).await;
 5753
 5754    // Test caret-only selections
 5755    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5756    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5757        .unwrap();
 5758    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5759
 5760    // Test left-to-right selections
 5761    cx.set_state("abc\n«abcˇ»\nabc");
 5762    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5763        .unwrap();
 5764    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5765
 5766    // Test right-to-left selections
 5767    cx.set_state("abc\n«ˇabc»\nabc");
 5768    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5769        .unwrap();
 5770    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5771
 5772    // Test selecting whitespace with caret selection
 5773    cx.set_state("abc\nˇ   abc\nabc");
 5774    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5775        .unwrap();
 5776    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5777
 5778    // Test selecting whitespace with left-to-right selection
 5779    cx.set_state("abc\n«ˇ  »abc\nabc");
 5780    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5781        .unwrap();
 5782    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5783
 5784    // Test no matches with right-to-left selection
 5785    cx.set_state("abc\n«  ˇ»abc\nabc");
 5786    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5787        .unwrap();
 5788    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5789}
 5790
 5791#[gpui::test]
 5792async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5793    init_test(cx, |_| {});
 5794
 5795    let mut cx = EditorTestContext::new(cx).await;
 5796    cx.set_state(
 5797        r#"let foo = 2;
 5798lˇet foo = 2;
 5799let fooˇ = 2;
 5800let foo = 2;
 5801let foo = ˇ2;"#,
 5802    );
 5803
 5804    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5805        .unwrap();
 5806    cx.assert_editor_state(
 5807        r#"let foo = 2;
 5808«letˇ» foo = 2;
 5809let «fooˇ» = 2;
 5810let foo = 2;
 5811let foo = «2ˇ»;"#,
 5812    );
 5813
 5814    // noop for multiple selections with different contents
 5815    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5816        .unwrap();
 5817    cx.assert_editor_state(
 5818        r#"let foo = 2;
 5819«letˇ» foo = 2;
 5820let «fooˇ» = 2;
 5821let foo = 2;
 5822let foo = «2ˇ»;"#,
 5823    );
 5824}
 5825
 5826#[gpui::test]
 5827async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5828    init_test(cx, |_| {});
 5829
 5830    let mut cx =
 5831        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5832
 5833    cx.assert_editor_state(indoc! {"
 5834        ˇbbb
 5835        ccc
 5836
 5837        bbb
 5838        ccc
 5839        "});
 5840    cx.dispatch_action(SelectPrevious::default());
 5841    cx.assert_editor_state(indoc! {"
 5842                «bbbˇ»
 5843                ccc
 5844
 5845                bbb
 5846                ccc
 5847                "});
 5848    cx.dispatch_action(SelectPrevious::default());
 5849    cx.assert_editor_state(indoc! {"
 5850                «bbbˇ»
 5851                ccc
 5852
 5853                «bbbˇ»
 5854                ccc
 5855                "});
 5856}
 5857
 5858#[gpui::test]
 5859async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5860    init_test(cx, |_| {});
 5861
 5862    let mut cx = EditorTestContext::new(cx).await;
 5863    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5864
 5865    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5866        .unwrap();
 5867    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5868
 5869    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5870        .unwrap();
 5871    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5872
 5873    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5874    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5875
 5876    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5877    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5878
 5879    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5880        .unwrap();
 5881    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5882
 5883    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5884        .unwrap();
 5885    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5886
 5887    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5888        .unwrap();
 5889    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5890}
 5891
 5892#[gpui::test]
 5893async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5894    init_test(cx, |_| {});
 5895
 5896    let mut cx = EditorTestContext::new(cx).await;
 5897    cx.set_state("");
 5898
 5899    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5900        .unwrap();
 5901    cx.assert_editor_state("«aˇ»");
 5902    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5903        .unwrap();
 5904    cx.assert_editor_state("«aˇ»");
 5905}
 5906
 5907#[gpui::test]
 5908async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5909    init_test(cx, |_| {});
 5910
 5911    let mut cx = EditorTestContext::new(cx).await;
 5912    cx.set_state(
 5913        r#"let foo = 2;
 5914lˇet foo = 2;
 5915let fooˇ = 2;
 5916let foo = 2;
 5917let foo = ˇ2;"#,
 5918    );
 5919
 5920    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5921        .unwrap();
 5922    cx.assert_editor_state(
 5923        r#"let foo = 2;
 5924«letˇ» foo = 2;
 5925let «fooˇ» = 2;
 5926let foo = 2;
 5927let foo = «2ˇ»;"#,
 5928    );
 5929
 5930    // noop for multiple selections with different contents
 5931    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5932        .unwrap();
 5933    cx.assert_editor_state(
 5934        r#"let foo = 2;
 5935«letˇ» foo = 2;
 5936let «fooˇ» = 2;
 5937let foo = 2;
 5938let foo = «2ˇ»;"#,
 5939    );
 5940}
 5941
 5942#[gpui::test]
 5943async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5944    init_test(cx, |_| {});
 5945
 5946    let mut cx = EditorTestContext::new(cx).await;
 5947    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5948
 5949    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5950        .unwrap();
 5951    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5952
 5953    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5954        .unwrap();
 5955    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5956
 5957    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5958    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5959
 5960    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5961    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5962
 5963    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5964        .unwrap();
 5965    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5966
 5967    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5968        .unwrap();
 5969    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5970}
 5971
 5972#[gpui::test]
 5973async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5974    init_test(cx, |_| {});
 5975
 5976    let language = Arc::new(Language::new(
 5977        LanguageConfig::default(),
 5978        Some(tree_sitter_rust::LANGUAGE.into()),
 5979    ));
 5980
 5981    let text = r#"
 5982        use mod1::mod2::{mod3, mod4};
 5983
 5984        fn fn_1(param1: bool, param2: &str) {
 5985            let var1 = "text";
 5986        }
 5987    "#
 5988    .unindent();
 5989
 5990    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5991    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5992    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5993
 5994    editor
 5995        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5996        .await;
 5997
 5998    editor.update_in(cx, |editor, window, cx| {
 5999        editor.change_selections(None, window, cx, |s| {
 6000            s.select_display_ranges([
 6001                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6002                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6003                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6004            ]);
 6005        });
 6006        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6007    });
 6008    editor.update(cx, |editor, cx| {
 6009        assert_text_with_selections(
 6010            editor,
 6011            indoc! {r#"
 6012                use mod1::mod2::{mod3, «mod4ˇ»};
 6013
 6014                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6015                    let var1 = "«ˇtext»";
 6016                }
 6017            "#},
 6018            cx,
 6019        );
 6020    });
 6021
 6022    editor.update_in(cx, |editor, window, cx| {
 6023        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6024    });
 6025    editor.update(cx, |editor, cx| {
 6026        assert_text_with_selections(
 6027            editor,
 6028            indoc! {r#"
 6029                use mod1::mod2::«{mod3, mod4}ˇ»;
 6030
 6031                «ˇfn fn_1(param1: bool, param2: &str) {
 6032                    let var1 = "text";
 6033 6034            "#},
 6035            cx,
 6036        );
 6037    });
 6038
 6039    editor.update_in(cx, |editor, window, cx| {
 6040        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6041    });
 6042    assert_eq!(
 6043        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6044        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6045    );
 6046
 6047    // Trying to expand the selected syntax node one more time has no effect.
 6048    editor.update_in(cx, |editor, window, cx| {
 6049        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6050    });
 6051    assert_eq!(
 6052        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6053        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6054    );
 6055
 6056    editor.update_in(cx, |editor, window, cx| {
 6057        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6058    });
 6059    editor.update(cx, |editor, cx| {
 6060        assert_text_with_selections(
 6061            editor,
 6062            indoc! {r#"
 6063                use mod1::mod2::«{mod3, mod4}ˇ»;
 6064
 6065                «ˇfn fn_1(param1: bool, param2: &str) {
 6066                    let var1 = "text";
 6067 6068            "#},
 6069            cx,
 6070        );
 6071    });
 6072
 6073    editor.update_in(cx, |editor, window, cx| {
 6074        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6075    });
 6076    editor.update(cx, |editor, cx| {
 6077        assert_text_with_selections(
 6078            editor,
 6079            indoc! {r#"
 6080                use mod1::mod2::{mod3, «mod4ˇ»};
 6081
 6082                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6083                    let var1 = "«ˇtext»";
 6084                }
 6085            "#},
 6086            cx,
 6087        );
 6088    });
 6089
 6090    editor.update_in(cx, |editor, window, cx| {
 6091        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6092    });
 6093    editor.update(cx, |editor, cx| {
 6094        assert_text_with_selections(
 6095            editor,
 6096            indoc! {r#"
 6097                use mod1::mod2::{mod3, mo«ˇ»d4};
 6098
 6099                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6100                    let var1 = "te«ˇ»xt";
 6101                }
 6102            "#},
 6103            cx,
 6104        );
 6105    });
 6106
 6107    // Trying to shrink the selected syntax node one more time has no effect.
 6108    editor.update_in(cx, |editor, window, cx| {
 6109        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6110    });
 6111    editor.update_in(cx, |editor, _, cx| {
 6112        assert_text_with_selections(
 6113            editor,
 6114            indoc! {r#"
 6115                use mod1::mod2::{mod3, mo«ˇ»d4};
 6116
 6117                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6118                    let var1 = "te«ˇ»xt";
 6119                }
 6120            "#},
 6121            cx,
 6122        );
 6123    });
 6124
 6125    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6126    // a fold.
 6127    editor.update_in(cx, |editor, window, cx| {
 6128        editor.fold_creases(
 6129            vec![
 6130                Crease::simple(
 6131                    Point::new(0, 21)..Point::new(0, 24),
 6132                    FoldPlaceholder::test(),
 6133                ),
 6134                Crease::simple(
 6135                    Point::new(3, 20)..Point::new(3, 22),
 6136                    FoldPlaceholder::test(),
 6137                ),
 6138            ],
 6139            true,
 6140            window,
 6141            cx,
 6142        );
 6143        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6144    });
 6145    editor.update(cx, |editor, cx| {
 6146        assert_text_with_selections(
 6147            editor,
 6148            indoc! {r#"
 6149                use mod1::mod2::«{mod3, mod4}ˇ»;
 6150
 6151                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6152                    «ˇlet var1 = "text";»
 6153                }
 6154            "#},
 6155            cx,
 6156        );
 6157    });
 6158}
 6159
 6160#[gpui::test]
 6161async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6162    init_test(cx, |_| {});
 6163
 6164    let base_text = r#"
 6165        impl A {
 6166            // this is an uncommitted comment
 6167
 6168            fn b() {
 6169                c();
 6170            }
 6171
 6172            // this is another uncommitted comment
 6173
 6174            fn d() {
 6175                // e
 6176                // f
 6177            }
 6178        }
 6179
 6180        fn g() {
 6181            // h
 6182        }
 6183    "#
 6184    .unindent();
 6185
 6186    let text = r#"
 6187        ˇimpl A {
 6188
 6189            fn b() {
 6190                c();
 6191            }
 6192
 6193            fn d() {
 6194                // e
 6195                // f
 6196            }
 6197        }
 6198
 6199        fn g() {
 6200            // h
 6201        }
 6202    "#
 6203    .unindent();
 6204
 6205    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6206    cx.set_state(&text);
 6207    cx.set_head_text(&base_text);
 6208    cx.update_editor(|editor, window, cx| {
 6209        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6210    });
 6211
 6212    cx.assert_state_with_diff(
 6213        "
 6214        ˇimpl A {
 6215      -     // this is an uncommitted comment
 6216
 6217            fn b() {
 6218                c();
 6219            }
 6220
 6221      -     // this is another uncommitted comment
 6222      -
 6223            fn d() {
 6224                // e
 6225                // f
 6226            }
 6227        }
 6228
 6229        fn g() {
 6230            // h
 6231        }
 6232    "
 6233        .unindent(),
 6234    );
 6235
 6236    let expected_display_text = "
 6237        impl A {
 6238            // this is an uncommitted comment
 6239
 6240            fn b() {
 6241 6242            }
 6243
 6244            // this is another uncommitted comment
 6245
 6246            fn d() {
 6247 6248            }
 6249        }
 6250
 6251        fn g() {
 6252 6253        }
 6254        "
 6255    .unindent();
 6256
 6257    cx.update_editor(|editor, window, cx| {
 6258        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6259        assert_eq!(editor.display_text(cx), expected_display_text);
 6260    });
 6261}
 6262
 6263#[gpui::test]
 6264async fn test_autoindent(cx: &mut TestAppContext) {
 6265    init_test(cx, |_| {});
 6266
 6267    let language = Arc::new(
 6268        Language::new(
 6269            LanguageConfig {
 6270                brackets: BracketPairConfig {
 6271                    pairs: vec![
 6272                        BracketPair {
 6273                            start: "{".to_string(),
 6274                            end: "}".to_string(),
 6275                            close: false,
 6276                            surround: false,
 6277                            newline: true,
 6278                        },
 6279                        BracketPair {
 6280                            start: "(".to_string(),
 6281                            end: ")".to_string(),
 6282                            close: false,
 6283                            surround: false,
 6284                            newline: true,
 6285                        },
 6286                    ],
 6287                    ..Default::default()
 6288                },
 6289                ..Default::default()
 6290            },
 6291            Some(tree_sitter_rust::LANGUAGE.into()),
 6292        )
 6293        .with_indents_query(
 6294            r#"
 6295                (_ "(" ")" @end) @indent
 6296                (_ "{" "}" @end) @indent
 6297            "#,
 6298        )
 6299        .unwrap(),
 6300    );
 6301
 6302    let text = "fn a() {}";
 6303
 6304    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6305    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6306    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6307    editor
 6308        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6309        .await;
 6310
 6311    editor.update_in(cx, |editor, window, cx| {
 6312        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6313        editor.newline(&Newline, window, cx);
 6314        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6315        assert_eq!(
 6316            editor.selections.ranges(cx),
 6317            &[
 6318                Point::new(1, 4)..Point::new(1, 4),
 6319                Point::new(3, 4)..Point::new(3, 4),
 6320                Point::new(5, 0)..Point::new(5, 0)
 6321            ]
 6322        );
 6323    });
 6324}
 6325
 6326#[gpui::test]
 6327async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6328    init_test(cx, |_| {});
 6329
 6330    {
 6331        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6332        cx.set_state(indoc! {"
 6333            impl A {
 6334
 6335                fn b() {}
 6336
 6337            «fn c() {
 6338
 6339            }ˇ»
 6340            }
 6341        "});
 6342
 6343        cx.update_editor(|editor, window, cx| {
 6344            editor.autoindent(&Default::default(), window, cx);
 6345        });
 6346
 6347        cx.assert_editor_state(indoc! {"
 6348            impl A {
 6349
 6350                fn b() {}
 6351
 6352                «fn c() {
 6353
 6354                }ˇ»
 6355            }
 6356        "});
 6357    }
 6358
 6359    {
 6360        let mut cx = EditorTestContext::new_multibuffer(
 6361            cx,
 6362            [indoc! { "
 6363                impl A {
 6364                «
 6365                // a
 6366                fn b(){}
 6367                »
 6368                «
 6369                    }
 6370                    fn c(){}
 6371                »
 6372            "}],
 6373        );
 6374
 6375        let buffer = cx.update_editor(|editor, _, cx| {
 6376            let buffer = editor.buffer().update(cx, |buffer, _| {
 6377                buffer.all_buffers().iter().next().unwrap().clone()
 6378            });
 6379            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6380            buffer
 6381        });
 6382
 6383        cx.run_until_parked();
 6384        cx.update_editor(|editor, window, cx| {
 6385            editor.select_all(&Default::default(), window, cx);
 6386            editor.autoindent(&Default::default(), window, cx)
 6387        });
 6388        cx.run_until_parked();
 6389
 6390        cx.update(|_, cx| {
 6391            pretty_assertions::assert_eq!(
 6392                buffer.read(cx).text(),
 6393                indoc! { "
 6394                    impl A {
 6395
 6396                        // a
 6397                        fn b(){}
 6398
 6399
 6400                    }
 6401                    fn c(){}
 6402
 6403                " }
 6404            )
 6405        });
 6406    }
 6407}
 6408
 6409#[gpui::test]
 6410async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6411    init_test(cx, |_| {});
 6412
 6413    let mut cx = EditorTestContext::new(cx).await;
 6414
 6415    let language = Arc::new(Language::new(
 6416        LanguageConfig {
 6417            brackets: BracketPairConfig {
 6418                pairs: vec![
 6419                    BracketPair {
 6420                        start: "{".to_string(),
 6421                        end: "}".to_string(),
 6422                        close: true,
 6423                        surround: true,
 6424                        newline: true,
 6425                    },
 6426                    BracketPair {
 6427                        start: "(".to_string(),
 6428                        end: ")".to_string(),
 6429                        close: true,
 6430                        surround: true,
 6431                        newline: true,
 6432                    },
 6433                    BracketPair {
 6434                        start: "/*".to_string(),
 6435                        end: " */".to_string(),
 6436                        close: true,
 6437                        surround: true,
 6438                        newline: true,
 6439                    },
 6440                    BracketPair {
 6441                        start: "[".to_string(),
 6442                        end: "]".to_string(),
 6443                        close: false,
 6444                        surround: false,
 6445                        newline: true,
 6446                    },
 6447                    BracketPair {
 6448                        start: "\"".to_string(),
 6449                        end: "\"".to_string(),
 6450                        close: true,
 6451                        surround: true,
 6452                        newline: false,
 6453                    },
 6454                    BracketPair {
 6455                        start: "<".to_string(),
 6456                        end: ">".to_string(),
 6457                        close: false,
 6458                        surround: true,
 6459                        newline: true,
 6460                    },
 6461                ],
 6462                ..Default::default()
 6463            },
 6464            autoclose_before: "})]".to_string(),
 6465            ..Default::default()
 6466        },
 6467        Some(tree_sitter_rust::LANGUAGE.into()),
 6468    ));
 6469
 6470    cx.language_registry().add(language.clone());
 6471    cx.update_buffer(|buffer, cx| {
 6472        buffer.set_language(Some(language), cx);
 6473    });
 6474
 6475    cx.set_state(
 6476        &r#"
 6477            🏀ˇ
 6478            εˇ
 6479            ❤️ˇ
 6480        "#
 6481        .unindent(),
 6482    );
 6483
 6484    // autoclose multiple nested brackets at multiple cursors
 6485    cx.update_editor(|editor, window, cx| {
 6486        editor.handle_input("{", window, cx);
 6487        editor.handle_input("{", window, cx);
 6488        editor.handle_input("{", window, cx);
 6489    });
 6490    cx.assert_editor_state(
 6491        &"
 6492            🏀{{{ˇ}}}
 6493            ε{{{ˇ}}}
 6494            ❤️{{{ˇ}}}
 6495        "
 6496        .unindent(),
 6497    );
 6498
 6499    // insert a different closing bracket
 6500    cx.update_editor(|editor, window, cx| {
 6501        editor.handle_input(")", window, cx);
 6502    });
 6503    cx.assert_editor_state(
 6504        &"
 6505            🏀{{{)ˇ}}}
 6506            ε{{{)ˇ}}}
 6507            ❤️{{{)ˇ}}}
 6508        "
 6509        .unindent(),
 6510    );
 6511
 6512    // skip over the auto-closed brackets when typing a closing bracket
 6513    cx.update_editor(|editor, window, cx| {
 6514        editor.move_right(&MoveRight, window, cx);
 6515        editor.handle_input("}", window, cx);
 6516        editor.handle_input("}", window, cx);
 6517        editor.handle_input("}", window, cx);
 6518    });
 6519    cx.assert_editor_state(
 6520        &"
 6521            🏀{{{)}}}}ˇ
 6522            ε{{{)}}}}ˇ
 6523            ❤️{{{)}}}}ˇ
 6524        "
 6525        .unindent(),
 6526    );
 6527
 6528    // autoclose multi-character pairs
 6529    cx.set_state(
 6530        &"
 6531            ˇ
 6532            ˇ
 6533        "
 6534        .unindent(),
 6535    );
 6536    cx.update_editor(|editor, window, cx| {
 6537        editor.handle_input("/", window, cx);
 6538        editor.handle_input("*", window, cx);
 6539    });
 6540    cx.assert_editor_state(
 6541        &"
 6542            /*ˇ */
 6543            /*ˇ */
 6544        "
 6545        .unindent(),
 6546    );
 6547
 6548    // one cursor autocloses a multi-character pair, one cursor
 6549    // does not autoclose.
 6550    cx.set_state(
 6551        &"
 6552 6553            ˇ
 6554        "
 6555        .unindent(),
 6556    );
 6557    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6558    cx.assert_editor_state(
 6559        &"
 6560            /*ˇ */
 6561 6562        "
 6563        .unindent(),
 6564    );
 6565
 6566    // Don't autoclose if the next character isn't whitespace and isn't
 6567    // listed in the language's "autoclose_before" section.
 6568    cx.set_state("ˇa b");
 6569    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6570    cx.assert_editor_state("{ˇa b");
 6571
 6572    // Don't autoclose if `close` is false for the bracket pair
 6573    cx.set_state("ˇ");
 6574    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6575    cx.assert_editor_state("");
 6576
 6577    // Surround with brackets if text is selected
 6578    cx.set_state("«aˇ» b");
 6579    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6580    cx.assert_editor_state("{«aˇ»} b");
 6581
 6582    // Autoclose when not immediately after a word character
 6583    cx.set_state("a ˇ");
 6584    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6585    cx.assert_editor_state("a \"ˇ\"");
 6586
 6587    // Autoclose pair where the start and end characters are the same
 6588    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6589    cx.assert_editor_state("a \"\"ˇ");
 6590
 6591    // Don't autoclose when immediately after a word character
 6592    cx.set_state("");
 6593    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6594    cx.assert_editor_state("a\"ˇ");
 6595
 6596    // Do autoclose when after a non-word character
 6597    cx.set_state("");
 6598    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6599    cx.assert_editor_state("{\"ˇ\"");
 6600
 6601    // Non identical pairs autoclose regardless of preceding character
 6602    cx.set_state("");
 6603    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6604    cx.assert_editor_state("a{ˇ}");
 6605
 6606    // Don't autoclose pair if autoclose is disabled
 6607    cx.set_state("ˇ");
 6608    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6609    cx.assert_editor_state("");
 6610
 6611    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6612    cx.set_state("«aˇ» b");
 6613    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6614    cx.assert_editor_state("<«aˇ»> b");
 6615}
 6616
 6617#[gpui::test]
 6618async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6619    init_test(cx, |settings| {
 6620        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6621    });
 6622
 6623    let mut cx = EditorTestContext::new(cx).await;
 6624
 6625    let language = Arc::new(Language::new(
 6626        LanguageConfig {
 6627            brackets: BracketPairConfig {
 6628                pairs: vec![
 6629                    BracketPair {
 6630                        start: "{".to_string(),
 6631                        end: "}".to_string(),
 6632                        close: true,
 6633                        surround: true,
 6634                        newline: true,
 6635                    },
 6636                    BracketPair {
 6637                        start: "(".to_string(),
 6638                        end: ")".to_string(),
 6639                        close: true,
 6640                        surround: true,
 6641                        newline: true,
 6642                    },
 6643                    BracketPair {
 6644                        start: "[".to_string(),
 6645                        end: "]".to_string(),
 6646                        close: false,
 6647                        surround: false,
 6648                        newline: true,
 6649                    },
 6650                ],
 6651                ..Default::default()
 6652            },
 6653            autoclose_before: "})]".to_string(),
 6654            ..Default::default()
 6655        },
 6656        Some(tree_sitter_rust::LANGUAGE.into()),
 6657    ));
 6658
 6659    cx.language_registry().add(language.clone());
 6660    cx.update_buffer(|buffer, cx| {
 6661        buffer.set_language(Some(language), cx);
 6662    });
 6663
 6664    cx.set_state(
 6665        &"
 6666            ˇ
 6667            ˇ
 6668            ˇ
 6669        "
 6670        .unindent(),
 6671    );
 6672
 6673    // ensure only matching closing brackets are skipped over
 6674    cx.update_editor(|editor, window, cx| {
 6675        editor.handle_input("}", window, cx);
 6676        editor.move_left(&MoveLeft, window, cx);
 6677        editor.handle_input(")", window, cx);
 6678        editor.move_left(&MoveLeft, window, cx);
 6679    });
 6680    cx.assert_editor_state(
 6681        &"
 6682            ˇ)}
 6683            ˇ)}
 6684            ˇ)}
 6685        "
 6686        .unindent(),
 6687    );
 6688
 6689    // skip-over closing brackets at multiple cursors
 6690    cx.update_editor(|editor, window, cx| {
 6691        editor.handle_input(")", window, cx);
 6692        editor.handle_input("}", window, cx);
 6693    });
 6694    cx.assert_editor_state(
 6695        &"
 6696            )}ˇ
 6697            )}ˇ
 6698            )}ˇ
 6699        "
 6700        .unindent(),
 6701    );
 6702
 6703    // ignore non-close brackets
 6704    cx.update_editor(|editor, window, cx| {
 6705        editor.handle_input("]", window, cx);
 6706        editor.move_left(&MoveLeft, window, cx);
 6707        editor.handle_input("]", window, cx);
 6708    });
 6709    cx.assert_editor_state(
 6710        &"
 6711            )}]ˇ]
 6712            )}]ˇ]
 6713            )}]ˇ]
 6714        "
 6715        .unindent(),
 6716    );
 6717}
 6718
 6719#[gpui::test]
 6720async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6721    init_test(cx, |_| {});
 6722
 6723    let mut cx = EditorTestContext::new(cx).await;
 6724
 6725    let html_language = Arc::new(
 6726        Language::new(
 6727            LanguageConfig {
 6728                name: "HTML".into(),
 6729                brackets: BracketPairConfig {
 6730                    pairs: vec![
 6731                        BracketPair {
 6732                            start: "<".into(),
 6733                            end: ">".into(),
 6734                            close: true,
 6735                            ..Default::default()
 6736                        },
 6737                        BracketPair {
 6738                            start: "{".into(),
 6739                            end: "}".into(),
 6740                            close: true,
 6741                            ..Default::default()
 6742                        },
 6743                        BracketPair {
 6744                            start: "(".into(),
 6745                            end: ")".into(),
 6746                            close: true,
 6747                            ..Default::default()
 6748                        },
 6749                    ],
 6750                    ..Default::default()
 6751                },
 6752                autoclose_before: "})]>".into(),
 6753                ..Default::default()
 6754            },
 6755            Some(tree_sitter_html::LANGUAGE.into()),
 6756        )
 6757        .with_injection_query(
 6758            r#"
 6759            (script_element
 6760                (raw_text) @injection.content
 6761                (#set! injection.language "javascript"))
 6762            "#,
 6763        )
 6764        .unwrap(),
 6765    );
 6766
 6767    let javascript_language = Arc::new(Language::new(
 6768        LanguageConfig {
 6769            name: "JavaScript".into(),
 6770            brackets: BracketPairConfig {
 6771                pairs: vec![
 6772                    BracketPair {
 6773                        start: "/*".into(),
 6774                        end: " */".into(),
 6775                        close: true,
 6776                        ..Default::default()
 6777                    },
 6778                    BracketPair {
 6779                        start: "{".into(),
 6780                        end: "}".into(),
 6781                        close: true,
 6782                        ..Default::default()
 6783                    },
 6784                    BracketPair {
 6785                        start: "(".into(),
 6786                        end: ")".into(),
 6787                        close: true,
 6788                        ..Default::default()
 6789                    },
 6790                ],
 6791                ..Default::default()
 6792            },
 6793            autoclose_before: "})]>".into(),
 6794            ..Default::default()
 6795        },
 6796        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6797    ));
 6798
 6799    cx.language_registry().add(html_language.clone());
 6800    cx.language_registry().add(javascript_language.clone());
 6801
 6802    cx.update_buffer(|buffer, cx| {
 6803        buffer.set_language(Some(html_language), cx);
 6804    });
 6805
 6806    cx.set_state(
 6807        &r#"
 6808            <body>ˇ
 6809                <script>
 6810                    var x = 1;ˇ
 6811                </script>
 6812            </body>ˇ
 6813        "#
 6814        .unindent(),
 6815    );
 6816
 6817    // Precondition: different languages are active at different locations.
 6818    cx.update_editor(|editor, window, cx| {
 6819        let snapshot = editor.snapshot(window, cx);
 6820        let cursors = editor.selections.ranges::<usize>(cx);
 6821        let languages = cursors
 6822            .iter()
 6823            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6824            .collect::<Vec<_>>();
 6825        assert_eq!(
 6826            languages,
 6827            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6828        );
 6829    });
 6830
 6831    // Angle brackets autoclose in HTML, but not JavaScript.
 6832    cx.update_editor(|editor, window, cx| {
 6833        editor.handle_input("<", window, cx);
 6834        editor.handle_input("a", window, cx);
 6835    });
 6836    cx.assert_editor_state(
 6837        &r#"
 6838            <body><aˇ>
 6839                <script>
 6840                    var x = 1;<aˇ
 6841                </script>
 6842            </body><aˇ>
 6843        "#
 6844        .unindent(),
 6845    );
 6846
 6847    // Curly braces and parens autoclose in both HTML and JavaScript.
 6848    cx.update_editor(|editor, window, cx| {
 6849        editor.handle_input(" b=", window, cx);
 6850        editor.handle_input("{", window, cx);
 6851        editor.handle_input("c", window, cx);
 6852        editor.handle_input("(", window, cx);
 6853    });
 6854    cx.assert_editor_state(
 6855        &r#"
 6856            <body><a b={c(ˇ)}>
 6857                <script>
 6858                    var x = 1;<a b={c(ˇ)}
 6859                </script>
 6860            </body><a b={c(ˇ)}>
 6861        "#
 6862        .unindent(),
 6863    );
 6864
 6865    // Brackets that were already autoclosed are skipped.
 6866    cx.update_editor(|editor, window, cx| {
 6867        editor.handle_input(")", window, cx);
 6868        editor.handle_input("d", window, cx);
 6869        editor.handle_input("}", window, cx);
 6870    });
 6871    cx.assert_editor_state(
 6872        &r#"
 6873            <body><a b={c()d}ˇ>
 6874                <script>
 6875                    var x = 1;<a b={c()d}ˇ
 6876                </script>
 6877            </body><a b={c()d}ˇ>
 6878        "#
 6879        .unindent(),
 6880    );
 6881    cx.update_editor(|editor, window, cx| {
 6882        editor.handle_input(">", window, cx);
 6883    });
 6884    cx.assert_editor_state(
 6885        &r#"
 6886            <body><a b={c()d}>ˇ
 6887                <script>
 6888                    var x = 1;<a b={c()d}>ˇ
 6889                </script>
 6890            </body><a b={c()d}>ˇ
 6891        "#
 6892        .unindent(),
 6893    );
 6894
 6895    // Reset
 6896    cx.set_state(
 6897        &r#"
 6898            <body>ˇ
 6899                <script>
 6900                    var x = 1;ˇ
 6901                </script>
 6902            </body>ˇ
 6903        "#
 6904        .unindent(),
 6905    );
 6906
 6907    cx.update_editor(|editor, window, cx| {
 6908        editor.handle_input("<", window, cx);
 6909    });
 6910    cx.assert_editor_state(
 6911        &r#"
 6912            <body><ˇ>
 6913                <script>
 6914                    var x = 1;<ˇ
 6915                </script>
 6916            </body><ˇ>
 6917        "#
 6918        .unindent(),
 6919    );
 6920
 6921    // When backspacing, the closing angle brackets are removed.
 6922    cx.update_editor(|editor, window, cx| {
 6923        editor.backspace(&Backspace, window, cx);
 6924    });
 6925    cx.assert_editor_state(
 6926        &r#"
 6927            <body>ˇ
 6928                <script>
 6929                    var x = 1;ˇ
 6930                </script>
 6931            </body>ˇ
 6932        "#
 6933        .unindent(),
 6934    );
 6935
 6936    // Block comments autoclose in JavaScript, but not HTML.
 6937    cx.update_editor(|editor, window, cx| {
 6938        editor.handle_input("/", window, cx);
 6939        editor.handle_input("*", window, cx);
 6940    });
 6941    cx.assert_editor_state(
 6942        &r#"
 6943            <body>/*ˇ
 6944                <script>
 6945                    var x = 1;/*ˇ */
 6946                </script>
 6947            </body>/*ˇ
 6948        "#
 6949        .unindent(),
 6950    );
 6951}
 6952
 6953#[gpui::test]
 6954async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6955    init_test(cx, |_| {});
 6956
 6957    let mut cx = EditorTestContext::new(cx).await;
 6958
 6959    let rust_language = Arc::new(
 6960        Language::new(
 6961            LanguageConfig {
 6962                name: "Rust".into(),
 6963                brackets: serde_json::from_value(json!([
 6964                    { "start": "{", "end": "}", "close": true, "newline": true },
 6965                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6966                ]))
 6967                .unwrap(),
 6968                autoclose_before: "})]>".into(),
 6969                ..Default::default()
 6970            },
 6971            Some(tree_sitter_rust::LANGUAGE.into()),
 6972        )
 6973        .with_override_query("(string_literal) @string")
 6974        .unwrap(),
 6975    );
 6976
 6977    cx.language_registry().add(rust_language.clone());
 6978    cx.update_buffer(|buffer, cx| {
 6979        buffer.set_language(Some(rust_language), cx);
 6980    });
 6981
 6982    cx.set_state(
 6983        &r#"
 6984            let x = ˇ
 6985        "#
 6986        .unindent(),
 6987    );
 6988
 6989    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6990    cx.update_editor(|editor, window, cx| {
 6991        editor.handle_input("\"", window, cx);
 6992    });
 6993    cx.assert_editor_state(
 6994        &r#"
 6995            let x = "ˇ"
 6996        "#
 6997        .unindent(),
 6998    );
 6999
 7000    // Inserting another quotation mark. The cursor moves across the existing
 7001    // automatically-inserted quotation mark.
 7002    cx.update_editor(|editor, window, cx| {
 7003        editor.handle_input("\"", window, cx);
 7004    });
 7005    cx.assert_editor_state(
 7006        &r#"
 7007            let x = ""ˇ
 7008        "#
 7009        .unindent(),
 7010    );
 7011
 7012    // Reset
 7013    cx.set_state(
 7014        &r#"
 7015            let x = ˇ
 7016        "#
 7017        .unindent(),
 7018    );
 7019
 7020    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7021    cx.update_editor(|editor, window, cx| {
 7022        editor.handle_input("\"", window, cx);
 7023        editor.handle_input(" ", window, cx);
 7024        editor.move_left(&Default::default(), window, cx);
 7025        editor.handle_input("\\", window, cx);
 7026        editor.handle_input("\"", window, cx);
 7027    });
 7028    cx.assert_editor_state(
 7029        &r#"
 7030            let x = "\"ˇ "
 7031        "#
 7032        .unindent(),
 7033    );
 7034
 7035    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7036    // mark. Nothing is inserted.
 7037    cx.update_editor(|editor, window, cx| {
 7038        editor.move_right(&Default::default(), window, cx);
 7039        editor.handle_input("\"", window, cx);
 7040    });
 7041    cx.assert_editor_state(
 7042        &r#"
 7043            let x = "\" "ˇ
 7044        "#
 7045        .unindent(),
 7046    );
 7047}
 7048
 7049#[gpui::test]
 7050async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7051    init_test(cx, |_| {});
 7052
 7053    let language = Arc::new(Language::new(
 7054        LanguageConfig {
 7055            brackets: BracketPairConfig {
 7056                pairs: vec![
 7057                    BracketPair {
 7058                        start: "{".to_string(),
 7059                        end: "}".to_string(),
 7060                        close: true,
 7061                        surround: true,
 7062                        newline: true,
 7063                    },
 7064                    BracketPair {
 7065                        start: "/* ".to_string(),
 7066                        end: "*/".to_string(),
 7067                        close: true,
 7068                        surround: true,
 7069                        ..Default::default()
 7070                    },
 7071                ],
 7072                ..Default::default()
 7073            },
 7074            ..Default::default()
 7075        },
 7076        Some(tree_sitter_rust::LANGUAGE.into()),
 7077    ));
 7078
 7079    let text = r#"
 7080        a
 7081        b
 7082        c
 7083    "#
 7084    .unindent();
 7085
 7086    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7087    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7088    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7089    editor
 7090        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7091        .await;
 7092
 7093    editor.update_in(cx, |editor, window, cx| {
 7094        editor.change_selections(None, window, cx, |s| {
 7095            s.select_display_ranges([
 7096                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7097                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7098                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7099            ])
 7100        });
 7101
 7102        editor.handle_input("{", window, cx);
 7103        editor.handle_input("{", window, cx);
 7104        editor.handle_input("{", window, cx);
 7105        assert_eq!(
 7106            editor.text(cx),
 7107            "
 7108                {{{a}}}
 7109                {{{b}}}
 7110                {{{c}}}
 7111            "
 7112            .unindent()
 7113        );
 7114        assert_eq!(
 7115            editor.selections.display_ranges(cx),
 7116            [
 7117                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7118                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7119                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7120            ]
 7121        );
 7122
 7123        editor.undo(&Undo, window, cx);
 7124        editor.undo(&Undo, window, cx);
 7125        editor.undo(&Undo, window, cx);
 7126        assert_eq!(
 7127            editor.text(cx),
 7128            "
 7129                a
 7130                b
 7131                c
 7132            "
 7133            .unindent()
 7134        );
 7135        assert_eq!(
 7136            editor.selections.display_ranges(cx),
 7137            [
 7138                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7139                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7140                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7141            ]
 7142        );
 7143
 7144        // Ensure inserting the first character of a multi-byte bracket pair
 7145        // doesn't surround the selections with the bracket.
 7146        editor.handle_input("/", window, cx);
 7147        assert_eq!(
 7148            editor.text(cx),
 7149            "
 7150                /
 7151                /
 7152                /
 7153            "
 7154            .unindent()
 7155        );
 7156        assert_eq!(
 7157            editor.selections.display_ranges(cx),
 7158            [
 7159                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7160                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7161                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7162            ]
 7163        );
 7164
 7165        editor.undo(&Undo, window, cx);
 7166        assert_eq!(
 7167            editor.text(cx),
 7168            "
 7169                a
 7170                b
 7171                c
 7172            "
 7173            .unindent()
 7174        );
 7175        assert_eq!(
 7176            editor.selections.display_ranges(cx),
 7177            [
 7178                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7179                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7180                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7181            ]
 7182        );
 7183
 7184        // Ensure inserting the last character of a multi-byte bracket pair
 7185        // doesn't surround the selections with the bracket.
 7186        editor.handle_input("*", window, cx);
 7187        assert_eq!(
 7188            editor.text(cx),
 7189            "
 7190                *
 7191                *
 7192                *
 7193            "
 7194            .unindent()
 7195        );
 7196        assert_eq!(
 7197            editor.selections.display_ranges(cx),
 7198            [
 7199                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7200                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7201                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7202            ]
 7203        );
 7204    });
 7205}
 7206
 7207#[gpui::test]
 7208async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7209    init_test(cx, |_| {});
 7210
 7211    let language = Arc::new(Language::new(
 7212        LanguageConfig {
 7213            brackets: BracketPairConfig {
 7214                pairs: vec![BracketPair {
 7215                    start: "{".to_string(),
 7216                    end: "}".to_string(),
 7217                    close: true,
 7218                    surround: true,
 7219                    newline: true,
 7220                }],
 7221                ..Default::default()
 7222            },
 7223            autoclose_before: "}".to_string(),
 7224            ..Default::default()
 7225        },
 7226        Some(tree_sitter_rust::LANGUAGE.into()),
 7227    ));
 7228
 7229    let text = r#"
 7230        a
 7231        b
 7232        c
 7233    "#
 7234    .unindent();
 7235
 7236    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7237    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7238    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7239    editor
 7240        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7241        .await;
 7242
 7243    editor.update_in(cx, |editor, window, cx| {
 7244        editor.change_selections(None, window, cx, |s| {
 7245            s.select_ranges([
 7246                Point::new(0, 1)..Point::new(0, 1),
 7247                Point::new(1, 1)..Point::new(1, 1),
 7248                Point::new(2, 1)..Point::new(2, 1),
 7249            ])
 7250        });
 7251
 7252        editor.handle_input("{", window, cx);
 7253        editor.handle_input("{", window, cx);
 7254        editor.handle_input("_", window, cx);
 7255        assert_eq!(
 7256            editor.text(cx),
 7257            "
 7258                a{{_}}
 7259                b{{_}}
 7260                c{{_}}
 7261            "
 7262            .unindent()
 7263        );
 7264        assert_eq!(
 7265            editor.selections.ranges::<Point>(cx),
 7266            [
 7267                Point::new(0, 4)..Point::new(0, 4),
 7268                Point::new(1, 4)..Point::new(1, 4),
 7269                Point::new(2, 4)..Point::new(2, 4)
 7270            ]
 7271        );
 7272
 7273        editor.backspace(&Default::default(), window, cx);
 7274        editor.backspace(&Default::default(), window, cx);
 7275        assert_eq!(
 7276            editor.text(cx),
 7277            "
 7278                a{}
 7279                b{}
 7280                c{}
 7281            "
 7282            .unindent()
 7283        );
 7284        assert_eq!(
 7285            editor.selections.ranges::<Point>(cx),
 7286            [
 7287                Point::new(0, 2)..Point::new(0, 2),
 7288                Point::new(1, 2)..Point::new(1, 2),
 7289                Point::new(2, 2)..Point::new(2, 2)
 7290            ]
 7291        );
 7292
 7293        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7294        assert_eq!(
 7295            editor.text(cx),
 7296            "
 7297                a
 7298                b
 7299                c
 7300            "
 7301            .unindent()
 7302        );
 7303        assert_eq!(
 7304            editor.selections.ranges::<Point>(cx),
 7305            [
 7306                Point::new(0, 1)..Point::new(0, 1),
 7307                Point::new(1, 1)..Point::new(1, 1),
 7308                Point::new(2, 1)..Point::new(2, 1)
 7309            ]
 7310        );
 7311    });
 7312}
 7313
 7314#[gpui::test]
 7315async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7316    init_test(cx, |settings| {
 7317        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7318    });
 7319
 7320    let mut cx = EditorTestContext::new(cx).await;
 7321
 7322    let language = Arc::new(Language::new(
 7323        LanguageConfig {
 7324            brackets: BracketPairConfig {
 7325                pairs: vec![
 7326                    BracketPair {
 7327                        start: "{".to_string(),
 7328                        end: "}".to_string(),
 7329                        close: true,
 7330                        surround: true,
 7331                        newline: true,
 7332                    },
 7333                    BracketPair {
 7334                        start: "(".to_string(),
 7335                        end: ")".to_string(),
 7336                        close: true,
 7337                        surround: true,
 7338                        newline: true,
 7339                    },
 7340                    BracketPair {
 7341                        start: "[".to_string(),
 7342                        end: "]".to_string(),
 7343                        close: false,
 7344                        surround: true,
 7345                        newline: true,
 7346                    },
 7347                ],
 7348                ..Default::default()
 7349            },
 7350            autoclose_before: "})]".to_string(),
 7351            ..Default::default()
 7352        },
 7353        Some(tree_sitter_rust::LANGUAGE.into()),
 7354    ));
 7355
 7356    cx.language_registry().add(language.clone());
 7357    cx.update_buffer(|buffer, cx| {
 7358        buffer.set_language(Some(language), cx);
 7359    });
 7360
 7361    cx.set_state(
 7362        &"
 7363            {(ˇ)}
 7364            [[ˇ]]
 7365            {(ˇ)}
 7366        "
 7367        .unindent(),
 7368    );
 7369
 7370    cx.update_editor(|editor, window, cx| {
 7371        editor.backspace(&Default::default(), window, cx);
 7372        editor.backspace(&Default::default(), window, cx);
 7373    });
 7374
 7375    cx.assert_editor_state(
 7376        &"
 7377            ˇ
 7378            ˇ]]
 7379            ˇ
 7380        "
 7381        .unindent(),
 7382    );
 7383
 7384    cx.update_editor(|editor, window, cx| {
 7385        editor.handle_input("{", window, cx);
 7386        editor.handle_input("{", window, cx);
 7387        editor.move_right(&MoveRight, window, cx);
 7388        editor.move_right(&MoveRight, window, cx);
 7389        editor.move_left(&MoveLeft, window, cx);
 7390        editor.move_left(&MoveLeft, window, cx);
 7391        editor.backspace(&Default::default(), window, cx);
 7392    });
 7393
 7394    cx.assert_editor_state(
 7395        &"
 7396            {ˇ}
 7397            {ˇ}]]
 7398            {ˇ}
 7399        "
 7400        .unindent(),
 7401    );
 7402
 7403    cx.update_editor(|editor, window, cx| {
 7404        editor.backspace(&Default::default(), window, cx);
 7405    });
 7406
 7407    cx.assert_editor_state(
 7408        &"
 7409            ˇ
 7410            ˇ]]
 7411            ˇ
 7412        "
 7413        .unindent(),
 7414    );
 7415}
 7416
 7417#[gpui::test]
 7418async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7419    init_test(cx, |_| {});
 7420
 7421    let language = Arc::new(Language::new(
 7422        LanguageConfig::default(),
 7423        Some(tree_sitter_rust::LANGUAGE.into()),
 7424    ));
 7425
 7426    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7427    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7428    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7429    editor
 7430        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7431        .await;
 7432
 7433    editor.update_in(cx, |editor, window, cx| {
 7434        editor.set_auto_replace_emoji_shortcode(true);
 7435
 7436        editor.handle_input("Hello ", window, cx);
 7437        editor.handle_input(":wave", window, cx);
 7438        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7439
 7440        editor.handle_input(":", window, cx);
 7441        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7442
 7443        editor.handle_input(" :smile", window, cx);
 7444        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7445
 7446        editor.handle_input(":", window, cx);
 7447        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7448
 7449        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7450        editor.handle_input(":wave", window, cx);
 7451        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7452
 7453        editor.handle_input(":", window, cx);
 7454        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7455
 7456        editor.handle_input(":1", window, cx);
 7457        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7458
 7459        editor.handle_input(":", window, cx);
 7460        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7461
 7462        // Ensure shortcode does not get replaced when it is part of a word
 7463        editor.handle_input(" Test:wave", window, cx);
 7464        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7465
 7466        editor.handle_input(":", window, cx);
 7467        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7468
 7469        editor.set_auto_replace_emoji_shortcode(false);
 7470
 7471        // Ensure shortcode does not get replaced when auto replace is off
 7472        editor.handle_input(" :wave", window, cx);
 7473        assert_eq!(
 7474            editor.text(cx),
 7475            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7476        );
 7477
 7478        editor.handle_input(":", window, cx);
 7479        assert_eq!(
 7480            editor.text(cx),
 7481            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7482        );
 7483    });
 7484}
 7485
 7486#[gpui::test]
 7487async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7488    init_test(cx, |_| {});
 7489
 7490    let (text, insertion_ranges) = marked_text_ranges(
 7491        indoc! {"
 7492            ˇ
 7493        "},
 7494        false,
 7495    );
 7496
 7497    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7498    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7499
 7500    _ = editor.update_in(cx, |editor, window, cx| {
 7501        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7502
 7503        editor
 7504            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7505            .unwrap();
 7506
 7507        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7508            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7509            assert_eq!(editor.text(cx), expected_text);
 7510            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7511        }
 7512
 7513        assert(
 7514            editor,
 7515            cx,
 7516            indoc! {"
 7517            type «» =•
 7518            "},
 7519        );
 7520
 7521        assert!(editor.context_menu_visible(), "There should be a matches");
 7522    });
 7523}
 7524
 7525#[gpui::test]
 7526async fn test_snippets(cx: &mut TestAppContext) {
 7527    init_test(cx, |_| {});
 7528
 7529    let (text, insertion_ranges) = marked_text_ranges(
 7530        indoc! {"
 7531            a.ˇ b
 7532            a.ˇ b
 7533            a.ˇ b
 7534        "},
 7535        false,
 7536    );
 7537
 7538    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7539    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7540
 7541    editor.update_in(cx, |editor, window, cx| {
 7542        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7543
 7544        editor
 7545            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7546            .unwrap();
 7547
 7548        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7549            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7550            assert_eq!(editor.text(cx), expected_text);
 7551            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7552        }
 7553
 7554        assert(
 7555            editor,
 7556            cx,
 7557            indoc! {"
 7558                a.f(«one», two, «three») b
 7559                a.f(«one», two, «three») b
 7560                a.f(«one», two, «three») b
 7561            "},
 7562        );
 7563
 7564        // Can't move earlier than the first tab stop
 7565        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7566        assert(
 7567            editor,
 7568            cx,
 7569            indoc! {"
 7570                a.f(«one», two, «three») b
 7571                a.f(«one», two, «three») b
 7572                a.f(«one», two, «three») b
 7573            "},
 7574        );
 7575
 7576        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7577        assert(
 7578            editor,
 7579            cx,
 7580            indoc! {"
 7581                a.f(one, «two», three) b
 7582                a.f(one, «two», three) b
 7583                a.f(one, «two», three) b
 7584            "},
 7585        );
 7586
 7587        editor.move_to_prev_snippet_tabstop(window, cx);
 7588        assert(
 7589            editor,
 7590            cx,
 7591            indoc! {"
 7592                a.f(«one», two, «three») b
 7593                a.f(«one», two, «three») b
 7594                a.f(«one», two, «three») b
 7595            "},
 7596        );
 7597
 7598        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7599        assert(
 7600            editor,
 7601            cx,
 7602            indoc! {"
 7603                a.f(one, «two», three) b
 7604                a.f(one, «two», three) b
 7605                a.f(one, «two», three) b
 7606            "},
 7607        );
 7608        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7609        assert(
 7610            editor,
 7611            cx,
 7612            indoc! {"
 7613                a.f(one, two, three)ˇ b
 7614                a.f(one, two, three)ˇ b
 7615                a.f(one, two, three)ˇ b
 7616            "},
 7617        );
 7618
 7619        // As soon as the last tab stop is reached, snippet state is gone
 7620        editor.move_to_prev_snippet_tabstop(window, cx);
 7621        assert(
 7622            editor,
 7623            cx,
 7624            indoc! {"
 7625                a.f(one, two, three)ˇ b
 7626                a.f(one, two, three)ˇ b
 7627                a.f(one, two, three)ˇ b
 7628            "},
 7629        );
 7630    });
 7631}
 7632
 7633#[gpui::test]
 7634async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7635    init_test(cx, |_| {});
 7636
 7637    let fs = FakeFs::new(cx.executor());
 7638    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7639
 7640    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7641
 7642    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7643    language_registry.add(rust_lang());
 7644    let mut fake_servers = language_registry.register_fake_lsp(
 7645        "Rust",
 7646        FakeLspAdapter {
 7647            capabilities: lsp::ServerCapabilities {
 7648                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7649                ..Default::default()
 7650            },
 7651            ..Default::default()
 7652        },
 7653    );
 7654
 7655    let buffer = project
 7656        .update(cx, |project, cx| {
 7657            project.open_local_buffer(path!("/file.rs"), cx)
 7658        })
 7659        .await
 7660        .unwrap();
 7661
 7662    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7663    let (editor, cx) = cx.add_window_view(|window, cx| {
 7664        build_editor_with_project(project.clone(), buffer, window, cx)
 7665    });
 7666    editor.update_in(cx, |editor, window, cx| {
 7667        editor.set_text("one\ntwo\nthree\n", window, cx)
 7668    });
 7669    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7670
 7671    cx.executor().start_waiting();
 7672    let fake_server = fake_servers.next().await.unwrap();
 7673
 7674    let save = editor
 7675        .update_in(cx, |editor, window, cx| {
 7676            editor.save(true, project.clone(), window, cx)
 7677        })
 7678        .unwrap();
 7679    fake_server
 7680        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7681            assert_eq!(
 7682                params.text_document.uri,
 7683                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7684            );
 7685            assert_eq!(params.options.tab_size, 4);
 7686            Ok(Some(vec![lsp::TextEdit::new(
 7687                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7688                ", ".to_string(),
 7689            )]))
 7690        })
 7691        .next()
 7692        .await;
 7693    cx.executor().start_waiting();
 7694    save.await;
 7695
 7696    assert_eq!(
 7697        editor.update(cx, |editor, cx| editor.text(cx)),
 7698        "one, two\nthree\n"
 7699    );
 7700    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7701
 7702    editor.update_in(cx, |editor, window, cx| {
 7703        editor.set_text("one\ntwo\nthree\n", window, cx)
 7704    });
 7705    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7706
 7707    // Ensure we can still save even if formatting hangs.
 7708    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7709        move |params, _| async move {
 7710            assert_eq!(
 7711                params.text_document.uri,
 7712                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7713            );
 7714            futures::future::pending::<()>().await;
 7715            unreachable!()
 7716        },
 7717    );
 7718    let save = editor
 7719        .update_in(cx, |editor, window, cx| {
 7720            editor.save(true, project.clone(), window, cx)
 7721        })
 7722        .unwrap();
 7723    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7724    cx.executor().start_waiting();
 7725    save.await;
 7726    assert_eq!(
 7727        editor.update(cx, |editor, cx| editor.text(cx)),
 7728        "one\ntwo\nthree\n"
 7729    );
 7730    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7731
 7732    // For non-dirty buffer, no formatting request should be sent
 7733    let save = editor
 7734        .update_in(cx, |editor, window, cx| {
 7735            editor.save(true, project.clone(), window, cx)
 7736        })
 7737        .unwrap();
 7738    let _pending_format_request = fake_server
 7739        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7740            panic!("Should not be invoked on non-dirty buffer");
 7741        })
 7742        .next();
 7743    cx.executor().start_waiting();
 7744    save.await;
 7745
 7746    // Set rust language override and assert overridden tabsize is sent to language server
 7747    update_test_language_settings(cx, |settings| {
 7748        settings.languages.insert(
 7749            "Rust".into(),
 7750            LanguageSettingsContent {
 7751                tab_size: NonZeroU32::new(8),
 7752                ..Default::default()
 7753            },
 7754        );
 7755    });
 7756
 7757    editor.update_in(cx, |editor, window, cx| {
 7758        editor.set_text("somehting_new\n", window, cx)
 7759    });
 7760    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7761    let save = editor
 7762        .update_in(cx, |editor, window, cx| {
 7763            editor.save(true, project.clone(), window, cx)
 7764        })
 7765        .unwrap();
 7766    fake_server
 7767        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7768            assert_eq!(
 7769                params.text_document.uri,
 7770                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7771            );
 7772            assert_eq!(params.options.tab_size, 8);
 7773            Ok(Some(vec![]))
 7774        })
 7775        .next()
 7776        .await;
 7777    cx.executor().start_waiting();
 7778    save.await;
 7779}
 7780
 7781#[gpui::test]
 7782async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7783    init_test(cx, |_| {});
 7784
 7785    let cols = 4;
 7786    let rows = 10;
 7787    let sample_text_1 = sample_text(rows, cols, 'a');
 7788    assert_eq!(
 7789        sample_text_1,
 7790        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7791    );
 7792    let sample_text_2 = sample_text(rows, cols, 'l');
 7793    assert_eq!(
 7794        sample_text_2,
 7795        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7796    );
 7797    let sample_text_3 = sample_text(rows, cols, 'v');
 7798    assert_eq!(
 7799        sample_text_3,
 7800        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7801    );
 7802
 7803    let fs = FakeFs::new(cx.executor());
 7804    fs.insert_tree(
 7805        path!("/a"),
 7806        json!({
 7807            "main.rs": sample_text_1,
 7808            "other.rs": sample_text_2,
 7809            "lib.rs": sample_text_3,
 7810        }),
 7811    )
 7812    .await;
 7813
 7814    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7815    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7816    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7817
 7818    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7819    language_registry.add(rust_lang());
 7820    let mut fake_servers = language_registry.register_fake_lsp(
 7821        "Rust",
 7822        FakeLspAdapter {
 7823            capabilities: lsp::ServerCapabilities {
 7824                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7825                ..Default::default()
 7826            },
 7827            ..Default::default()
 7828        },
 7829    );
 7830
 7831    let worktree = project.update(cx, |project, cx| {
 7832        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7833        assert_eq!(worktrees.len(), 1);
 7834        worktrees.pop().unwrap()
 7835    });
 7836    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7837
 7838    let buffer_1 = project
 7839        .update(cx, |project, cx| {
 7840            project.open_buffer((worktree_id, "main.rs"), cx)
 7841        })
 7842        .await
 7843        .unwrap();
 7844    let buffer_2 = project
 7845        .update(cx, |project, cx| {
 7846            project.open_buffer((worktree_id, "other.rs"), cx)
 7847        })
 7848        .await
 7849        .unwrap();
 7850    let buffer_3 = project
 7851        .update(cx, |project, cx| {
 7852            project.open_buffer((worktree_id, "lib.rs"), cx)
 7853        })
 7854        .await
 7855        .unwrap();
 7856
 7857    let multi_buffer = cx.new(|cx| {
 7858        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7859        multi_buffer.push_excerpts(
 7860            buffer_1.clone(),
 7861            [
 7862                ExcerptRange {
 7863                    context: Point::new(0, 0)..Point::new(3, 0),
 7864                    primary: None,
 7865                },
 7866                ExcerptRange {
 7867                    context: Point::new(5, 0)..Point::new(7, 0),
 7868                    primary: None,
 7869                },
 7870                ExcerptRange {
 7871                    context: Point::new(9, 0)..Point::new(10, 4),
 7872                    primary: None,
 7873                },
 7874            ],
 7875            cx,
 7876        );
 7877        multi_buffer.push_excerpts(
 7878            buffer_2.clone(),
 7879            [
 7880                ExcerptRange {
 7881                    context: Point::new(0, 0)..Point::new(3, 0),
 7882                    primary: None,
 7883                },
 7884                ExcerptRange {
 7885                    context: Point::new(5, 0)..Point::new(7, 0),
 7886                    primary: None,
 7887                },
 7888                ExcerptRange {
 7889                    context: Point::new(9, 0)..Point::new(10, 4),
 7890                    primary: None,
 7891                },
 7892            ],
 7893            cx,
 7894        );
 7895        multi_buffer.push_excerpts(
 7896            buffer_3.clone(),
 7897            [
 7898                ExcerptRange {
 7899                    context: Point::new(0, 0)..Point::new(3, 0),
 7900                    primary: None,
 7901                },
 7902                ExcerptRange {
 7903                    context: Point::new(5, 0)..Point::new(7, 0),
 7904                    primary: None,
 7905                },
 7906                ExcerptRange {
 7907                    context: Point::new(9, 0)..Point::new(10, 4),
 7908                    primary: None,
 7909                },
 7910            ],
 7911            cx,
 7912        );
 7913        multi_buffer
 7914    });
 7915    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7916        Editor::new(
 7917            EditorMode::Full,
 7918            multi_buffer,
 7919            Some(project.clone()),
 7920            window,
 7921            cx,
 7922        )
 7923    });
 7924
 7925    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7926        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7927            s.select_ranges(Some(1..2))
 7928        });
 7929        editor.insert("|one|two|three|", window, cx);
 7930    });
 7931    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7932    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7933        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7934            s.select_ranges(Some(60..70))
 7935        });
 7936        editor.insert("|four|five|six|", window, cx);
 7937    });
 7938    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7939
 7940    // First two buffers should be edited, but not the third one.
 7941    assert_eq!(
 7942        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7943        "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}",
 7944    );
 7945    buffer_1.update(cx, |buffer, _| {
 7946        assert!(buffer.is_dirty());
 7947        assert_eq!(
 7948            buffer.text(),
 7949            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7950        )
 7951    });
 7952    buffer_2.update(cx, |buffer, _| {
 7953        assert!(buffer.is_dirty());
 7954        assert_eq!(
 7955            buffer.text(),
 7956            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7957        )
 7958    });
 7959    buffer_3.update(cx, |buffer, _| {
 7960        assert!(!buffer.is_dirty());
 7961        assert_eq!(buffer.text(), sample_text_3,)
 7962    });
 7963    cx.executor().run_until_parked();
 7964
 7965    cx.executor().start_waiting();
 7966    let save = multi_buffer_editor
 7967        .update_in(cx, |editor, window, cx| {
 7968            editor.save(true, project.clone(), window, cx)
 7969        })
 7970        .unwrap();
 7971
 7972    let fake_server = fake_servers.next().await.unwrap();
 7973    fake_server
 7974        .server
 7975        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7976            Ok(Some(vec![lsp::TextEdit::new(
 7977                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7978                format!("[{} formatted]", params.text_document.uri),
 7979            )]))
 7980        })
 7981        .detach();
 7982    save.await;
 7983
 7984    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7985    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7986    assert_eq!(
 7987        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7988        uri!("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}"),
 7989    );
 7990    buffer_1.update(cx, |buffer, _| {
 7991        assert!(!buffer.is_dirty());
 7992        assert_eq!(
 7993            buffer.text(),
 7994            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7995        )
 7996    });
 7997    buffer_2.update(cx, |buffer, _| {
 7998        assert!(!buffer.is_dirty());
 7999        assert_eq!(
 8000            buffer.text(),
 8001            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8002        )
 8003    });
 8004    buffer_3.update(cx, |buffer, _| {
 8005        assert!(!buffer.is_dirty());
 8006        assert_eq!(buffer.text(), sample_text_3,)
 8007    });
 8008}
 8009
 8010#[gpui::test]
 8011async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8012    init_test(cx, |_| {});
 8013
 8014    let fs = FakeFs::new(cx.executor());
 8015    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8016
 8017    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8018
 8019    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8020    language_registry.add(rust_lang());
 8021    let mut fake_servers = language_registry.register_fake_lsp(
 8022        "Rust",
 8023        FakeLspAdapter {
 8024            capabilities: lsp::ServerCapabilities {
 8025                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8026                ..Default::default()
 8027            },
 8028            ..Default::default()
 8029        },
 8030    );
 8031
 8032    let buffer = project
 8033        .update(cx, |project, cx| {
 8034            project.open_local_buffer(path!("/file.rs"), cx)
 8035        })
 8036        .await
 8037        .unwrap();
 8038
 8039    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8040    let (editor, cx) = cx.add_window_view(|window, cx| {
 8041        build_editor_with_project(project.clone(), buffer, window, cx)
 8042    });
 8043    editor.update_in(cx, |editor, window, cx| {
 8044        editor.set_text("one\ntwo\nthree\n", window, cx)
 8045    });
 8046    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8047
 8048    cx.executor().start_waiting();
 8049    let fake_server = fake_servers.next().await.unwrap();
 8050
 8051    let save = editor
 8052        .update_in(cx, |editor, window, cx| {
 8053            editor.save(true, project.clone(), window, cx)
 8054        })
 8055        .unwrap();
 8056    fake_server
 8057        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8058            assert_eq!(
 8059                params.text_document.uri,
 8060                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8061            );
 8062            assert_eq!(params.options.tab_size, 4);
 8063            Ok(Some(vec![lsp::TextEdit::new(
 8064                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8065                ", ".to_string(),
 8066            )]))
 8067        })
 8068        .next()
 8069        .await;
 8070    cx.executor().start_waiting();
 8071    save.await;
 8072    assert_eq!(
 8073        editor.update(cx, |editor, cx| editor.text(cx)),
 8074        "one, two\nthree\n"
 8075    );
 8076    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8077
 8078    editor.update_in(cx, |editor, window, cx| {
 8079        editor.set_text("one\ntwo\nthree\n", window, cx)
 8080    });
 8081    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8082
 8083    // Ensure we can still save even if formatting hangs.
 8084    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8085        move |params, _| async move {
 8086            assert_eq!(
 8087                params.text_document.uri,
 8088                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8089            );
 8090            futures::future::pending::<()>().await;
 8091            unreachable!()
 8092        },
 8093    );
 8094    let save = editor
 8095        .update_in(cx, |editor, window, cx| {
 8096            editor.save(true, project.clone(), window, cx)
 8097        })
 8098        .unwrap();
 8099    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8100    cx.executor().start_waiting();
 8101    save.await;
 8102    assert_eq!(
 8103        editor.update(cx, |editor, cx| editor.text(cx)),
 8104        "one\ntwo\nthree\n"
 8105    );
 8106    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8107
 8108    // For non-dirty buffer, no formatting request should be sent
 8109    let save = editor
 8110        .update_in(cx, |editor, window, cx| {
 8111            editor.save(true, project.clone(), window, cx)
 8112        })
 8113        .unwrap();
 8114    let _pending_format_request = fake_server
 8115        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8116            panic!("Should not be invoked on non-dirty buffer");
 8117        })
 8118        .next();
 8119    cx.executor().start_waiting();
 8120    save.await;
 8121
 8122    // Set Rust language override and assert overridden tabsize is sent to language server
 8123    update_test_language_settings(cx, |settings| {
 8124        settings.languages.insert(
 8125            "Rust".into(),
 8126            LanguageSettingsContent {
 8127                tab_size: NonZeroU32::new(8),
 8128                ..Default::default()
 8129            },
 8130        );
 8131    });
 8132
 8133    editor.update_in(cx, |editor, window, cx| {
 8134        editor.set_text("somehting_new\n", window, cx)
 8135    });
 8136    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8137    let save = editor
 8138        .update_in(cx, |editor, window, cx| {
 8139            editor.save(true, project.clone(), window, cx)
 8140        })
 8141        .unwrap();
 8142    fake_server
 8143        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8144            assert_eq!(
 8145                params.text_document.uri,
 8146                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8147            );
 8148            assert_eq!(params.options.tab_size, 8);
 8149            Ok(Some(vec![]))
 8150        })
 8151        .next()
 8152        .await;
 8153    cx.executor().start_waiting();
 8154    save.await;
 8155}
 8156
 8157#[gpui::test]
 8158async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8159    init_test(cx, |settings| {
 8160        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8161            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8162        ))
 8163    });
 8164
 8165    let fs = FakeFs::new(cx.executor());
 8166    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8167
 8168    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8169
 8170    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8171    language_registry.add(Arc::new(Language::new(
 8172        LanguageConfig {
 8173            name: "Rust".into(),
 8174            matcher: LanguageMatcher {
 8175                path_suffixes: vec!["rs".to_string()],
 8176                ..Default::default()
 8177            },
 8178            ..LanguageConfig::default()
 8179        },
 8180        Some(tree_sitter_rust::LANGUAGE.into()),
 8181    )));
 8182    update_test_language_settings(cx, |settings| {
 8183        // Enable Prettier formatting for the same buffer, and ensure
 8184        // LSP is called instead of Prettier.
 8185        settings.defaults.prettier = Some(PrettierSettings {
 8186            allowed: true,
 8187            ..PrettierSettings::default()
 8188        });
 8189    });
 8190    let mut fake_servers = language_registry.register_fake_lsp(
 8191        "Rust",
 8192        FakeLspAdapter {
 8193            capabilities: lsp::ServerCapabilities {
 8194                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8195                ..Default::default()
 8196            },
 8197            ..Default::default()
 8198        },
 8199    );
 8200
 8201    let buffer = project
 8202        .update(cx, |project, cx| {
 8203            project.open_local_buffer(path!("/file.rs"), cx)
 8204        })
 8205        .await
 8206        .unwrap();
 8207
 8208    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8209    let (editor, cx) = cx.add_window_view(|window, cx| {
 8210        build_editor_with_project(project.clone(), buffer, window, cx)
 8211    });
 8212    editor.update_in(cx, |editor, window, cx| {
 8213        editor.set_text("one\ntwo\nthree\n", window, cx)
 8214    });
 8215
 8216    cx.executor().start_waiting();
 8217    let fake_server = fake_servers.next().await.unwrap();
 8218
 8219    let format = editor
 8220        .update_in(cx, |editor, window, cx| {
 8221            editor.perform_format(
 8222                project.clone(),
 8223                FormatTrigger::Manual,
 8224                FormatTarget::Buffers,
 8225                window,
 8226                cx,
 8227            )
 8228        })
 8229        .unwrap();
 8230    fake_server
 8231        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8232            assert_eq!(
 8233                params.text_document.uri,
 8234                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8235            );
 8236            assert_eq!(params.options.tab_size, 4);
 8237            Ok(Some(vec![lsp::TextEdit::new(
 8238                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8239                ", ".to_string(),
 8240            )]))
 8241        })
 8242        .next()
 8243        .await;
 8244    cx.executor().start_waiting();
 8245    format.await;
 8246    assert_eq!(
 8247        editor.update(cx, |editor, cx| editor.text(cx)),
 8248        "one, two\nthree\n"
 8249    );
 8250
 8251    editor.update_in(cx, |editor, window, cx| {
 8252        editor.set_text("one\ntwo\nthree\n", window, cx)
 8253    });
 8254    // Ensure we don't lock if formatting hangs.
 8255    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8256        move |params, _| async move {
 8257            assert_eq!(
 8258                params.text_document.uri,
 8259                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8260            );
 8261            futures::future::pending::<()>().await;
 8262            unreachable!()
 8263        },
 8264    );
 8265    let format = editor
 8266        .update_in(cx, |editor, window, cx| {
 8267            editor.perform_format(
 8268                project,
 8269                FormatTrigger::Manual,
 8270                FormatTarget::Buffers,
 8271                window,
 8272                cx,
 8273            )
 8274        })
 8275        .unwrap();
 8276    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8277    cx.executor().start_waiting();
 8278    format.await;
 8279    assert_eq!(
 8280        editor.update(cx, |editor, cx| editor.text(cx)),
 8281        "one\ntwo\nthree\n"
 8282    );
 8283}
 8284
 8285#[gpui::test]
 8286async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8287    init_test(cx, |settings| {
 8288        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8289            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8290        ))
 8291    });
 8292
 8293    let fs = FakeFs::new(cx.executor());
 8294    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8295
 8296    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8297
 8298    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8299    language_registry.add(Arc::new(Language::new(
 8300        LanguageConfig {
 8301            name: "TypeScript".into(),
 8302            matcher: LanguageMatcher {
 8303                path_suffixes: vec!["ts".to_string()],
 8304                ..Default::default()
 8305            },
 8306            ..LanguageConfig::default()
 8307        },
 8308        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8309    )));
 8310    update_test_language_settings(cx, |settings| {
 8311        settings.defaults.prettier = Some(PrettierSettings {
 8312            allowed: true,
 8313            ..PrettierSettings::default()
 8314        });
 8315    });
 8316    let mut fake_servers = language_registry.register_fake_lsp(
 8317        "TypeScript",
 8318        FakeLspAdapter {
 8319            capabilities: lsp::ServerCapabilities {
 8320                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8321                ..Default::default()
 8322            },
 8323            ..Default::default()
 8324        },
 8325    );
 8326
 8327    let buffer = project
 8328        .update(cx, |project, cx| {
 8329            project.open_local_buffer(path!("/file.ts"), cx)
 8330        })
 8331        .await
 8332        .unwrap();
 8333
 8334    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8335    let (editor, cx) = cx.add_window_view(|window, cx| {
 8336        build_editor_with_project(project.clone(), buffer, window, cx)
 8337    });
 8338    editor.update_in(cx, |editor, window, cx| {
 8339        editor.set_text(
 8340            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8341            window,
 8342            cx,
 8343        )
 8344    });
 8345
 8346    cx.executor().start_waiting();
 8347    let fake_server = fake_servers.next().await.unwrap();
 8348
 8349    let format = editor
 8350        .update_in(cx, |editor, window, cx| {
 8351            editor.perform_code_action_kind(
 8352                project.clone(),
 8353                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8354                window,
 8355                cx,
 8356            )
 8357        })
 8358        .unwrap();
 8359    fake_server
 8360        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8361            assert_eq!(
 8362                params.text_document.uri,
 8363                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8364            );
 8365            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8366                lsp::CodeAction {
 8367                    title: "Organize Imports".to_string(),
 8368                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8369                    edit: Some(lsp::WorkspaceEdit {
 8370                        changes: Some(
 8371                            [(
 8372                                params.text_document.uri.clone(),
 8373                                vec![lsp::TextEdit::new(
 8374                                    lsp::Range::new(
 8375                                        lsp::Position::new(1, 0),
 8376                                        lsp::Position::new(2, 0),
 8377                                    ),
 8378                                    "".to_string(),
 8379                                )],
 8380                            )]
 8381                            .into_iter()
 8382                            .collect(),
 8383                        ),
 8384                        ..Default::default()
 8385                    }),
 8386                    ..Default::default()
 8387                },
 8388            )]))
 8389        })
 8390        .next()
 8391        .await;
 8392    cx.executor().start_waiting();
 8393    format.await;
 8394    assert_eq!(
 8395        editor.update(cx, |editor, cx| editor.text(cx)),
 8396        "import { a } from 'module';\n\nconst x = a;\n"
 8397    );
 8398
 8399    editor.update_in(cx, |editor, window, cx| {
 8400        editor.set_text(
 8401            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8402            window,
 8403            cx,
 8404        )
 8405    });
 8406    // Ensure we don't lock if code action hangs.
 8407    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8408        move |params, _| async move {
 8409            assert_eq!(
 8410                params.text_document.uri,
 8411                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8412            );
 8413            futures::future::pending::<()>().await;
 8414            unreachable!()
 8415        },
 8416    );
 8417    let format = editor
 8418        .update_in(cx, |editor, window, cx| {
 8419            editor.perform_code_action_kind(
 8420                project,
 8421                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8422                window,
 8423                cx,
 8424            )
 8425        })
 8426        .unwrap();
 8427    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8428    cx.executor().start_waiting();
 8429    format.await;
 8430    assert_eq!(
 8431        editor.update(cx, |editor, cx| editor.text(cx)),
 8432        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8433    );
 8434}
 8435
 8436#[gpui::test]
 8437async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8438    init_test(cx, |_| {});
 8439
 8440    let mut cx = EditorLspTestContext::new_rust(
 8441        lsp::ServerCapabilities {
 8442            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8443            ..Default::default()
 8444        },
 8445        cx,
 8446    )
 8447    .await;
 8448
 8449    cx.set_state(indoc! {"
 8450        one.twoˇ
 8451    "});
 8452
 8453    // The format request takes a long time. When it completes, it inserts
 8454    // a newline and an indent before the `.`
 8455    cx.lsp
 8456        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8457            let executor = cx.background_executor().clone();
 8458            async move {
 8459                executor.timer(Duration::from_millis(100)).await;
 8460                Ok(Some(vec![lsp::TextEdit {
 8461                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8462                    new_text: "\n    ".into(),
 8463                }]))
 8464            }
 8465        });
 8466
 8467    // Submit a format request.
 8468    let format_1 = cx
 8469        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8470        .unwrap();
 8471    cx.executor().run_until_parked();
 8472
 8473    // Submit a second format request.
 8474    let format_2 = cx
 8475        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8476        .unwrap();
 8477    cx.executor().run_until_parked();
 8478
 8479    // Wait for both format requests to complete
 8480    cx.executor().advance_clock(Duration::from_millis(200));
 8481    cx.executor().start_waiting();
 8482    format_1.await.unwrap();
 8483    cx.executor().start_waiting();
 8484    format_2.await.unwrap();
 8485
 8486    // The formatting edits only happens once.
 8487    cx.assert_editor_state(indoc! {"
 8488        one
 8489            .twoˇ
 8490    "});
 8491}
 8492
 8493#[gpui::test]
 8494async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8495    init_test(cx, |settings| {
 8496        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8497    });
 8498
 8499    let mut cx = EditorLspTestContext::new_rust(
 8500        lsp::ServerCapabilities {
 8501            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8502            ..Default::default()
 8503        },
 8504        cx,
 8505    )
 8506    .await;
 8507
 8508    // Set up a buffer white some trailing whitespace and no trailing newline.
 8509    cx.set_state(
 8510        &[
 8511            "one ",   //
 8512            "twoˇ",   //
 8513            "three ", //
 8514            "four",   //
 8515        ]
 8516        .join("\n"),
 8517    );
 8518
 8519    // Submit a format request.
 8520    let format = cx
 8521        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8522        .unwrap();
 8523
 8524    // Record which buffer changes have been sent to the language server
 8525    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8526    cx.lsp
 8527        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8528            let buffer_changes = buffer_changes.clone();
 8529            move |params, _| {
 8530                buffer_changes.lock().extend(
 8531                    params
 8532                        .content_changes
 8533                        .into_iter()
 8534                        .map(|e| (e.range.unwrap(), e.text)),
 8535                );
 8536            }
 8537        });
 8538
 8539    // Handle formatting requests to the language server.
 8540    cx.lsp
 8541        .set_request_handler::<lsp::request::Formatting, _, _>({
 8542            let buffer_changes = buffer_changes.clone();
 8543            move |_, _| {
 8544                // When formatting is requested, trailing whitespace has already been stripped,
 8545                // and the trailing newline has already been added.
 8546                assert_eq!(
 8547                    &buffer_changes.lock()[1..],
 8548                    &[
 8549                        (
 8550                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8551                            "".into()
 8552                        ),
 8553                        (
 8554                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8555                            "".into()
 8556                        ),
 8557                        (
 8558                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8559                            "\n".into()
 8560                        ),
 8561                    ]
 8562                );
 8563
 8564                // Insert blank lines between each line of the buffer.
 8565                async move {
 8566                    Ok(Some(vec![
 8567                        lsp::TextEdit {
 8568                            range: lsp::Range::new(
 8569                                lsp::Position::new(1, 0),
 8570                                lsp::Position::new(1, 0),
 8571                            ),
 8572                            new_text: "\n".into(),
 8573                        },
 8574                        lsp::TextEdit {
 8575                            range: lsp::Range::new(
 8576                                lsp::Position::new(2, 0),
 8577                                lsp::Position::new(2, 0),
 8578                            ),
 8579                            new_text: "\n".into(),
 8580                        },
 8581                    ]))
 8582                }
 8583            }
 8584        });
 8585
 8586    // After formatting the buffer, the trailing whitespace is stripped,
 8587    // a newline is appended, and the edits provided by the language server
 8588    // have been applied.
 8589    format.await.unwrap();
 8590    cx.assert_editor_state(
 8591        &[
 8592            "one",   //
 8593            "",      //
 8594            "twoˇ",  //
 8595            "",      //
 8596            "three", //
 8597            "four",  //
 8598            "",      //
 8599        ]
 8600        .join("\n"),
 8601    );
 8602
 8603    // Undoing the formatting undoes the trailing whitespace removal, the
 8604    // trailing newline, and the LSP edits.
 8605    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8606    cx.assert_editor_state(
 8607        &[
 8608            "one ",   //
 8609            "twoˇ",   //
 8610            "three ", //
 8611            "four",   //
 8612        ]
 8613        .join("\n"),
 8614    );
 8615}
 8616
 8617#[gpui::test]
 8618async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8619    cx: &mut TestAppContext,
 8620) {
 8621    init_test(cx, |_| {});
 8622
 8623    cx.update(|cx| {
 8624        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8625            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8626                settings.auto_signature_help = Some(true);
 8627            });
 8628        });
 8629    });
 8630
 8631    let mut cx = EditorLspTestContext::new_rust(
 8632        lsp::ServerCapabilities {
 8633            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8634                ..Default::default()
 8635            }),
 8636            ..Default::default()
 8637        },
 8638        cx,
 8639    )
 8640    .await;
 8641
 8642    let language = Language::new(
 8643        LanguageConfig {
 8644            name: "Rust".into(),
 8645            brackets: BracketPairConfig {
 8646                pairs: vec![
 8647                    BracketPair {
 8648                        start: "{".to_string(),
 8649                        end: "}".to_string(),
 8650                        close: true,
 8651                        surround: true,
 8652                        newline: true,
 8653                    },
 8654                    BracketPair {
 8655                        start: "(".to_string(),
 8656                        end: ")".to_string(),
 8657                        close: true,
 8658                        surround: true,
 8659                        newline: true,
 8660                    },
 8661                    BracketPair {
 8662                        start: "/*".to_string(),
 8663                        end: " */".to_string(),
 8664                        close: true,
 8665                        surround: true,
 8666                        newline: true,
 8667                    },
 8668                    BracketPair {
 8669                        start: "[".to_string(),
 8670                        end: "]".to_string(),
 8671                        close: false,
 8672                        surround: false,
 8673                        newline: true,
 8674                    },
 8675                    BracketPair {
 8676                        start: "\"".to_string(),
 8677                        end: "\"".to_string(),
 8678                        close: true,
 8679                        surround: true,
 8680                        newline: false,
 8681                    },
 8682                    BracketPair {
 8683                        start: "<".to_string(),
 8684                        end: ">".to_string(),
 8685                        close: false,
 8686                        surround: true,
 8687                        newline: true,
 8688                    },
 8689                ],
 8690                ..Default::default()
 8691            },
 8692            autoclose_before: "})]".to_string(),
 8693            ..Default::default()
 8694        },
 8695        Some(tree_sitter_rust::LANGUAGE.into()),
 8696    );
 8697    let language = Arc::new(language);
 8698
 8699    cx.language_registry().add(language.clone());
 8700    cx.update_buffer(|buffer, cx| {
 8701        buffer.set_language(Some(language), cx);
 8702    });
 8703
 8704    cx.set_state(
 8705        &r#"
 8706            fn main() {
 8707                sampleˇ
 8708            }
 8709        "#
 8710        .unindent(),
 8711    );
 8712
 8713    cx.update_editor(|editor, window, cx| {
 8714        editor.handle_input("(", window, cx);
 8715    });
 8716    cx.assert_editor_state(
 8717        &"
 8718            fn main() {
 8719                sample(ˇ)
 8720            }
 8721        "
 8722        .unindent(),
 8723    );
 8724
 8725    let mocked_response = lsp::SignatureHelp {
 8726        signatures: vec![lsp::SignatureInformation {
 8727            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8728            documentation: None,
 8729            parameters: Some(vec![
 8730                lsp::ParameterInformation {
 8731                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8732                    documentation: None,
 8733                },
 8734                lsp::ParameterInformation {
 8735                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8736                    documentation: None,
 8737                },
 8738            ]),
 8739            active_parameter: None,
 8740        }],
 8741        active_signature: Some(0),
 8742        active_parameter: Some(0),
 8743    };
 8744    handle_signature_help_request(&mut cx, mocked_response).await;
 8745
 8746    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8747        .await;
 8748
 8749    cx.editor(|editor, _, _| {
 8750        let signature_help_state = editor.signature_help_state.popover().cloned();
 8751        assert_eq!(
 8752            signature_help_state.unwrap().label,
 8753            "param1: u8, param2: u8"
 8754        );
 8755    });
 8756}
 8757
 8758#[gpui::test]
 8759async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8760    init_test(cx, |_| {});
 8761
 8762    cx.update(|cx| {
 8763        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8764            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8765                settings.auto_signature_help = Some(false);
 8766                settings.show_signature_help_after_edits = Some(false);
 8767            });
 8768        });
 8769    });
 8770
 8771    let mut cx = EditorLspTestContext::new_rust(
 8772        lsp::ServerCapabilities {
 8773            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8774                ..Default::default()
 8775            }),
 8776            ..Default::default()
 8777        },
 8778        cx,
 8779    )
 8780    .await;
 8781
 8782    let language = Language::new(
 8783        LanguageConfig {
 8784            name: "Rust".into(),
 8785            brackets: BracketPairConfig {
 8786                pairs: vec![
 8787                    BracketPair {
 8788                        start: "{".to_string(),
 8789                        end: "}".to_string(),
 8790                        close: true,
 8791                        surround: true,
 8792                        newline: true,
 8793                    },
 8794                    BracketPair {
 8795                        start: "(".to_string(),
 8796                        end: ")".to_string(),
 8797                        close: true,
 8798                        surround: true,
 8799                        newline: true,
 8800                    },
 8801                    BracketPair {
 8802                        start: "/*".to_string(),
 8803                        end: " */".to_string(),
 8804                        close: true,
 8805                        surround: true,
 8806                        newline: true,
 8807                    },
 8808                    BracketPair {
 8809                        start: "[".to_string(),
 8810                        end: "]".to_string(),
 8811                        close: false,
 8812                        surround: false,
 8813                        newline: true,
 8814                    },
 8815                    BracketPair {
 8816                        start: "\"".to_string(),
 8817                        end: "\"".to_string(),
 8818                        close: true,
 8819                        surround: true,
 8820                        newline: false,
 8821                    },
 8822                    BracketPair {
 8823                        start: "<".to_string(),
 8824                        end: ">".to_string(),
 8825                        close: false,
 8826                        surround: true,
 8827                        newline: true,
 8828                    },
 8829                ],
 8830                ..Default::default()
 8831            },
 8832            autoclose_before: "})]".to_string(),
 8833            ..Default::default()
 8834        },
 8835        Some(tree_sitter_rust::LANGUAGE.into()),
 8836    );
 8837    let language = Arc::new(language);
 8838
 8839    cx.language_registry().add(language.clone());
 8840    cx.update_buffer(|buffer, cx| {
 8841        buffer.set_language(Some(language), cx);
 8842    });
 8843
 8844    // Ensure that signature_help is not called when no signature help is enabled.
 8845    cx.set_state(
 8846        &r#"
 8847            fn main() {
 8848                sampleˇ
 8849            }
 8850        "#
 8851        .unindent(),
 8852    );
 8853    cx.update_editor(|editor, window, cx| {
 8854        editor.handle_input("(", window, cx);
 8855    });
 8856    cx.assert_editor_state(
 8857        &"
 8858            fn main() {
 8859                sample(ˇ)
 8860            }
 8861        "
 8862        .unindent(),
 8863    );
 8864    cx.editor(|editor, _, _| {
 8865        assert!(editor.signature_help_state.task().is_none());
 8866    });
 8867
 8868    let mocked_response = lsp::SignatureHelp {
 8869        signatures: vec![lsp::SignatureInformation {
 8870            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8871            documentation: None,
 8872            parameters: Some(vec![
 8873                lsp::ParameterInformation {
 8874                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8875                    documentation: None,
 8876                },
 8877                lsp::ParameterInformation {
 8878                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8879                    documentation: None,
 8880                },
 8881            ]),
 8882            active_parameter: None,
 8883        }],
 8884        active_signature: Some(0),
 8885        active_parameter: Some(0),
 8886    };
 8887
 8888    // Ensure that signature_help is called when enabled afte edits
 8889    cx.update(|_, cx| {
 8890        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8891            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8892                settings.auto_signature_help = Some(false);
 8893                settings.show_signature_help_after_edits = Some(true);
 8894            });
 8895        });
 8896    });
 8897    cx.set_state(
 8898        &r#"
 8899            fn main() {
 8900                sampleˇ
 8901            }
 8902        "#
 8903        .unindent(),
 8904    );
 8905    cx.update_editor(|editor, window, cx| {
 8906        editor.handle_input("(", window, cx);
 8907    });
 8908    cx.assert_editor_state(
 8909        &"
 8910            fn main() {
 8911                sample(ˇ)
 8912            }
 8913        "
 8914        .unindent(),
 8915    );
 8916    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8917    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8918        .await;
 8919    cx.update_editor(|editor, _, _| {
 8920        let signature_help_state = editor.signature_help_state.popover().cloned();
 8921        assert!(signature_help_state.is_some());
 8922        assert_eq!(
 8923            signature_help_state.unwrap().label,
 8924            "param1: u8, param2: u8"
 8925        );
 8926        editor.signature_help_state = SignatureHelpState::default();
 8927    });
 8928
 8929    // Ensure that signature_help is called when auto signature help override is enabled
 8930    cx.update(|_, cx| {
 8931        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8932            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8933                settings.auto_signature_help = Some(true);
 8934                settings.show_signature_help_after_edits = Some(false);
 8935            });
 8936        });
 8937    });
 8938    cx.set_state(
 8939        &r#"
 8940            fn main() {
 8941                sampleˇ
 8942            }
 8943        "#
 8944        .unindent(),
 8945    );
 8946    cx.update_editor(|editor, window, cx| {
 8947        editor.handle_input("(", window, cx);
 8948    });
 8949    cx.assert_editor_state(
 8950        &"
 8951            fn main() {
 8952                sample(ˇ)
 8953            }
 8954        "
 8955        .unindent(),
 8956    );
 8957    handle_signature_help_request(&mut cx, mocked_response).await;
 8958    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8959        .await;
 8960    cx.editor(|editor, _, _| {
 8961        let signature_help_state = editor.signature_help_state.popover().cloned();
 8962        assert!(signature_help_state.is_some());
 8963        assert_eq!(
 8964            signature_help_state.unwrap().label,
 8965            "param1: u8, param2: u8"
 8966        );
 8967    });
 8968}
 8969
 8970#[gpui::test]
 8971async fn test_signature_help(cx: &mut TestAppContext) {
 8972    init_test(cx, |_| {});
 8973    cx.update(|cx| {
 8974        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8975            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8976                settings.auto_signature_help = Some(true);
 8977            });
 8978        });
 8979    });
 8980
 8981    let mut cx = EditorLspTestContext::new_rust(
 8982        lsp::ServerCapabilities {
 8983            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8984                ..Default::default()
 8985            }),
 8986            ..Default::default()
 8987        },
 8988        cx,
 8989    )
 8990    .await;
 8991
 8992    // A test that directly calls `show_signature_help`
 8993    cx.update_editor(|editor, window, cx| {
 8994        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8995    });
 8996
 8997    let mocked_response = lsp::SignatureHelp {
 8998        signatures: vec![lsp::SignatureInformation {
 8999            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9000            documentation: None,
 9001            parameters: Some(vec![
 9002                lsp::ParameterInformation {
 9003                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9004                    documentation: None,
 9005                },
 9006                lsp::ParameterInformation {
 9007                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9008                    documentation: None,
 9009                },
 9010            ]),
 9011            active_parameter: None,
 9012        }],
 9013        active_signature: Some(0),
 9014        active_parameter: Some(0),
 9015    };
 9016    handle_signature_help_request(&mut cx, mocked_response).await;
 9017
 9018    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9019        .await;
 9020
 9021    cx.editor(|editor, _, _| {
 9022        let signature_help_state = editor.signature_help_state.popover().cloned();
 9023        assert!(signature_help_state.is_some());
 9024        assert_eq!(
 9025            signature_help_state.unwrap().label,
 9026            "param1: u8, param2: u8"
 9027        );
 9028    });
 9029
 9030    // When exiting outside from inside the brackets, `signature_help` is closed.
 9031    cx.set_state(indoc! {"
 9032        fn main() {
 9033            sample(ˇ);
 9034        }
 9035
 9036        fn sample(param1: u8, param2: u8) {}
 9037    "});
 9038
 9039    cx.update_editor(|editor, window, cx| {
 9040        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9041    });
 9042
 9043    let mocked_response = lsp::SignatureHelp {
 9044        signatures: Vec::new(),
 9045        active_signature: None,
 9046        active_parameter: None,
 9047    };
 9048    handle_signature_help_request(&mut cx, mocked_response).await;
 9049
 9050    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9051        .await;
 9052
 9053    cx.editor(|editor, _, _| {
 9054        assert!(!editor.signature_help_state.is_shown());
 9055    });
 9056
 9057    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9058    cx.set_state(indoc! {"
 9059        fn main() {
 9060            sample(ˇ);
 9061        }
 9062
 9063        fn sample(param1: u8, param2: u8) {}
 9064    "});
 9065
 9066    let mocked_response = lsp::SignatureHelp {
 9067        signatures: vec![lsp::SignatureInformation {
 9068            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9069            documentation: None,
 9070            parameters: Some(vec![
 9071                lsp::ParameterInformation {
 9072                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9073                    documentation: None,
 9074                },
 9075                lsp::ParameterInformation {
 9076                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9077                    documentation: None,
 9078                },
 9079            ]),
 9080            active_parameter: None,
 9081        }],
 9082        active_signature: Some(0),
 9083        active_parameter: Some(0),
 9084    };
 9085    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9086    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9087        .await;
 9088    cx.editor(|editor, _, _| {
 9089        assert!(editor.signature_help_state.is_shown());
 9090    });
 9091
 9092    // Restore the popover with more parameter input
 9093    cx.set_state(indoc! {"
 9094        fn main() {
 9095            sample(param1, param2ˇ);
 9096        }
 9097
 9098        fn sample(param1: u8, param2: u8) {}
 9099    "});
 9100
 9101    let mocked_response = lsp::SignatureHelp {
 9102        signatures: vec![lsp::SignatureInformation {
 9103            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9104            documentation: None,
 9105            parameters: Some(vec![
 9106                lsp::ParameterInformation {
 9107                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9108                    documentation: None,
 9109                },
 9110                lsp::ParameterInformation {
 9111                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9112                    documentation: None,
 9113                },
 9114            ]),
 9115            active_parameter: None,
 9116        }],
 9117        active_signature: Some(0),
 9118        active_parameter: Some(1),
 9119    };
 9120    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9121    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9122        .await;
 9123
 9124    // When selecting a range, the popover is gone.
 9125    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9126    cx.update_editor(|editor, window, cx| {
 9127        editor.change_selections(None, window, cx, |s| {
 9128            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9129        })
 9130    });
 9131    cx.assert_editor_state(indoc! {"
 9132        fn main() {
 9133            sample(param1, «ˇparam2»);
 9134        }
 9135
 9136        fn sample(param1: u8, param2: u8) {}
 9137    "});
 9138    cx.editor(|editor, _, _| {
 9139        assert!(!editor.signature_help_state.is_shown());
 9140    });
 9141
 9142    // When unselecting again, the popover is back if within the brackets.
 9143    cx.update_editor(|editor, window, cx| {
 9144        editor.change_selections(None, window, cx, |s| {
 9145            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9146        })
 9147    });
 9148    cx.assert_editor_state(indoc! {"
 9149        fn main() {
 9150            sample(param1, ˇparam2);
 9151        }
 9152
 9153        fn sample(param1: u8, param2: u8) {}
 9154    "});
 9155    handle_signature_help_request(&mut cx, mocked_response).await;
 9156    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9157        .await;
 9158    cx.editor(|editor, _, _| {
 9159        assert!(editor.signature_help_state.is_shown());
 9160    });
 9161
 9162    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9163    cx.update_editor(|editor, window, cx| {
 9164        editor.change_selections(None, window, cx, |s| {
 9165            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9166            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9167        })
 9168    });
 9169    cx.assert_editor_state(indoc! {"
 9170        fn main() {
 9171            sample(param1, ˇparam2);
 9172        }
 9173
 9174        fn sample(param1: u8, param2: u8) {}
 9175    "});
 9176
 9177    let mocked_response = lsp::SignatureHelp {
 9178        signatures: vec![lsp::SignatureInformation {
 9179            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9180            documentation: None,
 9181            parameters: Some(vec![
 9182                lsp::ParameterInformation {
 9183                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9184                    documentation: None,
 9185                },
 9186                lsp::ParameterInformation {
 9187                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9188                    documentation: None,
 9189                },
 9190            ]),
 9191            active_parameter: None,
 9192        }],
 9193        active_signature: Some(0),
 9194        active_parameter: Some(1),
 9195    };
 9196    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9197    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9198        .await;
 9199    cx.update_editor(|editor, _, cx| {
 9200        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9201    });
 9202    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9203        .await;
 9204    cx.update_editor(|editor, window, cx| {
 9205        editor.change_selections(None, window, cx, |s| {
 9206            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9207        })
 9208    });
 9209    cx.assert_editor_state(indoc! {"
 9210        fn main() {
 9211            sample(param1, «ˇparam2»);
 9212        }
 9213
 9214        fn sample(param1: u8, param2: u8) {}
 9215    "});
 9216    cx.update_editor(|editor, window, cx| {
 9217        editor.change_selections(None, window, cx, |s| {
 9218            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9219        })
 9220    });
 9221    cx.assert_editor_state(indoc! {"
 9222        fn main() {
 9223            sample(param1, ˇparam2);
 9224        }
 9225
 9226        fn sample(param1: u8, param2: u8) {}
 9227    "});
 9228    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9229        .await;
 9230}
 9231
 9232#[gpui::test]
 9233async fn test_completion(cx: &mut TestAppContext) {
 9234    init_test(cx, |_| {});
 9235
 9236    let mut cx = EditorLspTestContext::new_rust(
 9237        lsp::ServerCapabilities {
 9238            completion_provider: Some(lsp::CompletionOptions {
 9239                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9240                resolve_provider: Some(true),
 9241                ..Default::default()
 9242            }),
 9243            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9244            ..Default::default()
 9245        },
 9246        cx,
 9247    )
 9248    .await;
 9249    let counter = Arc::new(AtomicUsize::new(0));
 9250
 9251    cx.set_state(indoc! {"
 9252        oneˇ
 9253        two
 9254        three
 9255    "});
 9256    cx.simulate_keystroke(".");
 9257    handle_completion_request(
 9258        &mut cx,
 9259        indoc! {"
 9260            one.|<>
 9261            two
 9262            three
 9263        "},
 9264        vec!["first_completion", "second_completion"],
 9265        counter.clone(),
 9266    )
 9267    .await;
 9268    cx.condition(|editor, _| editor.context_menu_visible())
 9269        .await;
 9270    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9271
 9272    let _handler = handle_signature_help_request(
 9273        &mut cx,
 9274        lsp::SignatureHelp {
 9275            signatures: vec![lsp::SignatureInformation {
 9276                label: "test signature".to_string(),
 9277                documentation: None,
 9278                parameters: Some(vec![lsp::ParameterInformation {
 9279                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9280                    documentation: None,
 9281                }]),
 9282                active_parameter: None,
 9283            }],
 9284            active_signature: None,
 9285            active_parameter: None,
 9286        },
 9287    );
 9288    cx.update_editor(|editor, window, cx| {
 9289        assert!(
 9290            !editor.signature_help_state.is_shown(),
 9291            "No signature help was called for"
 9292        );
 9293        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9294    });
 9295    cx.run_until_parked();
 9296    cx.update_editor(|editor, _, _| {
 9297        assert!(
 9298            !editor.signature_help_state.is_shown(),
 9299            "No signature help should be shown when completions menu is open"
 9300        );
 9301    });
 9302
 9303    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9304        editor.context_menu_next(&Default::default(), window, cx);
 9305        editor
 9306            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9307            .unwrap()
 9308    });
 9309    cx.assert_editor_state(indoc! {"
 9310        one.second_completionˇ
 9311        two
 9312        three
 9313    "});
 9314
 9315    handle_resolve_completion_request(
 9316        &mut cx,
 9317        Some(vec![
 9318            (
 9319                //This overlaps with the primary completion edit which is
 9320                //misbehavior from the LSP spec, test that we filter it out
 9321                indoc! {"
 9322                    one.second_ˇcompletion
 9323                    two
 9324                    threeˇ
 9325                "},
 9326                "overlapping additional edit",
 9327            ),
 9328            (
 9329                indoc! {"
 9330                    one.second_completion
 9331                    two
 9332                    threeˇ
 9333                "},
 9334                "\nadditional edit",
 9335            ),
 9336        ]),
 9337    )
 9338    .await;
 9339    apply_additional_edits.await.unwrap();
 9340    cx.assert_editor_state(indoc! {"
 9341        one.second_completionˇ
 9342        two
 9343        three
 9344        additional edit
 9345    "});
 9346
 9347    cx.set_state(indoc! {"
 9348        one.second_completion
 9349        twoˇ
 9350        threeˇ
 9351        additional edit
 9352    "});
 9353    cx.simulate_keystroke(" ");
 9354    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9355    cx.simulate_keystroke("s");
 9356    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9357
 9358    cx.assert_editor_state(indoc! {"
 9359        one.second_completion
 9360        two sˇ
 9361        three sˇ
 9362        additional edit
 9363    "});
 9364    handle_completion_request(
 9365        &mut cx,
 9366        indoc! {"
 9367            one.second_completion
 9368            two s
 9369            three <s|>
 9370            additional edit
 9371        "},
 9372        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9373        counter.clone(),
 9374    )
 9375    .await;
 9376    cx.condition(|editor, _| editor.context_menu_visible())
 9377        .await;
 9378    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9379
 9380    cx.simulate_keystroke("i");
 9381
 9382    handle_completion_request(
 9383        &mut cx,
 9384        indoc! {"
 9385            one.second_completion
 9386            two si
 9387            three <si|>
 9388            additional edit
 9389        "},
 9390        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9391        counter.clone(),
 9392    )
 9393    .await;
 9394    cx.condition(|editor, _| editor.context_menu_visible())
 9395        .await;
 9396    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9397
 9398    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9399        editor
 9400            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9401            .unwrap()
 9402    });
 9403    cx.assert_editor_state(indoc! {"
 9404        one.second_completion
 9405        two sixth_completionˇ
 9406        three sixth_completionˇ
 9407        additional edit
 9408    "});
 9409
 9410    apply_additional_edits.await.unwrap();
 9411
 9412    update_test_language_settings(&mut cx, |settings| {
 9413        settings.defaults.show_completions_on_input = Some(false);
 9414    });
 9415    cx.set_state("editorˇ");
 9416    cx.simulate_keystroke(".");
 9417    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9418    cx.simulate_keystrokes("c l o");
 9419    cx.assert_editor_state("editor.cloˇ");
 9420    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9421    cx.update_editor(|editor, window, cx| {
 9422        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9423    });
 9424    handle_completion_request(
 9425        &mut cx,
 9426        "editor.<clo|>",
 9427        vec!["close", "clobber"],
 9428        counter.clone(),
 9429    )
 9430    .await;
 9431    cx.condition(|editor, _| editor.context_menu_visible())
 9432        .await;
 9433    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9434
 9435    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9436        editor
 9437            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9438            .unwrap()
 9439    });
 9440    cx.assert_editor_state("editor.closeˇ");
 9441    handle_resolve_completion_request(&mut cx, None).await;
 9442    apply_additional_edits.await.unwrap();
 9443}
 9444
 9445#[gpui::test]
 9446async fn test_word_completion(cx: &mut TestAppContext) {
 9447    let lsp_fetch_timeout_ms = 10;
 9448    init_test(cx, |language_settings| {
 9449        language_settings.defaults.completions = Some(CompletionSettings {
 9450            words: WordsCompletionMode::Fallback,
 9451            lsp: true,
 9452            lsp_fetch_timeout_ms: 10,
 9453        });
 9454    });
 9455
 9456    let mut cx = EditorLspTestContext::new_rust(
 9457        lsp::ServerCapabilities {
 9458            completion_provider: Some(lsp::CompletionOptions {
 9459                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9460                ..lsp::CompletionOptions::default()
 9461            }),
 9462            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9463            ..lsp::ServerCapabilities::default()
 9464        },
 9465        cx,
 9466    )
 9467    .await;
 9468
 9469    let throttle_completions = Arc::new(AtomicBool::new(false));
 9470
 9471    let lsp_throttle_completions = throttle_completions.clone();
 9472    let _completion_requests_handler =
 9473        cx.lsp
 9474            .server
 9475            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9476                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9477                let cx = cx.clone();
 9478                async move {
 9479                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9480                        cx.background_executor()
 9481                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9482                            .await;
 9483                    }
 9484                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9485                        lsp::CompletionItem {
 9486                            label: "first".into(),
 9487                            ..lsp::CompletionItem::default()
 9488                        },
 9489                        lsp::CompletionItem {
 9490                            label: "last".into(),
 9491                            ..lsp::CompletionItem::default()
 9492                        },
 9493                    ])))
 9494                }
 9495            });
 9496
 9497    cx.set_state(indoc! {"
 9498        oneˇ
 9499        two
 9500        three
 9501    "});
 9502    cx.simulate_keystroke(".");
 9503    cx.executor().run_until_parked();
 9504    cx.condition(|editor, _| editor.context_menu_visible())
 9505        .await;
 9506    cx.update_editor(|editor, window, cx| {
 9507        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9508        {
 9509            assert_eq!(
 9510                completion_menu_entries(&menu),
 9511                &["first", "last"],
 9512                "When LSP server is fast to reply, no fallback word completions are used"
 9513            );
 9514        } else {
 9515            panic!("expected completion menu to be open");
 9516        }
 9517        editor.cancel(&Cancel, window, cx);
 9518    });
 9519    cx.executor().run_until_parked();
 9520    cx.condition(|editor, _| !editor.context_menu_visible())
 9521        .await;
 9522
 9523    throttle_completions.store(true, atomic::Ordering::Release);
 9524    cx.simulate_keystroke(".");
 9525    cx.executor()
 9526        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9527    cx.executor().run_until_parked();
 9528    cx.condition(|editor, _| editor.context_menu_visible())
 9529        .await;
 9530    cx.update_editor(|editor, _, _| {
 9531        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9532        {
 9533            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9534                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9535        } else {
 9536            panic!("expected completion menu to be open");
 9537        }
 9538    });
 9539}
 9540
 9541#[gpui::test]
 9542async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9543    init_test(cx, |language_settings| {
 9544        language_settings.defaults.completions = Some(CompletionSettings {
 9545            words: WordsCompletionMode::Enabled,
 9546            lsp: true,
 9547            lsp_fetch_timeout_ms: 0,
 9548        });
 9549    });
 9550
 9551    let mut cx = EditorLspTestContext::new_rust(
 9552        lsp::ServerCapabilities {
 9553            completion_provider: Some(lsp::CompletionOptions {
 9554                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9555                ..lsp::CompletionOptions::default()
 9556            }),
 9557            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9558            ..lsp::ServerCapabilities::default()
 9559        },
 9560        cx,
 9561    )
 9562    .await;
 9563
 9564    let _completion_requests_handler =
 9565        cx.lsp
 9566            .server
 9567            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9568                Ok(Some(lsp::CompletionResponse::Array(vec![
 9569                    lsp::CompletionItem {
 9570                        label: "first".into(),
 9571                        ..lsp::CompletionItem::default()
 9572                    },
 9573                    lsp::CompletionItem {
 9574                        label: "last".into(),
 9575                        ..lsp::CompletionItem::default()
 9576                    },
 9577                ])))
 9578            });
 9579
 9580    cx.set_state(indoc! {"ˇ
 9581        first
 9582        last
 9583        second
 9584    "});
 9585    cx.simulate_keystroke(".");
 9586    cx.executor().run_until_parked();
 9587    cx.condition(|editor, _| editor.context_menu_visible())
 9588        .await;
 9589    cx.update_editor(|editor, _, _| {
 9590        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9591        {
 9592            assert_eq!(
 9593                completion_menu_entries(&menu),
 9594                &["first", "last", "second"],
 9595                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9596            );
 9597        } else {
 9598            panic!("expected completion menu to be open");
 9599        }
 9600    });
 9601}
 9602
 9603#[gpui::test]
 9604async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9605    init_test(cx, |language_settings| {
 9606        language_settings.defaults.completions = Some(CompletionSettings {
 9607            words: WordsCompletionMode::Disabled,
 9608            lsp: true,
 9609            lsp_fetch_timeout_ms: 0,
 9610        });
 9611    });
 9612
 9613    let mut cx = EditorLspTestContext::new_rust(
 9614        lsp::ServerCapabilities {
 9615            completion_provider: Some(lsp::CompletionOptions {
 9616                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9617                ..lsp::CompletionOptions::default()
 9618            }),
 9619            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9620            ..lsp::ServerCapabilities::default()
 9621        },
 9622        cx,
 9623    )
 9624    .await;
 9625
 9626    let _completion_requests_handler =
 9627        cx.lsp
 9628            .server
 9629            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9630                panic!("LSP completions should not be queried when dealing with word completions")
 9631            });
 9632
 9633    cx.set_state(indoc! {"ˇ
 9634        first
 9635        last
 9636        second
 9637    "});
 9638    cx.update_editor(|editor, window, cx| {
 9639        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9640    });
 9641    cx.executor().run_until_parked();
 9642    cx.condition(|editor, _| editor.context_menu_visible())
 9643        .await;
 9644    cx.update_editor(|editor, _, _| {
 9645        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9646        {
 9647            assert_eq!(
 9648                completion_menu_entries(&menu),
 9649                &["first", "last", "second"],
 9650                "`ShowWordCompletions` action should show word completions"
 9651            );
 9652        } else {
 9653            panic!("expected completion menu to be open");
 9654        }
 9655    });
 9656
 9657    cx.simulate_keystroke("l");
 9658    cx.executor().run_until_parked();
 9659    cx.condition(|editor, _| editor.context_menu_visible())
 9660        .await;
 9661    cx.update_editor(|editor, _, _| {
 9662        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9663        {
 9664            assert_eq!(
 9665                completion_menu_entries(&menu),
 9666                &["last"],
 9667                "After showing word completions, further editing should filter them and not query the LSP"
 9668            );
 9669        } else {
 9670            panic!("expected completion menu to be open");
 9671        }
 9672    });
 9673}
 9674
 9675#[gpui::test]
 9676async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9677    init_test(cx, |language_settings| {
 9678        language_settings.defaults.completions = Some(CompletionSettings {
 9679            words: WordsCompletionMode::Fallback,
 9680            lsp: false,
 9681            lsp_fetch_timeout_ms: 0,
 9682        });
 9683    });
 9684
 9685    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9686
 9687    cx.set_state(indoc! {"ˇ
 9688        0_usize
 9689        let
 9690        33
 9691        4.5f32
 9692    "});
 9693    cx.update_editor(|editor, window, cx| {
 9694        editor.show_completions(&ShowCompletions::default(), window, cx);
 9695    });
 9696    cx.executor().run_until_parked();
 9697    cx.condition(|editor, _| editor.context_menu_visible())
 9698        .await;
 9699    cx.update_editor(|editor, window, cx| {
 9700        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9701        {
 9702            assert_eq!(
 9703                completion_menu_entries(&menu),
 9704                &["let"],
 9705                "With no digits in the completion query, no digits should be in the word completions"
 9706            );
 9707        } else {
 9708            panic!("expected completion menu to be open");
 9709        }
 9710        editor.cancel(&Cancel, window, cx);
 9711    });
 9712
 9713    cx.set_state(indoc! {" 9714        0_usize
 9715        let
 9716        3
 9717        33.35f32
 9718    "});
 9719    cx.update_editor(|editor, window, cx| {
 9720        editor.show_completions(&ShowCompletions::default(), window, cx);
 9721    });
 9722    cx.executor().run_until_parked();
 9723    cx.condition(|editor, _| editor.context_menu_visible())
 9724        .await;
 9725    cx.update_editor(|editor, _, _| {
 9726        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9727        {
 9728            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9729                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9730        } else {
 9731            panic!("expected completion menu to be open");
 9732        }
 9733    });
 9734}
 9735
 9736fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9737    let position = || lsp::Position {
 9738        line: params.text_document_position.position.line,
 9739        character: params.text_document_position.position.character,
 9740    };
 9741    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9742        range: lsp::Range {
 9743            start: position(),
 9744            end: position(),
 9745        },
 9746        new_text: text.to_string(),
 9747    }))
 9748}
 9749
 9750#[gpui::test]
 9751async fn test_multiline_completion(cx: &mut TestAppContext) {
 9752    init_test(cx, |_| {});
 9753
 9754    let fs = FakeFs::new(cx.executor());
 9755    fs.insert_tree(
 9756        path!("/a"),
 9757        json!({
 9758            "main.ts": "a",
 9759        }),
 9760    )
 9761    .await;
 9762
 9763    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9764    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9765    let typescript_language = Arc::new(Language::new(
 9766        LanguageConfig {
 9767            name: "TypeScript".into(),
 9768            matcher: LanguageMatcher {
 9769                path_suffixes: vec!["ts".to_string()],
 9770                ..LanguageMatcher::default()
 9771            },
 9772            line_comments: vec!["// ".into()],
 9773            ..LanguageConfig::default()
 9774        },
 9775        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9776    ));
 9777    language_registry.add(typescript_language.clone());
 9778    let mut fake_servers = language_registry.register_fake_lsp(
 9779        "TypeScript",
 9780        FakeLspAdapter {
 9781            capabilities: lsp::ServerCapabilities {
 9782                completion_provider: Some(lsp::CompletionOptions {
 9783                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9784                    ..lsp::CompletionOptions::default()
 9785                }),
 9786                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9787                ..lsp::ServerCapabilities::default()
 9788            },
 9789            // Emulate vtsls label generation
 9790            label_for_completion: Some(Box::new(|item, _| {
 9791                let text = if let Some(description) = item
 9792                    .label_details
 9793                    .as_ref()
 9794                    .and_then(|label_details| label_details.description.as_ref())
 9795                {
 9796                    format!("{} {}", item.label, description)
 9797                } else if let Some(detail) = &item.detail {
 9798                    format!("{} {}", item.label, detail)
 9799                } else {
 9800                    item.label.clone()
 9801                };
 9802                let len = text.len();
 9803                Some(language::CodeLabel {
 9804                    text,
 9805                    runs: Vec::new(),
 9806                    filter_range: 0..len,
 9807                })
 9808            })),
 9809            ..FakeLspAdapter::default()
 9810        },
 9811    );
 9812    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9813    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9814    let worktree_id = workspace
 9815        .update(cx, |workspace, _window, cx| {
 9816            workspace.project().update(cx, |project, cx| {
 9817                project.worktrees(cx).next().unwrap().read(cx).id()
 9818            })
 9819        })
 9820        .unwrap();
 9821    let _buffer = project
 9822        .update(cx, |project, cx| {
 9823            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9824        })
 9825        .await
 9826        .unwrap();
 9827    let editor = workspace
 9828        .update(cx, |workspace, window, cx| {
 9829            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9830        })
 9831        .unwrap()
 9832        .await
 9833        .unwrap()
 9834        .downcast::<Editor>()
 9835        .unwrap();
 9836    let fake_server = fake_servers.next().await.unwrap();
 9837
 9838    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9839    let multiline_label_2 = "a\nb\nc\n";
 9840    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9841    let multiline_description = "d\ne\nf\n";
 9842    let multiline_detail_2 = "g\nh\ni\n";
 9843
 9844    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
 9845        move |params, _| async move {
 9846            Ok(Some(lsp::CompletionResponse::Array(vec![
 9847                lsp::CompletionItem {
 9848                    label: multiline_label.to_string(),
 9849                    text_edit: gen_text_edit(&params, "new_text_1"),
 9850                    ..lsp::CompletionItem::default()
 9851                },
 9852                lsp::CompletionItem {
 9853                    label: "single line label 1".to_string(),
 9854                    detail: Some(multiline_detail.to_string()),
 9855                    text_edit: gen_text_edit(&params, "new_text_2"),
 9856                    ..lsp::CompletionItem::default()
 9857                },
 9858                lsp::CompletionItem {
 9859                    label: "single line label 2".to_string(),
 9860                    label_details: Some(lsp::CompletionItemLabelDetails {
 9861                        description: Some(multiline_description.to_string()),
 9862                        detail: None,
 9863                    }),
 9864                    text_edit: gen_text_edit(&params, "new_text_2"),
 9865                    ..lsp::CompletionItem::default()
 9866                },
 9867                lsp::CompletionItem {
 9868                    label: multiline_label_2.to_string(),
 9869                    detail: Some(multiline_detail_2.to_string()),
 9870                    text_edit: gen_text_edit(&params, "new_text_3"),
 9871                    ..lsp::CompletionItem::default()
 9872                },
 9873                lsp::CompletionItem {
 9874                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9875                    detail: Some(
 9876                        "Details with many     spaces and \t but without newlines".to_string(),
 9877                    ),
 9878                    text_edit: gen_text_edit(&params, "new_text_4"),
 9879                    ..lsp::CompletionItem::default()
 9880                },
 9881            ])))
 9882        },
 9883    );
 9884
 9885    editor.update_in(cx, |editor, window, cx| {
 9886        cx.focus_self(window);
 9887        editor.move_to_end(&MoveToEnd, window, cx);
 9888        editor.handle_input(".", window, cx);
 9889    });
 9890    cx.run_until_parked();
 9891    completion_handle.next().await.unwrap();
 9892
 9893    editor.update(cx, |editor, _| {
 9894        assert!(editor.context_menu_visible());
 9895        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9896        {
 9897            let completion_labels = menu
 9898                .completions
 9899                .borrow()
 9900                .iter()
 9901                .map(|c| c.label.text.clone())
 9902                .collect::<Vec<_>>();
 9903            assert_eq!(
 9904                completion_labels,
 9905                &[
 9906                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9907                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9908                    "single line label 2 d e f ",
 9909                    "a b c g h i ",
 9910                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9911                ],
 9912                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9913            );
 9914
 9915            for completion in menu
 9916                .completions
 9917                .borrow()
 9918                .iter() {
 9919                    assert_eq!(
 9920                        completion.label.filter_range,
 9921                        0..completion.label.text.len(),
 9922                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9923                    );
 9924                }
 9925        } else {
 9926            panic!("expected completion menu to be open");
 9927        }
 9928    });
 9929}
 9930
 9931#[gpui::test]
 9932async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9933    init_test(cx, |_| {});
 9934    let mut cx = EditorLspTestContext::new_rust(
 9935        lsp::ServerCapabilities {
 9936            completion_provider: Some(lsp::CompletionOptions {
 9937                trigger_characters: Some(vec![".".to_string()]),
 9938                ..Default::default()
 9939            }),
 9940            ..Default::default()
 9941        },
 9942        cx,
 9943    )
 9944    .await;
 9945    cx.lsp
 9946        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 9947            Ok(Some(lsp::CompletionResponse::Array(vec![
 9948                lsp::CompletionItem {
 9949                    label: "first".into(),
 9950                    ..Default::default()
 9951                },
 9952                lsp::CompletionItem {
 9953                    label: "last".into(),
 9954                    ..Default::default()
 9955                },
 9956            ])))
 9957        });
 9958    cx.set_state("variableˇ");
 9959    cx.simulate_keystroke(".");
 9960    cx.executor().run_until_parked();
 9961
 9962    cx.update_editor(|editor, _, _| {
 9963        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9964        {
 9965            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9966        } else {
 9967            panic!("expected completion menu to be open");
 9968        }
 9969    });
 9970
 9971    cx.update_editor(|editor, window, cx| {
 9972        editor.move_page_down(&MovePageDown::default(), window, cx);
 9973        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9974        {
 9975            assert!(
 9976                menu.selected_item == 1,
 9977                "expected PageDown to select the last item from the context menu"
 9978            );
 9979        } else {
 9980            panic!("expected completion menu to stay open after PageDown");
 9981        }
 9982    });
 9983
 9984    cx.update_editor(|editor, window, cx| {
 9985        editor.move_page_up(&MovePageUp::default(), window, cx);
 9986        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9987        {
 9988            assert!(
 9989                menu.selected_item == 0,
 9990                "expected PageUp to select the first item from the context menu"
 9991            );
 9992        } else {
 9993            panic!("expected completion menu to stay open after PageUp");
 9994        }
 9995    });
 9996}
 9997
 9998#[gpui::test]
 9999async fn test_completion_sort(cx: &mut TestAppContext) {
10000    init_test(cx, |_| {});
10001    let mut cx = EditorLspTestContext::new_rust(
10002        lsp::ServerCapabilities {
10003            completion_provider: Some(lsp::CompletionOptions {
10004                trigger_characters: Some(vec![".".to_string()]),
10005                ..Default::default()
10006            }),
10007            ..Default::default()
10008        },
10009        cx,
10010    )
10011    .await;
10012    cx.lsp
10013        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10014            Ok(Some(lsp::CompletionResponse::Array(vec![
10015                lsp::CompletionItem {
10016                    label: "Range".into(),
10017                    sort_text: Some("a".into()),
10018                    ..Default::default()
10019                },
10020                lsp::CompletionItem {
10021                    label: "r".into(),
10022                    sort_text: Some("b".into()),
10023                    ..Default::default()
10024                },
10025                lsp::CompletionItem {
10026                    label: "ret".into(),
10027                    sort_text: Some("c".into()),
10028                    ..Default::default()
10029                },
10030                lsp::CompletionItem {
10031                    label: "return".into(),
10032                    sort_text: Some("d".into()),
10033                    ..Default::default()
10034                },
10035                lsp::CompletionItem {
10036                    label: "slice".into(),
10037                    sort_text: Some("d".into()),
10038                    ..Default::default()
10039                },
10040            ])))
10041        });
10042    cx.set_state("");
10043    cx.executor().run_until_parked();
10044    cx.update_editor(|editor, window, cx| {
10045        editor.show_completions(
10046            &ShowCompletions {
10047                trigger: Some("r".into()),
10048            },
10049            window,
10050            cx,
10051        );
10052    });
10053    cx.executor().run_until_parked();
10054
10055    cx.update_editor(|editor, _, _| {
10056        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10057        {
10058            assert_eq!(
10059                completion_menu_entries(&menu),
10060                &["r", "ret", "Range", "return"]
10061            );
10062        } else {
10063            panic!("expected completion menu to be open");
10064        }
10065    });
10066}
10067
10068#[gpui::test]
10069async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10070    init_test(cx, |_| {});
10071
10072    let mut cx = EditorLspTestContext::new_rust(
10073        lsp::ServerCapabilities {
10074            completion_provider: Some(lsp::CompletionOptions {
10075                trigger_characters: Some(vec![".".to_string()]),
10076                resolve_provider: Some(true),
10077                ..Default::default()
10078            }),
10079            ..Default::default()
10080        },
10081        cx,
10082    )
10083    .await;
10084
10085    cx.set_state("fn main() { let a = 2ˇ; }");
10086    cx.simulate_keystroke(".");
10087    let completion_item = lsp::CompletionItem {
10088        label: "Some".into(),
10089        kind: Some(lsp::CompletionItemKind::SNIPPET),
10090        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10091        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10092            kind: lsp::MarkupKind::Markdown,
10093            value: "```rust\nSome(2)\n```".to_string(),
10094        })),
10095        deprecated: Some(false),
10096        sort_text: Some("Some".to_string()),
10097        filter_text: Some("Some".to_string()),
10098        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10099        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10100            range: lsp::Range {
10101                start: lsp::Position {
10102                    line: 0,
10103                    character: 22,
10104                },
10105                end: lsp::Position {
10106                    line: 0,
10107                    character: 22,
10108                },
10109            },
10110            new_text: "Some(2)".to_string(),
10111        })),
10112        additional_text_edits: Some(vec![lsp::TextEdit {
10113            range: lsp::Range {
10114                start: lsp::Position {
10115                    line: 0,
10116                    character: 20,
10117                },
10118                end: lsp::Position {
10119                    line: 0,
10120                    character: 22,
10121                },
10122            },
10123            new_text: "".to_string(),
10124        }]),
10125        ..Default::default()
10126    };
10127
10128    let closure_completion_item = completion_item.clone();
10129    let counter = Arc::new(AtomicUsize::new(0));
10130    let counter_clone = counter.clone();
10131    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10132        let task_completion_item = closure_completion_item.clone();
10133        counter_clone.fetch_add(1, atomic::Ordering::Release);
10134        async move {
10135            Ok(Some(lsp::CompletionResponse::Array(vec![
10136                task_completion_item,
10137            ])))
10138        }
10139    });
10140
10141    cx.condition(|editor, _| editor.context_menu_visible())
10142        .await;
10143    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10144    assert!(request.next().await.is_some());
10145    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10146
10147    cx.simulate_keystrokes("S o m");
10148    cx.condition(|editor, _| editor.context_menu_visible())
10149        .await;
10150    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10151    assert!(request.next().await.is_some());
10152    assert!(request.next().await.is_some());
10153    assert!(request.next().await.is_some());
10154    request.close();
10155    assert!(request.next().await.is_none());
10156    assert_eq!(
10157        counter.load(atomic::Ordering::Acquire),
10158        4,
10159        "With the completions menu open, only one LSP request should happen per input"
10160    );
10161}
10162
10163#[gpui::test]
10164async fn test_toggle_comment(cx: &mut TestAppContext) {
10165    init_test(cx, |_| {});
10166    let mut cx = EditorTestContext::new(cx).await;
10167    let language = Arc::new(Language::new(
10168        LanguageConfig {
10169            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10170            ..Default::default()
10171        },
10172        Some(tree_sitter_rust::LANGUAGE.into()),
10173    ));
10174    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10175
10176    // If multiple selections intersect a line, the line is only toggled once.
10177    cx.set_state(indoc! {"
10178        fn a() {
10179            «//b();
10180            ˇ»// «c();
10181            //ˇ»  d();
10182        }
10183    "});
10184
10185    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10186
10187    cx.assert_editor_state(indoc! {"
10188        fn a() {
10189            «b();
10190            c();
10191            ˇ» d();
10192        }
10193    "});
10194
10195    // The comment prefix is inserted at the same column for every line in a
10196    // selection.
10197    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10198
10199    cx.assert_editor_state(indoc! {"
10200        fn a() {
10201            // «b();
10202            // c();
10203            ˇ»//  d();
10204        }
10205    "});
10206
10207    // If a selection ends at the beginning of a line, that line is not toggled.
10208    cx.set_selections_state(indoc! {"
10209        fn a() {
10210            // b();
10211            «// c();
10212        ˇ»    //  d();
10213        }
10214    "});
10215
10216    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10217
10218    cx.assert_editor_state(indoc! {"
10219        fn a() {
10220            // b();
10221            «c();
10222        ˇ»    //  d();
10223        }
10224    "});
10225
10226    // If a selection span a single line and is empty, the line is toggled.
10227    cx.set_state(indoc! {"
10228        fn a() {
10229            a();
10230            b();
10231        ˇ
10232        }
10233    "});
10234
10235    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10236
10237    cx.assert_editor_state(indoc! {"
10238        fn a() {
10239            a();
10240            b();
10241        //•ˇ
10242        }
10243    "});
10244
10245    // If a selection span multiple lines, empty lines are not toggled.
10246    cx.set_state(indoc! {"
10247        fn a() {
10248            «a();
10249
10250            c();ˇ»
10251        }
10252    "});
10253
10254    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10255
10256    cx.assert_editor_state(indoc! {"
10257        fn a() {
10258            // «a();
10259
10260            // c();ˇ»
10261        }
10262    "});
10263
10264    // If a selection includes multiple comment prefixes, all lines are uncommented.
10265    cx.set_state(indoc! {"
10266        fn a() {
10267            «// a();
10268            /// b();
10269            //! c();ˇ»
10270        }
10271    "});
10272
10273    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10274
10275    cx.assert_editor_state(indoc! {"
10276        fn a() {
10277            «a();
10278            b();
10279            c();ˇ»
10280        }
10281    "});
10282}
10283
10284#[gpui::test]
10285async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10286    init_test(cx, |_| {});
10287    let mut cx = EditorTestContext::new(cx).await;
10288    let language = Arc::new(Language::new(
10289        LanguageConfig {
10290            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10291            ..Default::default()
10292        },
10293        Some(tree_sitter_rust::LANGUAGE.into()),
10294    ));
10295    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10296
10297    let toggle_comments = &ToggleComments {
10298        advance_downwards: false,
10299        ignore_indent: true,
10300    };
10301
10302    // If multiple selections intersect a line, the line is only toggled once.
10303    cx.set_state(indoc! {"
10304        fn a() {
10305        //    «b();
10306        //    c();
10307        //    ˇ» d();
10308        }
10309    "});
10310
10311    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10312
10313    cx.assert_editor_state(indoc! {"
10314        fn a() {
10315            «b();
10316            c();
10317            ˇ» d();
10318        }
10319    "});
10320
10321    // The comment prefix is inserted at the beginning of each line
10322    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10323
10324    cx.assert_editor_state(indoc! {"
10325        fn a() {
10326        //    «b();
10327        //    c();
10328        //    ˇ» d();
10329        }
10330    "});
10331
10332    // If a selection ends at the beginning of a line, that line is not toggled.
10333    cx.set_selections_state(indoc! {"
10334        fn a() {
10335        //    b();
10336        //    «c();
10337        ˇ»//     d();
10338        }
10339    "});
10340
10341    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10342
10343    cx.assert_editor_state(indoc! {"
10344        fn a() {
10345        //    b();
10346            «c();
10347        ˇ»//     d();
10348        }
10349    "});
10350
10351    // If a selection span a single line and is empty, the line is toggled.
10352    cx.set_state(indoc! {"
10353        fn a() {
10354            a();
10355            b();
10356        ˇ
10357        }
10358    "});
10359
10360    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10361
10362    cx.assert_editor_state(indoc! {"
10363        fn a() {
10364            a();
10365            b();
10366        //ˇ
10367        }
10368    "});
10369
10370    // If a selection span multiple lines, empty lines are not toggled.
10371    cx.set_state(indoc! {"
10372        fn a() {
10373            «a();
10374
10375            c();ˇ»
10376        }
10377    "});
10378
10379    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10380
10381    cx.assert_editor_state(indoc! {"
10382        fn a() {
10383        //    «a();
10384
10385        //    c();ˇ»
10386        }
10387    "});
10388
10389    // If a selection includes multiple comment prefixes, all lines are uncommented.
10390    cx.set_state(indoc! {"
10391        fn a() {
10392        //    «a();
10393        ///    b();
10394        //!    c();ˇ»
10395        }
10396    "});
10397
10398    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10399
10400    cx.assert_editor_state(indoc! {"
10401        fn a() {
10402            «a();
10403            b();
10404            c();ˇ»
10405        }
10406    "});
10407}
10408
10409#[gpui::test]
10410async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10411    init_test(cx, |_| {});
10412
10413    let language = Arc::new(Language::new(
10414        LanguageConfig {
10415            line_comments: vec!["// ".into()],
10416            ..Default::default()
10417        },
10418        Some(tree_sitter_rust::LANGUAGE.into()),
10419    ));
10420
10421    let mut cx = EditorTestContext::new(cx).await;
10422
10423    cx.language_registry().add(language.clone());
10424    cx.update_buffer(|buffer, cx| {
10425        buffer.set_language(Some(language), cx);
10426    });
10427
10428    let toggle_comments = &ToggleComments {
10429        advance_downwards: true,
10430        ignore_indent: false,
10431    };
10432
10433    // Single cursor on one line -> advance
10434    // Cursor moves horizontally 3 characters as well on non-blank line
10435    cx.set_state(indoc!(
10436        "fn a() {
10437             ˇdog();
10438             cat();
10439        }"
10440    ));
10441    cx.update_editor(|editor, window, cx| {
10442        editor.toggle_comments(toggle_comments, window, cx);
10443    });
10444    cx.assert_editor_state(indoc!(
10445        "fn a() {
10446             // dog();
10447             catˇ();
10448        }"
10449    ));
10450
10451    // Single selection on one line -> don't advance
10452    cx.set_state(indoc!(
10453        "fn a() {
10454             «dog()ˇ»;
10455             cat();
10456        }"
10457    ));
10458    cx.update_editor(|editor, window, cx| {
10459        editor.toggle_comments(toggle_comments, window, cx);
10460    });
10461    cx.assert_editor_state(indoc!(
10462        "fn a() {
10463             // «dog()ˇ»;
10464             cat();
10465        }"
10466    ));
10467
10468    // Multiple cursors on one line -> advance
10469    cx.set_state(indoc!(
10470        "fn a() {
10471             ˇdˇog();
10472             cat();
10473        }"
10474    ));
10475    cx.update_editor(|editor, window, cx| {
10476        editor.toggle_comments(toggle_comments, window, cx);
10477    });
10478    cx.assert_editor_state(indoc!(
10479        "fn a() {
10480             // dog();
10481             catˇ(ˇ);
10482        }"
10483    ));
10484
10485    // Multiple cursors on one line, with selection -> don't advance
10486    cx.set_state(indoc!(
10487        "fn a() {
10488             ˇdˇog«()ˇ»;
10489             cat();
10490        }"
10491    ));
10492    cx.update_editor(|editor, window, cx| {
10493        editor.toggle_comments(toggle_comments, window, cx);
10494    });
10495    cx.assert_editor_state(indoc!(
10496        "fn a() {
10497             // ˇdˇog«()ˇ»;
10498             cat();
10499        }"
10500    ));
10501
10502    // Single cursor on one line -> advance
10503    // Cursor moves to column 0 on blank line
10504    cx.set_state(indoc!(
10505        "fn a() {
10506             ˇdog();
10507
10508             cat();
10509        }"
10510    ));
10511    cx.update_editor(|editor, window, cx| {
10512        editor.toggle_comments(toggle_comments, window, cx);
10513    });
10514    cx.assert_editor_state(indoc!(
10515        "fn a() {
10516             // dog();
10517        ˇ
10518             cat();
10519        }"
10520    ));
10521
10522    // Single cursor on one line -> advance
10523    // Cursor starts and ends at column 0
10524    cx.set_state(indoc!(
10525        "fn a() {
10526         ˇ    dog();
10527             cat();
10528        }"
10529    ));
10530    cx.update_editor(|editor, window, cx| {
10531        editor.toggle_comments(toggle_comments, window, cx);
10532    });
10533    cx.assert_editor_state(indoc!(
10534        "fn a() {
10535             // dog();
10536         ˇ    cat();
10537        }"
10538    ));
10539}
10540
10541#[gpui::test]
10542async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10543    init_test(cx, |_| {});
10544
10545    let mut cx = EditorTestContext::new(cx).await;
10546
10547    let html_language = Arc::new(
10548        Language::new(
10549            LanguageConfig {
10550                name: "HTML".into(),
10551                block_comment: Some(("<!-- ".into(), " -->".into())),
10552                ..Default::default()
10553            },
10554            Some(tree_sitter_html::LANGUAGE.into()),
10555        )
10556        .with_injection_query(
10557            r#"
10558            (script_element
10559                (raw_text) @injection.content
10560                (#set! injection.language "javascript"))
10561            "#,
10562        )
10563        .unwrap(),
10564    );
10565
10566    let javascript_language = Arc::new(Language::new(
10567        LanguageConfig {
10568            name: "JavaScript".into(),
10569            line_comments: vec!["// ".into()],
10570            ..Default::default()
10571        },
10572        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10573    ));
10574
10575    cx.language_registry().add(html_language.clone());
10576    cx.language_registry().add(javascript_language.clone());
10577    cx.update_buffer(|buffer, cx| {
10578        buffer.set_language(Some(html_language), cx);
10579    });
10580
10581    // Toggle comments for empty selections
10582    cx.set_state(
10583        &r#"
10584            <p>A</p>ˇ
10585            <p>B</p>ˇ
10586            <p>C</p>ˇ
10587        "#
10588        .unindent(),
10589    );
10590    cx.update_editor(|editor, window, cx| {
10591        editor.toggle_comments(&ToggleComments::default(), window, cx)
10592    });
10593    cx.assert_editor_state(
10594        &r#"
10595            <!-- <p>A</p>ˇ -->
10596            <!-- <p>B</p>ˇ -->
10597            <!-- <p>C</p>ˇ -->
10598        "#
10599        .unindent(),
10600    );
10601    cx.update_editor(|editor, window, cx| {
10602        editor.toggle_comments(&ToggleComments::default(), window, cx)
10603    });
10604    cx.assert_editor_state(
10605        &r#"
10606            <p>A</p>ˇ
10607            <p>B</p>ˇ
10608            <p>C</p>ˇ
10609        "#
10610        .unindent(),
10611    );
10612
10613    // Toggle comments for mixture of empty and non-empty selections, where
10614    // multiple selections occupy a given line.
10615    cx.set_state(
10616        &r#"
10617            <p>A«</p>
10618            <p>ˇ»B</p>ˇ
10619            <p>C«</p>
10620            <p>ˇ»D</p>ˇ
10621        "#
10622        .unindent(),
10623    );
10624
10625    cx.update_editor(|editor, window, cx| {
10626        editor.toggle_comments(&ToggleComments::default(), window, cx)
10627    });
10628    cx.assert_editor_state(
10629        &r#"
10630            <!-- <p>A«</p>
10631            <p>ˇ»B</p>ˇ -->
10632            <!-- <p>C«</p>
10633            <p>ˇ»D</p>ˇ -->
10634        "#
10635        .unindent(),
10636    );
10637    cx.update_editor(|editor, window, cx| {
10638        editor.toggle_comments(&ToggleComments::default(), window, cx)
10639    });
10640    cx.assert_editor_state(
10641        &r#"
10642            <p>A«</p>
10643            <p>ˇ»B</p>ˇ
10644            <p>C«</p>
10645            <p>ˇ»D</p>ˇ
10646        "#
10647        .unindent(),
10648    );
10649
10650    // Toggle comments when different languages are active for different
10651    // selections.
10652    cx.set_state(
10653        &r#"
10654            ˇ<script>
10655                ˇvar x = new Y();
10656            ˇ</script>
10657        "#
10658        .unindent(),
10659    );
10660    cx.executor().run_until_parked();
10661    cx.update_editor(|editor, window, cx| {
10662        editor.toggle_comments(&ToggleComments::default(), window, cx)
10663    });
10664    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10665    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10666    cx.assert_editor_state(
10667        &r#"
10668            <!-- ˇ<script> -->
10669                // ˇvar x = new Y();
10670            <!-- ˇ</script> -->
10671        "#
10672        .unindent(),
10673    );
10674}
10675
10676#[gpui::test]
10677fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10678    init_test(cx, |_| {});
10679
10680    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10681    let multibuffer = cx.new(|cx| {
10682        let mut multibuffer = MultiBuffer::new(ReadWrite);
10683        multibuffer.push_excerpts(
10684            buffer.clone(),
10685            [
10686                ExcerptRange {
10687                    context: Point::new(0, 0)..Point::new(0, 4),
10688                    primary: None,
10689                },
10690                ExcerptRange {
10691                    context: Point::new(1, 0)..Point::new(1, 4),
10692                    primary: None,
10693                },
10694            ],
10695            cx,
10696        );
10697        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10698        multibuffer
10699    });
10700
10701    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10702    editor.update_in(cx, |editor, window, cx| {
10703        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10704        editor.change_selections(None, window, cx, |s| {
10705            s.select_ranges([
10706                Point::new(0, 0)..Point::new(0, 0),
10707                Point::new(1, 0)..Point::new(1, 0),
10708            ])
10709        });
10710
10711        editor.handle_input("X", window, cx);
10712        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10713        assert_eq!(
10714            editor.selections.ranges(cx),
10715            [
10716                Point::new(0, 1)..Point::new(0, 1),
10717                Point::new(1, 1)..Point::new(1, 1),
10718            ]
10719        );
10720
10721        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10722        editor.change_selections(None, window, cx, |s| {
10723            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10724        });
10725        editor.backspace(&Default::default(), window, cx);
10726        assert_eq!(editor.text(cx), "Xa\nbbb");
10727        assert_eq!(
10728            editor.selections.ranges(cx),
10729            [Point::new(1, 0)..Point::new(1, 0)]
10730        );
10731
10732        editor.change_selections(None, window, cx, |s| {
10733            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10734        });
10735        editor.backspace(&Default::default(), window, cx);
10736        assert_eq!(editor.text(cx), "X\nbb");
10737        assert_eq!(
10738            editor.selections.ranges(cx),
10739            [Point::new(0, 1)..Point::new(0, 1)]
10740        );
10741    });
10742}
10743
10744#[gpui::test]
10745fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10746    init_test(cx, |_| {});
10747
10748    let markers = vec![('[', ']').into(), ('(', ')').into()];
10749    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10750        indoc! {"
10751            [aaaa
10752            (bbbb]
10753            cccc)",
10754        },
10755        markers.clone(),
10756    );
10757    let excerpt_ranges = markers.into_iter().map(|marker| {
10758        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10759        ExcerptRange {
10760            context,
10761            primary: None,
10762        }
10763    });
10764    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10765    let multibuffer = cx.new(|cx| {
10766        let mut multibuffer = MultiBuffer::new(ReadWrite);
10767        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10768        multibuffer
10769    });
10770
10771    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10772    editor.update_in(cx, |editor, window, cx| {
10773        let (expected_text, selection_ranges) = marked_text_ranges(
10774            indoc! {"
10775                aaaa
10776                bˇbbb
10777                bˇbbˇb
10778                cccc"
10779            },
10780            true,
10781        );
10782        assert_eq!(editor.text(cx), expected_text);
10783        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10784
10785        editor.handle_input("X", window, cx);
10786
10787        let (expected_text, expected_selections) = marked_text_ranges(
10788            indoc! {"
10789                aaaa
10790                bXˇbbXb
10791                bXˇbbXˇb
10792                cccc"
10793            },
10794            false,
10795        );
10796        assert_eq!(editor.text(cx), expected_text);
10797        assert_eq!(editor.selections.ranges(cx), expected_selections);
10798
10799        editor.newline(&Newline, window, cx);
10800        let (expected_text, expected_selections) = marked_text_ranges(
10801            indoc! {"
10802                aaaa
10803                bX
10804                ˇbbX
10805                b
10806                bX
10807                ˇbbX
10808                ˇb
10809                cccc"
10810            },
10811            false,
10812        );
10813        assert_eq!(editor.text(cx), expected_text);
10814        assert_eq!(editor.selections.ranges(cx), expected_selections);
10815    });
10816}
10817
10818#[gpui::test]
10819fn test_refresh_selections(cx: &mut TestAppContext) {
10820    init_test(cx, |_| {});
10821
10822    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10823    let mut excerpt1_id = None;
10824    let multibuffer = cx.new(|cx| {
10825        let mut multibuffer = MultiBuffer::new(ReadWrite);
10826        excerpt1_id = multibuffer
10827            .push_excerpts(
10828                buffer.clone(),
10829                [
10830                    ExcerptRange {
10831                        context: Point::new(0, 0)..Point::new(1, 4),
10832                        primary: None,
10833                    },
10834                    ExcerptRange {
10835                        context: Point::new(1, 0)..Point::new(2, 4),
10836                        primary: None,
10837                    },
10838                ],
10839                cx,
10840            )
10841            .into_iter()
10842            .next();
10843        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10844        multibuffer
10845    });
10846
10847    let editor = cx.add_window(|window, cx| {
10848        let mut editor = build_editor(multibuffer.clone(), window, cx);
10849        let snapshot = editor.snapshot(window, cx);
10850        editor.change_selections(None, window, cx, |s| {
10851            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10852        });
10853        editor.begin_selection(
10854            Point::new(2, 1).to_display_point(&snapshot),
10855            true,
10856            1,
10857            window,
10858            cx,
10859        );
10860        assert_eq!(
10861            editor.selections.ranges(cx),
10862            [
10863                Point::new(1, 3)..Point::new(1, 3),
10864                Point::new(2, 1)..Point::new(2, 1),
10865            ]
10866        );
10867        editor
10868    });
10869
10870    // Refreshing selections is a no-op when excerpts haven't changed.
10871    _ = editor.update(cx, |editor, window, cx| {
10872        editor.change_selections(None, window, cx, |s| s.refresh());
10873        assert_eq!(
10874            editor.selections.ranges(cx),
10875            [
10876                Point::new(1, 3)..Point::new(1, 3),
10877                Point::new(2, 1)..Point::new(2, 1),
10878            ]
10879        );
10880    });
10881
10882    multibuffer.update(cx, |multibuffer, cx| {
10883        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10884    });
10885    _ = editor.update(cx, |editor, window, cx| {
10886        // Removing an excerpt causes the first selection to become degenerate.
10887        assert_eq!(
10888            editor.selections.ranges(cx),
10889            [
10890                Point::new(0, 0)..Point::new(0, 0),
10891                Point::new(0, 1)..Point::new(0, 1)
10892            ]
10893        );
10894
10895        // Refreshing selections will relocate the first selection to the original buffer
10896        // location.
10897        editor.change_selections(None, window, cx, |s| s.refresh());
10898        assert_eq!(
10899            editor.selections.ranges(cx),
10900            [
10901                Point::new(0, 1)..Point::new(0, 1),
10902                Point::new(0, 3)..Point::new(0, 3)
10903            ]
10904        );
10905        assert!(editor.selections.pending_anchor().is_some());
10906    });
10907}
10908
10909#[gpui::test]
10910fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10911    init_test(cx, |_| {});
10912
10913    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10914    let mut excerpt1_id = None;
10915    let multibuffer = cx.new(|cx| {
10916        let mut multibuffer = MultiBuffer::new(ReadWrite);
10917        excerpt1_id = multibuffer
10918            .push_excerpts(
10919                buffer.clone(),
10920                [
10921                    ExcerptRange {
10922                        context: Point::new(0, 0)..Point::new(1, 4),
10923                        primary: None,
10924                    },
10925                    ExcerptRange {
10926                        context: Point::new(1, 0)..Point::new(2, 4),
10927                        primary: None,
10928                    },
10929                ],
10930                cx,
10931            )
10932            .into_iter()
10933            .next();
10934        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10935        multibuffer
10936    });
10937
10938    let editor = cx.add_window(|window, cx| {
10939        let mut editor = build_editor(multibuffer.clone(), window, cx);
10940        let snapshot = editor.snapshot(window, cx);
10941        editor.begin_selection(
10942            Point::new(1, 3).to_display_point(&snapshot),
10943            false,
10944            1,
10945            window,
10946            cx,
10947        );
10948        assert_eq!(
10949            editor.selections.ranges(cx),
10950            [Point::new(1, 3)..Point::new(1, 3)]
10951        );
10952        editor
10953    });
10954
10955    multibuffer.update(cx, |multibuffer, cx| {
10956        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10957    });
10958    _ = editor.update(cx, |editor, window, cx| {
10959        assert_eq!(
10960            editor.selections.ranges(cx),
10961            [Point::new(0, 0)..Point::new(0, 0)]
10962        );
10963
10964        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10965        editor.change_selections(None, window, cx, |s| s.refresh());
10966        assert_eq!(
10967            editor.selections.ranges(cx),
10968            [Point::new(0, 3)..Point::new(0, 3)]
10969        );
10970        assert!(editor.selections.pending_anchor().is_some());
10971    });
10972}
10973
10974#[gpui::test]
10975async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10976    init_test(cx, |_| {});
10977
10978    let language = Arc::new(
10979        Language::new(
10980            LanguageConfig {
10981                brackets: BracketPairConfig {
10982                    pairs: vec![
10983                        BracketPair {
10984                            start: "{".to_string(),
10985                            end: "}".to_string(),
10986                            close: true,
10987                            surround: true,
10988                            newline: true,
10989                        },
10990                        BracketPair {
10991                            start: "/* ".to_string(),
10992                            end: " */".to_string(),
10993                            close: true,
10994                            surround: true,
10995                            newline: true,
10996                        },
10997                    ],
10998                    ..Default::default()
10999                },
11000                ..Default::default()
11001            },
11002            Some(tree_sitter_rust::LANGUAGE.into()),
11003        )
11004        .with_indents_query("")
11005        .unwrap(),
11006    );
11007
11008    let text = concat!(
11009        "{   }\n",     //
11010        "  x\n",       //
11011        "  /*   */\n", //
11012        "x\n",         //
11013        "{{} }\n",     //
11014    );
11015
11016    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11017    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11018    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11019    editor
11020        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11021        .await;
11022
11023    editor.update_in(cx, |editor, window, cx| {
11024        editor.change_selections(None, window, cx, |s| {
11025            s.select_display_ranges([
11026                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11027                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11028                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11029            ])
11030        });
11031        editor.newline(&Newline, window, cx);
11032
11033        assert_eq!(
11034            editor.buffer().read(cx).read(cx).text(),
11035            concat!(
11036                "{ \n",    // Suppress rustfmt
11037                "\n",      //
11038                "}\n",     //
11039                "  x\n",   //
11040                "  /* \n", //
11041                "  \n",    //
11042                "  */\n",  //
11043                "x\n",     //
11044                "{{} \n",  //
11045                "}\n",     //
11046            )
11047        );
11048    });
11049}
11050
11051#[gpui::test]
11052fn test_highlighted_ranges(cx: &mut TestAppContext) {
11053    init_test(cx, |_| {});
11054
11055    let editor = cx.add_window(|window, cx| {
11056        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11057        build_editor(buffer.clone(), window, cx)
11058    });
11059
11060    _ = editor.update(cx, |editor, window, cx| {
11061        struct Type1;
11062        struct Type2;
11063
11064        let buffer = editor.buffer.read(cx).snapshot(cx);
11065
11066        let anchor_range =
11067            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11068
11069        editor.highlight_background::<Type1>(
11070            &[
11071                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11072                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11073                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11074                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11075            ],
11076            |_| Hsla::red(),
11077            cx,
11078        );
11079        editor.highlight_background::<Type2>(
11080            &[
11081                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11082                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11083                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11084                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11085            ],
11086            |_| Hsla::green(),
11087            cx,
11088        );
11089
11090        let snapshot = editor.snapshot(window, cx);
11091        let mut highlighted_ranges = editor.background_highlights_in_range(
11092            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11093            &snapshot,
11094            cx.theme().colors(),
11095        );
11096        // Enforce a consistent ordering based on color without relying on the ordering of the
11097        // highlight's `TypeId` which is non-executor.
11098        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11099        assert_eq!(
11100            highlighted_ranges,
11101            &[
11102                (
11103                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11104                    Hsla::red(),
11105                ),
11106                (
11107                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11108                    Hsla::red(),
11109                ),
11110                (
11111                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11112                    Hsla::green(),
11113                ),
11114                (
11115                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11116                    Hsla::green(),
11117                ),
11118            ]
11119        );
11120        assert_eq!(
11121            editor.background_highlights_in_range(
11122                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11123                &snapshot,
11124                cx.theme().colors(),
11125            ),
11126            &[(
11127                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11128                Hsla::red(),
11129            )]
11130        );
11131    });
11132}
11133
11134#[gpui::test]
11135async fn test_following(cx: &mut TestAppContext) {
11136    init_test(cx, |_| {});
11137
11138    let fs = FakeFs::new(cx.executor());
11139    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11140
11141    let buffer = project.update(cx, |project, cx| {
11142        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11143        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11144    });
11145    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11146    let follower = cx.update(|cx| {
11147        cx.open_window(
11148            WindowOptions {
11149                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11150                    gpui::Point::new(px(0.), px(0.)),
11151                    gpui::Point::new(px(10.), px(80.)),
11152                ))),
11153                ..Default::default()
11154            },
11155            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11156        )
11157        .unwrap()
11158    });
11159
11160    let is_still_following = Rc::new(RefCell::new(true));
11161    let follower_edit_event_count = Rc::new(RefCell::new(0));
11162    let pending_update = Rc::new(RefCell::new(None));
11163    let leader_entity = leader.root(cx).unwrap();
11164    let follower_entity = follower.root(cx).unwrap();
11165    _ = follower.update(cx, {
11166        let update = pending_update.clone();
11167        let is_still_following = is_still_following.clone();
11168        let follower_edit_event_count = follower_edit_event_count.clone();
11169        |_, window, cx| {
11170            cx.subscribe_in(
11171                &leader_entity,
11172                window,
11173                move |_, leader, event, window, cx| {
11174                    leader.read(cx).add_event_to_update_proto(
11175                        event,
11176                        &mut update.borrow_mut(),
11177                        window,
11178                        cx,
11179                    );
11180                },
11181            )
11182            .detach();
11183
11184            cx.subscribe_in(
11185                &follower_entity,
11186                window,
11187                move |_, _, event: &EditorEvent, _window, _cx| {
11188                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11189                        *is_still_following.borrow_mut() = false;
11190                    }
11191
11192                    if let EditorEvent::BufferEdited = event {
11193                        *follower_edit_event_count.borrow_mut() += 1;
11194                    }
11195                },
11196            )
11197            .detach();
11198        }
11199    });
11200
11201    // Update the selections only
11202    _ = leader.update(cx, |leader, window, cx| {
11203        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11204    });
11205    follower
11206        .update(cx, |follower, window, cx| {
11207            follower.apply_update_proto(
11208                &project,
11209                pending_update.borrow_mut().take().unwrap(),
11210                window,
11211                cx,
11212            )
11213        })
11214        .unwrap()
11215        .await
11216        .unwrap();
11217    _ = follower.update(cx, |follower, _, cx| {
11218        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11219    });
11220    assert!(*is_still_following.borrow());
11221    assert_eq!(*follower_edit_event_count.borrow(), 0);
11222
11223    // Update the scroll position only
11224    _ = leader.update(cx, |leader, window, cx| {
11225        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11226    });
11227    follower
11228        .update(cx, |follower, window, cx| {
11229            follower.apply_update_proto(
11230                &project,
11231                pending_update.borrow_mut().take().unwrap(),
11232                window,
11233                cx,
11234            )
11235        })
11236        .unwrap()
11237        .await
11238        .unwrap();
11239    assert_eq!(
11240        follower
11241            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11242            .unwrap(),
11243        gpui::Point::new(1.5, 3.5)
11244    );
11245    assert!(*is_still_following.borrow());
11246    assert_eq!(*follower_edit_event_count.borrow(), 0);
11247
11248    // Update the selections and scroll position. The follower's scroll position is updated
11249    // via autoscroll, not via the leader's exact scroll position.
11250    _ = leader.update(cx, |leader, window, cx| {
11251        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11252        leader.request_autoscroll(Autoscroll::newest(), cx);
11253        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11254    });
11255    follower
11256        .update(cx, |follower, window, cx| {
11257            follower.apply_update_proto(
11258                &project,
11259                pending_update.borrow_mut().take().unwrap(),
11260                window,
11261                cx,
11262            )
11263        })
11264        .unwrap()
11265        .await
11266        .unwrap();
11267    _ = follower.update(cx, |follower, _, cx| {
11268        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11269        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11270    });
11271    assert!(*is_still_following.borrow());
11272
11273    // Creating a pending selection that precedes another selection
11274    _ = leader.update(cx, |leader, window, cx| {
11275        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11276        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11277    });
11278    follower
11279        .update(cx, |follower, window, cx| {
11280            follower.apply_update_proto(
11281                &project,
11282                pending_update.borrow_mut().take().unwrap(),
11283                window,
11284                cx,
11285            )
11286        })
11287        .unwrap()
11288        .await
11289        .unwrap();
11290    _ = follower.update(cx, |follower, _, cx| {
11291        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11292    });
11293    assert!(*is_still_following.borrow());
11294
11295    // Extend the pending selection so that it surrounds another selection
11296    _ = leader.update(cx, |leader, window, cx| {
11297        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11298    });
11299    follower
11300        .update(cx, |follower, window, cx| {
11301            follower.apply_update_proto(
11302                &project,
11303                pending_update.borrow_mut().take().unwrap(),
11304                window,
11305                cx,
11306            )
11307        })
11308        .unwrap()
11309        .await
11310        .unwrap();
11311    _ = follower.update(cx, |follower, _, cx| {
11312        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11313    });
11314
11315    // Scrolling locally breaks the follow
11316    _ = follower.update(cx, |follower, window, cx| {
11317        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11318        follower.set_scroll_anchor(
11319            ScrollAnchor {
11320                anchor: top_anchor,
11321                offset: gpui::Point::new(0.0, 0.5),
11322            },
11323            window,
11324            cx,
11325        );
11326    });
11327    assert!(!(*is_still_following.borrow()));
11328}
11329
11330#[gpui::test]
11331async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11332    init_test(cx, |_| {});
11333
11334    let fs = FakeFs::new(cx.executor());
11335    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11336    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11337    let pane = workspace
11338        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11339        .unwrap();
11340
11341    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11342
11343    let leader = pane.update_in(cx, |_, window, cx| {
11344        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11345        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11346    });
11347
11348    // Start following the editor when it has no excerpts.
11349    let mut state_message =
11350        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11351    let workspace_entity = workspace.root(cx).unwrap();
11352    let follower_1 = cx
11353        .update_window(*workspace.deref(), |_, window, cx| {
11354            Editor::from_state_proto(
11355                workspace_entity,
11356                ViewId {
11357                    creator: Default::default(),
11358                    id: 0,
11359                },
11360                &mut state_message,
11361                window,
11362                cx,
11363            )
11364        })
11365        .unwrap()
11366        .unwrap()
11367        .await
11368        .unwrap();
11369
11370    let update_message = Rc::new(RefCell::new(None));
11371    follower_1.update_in(cx, {
11372        let update = update_message.clone();
11373        |_, window, cx| {
11374            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11375                leader.read(cx).add_event_to_update_proto(
11376                    event,
11377                    &mut update.borrow_mut(),
11378                    window,
11379                    cx,
11380                );
11381            })
11382            .detach();
11383        }
11384    });
11385
11386    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11387        (
11388            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11389            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11390        )
11391    });
11392
11393    // Insert some excerpts.
11394    leader.update(cx, |leader, cx| {
11395        leader.buffer.update(cx, |multibuffer, cx| {
11396            let excerpt_ids = multibuffer.push_excerpts(
11397                buffer_1.clone(),
11398                [
11399                    ExcerptRange {
11400                        context: 1..6,
11401                        primary: None,
11402                    },
11403                    ExcerptRange {
11404                        context: 12..15,
11405                        primary: None,
11406                    },
11407                    ExcerptRange {
11408                        context: 0..3,
11409                        primary: None,
11410                    },
11411                ],
11412                cx,
11413            );
11414            multibuffer.insert_excerpts_after(
11415                excerpt_ids[0],
11416                buffer_2.clone(),
11417                [
11418                    ExcerptRange {
11419                        context: 8..12,
11420                        primary: None,
11421                    },
11422                    ExcerptRange {
11423                        context: 0..6,
11424                        primary: None,
11425                    },
11426                ],
11427                cx,
11428            );
11429        });
11430    });
11431
11432    // Apply the update of adding the excerpts.
11433    follower_1
11434        .update_in(cx, |follower, window, cx| {
11435            follower.apply_update_proto(
11436                &project,
11437                update_message.borrow().clone().unwrap(),
11438                window,
11439                cx,
11440            )
11441        })
11442        .await
11443        .unwrap();
11444    assert_eq!(
11445        follower_1.update(cx, |editor, cx| editor.text(cx)),
11446        leader.update(cx, |editor, cx| editor.text(cx))
11447    );
11448    update_message.borrow_mut().take();
11449
11450    // Start following separately after it already has excerpts.
11451    let mut state_message =
11452        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11453    let workspace_entity = workspace.root(cx).unwrap();
11454    let follower_2 = cx
11455        .update_window(*workspace.deref(), |_, window, cx| {
11456            Editor::from_state_proto(
11457                workspace_entity,
11458                ViewId {
11459                    creator: Default::default(),
11460                    id: 0,
11461                },
11462                &mut state_message,
11463                window,
11464                cx,
11465            )
11466        })
11467        .unwrap()
11468        .unwrap()
11469        .await
11470        .unwrap();
11471    assert_eq!(
11472        follower_2.update(cx, |editor, cx| editor.text(cx)),
11473        leader.update(cx, |editor, cx| editor.text(cx))
11474    );
11475
11476    // Remove some excerpts.
11477    leader.update(cx, |leader, cx| {
11478        leader.buffer.update(cx, |multibuffer, cx| {
11479            let excerpt_ids = multibuffer.excerpt_ids();
11480            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11481            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11482        });
11483    });
11484
11485    // Apply the update of removing the excerpts.
11486    follower_1
11487        .update_in(cx, |follower, window, cx| {
11488            follower.apply_update_proto(
11489                &project,
11490                update_message.borrow().clone().unwrap(),
11491                window,
11492                cx,
11493            )
11494        })
11495        .await
11496        .unwrap();
11497    follower_2
11498        .update_in(cx, |follower, window, cx| {
11499            follower.apply_update_proto(
11500                &project,
11501                update_message.borrow().clone().unwrap(),
11502                window,
11503                cx,
11504            )
11505        })
11506        .await
11507        .unwrap();
11508    update_message.borrow_mut().take();
11509    assert_eq!(
11510        follower_1.update(cx, |editor, cx| editor.text(cx)),
11511        leader.update(cx, |editor, cx| editor.text(cx))
11512    );
11513}
11514
11515#[gpui::test]
11516async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11517    init_test(cx, |_| {});
11518
11519    let mut cx = EditorTestContext::new(cx).await;
11520    let lsp_store =
11521        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11522
11523    cx.set_state(indoc! {"
11524        ˇfn func(abc def: i32) -> u32 {
11525        }
11526    "});
11527
11528    cx.update(|_, cx| {
11529        lsp_store.update(cx, |lsp_store, cx| {
11530            lsp_store
11531                .update_diagnostics(
11532                    LanguageServerId(0),
11533                    lsp::PublishDiagnosticsParams {
11534                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11535                        version: None,
11536                        diagnostics: vec![
11537                            lsp::Diagnostic {
11538                                range: lsp::Range::new(
11539                                    lsp::Position::new(0, 11),
11540                                    lsp::Position::new(0, 12),
11541                                ),
11542                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11543                                ..Default::default()
11544                            },
11545                            lsp::Diagnostic {
11546                                range: lsp::Range::new(
11547                                    lsp::Position::new(0, 12),
11548                                    lsp::Position::new(0, 15),
11549                                ),
11550                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11551                                ..Default::default()
11552                            },
11553                            lsp::Diagnostic {
11554                                range: lsp::Range::new(
11555                                    lsp::Position::new(0, 25),
11556                                    lsp::Position::new(0, 28),
11557                                ),
11558                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11559                                ..Default::default()
11560                            },
11561                        ],
11562                    },
11563                    &[],
11564                    cx,
11565                )
11566                .unwrap()
11567        });
11568    });
11569
11570    executor.run_until_parked();
11571
11572    cx.update_editor(|editor, window, cx| {
11573        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11574    });
11575
11576    cx.assert_editor_state(indoc! {"
11577        fn func(abc def: i32) -> ˇu32 {
11578        }
11579    "});
11580
11581    cx.update_editor(|editor, window, cx| {
11582        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11583    });
11584
11585    cx.assert_editor_state(indoc! {"
11586        fn func(abc ˇdef: i32) -> u32 {
11587        }
11588    "});
11589
11590    cx.update_editor(|editor, window, cx| {
11591        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11592    });
11593
11594    cx.assert_editor_state(indoc! {"
11595        fn func(abcˇ def: i32) -> u32 {
11596        }
11597    "});
11598
11599    cx.update_editor(|editor, window, cx| {
11600        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11601    });
11602
11603    cx.assert_editor_state(indoc! {"
11604        fn func(abc def: i32) -> ˇu32 {
11605        }
11606    "});
11607}
11608
11609#[gpui::test]
11610async fn cycle_through_same_place_diagnostics(
11611    executor: BackgroundExecutor,
11612    cx: &mut TestAppContext,
11613) {
11614    init_test(cx, |_| {});
11615
11616    let mut cx = EditorTestContext::new(cx).await;
11617    let lsp_store =
11618        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11619
11620    cx.set_state(indoc! {"
11621        ˇfn func(abc def: i32) -> u32 {
11622        }
11623    "});
11624
11625    cx.update(|_, cx| {
11626        lsp_store.update(cx, |lsp_store, cx| {
11627            lsp_store
11628                .update_diagnostics(
11629                    LanguageServerId(0),
11630                    lsp::PublishDiagnosticsParams {
11631                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11632                        version: None,
11633                        diagnostics: vec![
11634                            lsp::Diagnostic {
11635                                range: lsp::Range::new(
11636                                    lsp::Position::new(0, 11),
11637                                    lsp::Position::new(0, 12),
11638                                ),
11639                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11640                                ..Default::default()
11641                            },
11642                            lsp::Diagnostic {
11643                                range: lsp::Range::new(
11644                                    lsp::Position::new(0, 12),
11645                                    lsp::Position::new(0, 15),
11646                                ),
11647                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11648                                ..Default::default()
11649                            },
11650                            lsp::Diagnostic {
11651                                range: lsp::Range::new(
11652                                    lsp::Position::new(0, 12),
11653                                    lsp::Position::new(0, 15),
11654                                ),
11655                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11656                                ..Default::default()
11657                            },
11658                            lsp::Diagnostic {
11659                                range: lsp::Range::new(
11660                                    lsp::Position::new(0, 25),
11661                                    lsp::Position::new(0, 28),
11662                                ),
11663                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11664                                ..Default::default()
11665                            },
11666                        ],
11667                    },
11668                    &[],
11669                    cx,
11670                )
11671                .unwrap()
11672        });
11673    });
11674    executor.run_until_parked();
11675
11676    //// Backward
11677
11678    // Fourth diagnostic
11679    cx.update_editor(|editor, window, cx| {
11680        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11681    });
11682    cx.assert_editor_state(indoc! {"
11683        fn func(abc def: i32) -> ˇu32 {
11684        }
11685    "});
11686
11687    // Third diagnostic
11688    cx.update_editor(|editor, window, cx| {
11689        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11690    });
11691    cx.assert_editor_state(indoc! {"
11692        fn func(abc ˇdef: i32) -> u32 {
11693        }
11694    "});
11695
11696    // Second diagnostic, same place
11697    cx.update_editor(|editor, window, cx| {
11698        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11699    });
11700    cx.assert_editor_state(indoc! {"
11701        fn func(abc ˇdef: i32) -> u32 {
11702        }
11703    "});
11704
11705    // First diagnostic
11706    cx.update_editor(|editor, window, cx| {
11707        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11708    });
11709    cx.assert_editor_state(indoc! {"
11710        fn func(abcˇ def: i32) -> u32 {
11711        }
11712    "});
11713
11714    // Wrapped over, fourth diagnostic
11715    cx.update_editor(|editor, window, cx| {
11716        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11717    });
11718    cx.assert_editor_state(indoc! {"
11719        fn func(abc def: i32) -> ˇu32 {
11720        }
11721    "});
11722
11723    cx.update_editor(|editor, window, cx| {
11724        editor.move_to_beginning(&MoveToBeginning, window, cx);
11725    });
11726    cx.assert_editor_state(indoc! {"
11727        ˇfn func(abc def: i32) -> u32 {
11728        }
11729    "});
11730
11731    //// Forward
11732
11733    // First diagnostic
11734    cx.update_editor(|editor, window, cx| {
11735        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11736    });
11737    cx.assert_editor_state(indoc! {"
11738        fn func(abcˇ def: i32) -> u32 {
11739        }
11740    "});
11741
11742    // Second diagnostic
11743    cx.update_editor(|editor, window, cx| {
11744        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11745    });
11746    cx.assert_editor_state(indoc! {"
11747        fn func(abc ˇdef: i32) -> u32 {
11748        }
11749    "});
11750
11751    // Third diagnostic, same place
11752    cx.update_editor(|editor, window, cx| {
11753        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11754    });
11755    cx.assert_editor_state(indoc! {"
11756        fn func(abc ˇdef: i32) -> u32 {
11757        }
11758    "});
11759
11760    // Fourth diagnostic
11761    cx.update_editor(|editor, window, cx| {
11762        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11763    });
11764    cx.assert_editor_state(indoc! {"
11765        fn func(abc def: i32) -> ˇu32 {
11766        }
11767    "});
11768
11769    // Wrapped around, first diagnostic
11770    cx.update_editor(|editor, window, cx| {
11771        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11772    });
11773    cx.assert_editor_state(indoc! {"
11774        fn func(abcˇ def: i32) -> u32 {
11775        }
11776    "});
11777}
11778
11779#[gpui::test]
11780async fn active_diagnostics_dismiss_after_invalidation(
11781    executor: BackgroundExecutor,
11782    cx: &mut TestAppContext,
11783) {
11784    init_test(cx, |_| {});
11785
11786    let mut cx = EditorTestContext::new(cx).await;
11787    let lsp_store =
11788        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11789
11790    cx.set_state(indoc! {"
11791        ˇfn func(abc def: i32) -> u32 {
11792        }
11793    "});
11794
11795    let message = "Something's wrong!";
11796    cx.update(|_, cx| {
11797        lsp_store.update(cx, |lsp_store, cx| {
11798            lsp_store
11799                .update_diagnostics(
11800                    LanguageServerId(0),
11801                    lsp::PublishDiagnosticsParams {
11802                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11803                        version: None,
11804                        diagnostics: vec![lsp::Diagnostic {
11805                            range: lsp::Range::new(
11806                                lsp::Position::new(0, 11),
11807                                lsp::Position::new(0, 12),
11808                            ),
11809                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11810                            message: message.to_string(),
11811                            ..Default::default()
11812                        }],
11813                    },
11814                    &[],
11815                    cx,
11816                )
11817                .unwrap()
11818        });
11819    });
11820    executor.run_until_parked();
11821
11822    cx.update_editor(|editor, window, cx| {
11823        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11824        assert_eq!(
11825            editor
11826                .active_diagnostics
11827                .as_ref()
11828                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11829            Some(message),
11830            "Should have a diagnostics group activated"
11831        );
11832    });
11833    cx.assert_editor_state(indoc! {"
11834        fn func(abcˇ def: i32) -> u32 {
11835        }
11836    "});
11837
11838    cx.update(|_, cx| {
11839        lsp_store.update(cx, |lsp_store, cx| {
11840            lsp_store
11841                .update_diagnostics(
11842                    LanguageServerId(0),
11843                    lsp::PublishDiagnosticsParams {
11844                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11845                        version: None,
11846                        diagnostics: Vec::new(),
11847                    },
11848                    &[],
11849                    cx,
11850                )
11851                .unwrap()
11852        });
11853    });
11854    executor.run_until_parked();
11855    cx.update_editor(|editor, _, _| {
11856        assert_eq!(
11857            editor.active_diagnostics, None,
11858            "After no diagnostics set to the editor, no diagnostics should be active"
11859        );
11860    });
11861    cx.assert_editor_state(indoc! {"
11862        fn func(abcˇ def: i32) -> u32 {
11863        }
11864    "});
11865
11866    cx.update_editor(|editor, window, cx| {
11867        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11868        assert_eq!(
11869            editor.active_diagnostics, None,
11870            "Should be no diagnostics to go to and activate"
11871        );
11872    });
11873    cx.assert_editor_state(indoc! {"
11874        fn func(abcˇ def: i32) -> u32 {
11875        }
11876    "});
11877}
11878
11879#[gpui::test]
11880async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11881    init_test(cx, |_| {});
11882
11883    let mut cx = EditorTestContext::new(cx).await;
11884
11885    cx.set_state(indoc! {"
11886        fn func(abˇc def: i32) -> u32 {
11887        }
11888    "});
11889    let lsp_store =
11890        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11891
11892    cx.update(|_, cx| {
11893        lsp_store.update(cx, |lsp_store, cx| {
11894            lsp_store.update_diagnostics(
11895                LanguageServerId(0),
11896                lsp::PublishDiagnosticsParams {
11897                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11898                    version: None,
11899                    diagnostics: vec![lsp::Diagnostic {
11900                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11901                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11902                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11903                        ..Default::default()
11904                    }],
11905                },
11906                &[],
11907                cx,
11908            )
11909        })
11910    }).unwrap();
11911    cx.run_until_parked();
11912    cx.update_editor(|editor, window, cx| {
11913        hover_popover::hover(editor, &Default::default(), window, cx)
11914    });
11915    cx.run_until_parked();
11916    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11917}
11918
11919#[gpui::test]
11920async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11921    init_test(cx, |_| {});
11922
11923    let mut cx = EditorTestContext::new(cx).await;
11924
11925    let diff_base = r#"
11926        use some::mod;
11927
11928        const A: u32 = 42;
11929
11930        fn main() {
11931            println!("hello");
11932
11933            println!("world");
11934        }
11935        "#
11936    .unindent();
11937
11938    // Edits are modified, removed, modified, added
11939    cx.set_state(
11940        &r#"
11941        use some::modified;
11942
11943        ˇ
11944        fn main() {
11945            println!("hello there");
11946
11947            println!("around the");
11948            println!("world");
11949        }
11950        "#
11951        .unindent(),
11952    );
11953
11954    cx.set_head_text(&diff_base);
11955    executor.run_until_parked();
11956
11957    cx.update_editor(|editor, window, cx| {
11958        //Wrap around the bottom of the buffer
11959        for _ in 0..3 {
11960            editor.go_to_next_hunk(&GoToHunk, window, cx);
11961        }
11962    });
11963
11964    cx.assert_editor_state(
11965        &r#"
11966        ˇuse some::modified;
11967
11968
11969        fn main() {
11970            println!("hello there");
11971
11972            println!("around the");
11973            println!("world");
11974        }
11975        "#
11976        .unindent(),
11977    );
11978
11979    cx.update_editor(|editor, window, cx| {
11980        //Wrap around the top of the buffer
11981        for _ in 0..2 {
11982            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11983        }
11984    });
11985
11986    cx.assert_editor_state(
11987        &r#"
11988        use some::modified;
11989
11990
11991        fn main() {
11992        ˇ    println!("hello there");
11993
11994            println!("around the");
11995            println!("world");
11996        }
11997        "#
11998        .unindent(),
11999    );
12000
12001    cx.update_editor(|editor, window, cx| {
12002        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12003    });
12004
12005    cx.assert_editor_state(
12006        &r#"
12007        use some::modified;
12008
12009        ˇ
12010        fn main() {
12011            println!("hello there");
12012
12013            println!("around the");
12014            println!("world");
12015        }
12016        "#
12017        .unindent(),
12018    );
12019
12020    cx.update_editor(|editor, window, cx| {
12021        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12022    });
12023
12024    cx.assert_editor_state(
12025        &r#"
12026        ˇuse some::modified;
12027
12028
12029        fn main() {
12030            println!("hello there");
12031
12032            println!("around the");
12033            println!("world");
12034        }
12035        "#
12036        .unindent(),
12037    );
12038
12039    cx.update_editor(|editor, window, cx| {
12040        for _ in 0..2 {
12041            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12042        }
12043    });
12044
12045    cx.assert_editor_state(
12046        &r#"
12047        use some::modified;
12048
12049
12050        fn main() {
12051        ˇ    println!("hello there");
12052
12053            println!("around the");
12054            println!("world");
12055        }
12056        "#
12057        .unindent(),
12058    );
12059
12060    cx.update_editor(|editor, window, cx| {
12061        editor.fold(&Fold, window, cx);
12062    });
12063
12064    cx.update_editor(|editor, window, cx| {
12065        editor.go_to_next_hunk(&GoToHunk, window, cx);
12066    });
12067
12068    cx.assert_editor_state(
12069        &r#"
12070        ˇuse some::modified;
12071
12072
12073        fn main() {
12074            println!("hello there");
12075
12076            println!("around the");
12077            println!("world");
12078        }
12079        "#
12080        .unindent(),
12081    );
12082}
12083
12084#[test]
12085fn test_split_words() {
12086    fn split(text: &str) -> Vec<&str> {
12087        split_words(text).collect()
12088    }
12089
12090    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12091    assert_eq!(split("hello_world"), &["hello_", "world"]);
12092    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12093    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12094    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12095    assert_eq!(split("helloworld"), &["helloworld"]);
12096
12097    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12098}
12099
12100#[gpui::test]
12101async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12102    init_test(cx, |_| {});
12103
12104    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12105    let mut assert = |before, after| {
12106        let _state_context = cx.set_state(before);
12107        cx.run_until_parked();
12108        cx.update_editor(|editor, window, cx| {
12109            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12110        });
12111        cx.run_until_parked();
12112        cx.assert_editor_state(after);
12113    };
12114
12115    // Outside bracket jumps to outside of matching bracket
12116    assert("console.logˇ(var);", "console.log(var)ˇ;");
12117    assert("console.log(var)ˇ;", "console.logˇ(var);");
12118
12119    // Inside bracket jumps to inside of matching bracket
12120    assert("console.log(ˇvar);", "console.log(varˇ);");
12121    assert("console.log(varˇ);", "console.log(ˇvar);");
12122
12123    // When outside a bracket and inside, favor jumping to the inside bracket
12124    assert(
12125        "console.log('foo', [1, 2, 3]ˇ);",
12126        "console.log(ˇ'foo', [1, 2, 3]);",
12127    );
12128    assert(
12129        "console.log(ˇ'foo', [1, 2, 3]);",
12130        "console.log('foo', [1, 2, 3]ˇ);",
12131    );
12132
12133    // Bias forward if two options are equally likely
12134    assert(
12135        "let result = curried_fun()ˇ();",
12136        "let result = curried_fun()()ˇ;",
12137    );
12138
12139    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12140    assert(
12141        indoc! {"
12142            function test() {
12143                console.log('test')ˇ
12144            }"},
12145        indoc! {"
12146            function test() {
12147                console.logˇ('test')
12148            }"},
12149    );
12150}
12151
12152#[gpui::test]
12153async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12154    init_test(cx, |_| {});
12155
12156    let fs = FakeFs::new(cx.executor());
12157    fs.insert_tree(
12158        path!("/a"),
12159        json!({
12160            "main.rs": "fn main() { let a = 5; }",
12161            "other.rs": "// Test file",
12162        }),
12163    )
12164    .await;
12165    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12166
12167    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12168    language_registry.add(Arc::new(Language::new(
12169        LanguageConfig {
12170            name: "Rust".into(),
12171            matcher: LanguageMatcher {
12172                path_suffixes: vec!["rs".to_string()],
12173                ..Default::default()
12174            },
12175            brackets: BracketPairConfig {
12176                pairs: vec![BracketPair {
12177                    start: "{".to_string(),
12178                    end: "}".to_string(),
12179                    close: true,
12180                    surround: true,
12181                    newline: true,
12182                }],
12183                disabled_scopes_by_bracket_ix: Vec::new(),
12184            },
12185            ..Default::default()
12186        },
12187        Some(tree_sitter_rust::LANGUAGE.into()),
12188    )));
12189    let mut fake_servers = language_registry.register_fake_lsp(
12190        "Rust",
12191        FakeLspAdapter {
12192            capabilities: lsp::ServerCapabilities {
12193                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12194                    first_trigger_character: "{".to_string(),
12195                    more_trigger_character: None,
12196                }),
12197                ..Default::default()
12198            },
12199            ..Default::default()
12200        },
12201    );
12202
12203    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12204
12205    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12206
12207    let worktree_id = workspace
12208        .update(cx, |workspace, _, cx| {
12209            workspace.project().update(cx, |project, cx| {
12210                project.worktrees(cx).next().unwrap().read(cx).id()
12211            })
12212        })
12213        .unwrap();
12214
12215    let buffer = project
12216        .update(cx, |project, cx| {
12217            project.open_local_buffer(path!("/a/main.rs"), cx)
12218        })
12219        .await
12220        .unwrap();
12221    let editor_handle = workspace
12222        .update(cx, |workspace, window, cx| {
12223            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12224        })
12225        .unwrap()
12226        .await
12227        .unwrap()
12228        .downcast::<Editor>()
12229        .unwrap();
12230
12231    cx.executor().start_waiting();
12232    let fake_server = fake_servers.next().await.unwrap();
12233
12234    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12235        |params, _| async move {
12236            assert_eq!(
12237                params.text_document_position.text_document.uri,
12238                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12239            );
12240            assert_eq!(
12241                params.text_document_position.position,
12242                lsp::Position::new(0, 21),
12243            );
12244
12245            Ok(Some(vec![lsp::TextEdit {
12246                new_text: "]".to_string(),
12247                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12248            }]))
12249        },
12250    );
12251
12252    editor_handle.update_in(cx, |editor, window, cx| {
12253        window.focus(&editor.focus_handle(cx));
12254        editor.change_selections(None, window, cx, |s| {
12255            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12256        });
12257        editor.handle_input("{", window, cx);
12258    });
12259
12260    cx.executor().run_until_parked();
12261
12262    buffer.update(cx, |buffer, _| {
12263        assert_eq!(
12264            buffer.text(),
12265            "fn main() { let a = {5}; }",
12266            "No extra braces from on type formatting should appear in the buffer"
12267        )
12268    });
12269}
12270
12271#[gpui::test]
12272async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12273    init_test(cx, |_| {});
12274
12275    let fs = FakeFs::new(cx.executor());
12276    fs.insert_tree(
12277        path!("/a"),
12278        json!({
12279            "main.rs": "fn main() { let a = 5; }",
12280            "other.rs": "// Test file",
12281        }),
12282    )
12283    .await;
12284
12285    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12286
12287    let server_restarts = Arc::new(AtomicUsize::new(0));
12288    let closure_restarts = Arc::clone(&server_restarts);
12289    let language_server_name = "test language server";
12290    let language_name: LanguageName = "Rust".into();
12291
12292    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12293    language_registry.add(Arc::new(Language::new(
12294        LanguageConfig {
12295            name: language_name.clone(),
12296            matcher: LanguageMatcher {
12297                path_suffixes: vec!["rs".to_string()],
12298                ..Default::default()
12299            },
12300            ..Default::default()
12301        },
12302        Some(tree_sitter_rust::LANGUAGE.into()),
12303    )));
12304    let mut fake_servers = language_registry.register_fake_lsp(
12305        "Rust",
12306        FakeLspAdapter {
12307            name: language_server_name,
12308            initialization_options: Some(json!({
12309                "testOptionValue": true
12310            })),
12311            initializer: Some(Box::new(move |fake_server| {
12312                let task_restarts = Arc::clone(&closure_restarts);
12313                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12314                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12315                    futures::future::ready(Ok(()))
12316                });
12317            })),
12318            ..Default::default()
12319        },
12320    );
12321
12322    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12323    let _buffer = project
12324        .update(cx, |project, cx| {
12325            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12326        })
12327        .await
12328        .unwrap();
12329    let _fake_server = fake_servers.next().await.unwrap();
12330    update_test_language_settings(cx, |language_settings| {
12331        language_settings.languages.insert(
12332            language_name.clone(),
12333            LanguageSettingsContent {
12334                tab_size: NonZeroU32::new(8),
12335                ..Default::default()
12336            },
12337        );
12338    });
12339    cx.executor().run_until_parked();
12340    assert_eq!(
12341        server_restarts.load(atomic::Ordering::Acquire),
12342        0,
12343        "Should not restart LSP server on an unrelated change"
12344    );
12345
12346    update_test_project_settings(cx, |project_settings| {
12347        project_settings.lsp.insert(
12348            "Some other server name".into(),
12349            LspSettings {
12350                binary: None,
12351                settings: None,
12352                initialization_options: Some(json!({
12353                    "some other init value": false
12354                })),
12355            },
12356        );
12357    });
12358    cx.executor().run_until_parked();
12359    assert_eq!(
12360        server_restarts.load(atomic::Ordering::Acquire),
12361        0,
12362        "Should not restart LSP server on an unrelated LSP settings change"
12363    );
12364
12365    update_test_project_settings(cx, |project_settings| {
12366        project_settings.lsp.insert(
12367            language_server_name.into(),
12368            LspSettings {
12369                binary: None,
12370                settings: None,
12371                initialization_options: Some(json!({
12372                    "anotherInitValue": false
12373                })),
12374            },
12375        );
12376    });
12377    cx.executor().run_until_parked();
12378    assert_eq!(
12379        server_restarts.load(atomic::Ordering::Acquire),
12380        1,
12381        "Should restart LSP server on a related LSP settings change"
12382    );
12383
12384    update_test_project_settings(cx, |project_settings| {
12385        project_settings.lsp.insert(
12386            language_server_name.into(),
12387            LspSettings {
12388                binary: None,
12389                settings: None,
12390                initialization_options: Some(json!({
12391                    "anotherInitValue": false
12392                })),
12393            },
12394        );
12395    });
12396    cx.executor().run_until_parked();
12397    assert_eq!(
12398        server_restarts.load(atomic::Ordering::Acquire),
12399        1,
12400        "Should not restart LSP server on a related LSP settings change that is the same"
12401    );
12402
12403    update_test_project_settings(cx, |project_settings| {
12404        project_settings.lsp.insert(
12405            language_server_name.into(),
12406            LspSettings {
12407                binary: None,
12408                settings: None,
12409                initialization_options: None,
12410            },
12411        );
12412    });
12413    cx.executor().run_until_parked();
12414    assert_eq!(
12415        server_restarts.load(atomic::Ordering::Acquire),
12416        2,
12417        "Should restart LSP server on another related LSP settings change"
12418    );
12419}
12420
12421#[gpui::test]
12422async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12423    init_test(cx, |_| {});
12424
12425    let mut cx = EditorLspTestContext::new_rust(
12426        lsp::ServerCapabilities {
12427            completion_provider: Some(lsp::CompletionOptions {
12428                trigger_characters: Some(vec![".".to_string()]),
12429                resolve_provider: Some(true),
12430                ..Default::default()
12431            }),
12432            ..Default::default()
12433        },
12434        cx,
12435    )
12436    .await;
12437
12438    cx.set_state("fn main() { let a = 2ˇ; }");
12439    cx.simulate_keystroke(".");
12440    let completion_item = lsp::CompletionItem {
12441        label: "some".into(),
12442        kind: Some(lsp::CompletionItemKind::SNIPPET),
12443        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12444        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12445            kind: lsp::MarkupKind::Markdown,
12446            value: "```rust\nSome(2)\n```".to_string(),
12447        })),
12448        deprecated: Some(false),
12449        sort_text: Some("fffffff2".to_string()),
12450        filter_text: Some("some".to_string()),
12451        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12452        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12453            range: lsp::Range {
12454                start: lsp::Position {
12455                    line: 0,
12456                    character: 22,
12457                },
12458                end: lsp::Position {
12459                    line: 0,
12460                    character: 22,
12461                },
12462            },
12463            new_text: "Some(2)".to_string(),
12464        })),
12465        additional_text_edits: Some(vec![lsp::TextEdit {
12466            range: lsp::Range {
12467                start: lsp::Position {
12468                    line: 0,
12469                    character: 20,
12470                },
12471                end: lsp::Position {
12472                    line: 0,
12473                    character: 22,
12474                },
12475            },
12476            new_text: "".to_string(),
12477        }]),
12478        ..Default::default()
12479    };
12480
12481    let closure_completion_item = completion_item.clone();
12482    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12483        let task_completion_item = closure_completion_item.clone();
12484        async move {
12485            Ok(Some(lsp::CompletionResponse::Array(vec![
12486                task_completion_item,
12487            ])))
12488        }
12489    });
12490
12491    request.next().await;
12492
12493    cx.condition(|editor, _| editor.context_menu_visible())
12494        .await;
12495    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12496        editor
12497            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12498            .unwrap()
12499    });
12500    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12501
12502    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12503        let task_completion_item = completion_item.clone();
12504        async move { Ok(task_completion_item) }
12505    })
12506    .next()
12507    .await
12508    .unwrap();
12509    apply_additional_edits.await.unwrap();
12510    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12511}
12512
12513#[gpui::test]
12514async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12515    init_test(cx, |_| {});
12516
12517    let mut cx = EditorLspTestContext::new_rust(
12518        lsp::ServerCapabilities {
12519            completion_provider: Some(lsp::CompletionOptions {
12520                trigger_characters: Some(vec![".".to_string()]),
12521                resolve_provider: Some(true),
12522                ..Default::default()
12523            }),
12524            ..Default::default()
12525        },
12526        cx,
12527    )
12528    .await;
12529
12530    cx.set_state("fn main() { let a = 2ˇ; }");
12531    cx.simulate_keystroke(".");
12532
12533    let item1 = lsp::CompletionItem {
12534        label: "method id()".to_string(),
12535        filter_text: Some("id".to_string()),
12536        detail: None,
12537        documentation: None,
12538        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12539            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12540            new_text: ".id".to_string(),
12541        })),
12542        ..lsp::CompletionItem::default()
12543    };
12544
12545    let item2 = lsp::CompletionItem {
12546        label: "other".to_string(),
12547        filter_text: Some("other".to_string()),
12548        detail: None,
12549        documentation: None,
12550        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12551            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12552            new_text: ".other".to_string(),
12553        })),
12554        ..lsp::CompletionItem::default()
12555    };
12556
12557    let item1 = item1.clone();
12558    cx.set_request_handler::<lsp::request::Completion, _, _>({
12559        let item1 = item1.clone();
12560        move |_, _, _| {
12561            let item1 = item1.clone();
12562            let item2 = item2.clone();
12563            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12564        }
12565    })
12566    .next()
12567    .await;
12568
12569    cx.condition(|editor, _| editor.context_menu_visible())
12570        .await;
12571    cx.update_editor(|editor, _, _| {
12572        let context_menu = editor.context_menu.borrow_mut();
12573        let context_menu = context_menu
12574            .as_ref()
12575            .expect("Should have the context menu deployed");
12576        match context_menu {
12577            CodeContextMenu::Completions(completions_menu) => {
12578                let completions = completions_menu.completions.borrow_mut();
12579                assert_eq!(
12580                    completions
12581                        .iter()
12582                        .map(|completion| &completion.label.text)
12583                        .collect::<Vec<_>>(),
12584                    vec!["method id()", "other"]
12585                )
12586            }
12587            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12588        }
12589    });
12590
12591    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12592        let item1 = item1.clone();
12593        move |_, item_to_resolve, _| {
12594            let item1 = item1.clone();
12595            async move {
12596                if item1 == item_to_resolve {
12597                    Ok(lsp::CompletionItem {
12598                        label: "method id()".to_string(),
12599                        filter_text: Some("id".to_string()),
12600                        detail: Some("Now resolved!".to_string()),
12601                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12602                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12603                            range: lsp::Range::new(
12604                                lsp::Position::new(0, 22),
12605                                lsp::Position::new(0, 22),
12606                            ),
12607                            new_text: ".id".to_string(),
12608                        })),
12609                        ..lsp::CompletionItem::default()
12610                    })
12611                } else {
12612                    Ok(item_to_resolve)
12613                }
12614            }
12615        }
12616    })
12617    .next()
12618    .await
12619    .unwrap();
12620    cx.run_until_parked();
12621
12622    cx.update_editor(|editor, window, cx| {
12623        editor.context_menu_next(&Default::default(), window, cx);
12624    });
12625
12626    cx.update_editor(|editor, _, _| {
12627        let context_menu = editor.context_menu.borrow_mut();
12628        let context_menu = context_menu
12629            .as_ref()
12630            .expect("Should have the context menu deployed");
12631        match context_menu {
12632            CodeContextMenu::Completions(completions_menu) => {
12633                let completions = completions_menu.completions.borrow_mut();
12634                assert_eq!(
12635                    completions
12636                        .iter()
12637                        .map(|completion| &completion.label.text)
12638                        .collect::<Vec<_>>(),
12639                    vec!["method id() Now resolved!", "other"],
12640                    "Should update first completion label, but not second as the filter text did not match."
12641                );
12642            }
12643            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12644        }
12645    });
12646}
12647
12648#[gpui::test]
12649async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12650    init_test(cx, |_| {});
12651
12652    let mut cx = EditorLspTestContext::new_rust(
12653        lsp::ServerCapabilities {
12654            completion_provider: Some(lsp::CompletionOptions {
12655                trigger_characters: Some(vec![".".to_string()]),
12656                resolve_provider: Some(true),
12657                ..Default::default()
12658            }),
12659            ..Default::default()
12660        },
12661        cx,
12662    )
12663    .await;
12664
12665    cx.set_state("fn main() { let a = 2ˇ; }");
12666    cx.simulate_keystroke(".");
12667
12668    let unresolved_item_1 = lsp::CompletionItem {
12669        label: "id".to_string(),
12670        filter_text: Some("id".to_string()),
12671        detail: None,
12672        documentation: None,
12673        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12674            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12675            new_text: ".id".to_string(),
12676        })),
12677        ..lsp::CompletionItem::default()
12678    };
12679    let resolved_item_1 = lsp::CompletionItem {
12680        additional_text_edits: Some(vec![lsp::TextEdit {
12681            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12682            new_text: "!!".to_string(),
12683        }]),
12684        ..unresolved_item_1.clone()
12685    };
12686    let unresolved_item_2 = lsp::CompletionItem {
12687        label: "other".to_string(),
12688        filter_text: Some("other".to_string()),
12689        detail: None,
12690        documentation: None,
12691        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12692            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12693            new_text: ".other".to_string(),
12694        })),
12695        ..lsp::CompletionItem::default()
12696    };
12697    let resolved_item_2 = lsp::CompletionItem {
12698        additional_text_edits: Some(vec![lsp::TextEdit {
12699            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12700            new_text: "??".to_string(),
12701        }]),
12702        ..unresolved_item_2.clone()
12703    };
12704
12705    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12706    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12707    cx.lsp
12708        .server
12709        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12710            let unresolved_item_1 = unresolved_item_1.clone();
12711            let resolved_item_1 = resolved_item_1.clone();
12712            let unresolved_item_2 = unresolved_item_2.clone();
12713            let resolved_item_2 = resolved_item_2.clone();
12714            let resolve_requests_1 = resolve_requests_1.clone();
12715            let resolve_requests_2 = resolve_requests_2.clone();
12716            move |unresolved_request, _| {
12717                let unresolved_item_1 = unresolved_item_1.clone();
12718                let resolved_item_1 = resolved_item_1.clone();
12719                let unresolved_item_2 = unresolved_item_2.clone();
12720                let resolved_item_2 = resolved_item_2.clone();
12721                let resolve_requests_1 = resolve_requests_1.clone();
12722                let resolve_requests_2 = resolve_requests_2.clone();
12723                async move {
12724                    if unresolved_request == unresolved_item_1 {
12725                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12726                        Ok(resolved_item_1.clone())
12727                    } else if unresolved_request == unresolved_item_2 {
12728                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12729                        Ok(resolved_item_2.clone())
12730                    } else {
12731                        panic!("Unexpected completion item {unresolved_request:?}")
12732                    }
12733                }
12734            }
12735        })
12736        .detach();
12737
12738    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12739        let unresolved_item_1 = unresolved_item_1.clone();
12740        let unresolved_item_2 = unresolved_item_2.clone();
12741        async move {
12742            Ok(Some(lsp::CompletionResponse::Array(vec![
12743                unresolved_item_1,
12744                unresolved_item_2,
12745            ])))
12746        }
12747    })
12748    .next()
12749    .await;
12750
12751    cx.condition(|editor, _| editor.context_menu_visible())
12752        .await;
12753    cx.update_editor(|editor, _, _| {
12754        let context_menu = editor.context_menu.borrow_mut();
12755        let context_menu = context_menu
12756            .as_ref()
12757            .expect("Should have the context menu deployed");
12758        match context_menu {
12759            CodeContextMenu::Completions(completions_menu) => {
12760                let completions = completions_menu.completions.borrow_mut();
12761                assert_eq!(
12762                    completions
12763                        .iter()
12764                        .map(|completion| &completion.label.text)
12765                        .collect::<Vec<_>>(),
12766                    vec!["id", "other"]
12767                )
12768            }
12769            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12770        }
12771    });
12772    cx.run_until_parked();
12773
12774    cx.update_editor(|editor, window, cx| {
12775        editor.context_menu_next(&ContextMenuNext, window, cx);
12776    });
12777    cx.run_until_parked();
12778    cx.update_editor(|editor, window, cx| {
12779        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12780    });
12781    cx.run_until_parked();
12782    cx.update_editor(|editor, window, cx| {
12783        editor.context_menu_next(&ContextMenuNext, window, cx);
12784    });
12785    cx.run_until_parked();
12786    cx.update_editor(|editor, window, cx| {
12787        editor
12788            .compose_completion(&ComposeCompletion::default(), window, cx)
12789            .expect("No task returned")
12790    })
12791    .await
12792    .expect("Completion failed");
12793    cx.run_until_parked();
12794
12795    cx.update_editor(|editor, _, cx| {
12796        assert_eq!(
12797            resolve_requests_1.load(atomic::Ordering::Acquire),
12798            1,
12799            "Should always resolve once despite multiple selections"
12800        );
12801        assert_eq!(
12802            resolve_requests_2.load(atomic::Ordering::Acquire),
12803            1,
12804            "Should always resolve once after multiple selections and applying the completion"
12805        );
12806        assert_eq!(
12807            editor.text(cx),
12808            "fn main() { let a = ??.other; }",
12809            "Should use resolved data when applying the completion"
12810        );
12811    });
12812}
12813
12814#[gpui::test]
12815async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12816    init_test(cx, |_| {});
12817
12818    let item_0 = lsp::CompletionItem {
12819        label: "abs".into(),
12820        insert_text: Some("abs".into()),
12821        data: Some(json!({ "very": "special"})),
12822        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12823        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12824            lsp::InsertReplaceEdit {
12825                new_text: "abs".to_string(),
12826                insert: lsp::Range::default(),
12827                replace: lsp::Range::default(),
12828            },
12829        )),
12830        ..lsp::CompletionItem::default()
12831    };
12832    let items = iter::once(item_0.clone())
12833        .chain((11..51).map(|i| lsp::CompletionItem {
12834            label: format!("item_{}", i),
12835            insert_text: Some(format!("item_{}", i)),
12836            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12837            ..lsp::CompletionItem::default()
12838        }))
12839        .collect::<Vec<_>>();
12840
12841    let default_commit_characters = vec!["?".to_string()];
12842    let default_data = json!({ "default": "data"});
12843    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12844    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12845    let default_edit_range = lsp::Range {
12846        start: lsp::Position {
12847            line: 0,
12848            character: 5,
12849        },
12850        end: lsp::Position {
12851            line: 0,
12852            character: 5,
12853        },
12854    };
12855
12856    let mut cx = EditorLspTestContext::new_rust(
12857        lsp::ServerCapabilities {
12858            completion_provider: Some(lsp::CompletionOptions {
12859                trigger_characters: Some(vec![".".to_string()]),
12860                resolve_provider: Some(true),
12861                ..Default::default()
12862            }),
12863            ..Default::default()
12864        },
12865        cx,
12866    )
12867    .await;
12868
12869    cx.set_state("fn main() { let a = 2ˇ; }");
12870    cx.simulate_keystroke(".");
12871
12872    let completion_data = default_data.clone();
12873    let completion_characters = default_commit_characters.clone();
12874    let completion_items = items.clone();
12875    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12876        let default_data = completion_data.clone();
12877        let default_commit_characters = completion_characters.clone();
12878        let items = completion_items.clone();
12879        async move {
12880            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12881                items,
12882                item_defaults: Some(lsp::CompletionListItemDefaults {
12883                    data: Some(default_data.clone()),
12884                    commit_characters: Some(default_commit_characters.clone()),
12885                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12886                        default_edit_range,
12887                    )),
12888                    insert_text_format: Some(default_insert_text_format),
12889                    insert_text_mode: Some(default_insert_text_mode),
12890                }),
12891                ..lsp::CompletionList::default()
12892            })))
12893        }
12894    })
12895    .next()
12896    .await;
12897
12898    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12899    cx.lsp
12900        .server
12901        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12902            let closure_resolved_items = resolved_items.clone();
12903            move |item_to_resolve, _| {
12904                let closure_resolved_items = closure_resolved_items.clone();
12905                async move {
12906                    closure_resolved_items.lock().push(item_to_resolve.clone());
12907                    Ok(item_to_resolve)
12908                }
12909            }
12910        })
12911        .detach();
12912
12913    cx.condition(|editor, _| editor.context_menu_visible())
12914        .await;
12915    cx.run_until_parked();
12916    cx.update_editor(|editor, _, _| {
12917        let menu = editor.context_menu.borrow_mut();
12918        match menu.as_ref().expect("should have the completions menu") {
12919            CodeContextMenu::Completions(completions_menu) => {
12920                assert_eq!(
12921                    completions_menu
12922                        .entries
12923                        .borrow()
12924                        .iter()
12925                        .map(|mat| mat.string.clone())
12926                        .collect::<Vec<String>>(),
12927                    items
12928                        .iter()
12929                        .map(|completion| completion.label.clone())
12930                        .collect::<Vec<String>>()
12931                );
12932            }
12933            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12934        }
12935    });
12936    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12937    // with 4 from the end.
12938    assert_eq!(
12939        *resolved_items.lock(),
12940        [&items[0..16], &items[items.len() - 4..items.len()]]
12941            .concat()
12942            .iter()
12943            .cloned()
12944            .map(|mut item| {
12945                if item.data.is_none() {
12946                    item.data = Some(default_data.clone());
12947                }
12948                item
12949            })
12950            .collect::<Vec<lsp::CompletionItem>>(),
12951        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12952    );
12953    resolved_items.lock().clear();
12954
12955    cx.update_editor(|editor, window, cx| {
12956        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12957    });
12958    cx.run_until_parked();
12959    // Completions that have already been resolved are skipped.
12960    assert_eq!(
12961        *resolved_items.lock(),
12962        items[items.len() - 16..items.len() - 4]
12963            .iter()
12964            .cloned()
12965            .map(|mut item| {
12966                if item.data.is_none() {
12967                    item.data = Some(default_data.clone());
12968                }
12969                item
12970            })
12971            .collect::<Vec<lsp::CompletionItem>>()
12972    );
12973    resolved_items.lock().clear();
12974}
12975
12976#[gpui::test]
12977async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12978    init_test(cx, |_| {});
12979
12980    let mut cx = EditorLspTestContext::new(
12981        Language::new(
12982            LanguageConfig {
12983                matcher: LanguageMatcher {
12984                    path_suffixes: vec!["jsx".into()],
12985                    ..Default::default()
12986                },
12987                overrides: [(
12988                    "element".into(),
12989                    LanguageConfigOverride {
12990                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
12991                        ..Default::default()
12992                    },
12993                )]
12994                .into_iter()
12995                .collect(),
12996                ..Default::default()
12997            },
12998            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12999        )
13000        .with_override_query("(jsx_self_closing_element) @element")
13001        .unwrap(),
13002        lsp::ServerCapabilities {
13003            completion_provider: Some(lsp::CompletionOptions {
13004                trigger_characters: Some(vec![":".to_string()]),
13005                ..Default::default()
13006            }),
13007            ..Default::default()
13008        },
13009        cx,
13010    )
13011    .await;
13012
13013    cx.lsp
13014        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13015            Ok(Some(lsp::CompletionResponse::Array(vec![
13016                lsp::CompletionItem {
13017                    label: "bg-blue".into(),
13018                    ..Default::default()
13019                },
13020                lsp::CompletionItem {
13021                    label: "bg-red".into(),
13022                    ..Default::default()
13023                },
13024                lsp::CompletionItem {
13025                    label: "bg-yellow".into(),
13026                    ..Default::default()
13027                },
13028            ])))
13029        });
13030
13031    cx.set_state(r#"<p class="bgˇ" />"#);
13032
13033    // Trigger completion when typing a dash, because the dash is an extra
13034    // word character in the 'element' scope, which contains the cursor.
13035    cx.simulate_keystroke("-");
13036    cx.executor().run_until_parked();
13037    cx.update_editor(|editor, _, _| {
13038        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13039        {
13040            assert_eq!(
13041                completion_menu_entries(&menu),
13042                &["bg-red", "bg-blue", "bg-yellow"]
13043            );
13044        } else {
13045            panic!("expected completion menu to be open");
13046        }
13047    });
13048
13049    cx.simulate_keystroke("l");
13050    cx.executor().run_until_parked();
13051    cx.update_editor(|editor, _, _| {
13052        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13053        {
13054            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13055        } else {
13056            panic!("expected completion menu to be open");
13057        }
13058    });
13059
13060    // When filtering completions, consider the character after the '-' to
13061    // be the start of a subword.
13062    cx.set_state(r#"<p class="yelˇ" />"#);
13063    cx.simulate_keystroke("l");
13064    cx.executor().run_until_parked();
13065    cx.update_editor(|editor, _, _| {
13066        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13067        {
13068            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13069        } else {
13070            panic!("expected completion menu to be open");
13071        }
13072    });
13073}
13074
13075fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13076    let entries = menu.entries.borrow();
13077    entries.iter().map(|mat| mat.string.clone()).collect()
13078}
13079
13080#[gpui::test]
13081async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13082    init_test(cx, |settings| {
13083        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13084            FormatterList(vec![Formatter::Prettier].into()),
13085        ))
13086    });
13087
13088    let fs = FakeFs::new(cx.executor());
13089    fs.insert_file(path!("/file.ts"), Default::default()).await;
13090
13091    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13092    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13093
13094    language_registry.add(Arc::new(Language::new(
13095        LanguageConfig {
13096            name: "TypeScript".into(),
13097            matcher: LanguageMatcher {
13098                path_suffixes: vec!["ts".to_string()],
13099                ..Default::default()
13100            },
13101            ..Default::default()
13102        },
13103        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13104    )));
13105    update_test_language_settings(cx, |settings| {
13106        settings.defaults.prettier = Some(PrettierSettings {
13107            allowed: true,
13108            ..PrettierSettings::default()
13109        });
13110    });
13111
13112    let test_plugin = "test_plugin";
13113    let _ = language_registry.register_fake_lsp(
13114        "TypeScript",
13115        FakeLspAdapter {
13116            prettier_plugins: vec![test_plugin],
13117            ..Default::default()
13118        },
13119    );
13120
13121    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13122    let buffer = project
13123        .update(cx, |project, cx| {
13124            project.open_local_buffer(path!("/file.ts"), cx)
13125        })
13126        .await
13127        .unwrap();
13128
13129    let buffer_text = "one\ntwo\nthree\n";
13130    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13131    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13132    editor.update_in(cx, |editor, window, cx| {
13133        editor.set_text(buffer_text, window, cx)
13134    });
13135
13136    editor
13137        .update_in(cx, |editor, window, cx| {
13138            editor.perform_format(
13139                project.clone(),
13140                FormatTrigger::Manual,
13141                FormatTarget::Buffers,
13142                window,
13143                cx,
13144            )
13145        })
13146        .unwrap()
13147        .await;
13148    assert_eq!(
13149        editor.update(cx, |editor, cx| editor.text(cx)),
13150        buffer_text.to_string() + prettier_format_suffix,
13151        "Test prettier formatting was not applied to the original buffer text",
13152    );
13153
13154    update_test_language_settings(cx, |settings| {
13155        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13156    });
13157    let format = editor.update_in(cx, |editor, window, cx| {
13158        editor.perform_format(
13159            project.clone(),
13160            FormatTrigger::Manual,
13161            FormatTarget::Buffers,
13162            window,
13163            cx,
13164        )
13165    });
13166    format.await.unwrap();
13167    assert_eq!(
13168        editor.update(cx, |editor, cx| editor.text(cx)),
13169        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13170        "Autoformatting (via test prettier) was not applied to the original buffer text",
13171    );
13172}
13173
13174#[gpui::test]
13175async fn test_addition_reverts(cx: &mut TestAppContext) {
13176    init_test(cx, |_| {});
13177    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13178    let base_text = indoc! {r#"
13179        struct Row;
13180        struct Row1;
13181        struct Row2;
13182
13183        struct Row4;
13184        struct Row5;
13185        struct Row6;
13186
13187        struct Row8;
13188        struct Row9;
13189        struct Row10;"#};
13190
13191    // When addition hunks are not adjacent to carets, no hunk revert is performed
13192    assert_hunk_revert(
13193        indoc! {r#"struct Row;
13194                   struct Row1;
13195                   struct Row1.1;
13196                   struct Row1.2;
13197                   struct Row2;ˇ
13198
13199                   struct Row4;
13200                   struct Row5;
13201                   struct Row6;
13202
13203                   struct Row8;
13204                   ˇstruct Row9;
13205                   struct Row9.1;
13206                   struct Row9.2;
13207                   struct Row9.3;
13208                   struct Row10;"#},
13209        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13210        indoc! {r#"struct Row;
13211                   struct Row1;
13212                   struct Row1.1;
13213                   struct Row1.2;
13214                   struct Row2;ˇ
13215
13216                   struct Row4;
13217                   struct Row5;
13218                   struct Row6;
13219
13220                   struct Row8;
13221                   ˇstruct Row9;
13222                   struct Row9.1;
13223                   struct Row9.2;
13224                   struct Row9.3;
13225                   struct Row10;"#},
13226        base_text,
13227        &mut cx,
13228    );
13229    // Same for selections
13230    assert_hunk_revert(
13231        indoc! {r#"struct Row;
13232                   struct Row1;
13233                   struct Row2;
13234                   struct Row2.1;
13235                   struct Row2.2;
13236                   «ˇ
13237                   struct Row4;
13238                   struct» Row5;
13239                   «struct Row6;
13240                   ˇ»
13241                   struct Row9.1;
13242                   struct Row9.2;
13243                   struct Row9.3;
13244                   struct Row8;
13245                   struct Row9;
13246                   struct Row10;"#},
13247        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13248        indoc! {r#"struct Row;
13249                   struct Row1;
13250                   struct Row2;
13251                   struct Row2.1;
13252                   struct Row2.2;
13253                   «ˇ
13254                   struct Row4;
13255                   struct» Row5;
13256                   «struct Row6;
13257                   ˇ»
13258                   struct Row9.1;
13259                   struct Row9.2;
13260                   struct Row9.3;
13261                   struct Row8;
13262                   struct Row9;
13263                   struct Row10;"#},
13264        base_text,
13265        &mut cx,
13266    );
13267
13268    // When carets and selections intersect the addition hunks, those are reverted.
13269    // Adjacent carets got merged.
13270    assert_hunk_revert(
13271        indoc! {r#"struct Row;
13272                   ˇ// something on the top
13273                   struct Row1;
13274                   struct Row2;
13275                   struct Roˇw3.1;
13276                   struct Row2.2;
13277                   struct Row2.3;ˇ
13278
13279                   struct Row4;
13280                   struct ˇRow5.1;
13281                   struct Row5.2;
13282                   struct «Rowˇ»5.3;
13283                   struct Row5;
13284                   struct Row6;
13285                   ˇ
13286                   struct Row9.1;
13287                   struct «Rowˇ»9.2;
13288                   struct «ˇRow»9.3;
13289                   struct Row8;
13290                   struct Row9;
13291                   «ˇ// something on bottom»
13292                   struct Row10;"#},
13293        vec![
13294            DiffHunkStatusKind::Added,
13295            DiffHunkStatusKind::Added,
13296            DiffHunkStatusKind::Added,
13297            DiffHunkStatusKind::Added,
13298            DiffHunkStatusKind::Added,
13299        ],
13300        indoc! {r#"struct Row;
13301                   ˇstruct Row1;
13302                   struct Row2;
13303                   ˇ
13304                   struct Row4;
13305                   ˇstruct Row5;
13306                   struct Row6;
13307                   ˇ
13308                   ˇstruct Row8;
13309                   struct Row9;
13310                   ˇstruct Row10;"#},
13311        base_text,
13312        &mut cx,
13313    );
13314}
13315
13316#[gpui::test]
13317async fn test_modification_reverts(cx: &mut TestAppContext) {
13318    init_test(cx, |_| {});
13319    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13320    let base_text = indoc! {r#"
13321        struct Row;
13322        struct Row1;
13323        struct Row2;
13324
13325        struct Row4;
13326        struct Row5;
13327        struct Row6;
13328
13329        struct Row8;
13330        struct Row9;
13331        struct Row10;"#};
13332
13333    // Modification hunks behave the same as the addition ones.
13334    assert_hunk_revert(
13335        indoc! {r#"struct Row;
13336                   struct Row1;
13337                   struct Row33;
13338                   ˇ
13339                   struct Row4;
13340                   struct Row5;
13341                   struct Row6;
13342                   ˇ
13343                   struct Row99;
13344                   struct Row9;
13345                   struct Row10;"#},
13346        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13347        indoc! {r#"struct Row;
13348                   struct Row1;
13349                   struct Row33;
13350                   ˇ
13351                   struct Row4;
13352                   struct Row5;
13353                   struct Row6;
13354                   ˇ
13355                   struct Row99;
13356                   struct Row9;
13357                   struct Row10;"#},
13358        base_text,
13359        &mut cx,
13360    );
13361    assert_hunk_revert(
13362        indoc! {r#"struct Row;
13363                   struct Row1;
13364                   struct Row33;
13365                   «ˇ
13366                   struct Row4;
13367                   struct» Row5;
13368                   «struct Row6;
13369                   ˇ»
13370                   struct Row99;
13371                   struct Row9;
13372                   struct Row10;"#},
13373        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13374        indoc! {r#"struct Row;
13375                   struct Row1;
13376                   struct Row33;
13377                   «ˇ
13378                   struct Row4;
13379                   struct» Row5;
13380                   «struct Row6;
13381                   ˇ»
13382                   struct Row99;
13383                   struct Row9;
13384                   struct Row10;"#},
13385        base_text,
13386        &mut cx,
13387    );
13388
13389    assert_hunk_revert(
13390        indoc! {r#"ˇstruct Row1.1;
13391                   struct Row1;
13392                   «ˇstr»uct Row22;
13393
13394                   struct ˇRow44;
13395                   struct Row5;
13396                   struct «Rˇ»ow66;ˇ
13397
13398                   «struˇ»ct Row88;
13399                   struct Row9;
13400                   struct Row1011;ˇ"#},
13401        vec![
13402            DiffHunkStatusKind::Modified,
13403            DiffHunkStatusKind::Modified,
13404            DiffHunkStatusKind::Modified,
13405            DiffHunkStatusKind::Modified,
13406            DiffHunkStatusKind::Modified,
13407            DiffHunkStatusKind::Modified,
13408        ],
13409        indoc! {r#"struct Row;
13410                   ˇstruct Row1;
13411                   struct Row2;
13412                   ˇ
13413                   struct Row4;
13414                   ˇstruct Row5;
13415                   struct Row6;
13416                   ˇ
13417                   struct Row8;
13418                   ˇstruct Row9;
13419                   struct Row10;ˇ"#},
13420        base_text,
13421        &mut cx,
13422    );
13423}
13424
13425#[gpui::test]
13426async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13427    init_test(cx, |_| {});
13428    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13429    let base_text = indoc! {r#"
13430        one
13431
13432        two
13433        three
13434        "#};
13435
13436    cx.set_head_text(base_text);
13437    cx.set_state("\nˇ\n");
13438    cx.executor().run_until_parked();
13439    cx.update_editor(|editor, _window, cx| {
13440        editor.expand_selected_diff_hunks(cx);
13441    });
13442    cx.executor().run_until_parked();
13443    cx.update_editor(|editor, window, cx| {
13444        editor.backspace(&Default::default(), window, cx);
13445    });
13446    cx.run_until_parked();
13447    cx.assert_state_with_diff(
13448        indoc! {r#"
13449
13450        - two
13451        - threeˇ
13452        +
13453        "#}
13454        .to_string(),
13455    );
13456}
13457
13458#[gpui::test]
13459async fn test_deletion_reverts(cx: &mut TestAppContext) {
13460    init_test(cx, |_| {});
13461    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13462    let base_text = indoc! {r#"struct Row;
13463struct Row1;
13464struct Row2;
13465
13466struct Row4;
13467struct Row5;
13468struct Row6;
13469
13470struct Row8;
13471struct Row9;
13472struct Row10;"#};
13473
13474    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13475    assert_hunk_revert(
13476        indoc! {r#"struct Row;
13477                   struct Row2;
13478
13479                   ˇstruct Row4;
13480                   struct Row5;
13481                   struct Row6;
13482                   ˇ
13483                   struct Row8;
13484                   struct Row10;"#},
13485        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13486        indoc! {r#"struct Row;
13487                   struct Row2;
13488
13489                   ˇstruct Row4;
13490                   struct Row5;
13491                   struct Row6;
13492                   ˇ
13493                   struct Row8;
13494                   struct Row10;"#},
13495        base_text,
13496        &mut cx,
13497    );
13498    assert_hunk_revert(
13499        indoc! {r#"struct Row;
13500                   struct Row2;
13501
13502                   «ˇstruct Row4;
13503                   struct» Row5;
13504                   «struct Row6;
13505                   ˇ»
13506                   struct Row8;
13507                   struct Row10;"#},
13508        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13509        indoc! {r#"struct Row;
13510                   struct Row2;
13511
13512                   «ˇstruct Row4;
13513                   struct» Row5;
13514                   «struct Row6;
13515                   ˇ»
13516                   struct Row8;
13517                   struct Row10;"#},
13518        base_text,
13519        &mut cx,
13520    );
13521
13522    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13523    assert_hunk_revert(
13524        indoc! {r#"struct Row;
13525                   ˇstruct Row2;
13526
13527                   struct Row4;
13528                   struct Row5;
13529                   struct Row6;
13530
13531                   struct Row8;ˇ
13532                   struct Row10;"#},
13533        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13534        indoc! {r#"struct Row;
13535                   struct Row1;
13536                   ˇstruct Row2;
13537
13538                   struct Row4;
13539                   struct Row5;
13540                   struct Row6;
13541
13542                   struct Row8;ˇ
13543                   struct Row9;
13544                   struct Row10;"#},
13545        base_text,
13546        &mut cx,
13547    );
13548    assert_hunk_revert(
13549        indoc! {r#"struct Row;
13550                   struct Row2«ˇ;
13551                   struct Row4;
13552                   struct» Row5;
13553                   «struct Row6;
13554
13555                   struct Row8;ˇ»
13556                   struct Row10;"#},
13557        vec![
13558            DiffHunkStatusKind::Deleted,
13559            DiffHunkStatusKind::Deleted,
13560            DiffHunkStatusKind::Deleted,
13561        ],
13562        indoc! {r#"struct Row;
13563                   struct Row1;
13564                   struct Row2«ˇ;
13565
13566                   struct Row4;
13567                   struct» Row5;
13568                   «struct Row6;
13569
13570                   struct Row8;ˇ»
13571                   struct Row9;
13572                   struct Row10;"#},
13573        base_text,
13574        &mut cx,
13575    );
13576}
13577
13578#[gpui::test]
13579async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13580    init_test(cx, |_| {});
13581
13582    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13583    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13584    let base_text_3 =
13585        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13586
13587    let text_1 = edit_first_char_of_every_line(base_text_1);
13588    let text_2 = edit_first_char_of_every_line(base_text_2);
13589    let text_3 = edit_first_char_of_every_line(base_text_3);
13590
13591    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13592    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13593    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13594
13595    let multibuffer = cx.new(|cx| {
13596        let mut multibuffer = MultiBuffer::new(ReadWrite);
13597        multibuffer.push_excerpts(
13598            buffer_1.clone(),
13599            [
13600                ExcerptRange {
13601                    context: Point::new(0, 0)..Point::new(3, 0),
13602                    primary: None,
13603                },
13604                ExcerptRange {
13605                    context: Point::new(5, 0)..Point::new(7, 0),
13606                    primary: None,
13607                },
13608                ExcerptRange {
13609                    context: Point::new(9, 0)..Point::new(10, 4),
13610                    primary: None,
13611                },
13612            ],
13613            cx,
13614        );
13615        multibuffer.push_excerpts(
13616            buffer_2.clone(),
13617            [
13618                ExcerptRange {
13619                    context: Point::new(0, 0)..Point::new(3, 0),
13620                    primary: None,
13621                },
13622                ExcerptRange {
13623                    context: Point::new(5, 0)..Point::new(7, 0),
13624                    primary: None,
13625                },
13626                ExcerptRange {
13627                    context: Point::new(9, 0)..Point::new(10, 4),
13628                    primary: None,
13629                },
13630            ],
13631            cx,
13632        );
13633        multibuffer.push_excerpts(
13634            buffer_3.clone(),
13635            [
13636                ExcerptRange {
13637                    context: Point::new(0, 0)..Point::new(3, 0),
13638                    primary: None,
13639                },
13640                ExcerptRange {
13641                    context: Point::new(5, 0)..Point::new(7, 0),
13642                    primary: None,
13643                },
13644                ExcerptRange {
13645                    context: Point::new(9, 0)..Point::new(10, 4),
13646                    primary: None,
13647                },
13648            ],
13649            cx,
13650        );
13651        multibuffer
13652    });
13653
13654    let fs = FakeFs::new(cx.executor());
13655    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13656    let (editor, cx) = cx
13657        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13658    editor.update_in(cx, |editor, _window, cx| {
13659        for (buffer, diff_base) in [
13660            (buffer_1.clone(), base_text_1),
13661            (buffer_2.clone(), base_text_2),
13662            (buffer_3.clone(), base_text_3),
13663        ] {
13664            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13665            editor
13666                .buffer
13667                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13668        }
13669    });
13670    cx.executor().run_until_parked();
13671
13672    editor.update_in(cx, |editor, window, cx| {
13673        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}");
13674        editor.select_all(&SelectAll, window, cx);
13675        editor.git_restore(&Default::default(), window, cx);
13676    });
13677    cx.executor().run_until_parked();
13678
13679    // When all ranges are selected, all buffer hunks are reverted.
13680    editor.update(cx, |editor, cx| {
13681        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");
13682    });
13683    buffer_1.update(cx, |buffer, _| {
13684        assert_eq!(buffer.text(), base_text_1);
13685    });
13686    buffer_2.update(cx, |buffer, _| {
13687        assert_eq!(buffer.text(), base_text_2);
13688    });
13689    buffer_3.update(cx, |buffer, _| {
13690        assert_eq!(buffer.text(), base_text_3);
13691    });
13692
13693    editor.update_in(cx, |editor, window, cx| {
13694        editor.undo(&Default::default(), window, cx);
13695    });
13696
13697    editor.update_in(cx, |editor, window, cx| {
13698        editor.change_selections(None, window, cx, |s| {
13699            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13700        });
13701        editor.git_restore(&Default::default(), window, cx);
13702    });
13703
13704    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13705    // but not affect buffer_2 and its related excerpts.
13706    editor.update(cx, |editor, cx| {
13707        assert_eq!(
13708            editor.text(cx),
13709            "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}"
13710        );
13711    });
13712    buffer_1.update(cx, |buffer, _| {
13713        assert_eq!(buffer.text(), base_text_1);
13714    });
13715    buffer_2.update(cx, |buffer, _| {
13716        assert_eq!(
13717            buffer.text(),
13718            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13719        );
13720    });
13721    buffer_3.update(cx, |buffer, _| {
13722        assert_eq!(
13723            buffer.text(),
13724            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13725        );
13726    });
13727
13728    fn edit_first_char_of_every_line(text: &str) -> String {
13729        text.split('\n')
13730            .map(|line| format!("X{}", &line[1..]))
13731            .collect::<Vec<_>>()
13732            .join("\n")
13733    }
13734}
13735
13736#[gpui::test]
13737async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13738    init_test(cx, |_| {});
13739
13740    let cols = 4;
13741    let rows = 10;
13742    let sample_text_1 = sample_text(rows, cols, 'a');
13743    assert_eq!(
13744        sample_text_1,
13745        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13746    );
13747    let sample_text_2 = sample_text(rows, cols, 'l');
13748    assert_eq!(
13749        sample_text_2,
13750        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13751    );
13752    let sample_text_3 = sample_text(rows, cols, 'v');
13753    assert_eq!(
13754        sample_text_3,
13755        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13756    );
13757
13758    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13759    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13760    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13761
13762    let multi_buffer = cx.new(|cx| {
13763        let mut multibuffer = MultiBuffer::new(ReadWrite);
13764        multibuffer.push_excerpts(
13765            buffer_1.clone(),
13766            [
13767                ExcerptRange {
13768                    context: Point::new(0, 0)..Point::new(3, 0),
13769                    primary: None,
13770                },
13771                ExcerptRange {
13772                    context: Point::new(5, 0)..Point::new(7, 0),
13773                    primary: None,
13774                },
13775                ExcerptRange {
13776                    context: Point::new(9, 0)..Point::new(10, 4),
13777                    primary: None,
13778                },
13779            ],
13780            cx,
13781        );
13782        multibuffer.push_excerpts(
13783            buffer_2.clone(),
13784            [
13785                ExcerptRange {
13786                    context: Point::new(0, 0)..Point::new(3, 0),
13787                    primary: None,
13788                },
13789                ExcerptRange {
13790                    context: Point::new(5, 0)..Point::new(7, 0),
13791                    primary: None,
13792                },
13793                ExcerptRange {
13794                    context: Point::new(9, 0)..Point::new(10, 4),
13795                    primary: None,
13796                },
13797            ],
13798            cx,
13799        );
13800        multibuffer.push_excerpts(
13801            buffer_3.clone(),
13802            [
13803                ExcerptRange {
13804                    context: Point::new(0, 0)..Point::new(3, 0),
13805                    primary: None,
13806                },
13807                ExcerptRange {
13808                    context: Point::new(5, 0)..Point::new(7, 0),
13809                    primary: None,
13810                },
13811                ExcerptRange {
13812                    context: Point::new(9, 0)..Point::new(10, 4),
13813                    primary: None,
13814                },
13815            ],
13816            cx,
13817        );
13818        multibuffer
13819    });
13820
13821    let fs = FakeFs::new(cx.executor());
13822    fs.insert_tree(
13823        "/a",
13824        json!({
13825            "main.rs": sample_text_1,
13826            "other.rs": sample_text_2,
13827            "lib.rs": sample_text_3,
13828        }),
13829    )
13830    .await;
13831    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13832    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13833    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13834    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13835        Editor::new(
13836            EditorMode::Full,
13837            multi_buffer,
13838            Some(project.clone()),
13839            window,
13840            cx,
13841        )
13842    });
13843    let multibuffer_item_id = workspace
13844        .update(cx, |workspace, window, cx| {
13845            assert!(
13846                workspace.active_item(cx).is_none(),
13847                "active item should be None before the first item is added"
13848            );
13849            workspace.add_item_to_active_pane(
13850                Box::new(multi_buffer_editor.clone()),
13851                None,
13852                true,
13853                window,
13854                cx,
13855            );
13856            let active_item = workspace
13857                .active_item(cx)
13858                .expect("should have an active item after adding the multi buffer");
13859            assert!(
13860                !active_item.is_singleton(cx),
13861                "A multi buffer was expected to active after adding"
13862            );
13863            active_item.item_id()
13864        })
13865        .unwrap();
13866    cx.executor().run_until_parked();
13867
13868    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13869        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13870            s.select_ranges(Some(1..2))
13871        });
13872        editor.open_excerpts(&OpenExcerpts, window, cx);
13873    });
13874    cx.executor().run_until_parked();
13875    let first_item_id = workspace
13876        .update(cx, |workspace, window, cx| {
13877            let active_item = workspace
13878                .active_item(cx)
13879                .expect("should have an active item after navigating into the 1st buffer");
13880            let first_item_id = active_item.item_id();
13881            assert_ne!(
13882                first_item_id, multibuffer_item_id,
13883                "Should navigate into the 1st buffer and activate it"
13884            );
13885            assert!(
13886                active_item.is_singleton(cx),
13887                "New active item should be a singleton buffer"
13888            );
13889            assert_eq!(
13890                active_item
13891                    .act_as::<Editor>(cx)
13892                    .expect("should have navigated into an editor for the 1st buffer")
13893                    .read(cx)
13894                    .text(cx),
13895                sample_text_1
13896            );
13897
13898            workspace
13899                .go_back(workspace.active_pane().downgrade(), window, cx)
13900                .detach_and_log_err(cx);
13901
13902            first_item_id
13903        })
13904        .unwrap();
13905    cx.executor().run_until_parked();
13906    workspace
13907        .update(cx, |workspace, _, cx| {
13908            let active_item = workspace
13909                .active_item(cx)
13910                .expect("should have an active item after navigating back");
13911            assert_eq!(
13912                active_item.item_id(),
13913                multibuffer_item_id,
13914                "Should navigate back to the multi buffer"
13915            );
13916            assert!(!active_item.is_singleton(cx));
13917        })
13918        .unwrap();
13919
13920    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13921        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13922            s.select_ranges(Some(39..40))
13923        });
13924        editor.open_excerpts(&OpenExcerpts, window, cx);
13925    });
13926    cx.executor().run_until_parked();
13927    let second_item_id = workspace
13928        .update(cx, |workspace, window, cx| {
13929            let active_item = workspace
13930                .active_item(cx)
13931                .expect("should have an active item after navigating into the 2nd buffer");
13932            let second_item_id = active_item.item_id();
13933            assert_ne!(
13934                second_item_id, multibuffer_item_id,
13935                "Should navigate away from the multibuffer"
13936            );
13937            assert_ne!(
13938                second_item_id, first_item_id,
13939                "Should navigate into the 2nd buffer and activate it"
13940            );
13941            assert!(
13942                active_item.is_singleton(cx),
13943                "New active item should be a singleton buffer"
13944            );
13945            assert_eq!(
13946                active_item
13947                    .act_as::<Editor>(cx)
13948                    .expect("should have navigated into an editor")
13949                    .read(cx)
13950                    .text(cx),
13951                sample_text_2
13952            );
13953
13954            workspace
13955                .go_back(workspace.active_pane().downgrade(), window, cx)
13956                .detach_and_log_err(cx);
13957
13958            second_item_id
13959        })
13960        .unwrap();
13961    cx.executor().run_until_parked();
13962    workspace
13963        .update(cx, |workspace, _, cx| {
13964            let active_item = workspace
13965                .active_item(cx)
13966                .expect("should have an active item after navigating back from the 2nd buffer");
13967            assert_eq!(
13968                active_item.item_id(),
13969                multibuffer_item_id,
13970                "Should navigate back from the 2nd buffer to the multi buffer"
13971            );
13972            assert!(!active_item.is_singleton(cx));
13973        })
13974        .unwrap();
13975
13976    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13977        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13978            s.select_ranges(Some(70..70))
13979        });
13980        editor.open_excerpts(&OpenExcerpts, window, cx);
13981    });
13982    cx.executor().run_until_parked();
13983    workspace
13984        .update(cx, |workspace, window, cx| {
13985            let active_item = workspace
13986                .active_item(cx)
13987                .expect("should have an active item after navigating into the 3rd buffer");
13988            let third_item_id = active_item.item_id();
13989            assert_ne!(
13990                third_item_id, multibuffer_item_id,
13991                "Should navigate into the 3rd buffer and activate it"
13992            );
13993            assert_ne!(third_item_id, first_item_id);
13994            assert_ne!(third_item_id, second_item_id);
13995            assert!(
13996                active_item.is_singleton(cx),
13997                "New active item should be a singleton buffer"
13998            );
13999            assert_eq!(
14000                active_item
14001                    .act_as::<Editor>(cx)
14002                    .expect("should have navigated into an editor")
14003                    .read(cx)
14004                    .text(cx),
14005                sample_text_3
14006            );
14007
14008            workspace
14009                .go_back(workspace.active_pane().downgrade(), window, cx)
14010                .detach_and_log_err(cx);
14011        })
14012        .unwrap();
14013    cx.executor().run_until_parked();
14014    workspace
14015        .update(cx, |workspace, _, cx| {
14016            let active_item = workspace
14017                .active_item(cx)
14018                .expect("should have an active item after navigating back from the 3rd buffer");
14019            assert_eq!(
14020                active_item.item_id(),
14021                multibuffer_item_id,
14022                "Should navigate back from the 3rd buffer to the multi buffer"
14023            );
14024            assert!(!active_item.is_singleton(cx));
14025        })
14026        .unwrap();
14027}
14028
14029#[gpui::test]
14030async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14031    init_test(cx, |_| {});
14032
14033    let mut cx = EditorTestContext::new(cx).await;
14034
14035    let diff_base = r#"
14036        use some::mod;
14037
14038        const A: u32 = 42;
14039
14040        fn main() {
14041            println!("hello");
14042
14043            println!("world");
14044        }
14045        "#
14046    .unindent();
14047
14048    cx.set_state(
14049        &r#"
14050        use some::modified;
14051
14052        ˇ
14053        fn main() {
14054            println!("hello there");
14055
14056            println!("around the");
14057            println!("world");
14058        }
14059        "#
14060        .unindent(),
14061    );
14062
14063    cx.set_head_text(&diff_base);
14064    executor.run_until_parked();
14065
14066    cx.update_editor(|editor, window, cx| {
14067        editor.go_to_next_hunk(&GoToHunk, window, cx);
14068        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14069    });
14070    executor.run_until_parked();
14071    cx.assert_state_with_diff(
14072        r#"
14073          use some::modified;
14074
14075
14076          fn main() {
14077        -     println!("hello");
14078        + ˇ    println!("hello there");
14079
14080              println!("around the");
14081              println!("world");
14082          }
14083        "#
14084        .unindent(),
14085    );
14086
14087    cx.update_editor(|editor, window, cx| {
14088        for _ in 0..2 {
14089            editor.go_to_next_hunk(&GoToHunk, window, cx);
14090            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14091        }
14092    });
14093    executor.run_until_parked();
14094    cx.assert_state_with_diff(
14095        r#"
14096        - use some::mod;
14097        + ˇuse some::modified;
14098
14099
14100          fn main() {
14101        -     println!("hello");
14102        +     println!("hello there");
14103
14104        +     println!("around the");
14105              println!("world");
14106          }
14107        "#
14108        .unindent(),
14109    );
14110
14111    cx.update_editor(|editor, window, cx| {
14112        editor.go_to_next_hunk(&GoToHunk, window, cx);
14113        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14114    });
14115    executor.run_until_parked();
14116    cx.assert_state_with_diff(
14117        r#"
14118        - use some::mod;
14119        + use some::modified;
14120
14121        - const A: u32 = 42;
14122          ˇ
14123          fn main() {
14124        -     println!("hello");
14125        +     println!("hello there");
14126
14127        +     println!("around the");
14128              println!("world");
14129          }
14130        "#
14131        .unindent(),
14132    );
14133
14134    cx.update_editor(|editor, window, cx| {
14135        editor.cancel(&Cancel, window, cx);
14136    });
14137
14138    cx.assert_state_with_diff(
14139        r#"
14140          use some::modified;
14141
14142          ˇ
14143          fn main() {
14144              println!("hello there");
14145
14146              println!("around the");
14147              println!("world");
14148          }
14149        "#
14150        .unindent(),
14151    );
14152}
14153
14154#[gpui::test]
14155async fn test_diff_base_change_with_expanded_diff_hunks(
14156    executor: BackgroundExecutor,
14157    cx: &mut TestAppContext,
14158) {
14159    init_test(cx, |_| {});
14160
14161    let mut cx = EditorTestContext::new(cx).await;
14162
14163    let diff_base = r#"
14164        use some::mod1;
14165        use some::mod2;
14166
14167        const A: u32 = 42;
14168        const B: u32 = 42;
14169        const C: u32 = 42;
14170
14171        fn main() {
14172            println!("hello");
14173
14174            println!("world");
14175        }
14176        "#
14177    .unindent();
14178
14179    cx.set_state(
14180        &r#"
14181        use some::mod2;
14182
14183        const A: u32 = 42;
14184        const C: u32 = 42;
14185
14186        fn main(ˇ) {
14187            //println!("hello");
14188
14189            println!("world");
14190            //
14191            //
14192        }
14193        "#
14194        .unindent(),
14195    );
14196
14197    cx.set_head_text(&diff_base);
14198    executor.run_until_parked();
14199
14200    cx.update_editor(|editor, window, cx| {
14201        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14202    });
14203    executor.run_until_parked();
14204    cx.assert_state_with_diff(
14205        r#"
14206        - use some::mod1;
14207          use some::mod2;
14208
14209          const A: u32 = 42;
14210        - const B: u32 = 42;
14211          const C: u32 = 42;
14212
14213          fn main(ˇ) {
14214        -     println!("hello");
14215        +     //println!("hello");
14216
14217              println!("world");
14218        +     //
14219        +     //
14220          }
14221        "#
14222        .unindent(),
14223    );
14224
14225    cx.set_head_text("new diff base!");
14226    executor.run_until_parked();
14227    cx.assert_state_with_diff(
14228        r#"
14229        - new diff base!
14230        + use some::mod2;
14231        +
14232        + const A: u32 = 42;
14233        + const C: u32 = 42;
14234        +
14235        + fn main(ˇ) {
14236        +     //println!("hello");
14237        +
14238        +     println!("world");
14239        +     //
14240        +     //
14241        + }
14242        "#
14243        .unindent(),
14244    );
14245}
14246
14247#[gpui::test]
14248async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14249    init_test(cx, |_| {});
14250
14251    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14252    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14253    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14254    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14255    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14256    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14257
14258    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14259    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14260    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14261
14262    let multi_buffer = cx.new(|cx| {
14263        let mut multibuffer = MultiBuffer::new(ReadWrite);
14264        multibuffer.push_excerpts(
14265            buffer_1.clone(),
14266            [
14267                ExcerptRange {
14268                    context: Point::new(0, 0)..Point::new(3, 0),
14269                    primary: None,
14270                },
14271                ExcerptRange {
14272                    context: Point::new(5, 0)..Point::new(7, 0),
14273                    primary: None,
14274                },
14275                ExcerptRange {
14276                    context: Point::new(9, 0)..Point::new(10, 3),
14277                    primary: None,
14278                },
14279            ],
14280            cx,
14281        );
14282        multibuffer.push_excerpts(
14283            buffer_2.clone(),
14284            [
14285                ExcerptRange {
14286                    context: Point::new(0, 0)..Point::new(3, 0),
14287                    primary: None,
14288                },
14289                ExcerptRange {
14290                    context: Point::new(5, 0)..Point::new(7, 0),
14291                    primary: None,
14292                },
14293                ExcerptRange {
14294                    context: Point::new(9, 0)..Point::new(10, 3),
14295                    primary: None,
14296                },
14297            ],
14298            cx,
14299        );
14300        multibuffer.push_excerpts(
14301            buffer_3.clone(),
14302            [
14303                ExcerptRange {
14304                    context: Point::new(0, 0)..Point::new(3, 0),
14305                    primary: None,
14306                },
14307                ExcerptRange {
14308                    context: Point::new(5, 0)..Point::new(7, 0),
14309                    primary: None,
14310                },
14311                ExcerptRange {
14312                    context: Point::new(9, 0)..Point::new(10, 3),
14313                    primary: None,
14314                },
14315            ],
14316            cx,
14317        );
14318        multibuffer
14319    });
14320
14321    let editor =
14322        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14323    editor
14324        .update(cx, |editor, _window, cx| {
14325            for (buffer, diff_base) in [
14326                (buffer_1.clone(), file_1_old),
14327                (buffer_2.clone(), file_2_old),
14328                (buffer_3.clone(), file_3_old),
14329            ] {
14330                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14331                editor
14332                    .buffer
14333                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14334            }
14335        })
14336        .unwrap();
14337
14338    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14339    cx.run_until_parked();
14340
14341    cx.assert_editor_state(
14342        &"
14343            ˇaaa
14344            ccc
14345            ddd
14346
14347            ggg
14348            hhh
14349
14350
14351            lll
14352            mmm
14353            NNN
14354
14355            qqq
14356            rrr
14357
14358            uuu
14359            111
14360            222
14361            333
14362
14363            666
14364            777
14365
14366            000
14367            !!!"
14368        .unindent(),
14369    );
14370
14371    cx.update_editor(|editor, window, cx| {
14372        editor.select_all(&SelectAll, window, cx);
14373        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14374    });
14375    cx.executor().run_until_parked();
14376
14377    cx.assert_state_with_diff(
14378        "
14379            «aaa
14380          - bbb
14381            ccc
14382            ddd
14383
14384            ggg
14385            hhh
14386
14387
14388            lll
14389            mmm
14390          - nnn
14391          + NNN
14392
14393            qqq
14394            rrr
14395
14396            uuu
14397            111
14398            222
14399            333
14400
14401          + 666
14402            777
14403
14404            000
14405            !!!ˇ»"
14406            .unindent(),
14407    );
14408}
14409
14410#[gpui::test]
14411async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14412    init_test(cx, |_| {});
14413
14414    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14415    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14416
14417    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14418    let multi_buffer = cx.new(|cx| {
14419        let mut multibuffer = MultiBuffer::new(ReadWrite);
14420        multibuffer.push_excerpts(
14421            buffer.clone(),
14422            [
14423                ExcerptRange {
14424                    context: Point::new(0, 0)..Point::new(2, 0),
14425                    primary: None,
14426                },
14427                ExcerptRange {
14428                    context: Point::new(4, 0)..Point::new(7, 0),
14429                    primary: None,
14430                },
14431                ExcerptRange {
14432                    context: Point::new(9, 0)..Point::new(10, 0),
14433                    primary: None,
14434                },
14435            ],
14436            cx,
14437        );
14438        multibuffer
14439    });
14440
14441    let editor =
14442        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14443    editor
14444        .update(cx, |editor, _window, cx| {
14445            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14446            editor
14447                .buffer
14448                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14449        })
14450        .unwrap();
14451
14452    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14453    cx.run_until_parked();
14454
14455    cx.update_editor(|editor, window, cx| {
14456        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14457    });
14458    cx.executor().run_until_parked();
14459
14460    // When the start of a hunk coincides with the start of its excerpt,
14461    // the hunk is expanded. When the start of a a hunk is earlier than
14462    // the start of its excerpt, the hunk is not expanded.
14463    cx.assert_state_with_diff(
14464        "
14465            ˇaaa
14466          - bbb
14467          + BBB
14468
14469          - ddd
14470          - eee
14471          + DDD
14472          + EEE
14473            fff
14474
14475            iii
14476        "
14477        .unindent(),
14478    );
14479}
14480
14481#[gpui::test]
14482async fn test_edits_around_expanded_insertion_hunks(
14483    executor: BackgroundExecutor,
14484    cx: &mut TestAppContext,
14485) {
14486    init_test(cx, |_| {});
14487
14488    let mut cx = EditorTestContext::new(cx).await;
14489
14490    let diff_base = r#"
14491        use some::mod1;
14492        use some::mod2;
14493
14494        const A: u32 = 42;
14495
14496        fn main() {
14497            println!("hello");
14498
14499            println!("world");
14500        }
14501        "#
14502    .unindent();
14503    executor.run_until_parked();
14504    cx.set_state(
14505        &r#"
14506        use some::mod1;
14507        use some::mod2;
14508
14509        const A: u32 = 42;
14510        const B: u32 = 42;
14511        const C: u32 = 42;
14512        ˇ
14513
14514        fn main() {
14515            println!("hello");
14516
14517            println!("world");
14518        }
14519        "#
14520        .unindent(),
14521    );
14522
14523    cx.set_head_text(&diff_base);
14524    executor.run_until_parked();
14525
14526    cx.update_editor(|editor, window, cx| {
14527        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14528    });
14529    executor.run_until_parked();
14530
14531    cx.assert_state_with_diff(
14532        r#"
14533        use some::mod1;
14534        use some::mod2;
14535
14536        const A: u32 = 42;
14537      + const B: u32 = 42;
14538      + const C: u32 = 42;
14539      + ˇ
14540
14541        fn main() {
14542            println!("hello");
14543
14544            println!("world");
14545        }
14546      "#
14547        .unindent(),
14548    );
14549
14550    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14551    executor.run_until_parked();
14552
14553    cx.assert_state_with_diff(
14554        r#"
14555        use some::mod1;
14556        use some::mod2;
14557
14558        const A: u32 = 42;
14559      + const B: u32 = 42;
14560      + const C: u32 = 42;
14561      + const D: u32 = 42;
14562      + ˇ
14563
14564        fn main() {
14565            println!("hello");
14566
14567            println!("world");
14568        }
14569      "#
14570        .unindent(),
14571    );
14572
14573    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14574    executor.run_until_parked();
14575
14576    cx.assert_state_with_diff(
14577        r#"
14578        use some::mod1;
14579        use some::mod2;
14580
14581        const A: u32 = 42;
14582      + const B: u32 = 42;
14583      + const C: u32 = 42;
14584      + const D: u32 = 42;
14585      + const E: u32 = 42;
14586      + ˇ
14587
14588        fn main() {
14589            println!("hello");
14590
14591            println!("world");
14592        }
14593      "#
14594        .unindent(),
14595    );
14596
14597    cx.update_editor(|editor, window, cx| {
14598        editor.delete_line(&DeleteLine, window, cx);
14599    });
14600    executor.run_until_parked();
14601
14602    cx.assert_state_with_diff(
14603        r#"
14604        use some::mod1;
14605        use some::mod2;
14606
14607        const A: u32 = 42;
14608      + const B: u32 = 42;
14609      + const C: u32 = 42;
14610      + const D: u32 = 42;
14611      + const E: u32 = 42;
14612        ˇ
14613        fn main() {
14614            println!("hello");
14615
14616            println!("world");
14617        }
14618      "#
14619        .unindent(),
14620    );
14621
14622    cx.update_editor(|editor, window, cx| {
14623        editor.move_up(&MoveUp, window, cx);
14624        editor.delete_line(&DeleteLine, window, cx);
14625        editor.move_up(&MoveUp, window, cx);
14626        editor.delete_line(&DeleteLine, window, cx);
14627        editor.move_up(&MoveUp, window, cx);
14628        editor.delete_line(&DeleteLine, window, cx);
14629    });
14630    executor.run_until_parked();
14631    cx.assert_state_with_diff(
14632        r#"
14633        use some::mod1;
14634        use some::mod2;
14635
14636        const A: u32 = 42;
14637      + const B: u32 = 42;
14638        ˇ
14639        fn main() {
14640            println!("hello");
14641
14642            println!("world");
14643        }
14644      "#
14645        .unindent(),
14646    );
14647
14648    cx.update_editor(|editor, window, cx| {
14649        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14650        editor.delete_line(&DeleteLine, window, cx);
14651    });
14652    executor.run_until_parked();
14653    cx.assert_state_with_diff(
14654        r#"
14655        ˇ
14656        fn main() {
14657            println!("hello");
14658
14659            println!("world");
14660        }
14661      "#
14662        .unindent(),
14663    );
14664}
14665
14666#[gpui::test]
14667async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14668    init_test(cx, |_| {});
14669
14670    let mut cx = EditorTestContext::new(cx).await;
14671    cx.set_head_text(indoc! { "
14672        one
14673        two
14674        three
14675        four
14676        five
14677        "
14678    });
14679    cx.set_state(indoc! { "
14680        one
14681        ˇthree
14682        five
14683    "});
14684    cx.run_until_parked();
14685    cx.update_editor(|editor, window, cx| {
14686        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14687    });
14688    cx.assert_state_with_diff(
14689        indoc! { "
14690        one
14691      - two
14692        ˇthree
14693      - four
14694        five
14695    "}
14696        .to_string(),
14697    );
14698    cx.update_editor(|editor, window, cx| {
14699        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14700    });
14701
14702    cx.assert_state_with_diff(
14703        indoc! { "
14704        one
14705        ˇthree
14706        five
14707    "}
14708        .to_string(),
14709    );
14710
14711    cx.set_state(indoc! { "
14712        one
14713        ˇTWO
14714        three
14715        four
14716        five
14717    "});
14718    cx.run_until_parked();
14719    cx.update_editor(|editor, window, cx| {
14720        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14721    });
14722
14723    cx.assert_state_with_diff(
14724        indoc! { "
14725            one
14726          - two
14727          + ˇTWO
14728            three
14729            four
14730            five
14731        "}
14732        .to_string(),
14733    );
14734    cx.update_editor(|editor, window, cx| {
14735        editor.move_up(&Default::default(), window, cx);
14736        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14737    });
14738    cx.assert_state_with_diff(
14739        indoc! { "
14740            one
14741            ˇTWO
14742            three
14743            four
14744            five
14745        "}
14746        .to_string(),
14747    );
14748}
14749
14750#[gpui::test]
14751async fn test_edits_around_expanded_deletion_hunks(
14752    executor: BackgroundExecutor,
14753    cx: &mut TestAppContext,
14754) {
14755    init_test(cx, |_| {});
14756
14757    let mut cx = EditorTestContext::new(cx).await;
14758
14759    let diff_base = r#"
14760        use some::mod1;
14761        use some::mod2;
14762
14763        const A: u32 = 42;
14764        const B: u32 = 42;
14765        const C: u32 = 42;
14766
14767
14768        fn main() {
14769            println!("hello");
14770
14771            println!("world");
14772        }
14773    "#
14774    .unindent();
14775    executor.run_until_parked();
14776    cx.set_state(
14777        &r#"
14778        use some::mod1;
14779        use some::mod2;
14780
14781        ˇconst B: u32 = 42;
14782        const C: u32 = 42;
14783
14784
14785        fn main() {
14786            println!("hello");
14787
14788            println!("world");
14789        }
14790        "#
14791        .unindent(),
14792    );
14793
14794    cx.set_head_text(&diff_base);
14795    executor.run_until_parked();
14796
14797    cx.update_editor(|editor, window, cx| {
14798        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14799    });
14800    executor.run_until_parked();
14801
14802    cx.assert_state_with_diff(
14803        r#"
14804        use some::mod1;
14805        use some::mod2;
14806
14807      - const A: u32 = 42;
14808        ˇconst B: u32 = 42;
14809        const C: u32 = 42;
14810
14811
14812        fn main() {
14813            println!("hello");
14814
14815            println!("world");
14816        }
14817      "#
14818        .unindent(),
14819    );
14820
14821    cx.update_editor(|editor, window, cx| {
14822        editor.delete_line(&DeleteLine, window, cx);
14823    });
14824    executor.run_until_parked();
14825    cx.assert_state_with_diff(
14826        r#"
14827        use some::mod1;
14828        use some::mod2;
14829
14830      - const A: u32 = 42;
14831      - const B: u32 = 42;
14832        ˇconst C: u32 = 42;
14833
14834
14835        fn main() {
14836            println!("hello");
14837
14838            println!("world");
14839        }
14840      "#
14841        .unindent(),
14842    );
14843
14844    cx.update_editor(|editor, window, cx| {
14845        editor.delete_line(&DeleteLine, window, cx);
14846    });
14847    executor.run_until_parked();
14848    cx.assert_state_with_diff(
14849        r#"
14850        use some::mod1;
14851        use some::mod2;
14852
14853      - const A: u32 = 42;
14854      - const B: u32 = 42;
14855      - const C: u32 = 42;
14856        ˇ
14857
14858        fn main() {
14859            println!("hello");
14860
14861            println!("world");
14862        }
14863      "#
14864        .unindent(),
14865    );
14866
14867    cx.update_editor(|editor, window, cx| {
14868        editor.handle_input("replacement", window, cx);
14869    });
14870    executor.run_until_parked();
14871    cx.assert_state_with_diff(
14872        r#"
14873        use some::mod1;
14874        use some::mod2;
14875
14876      - const A: u32 = 42;
14877      - const B: u32 = 42;
14878      - const C: u32 = 42;
14879      -
14880      + replacementˇ
14881
14882        fn main() {
14883            println!("hello");
14884
14885            println!("world");
14886        }
14887      "#
14888        .unindent(),
14889    );
14890}
14891
14892#[gpui::test]
14893async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14894    init_test(cx, |_| {});
14895
14896    let mut cx = EditorTestContext::new(cx).await;
14897
14898    let base_text = r#"
14899        one
14900        two
14901        three
14902        four
14903        five
14904    "#
14905    .unindent();
14906    executor.run_until_parked();
14907    cx.set_state(
14908        &r#"
14909        one
14910        two
14911        fˇour
14912        five
14913        "#
14914        .unindent(),
14915    );
14916
14917    cx.set_head_text(&base_text);
14918    executor.run_until_parked();
14919
14920    cx.update_editor(|editor, window, cx| {
14921        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14922    });
14923    executor.run_until_parked();
14924
14925    cx.assert_state_with_diff(
14926        r#"
14927          one
14928          two
14929        - three
14930          fˇour
14931          five
14932        "#
14933        .unindent(),
14934    );
14935
14936    cx.update_editor(|editor, window, cx| {
14937        editor.backspace(&Backspace, window, cx);
14938        editor.backspace(&Backspace, window, cx);
14939    });
14940    executor.run_until_parked();
14941    cx.assert_state_with_diff(
14942        r#"
14943          one
14944          two
14945        - threeˇ
14946        - four
14947        + our
14948          five
14949        "#
14950        .unindent(),
14951    );
14952}
14953
14954#[gpui::test]
14955async fn test_edit_after_expanded_modification_hunk(
14956    executor: BackgroundExecutor,
14957    cx: &mut TestAppContext,
14958) {
14959    init_test(cx, |_| {});
14960
14961    let mut cx = EditorTestContext::new(cx).await;
14962
14963    let diff_base = r#"
14964        use some::mod1;
14965        use some::mod2;
14966
14967        const A: u32 = 42;
14968        const B: u32 = 42;
14969        const C: u32 = 42;
14970        const D: u32 = 42;
14971
14972
14973        fn main() {
14974            println!("hello");
14975
14976            println!("world");
14977        }"#
14978    .unindent();
14979
14980    cx.set_state(
14981        &r#"
14982        use some::mod1;
14983        use some::mod2;
14984
14985        const A: u32 = 42;
14986        const B: u32 = 42;
14987        const C: u32 = 43ˇ
14988        const D: u32 = 42;
14989
14990
14991        fn main() {
14992            println!("hello");
14993
14994            println!("world");
14995        }"#
14996        .unindent(),
14997    );
14998
14999    cx.set_head_text(&diff_base);
15000    executor.run_until_parked();
15001    cx.update_editor(|editor, window, cx| {
15002        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15003    });
15004    executor.run_until_parked();
15005
15006    cx.assert_state_with_diff(
15007        r#"
15008        use some::mod1;
15009        use some::mod2;
15010
15011        const A: u32 = 42;
15012        const B: u32 = 42;
15013      - const C: u32 = 42;
15014      + const C: u32 = 43ˇ
15015        const D: u32 = 42;
15016
15017
15018        fn main() {
15019            println!("hello");
15020
15021            println!("world");
15022        }"#
15023        .unindent(),
15024    );
15025
15026    cx.update_editor(|editor, window, cx| {
15027        editor.handle_input("\nnew_line\n", window, cx);
15028    });
15029    executor.run_until_parked();
15030
15031    cx.assert_state_with_diff(
15032        r#"
15033        use some::mod1;
15034        use some::mod2;
15035
15036        const A: u32 = 42;
15037        const B: u32 = 42;
15038      - const C: u32 = 42;
15039      + const C: u32 = 43
15040      + new_line
15041      + ˇ
15042        const D: u32 = 42;
15043
15044
15045        fn main() {
15046            println!("hello");
15047
15048            println!("world");
15049        }"#
15050        .unindent(),
15051    );
15052}
15053
15054#[gpui::test]
15055async fn test_stage_and_unstage_added_file_hunk(
15056    executor: BackgroundExecutor,
15057    cx: &mut TestAppContext,
15058) {
15059    init_test(cx, |_| {});
15060
15061    let mut cx = EditorTestContext::new(cx).await;
15062    cx.update_editor(|editor, _, cx| {
15063        editor.set_expand_all_diff_hunks(cx);
15064    });
15065
15066    let working_copy = r#"
15067            ˇfn main() {
15068                println!("hello, world!");
15069            }
15070        "#
15071    .unindent();
15072
15073    cx.set_state(&working_copy);
15074    executor.run_until_parked();
15075
15076    cx.assert_state_with_diff(
15077        r#"
15078            + ˇfn main() {
15079            +     println!("hello, world!");
15080            + }
15081        "#
15082        .unindent(),
15083    );
15084    cx.assert_index_text(None);
15085
15086    cx.update_editor(|editor, window, cx| {
15087        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15088    });
15089    executor.run_until_parked();
15090    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15091    cx.assert_state_with_diff(
15092        r#"
15093            + ˇfn main() {
15094            +     println!("hello, world!");
15095            + }
15096        "#
15097        .unindent(),
15098    );
15099
15100    cx.update_editor(|editor, window, cx| {
15101        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15102    });
15103    executor.run_until_parked();
15104    cx.assert_index_text(None);
15105}
15106
15107async fn setup_indent_guides_editor(
15108    text: &str,
15109    cx: &mut TestAppContext,
15110) -> (BufferId, EditorTestContext) {
15111    init_test(cx, |_| {});
15112
15113    let mut cx = EditorTestContext::new(cx).await;
15114
15115    let buffer_id = cx.update_editor(|editor, window, cx| {
15116        editor.set_text(text, window, cx);
15117        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15118
15119        buffer_ids[0]
15120    });
15121
15122    (buffer_id, cx)
15123}
15124
15125fn assert_indent_guides(
15126    range: Range<u32>,
15127    expected: Vec<IndentGuide>,
15128    active_indices: Option<Vec<usize>>,
15129    cx: &mut EditorTestContext,
15130) {
15131    let indent_guides = cx.update_editor(|editor, window, cx| {
15132        let snapshot = editor.snapshot(window, cx).display_snapshot;
15133        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15134            editor,
15135            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15136            true,
15137            &snapshot,
15138            cx,
15139        );
15140
15141        indent_guides.sort_by(|a, b| {
15142            a.depth.cmp(&b.depth).then(
15143                a.start_row
15144                    .cmp(&b.start_row)
15145                    .then(a.end_row.cmp(&b.end_row)),
15146            )
15147        });
15148        indent_guides
15149    });
15150
15151    if let Some(expected) = active_indices {
15152        let active_indices = cx.update_editor(|editor, window, cx| {
15153            let snapshot = editor.snapshot(window, cx).display_snapshot;
15154            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15155        });
15156
15157        assert_eq!(
15158            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15159            expected,
15160            "Active indent guide indices do not match"
15161        );
15162    }
15163
15164    assert_eq!(indent_guides, expected, "Indent guides do not match");
15165}
15166
15167fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15168    IndentGuide {
15169        buffer_id,
15170        start_row: MultiBufferRow(start_row),
15171        end_row: MultiBufferRow(end_row),
15172        depth,
15173        tab_size: 4,
15174        settings: IndentGuideSettings {
15175            enabled: true,
15176            line_width: 1,
15177            active_line_width: 1,
15178            ..Default::default()
15179        },
15180    }
15181}
15182
15183#[gpui::test]
15184async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15185    let (buffer_id, mut cx) = setup_indent_guides_editor(
15186        &"
15187    fn main() {
15188        let a = 1;
15189    }"
15190        .unindent(),
15191        cx,
15192    )
15193    .await;
15194
15195    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15196}
15197
15198#[gpui::test]
15199async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15200    let (buffer_id, mut cx) = setup_indent_guides_editor(
15201        &"
15202    fn main() {
15203        let a = 1;
15204        let b = 2;
15205    }"
15206        .unindent(),
15207        cx,
15208    )
15209    .await;
15210
15211    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15212}
15213
15214#[gpui::test]
15215async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15216    let (buffer_id, mut cx) = setup_indent_guides_editor(
15217        &"
15218    fn main() {
15219        let a = 1;
15220        if a == 3 {
15221            let b = 2;
15222        } else {
15223            let c = 3;
15224        }
15225    }"
15226        .unindent(),
15227        cx,
15228    )
15229    .await;
15230
15231    assert_indent_guides(
15232        0..8,
15233        vec![
15234            indent_guide(buffer_id, 1, 6, 0),
15235            indent_guide(buffer_id, 3, 3, 1),
15236            indent_guide(buffer_id, 5, 5, 1),
15237        ],
15238        None,
15239        &mut cx,
15240    );
15241}
15242
15243#[gpui::test]
15244async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15245    let (buffer_id, mut cx) = setup_indent_guides_editor(
15246        &"
15247    fn main() {
15248        let a = 1;
15249            let b = 2;
15250        let c = 3;
15251    }"
15252        .unindent(),
15253        cx,
15254    )
15255    .await;
15256
15257    assert_indent_guides(
15258        0..5,
15259        vec![
15260            indent_guide(buffer_id, 1, 3, 0),
15261            indent_guide(buffer_id, 2, 2, 1),
15262        ],
15263        None,
15264        &mut cx,
15265    );
15266}
15267
15268#[gpui::test]
15269async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15270    let (buffer_id, mut cx) = setup_indent_guides_editor(
15271        &"
15272        fn main() {
15273            let a = 1;
15274
15275            let c = 3;
15276        }"
15277        .unindent(),
15278        cx,
15279    )
15280    .await;
15281
15282    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15283}
15284
15285#[gpui::test]
15286async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15287    let (buffer_id, mut cx) = setup_indent_guides_editor(
15288        &"
15289        fn main() {
15290            let a = 1;
15291
15292            let c = 3;
15293
15294            if a == 3 {
15295                let b = 2;
15296            } else {
15297                let c = 3;
15298            }
15299        }"
15300        .unindent(),
15301        cx,
15302    )
15303    .await;
15304
15305    assert_indent_guides(
15306        0..11,
15307        vec![
15308            indent_guide(buffer_id, 1, 9, 0),
15309            indent_guide(buffer_id, 6, 6, 1),
15310            indent_guide(buffer_id, 8, 8, 1),
15311        ],
15312        None,
15313        &mut cx,
15314    );
15315}
15316
15317#[gpui::test]
15318async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15319    let (buffer_id, mut cx) = setup_indent_guides_editor(
15320        &"
15321        fn main() {
15322            let a = 1;
15323
15324            let c = 3;
15325
15326            if a == 3 {
15327                let b = 2;
15328            } else {
15329                let c = 3;
15330            }
15331        }"
15332        .unindent(),
15333        cx,
15334    )
15335    .await;
15336
15337    assert_indent_guides(
15338        1..11,
15339        vec![
15340            indent_guide(buffer_id, 1, 9, 0),
15341            indent_guide(buffer_id, 6, 6, 1),
15342            indent_guide(buffer_id, 8, 8, 1),
15343        ],
15344        None,
15345        &mut cx,
15346    );
15347}
15348
15349#[gpui::test]
15350async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15351    let (buffer_id, mut cx) = setup_indent_guides_editor(
15352        &"
15353        fn main() {
15354            let a = 1;
15355
15356            let c = 3;
15357
15358            if a == 3 {
15359                let b = 2;
15360            } else {
15361                let c = 3;
15362            }
15363        }"
15364        .unindent(),
15365        cx,
15366    )
15367    .await;
15368
15369    assert_indent_guides(
15370        1..10,
15371        vec![
15372            indent_guide(buffer_id, 1, 9, 0),
15373            indent_guide(buffer_id, 6, 6, 1),
15374            indent_guide(buffer_id, 8, 8, 1),
15375        ],
15376        None,
15377        &mut cx,
15378    );
15379}
15380
15381#[gpui::test]
15382async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15383    let (buffer_id, mut cx) = setup_indent_guides_editor(
15384        &"
15385        block1
15386            block2
15387                block3
15388                    block4
15389            block2
15390        block1
15391        block1"
15392            .unindent(),
15393        cx,
15394    )
15395    .await;
15396
15397    assert_indent_guides(
15398        1..10,
15399        vec![
15400            indent_guide(buffer_id, 1, 4, 0),
15401            indent_guide(buffer_id, 2, 3, 1),
15402            indent_guide(buffer_id, 3, 3, 2),
15403        ],
15404        None,
15405        &mut cx,
15406    );
15407}
15408
15409#[gpui::test]
15410async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15411    let (buffer_id, mut cx) = setup_indent_guides_editor(
15412        &"
15413        block1
15414            block2
15415                block3
15416
15417        block1
15418        block1"
15419            .unindent(),
15420        cx,
15421    )
15422    .await;
15423
15424    assert_indent_guides(
15425        0..6,
15426        vec![
15427            indent_guide(buffer_id, 1, 2, 0),
15428            indent_guide(buffer_id, 2, 2, 1),
15429        ],
15430        None,
15431        &mut cx,
15432    );
15433}
15434
15435#[gpui::test]
15436async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15437    let (buffer_id, mut cx) = setup_indent_guides_editor(
15438        &"
15439        block1
15440
15441
15442
15443            block2
15444        "
15445        .unindent(),
15446        cx,
15447    )
15448    .await;
15449
15450    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15451}
15452
15453#[gpui::test]
15454async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15455    let (buffer_id, mut cx) = setup_indent_guides_editor(
15456        &"
15457        def a:
15458        \tb = 3
15459        \tif True:
15460        \t\tc = 4
15461        \t\td = 5
15462        \tprint(b)
15463        "
15464        .unindent(),
15465        cx,
15466    )
15467    .await;
15468
15469    assert_indent_guides(
15470        0..6,
15471        vec![
15472            indent_guide(buffer_id, 1, 6, 0),
15473            indent_guide(buffer_id, 3, 4, 1),
15474        ],
15475        None,
15476        &mut cx,
15477    );
15478}
15479
15480#[gpui::test]
15481async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15482    let (buffer_id, mut cx) = setup_indent_guides_editor(
15483        &"
15484    fn main() {
15485        let a = 1;
15486    }"
15487        .unindent(),
15488        cx,
15489    )
15490    .await;
15491
15492    cx.update_editor(|editor, window, cx| {
15493        editor.change_selections(None, window, cx, |s| {
15494            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15495        });
15496    });
15497
15498    assert_indent_guides(
15499        0..3,
15500        vec![indent_guide(buffer_id, 1, 1, 0)],
15501        Some(vec![0]),
15502        &mut cx,
15503    );
15504}
15505
15506#[gpui::test]
15507async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15508    let (buffer_id, mut cx) = setup_indent_guides_editor(
15509        &"
15510    fn main() {
15511        if 1 == 2 {
15512            let a = 1;
15513        }
15514    }"
15515        .unindent(),
15516        cx,
15517    )
15518    .await;
15519
15520    cx.update_editor(|editor, window, cx| {
15521        editor.change_selections(None, window, cx, |s| {
15522            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15523        });
15524    });
15525
15526    assert_indent_guides(
15527        0..4,
15528        vec![
15529            indent_guide(buffer_id, 1, 3, 0),
15530            indent_guide(buffer_id, 2, 2, 1),
15531        ],
15532        Some(vec![1]),
15533        &mut cx,
15534    );
15535
15536    cx.update_editor(|editor, window, cx| {
15537        editor.change_selections(None, window, cx, |s| {
15538            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15539        });
15540    });
15541
15542    assert_indent_guides(
15543        0..4,
15544        vec![
15545            indent_guide(buffer_id, 1, 3, 0),
15546            indent_guide(buffer_id, 2, 2, 1),
15547        ],
15548        Some(vec![1]),
15549        &mut cx,
15550    );
15551
15552    cx.update_editor(|editor, window, cx| {
15553        editor.change_selections(None, window, cx, |s| {
15554            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15555        });
15556    });
15557
15558    assert_indent_guides(
15559        0..4,
15560        vec![
15561            indent_guide(buffer_id, 1, 3, 0),
15562            indent_guide(buffer_id, 2, 2, 1),
15563        ],
15564        Some(vec![0]),
15565        &mut cx,
15566    );
15567}
15568
15569#[gpui::test]
15570async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15571    let (buffer_id, mut cx) = setup_indent_guides_editor(
15572        &"
15573    fn main() {
15574        let a = 1;
15575
15576        let b = 2;
15577    }"
15578        .unindent(),
15579        cx,
15580    )
15581    .await;
15582
15583    cx.update_editor(|editor, window, cx| {
15584        editor.change_selections(None, window, cx, |s| {
15585            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15586        });
15587    });
15588
15589    assert_indent_guides(
15590        0..5,
15591        vec![indent_guide(buffer_id, 1, 3, 0)],
15592        Some(vec![0]),
15593        &mut cx,
15594    );
15595}
15596
15597#[gpui::test]
15598async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15599    let (buffer_id, mut cx) = setup_indent_guides_editor(
15600        &"
15601    def m:
15602        a = 1
15603        pass"
15604            .unindent(),
15605        cx,
15606    )
15607    .await;
15608
15609    cx.update_editor(|editor, window, cx| {
15610        editor.change_selections(None, window, cx, |s| {
15611            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15612        });
15613    });
15614
15615    assert_indent_guides(
15616        0..3,
15617        vec![indent_guide(buffer_id, 1, 2, 0)],
15618        Some(vec![0]),
15619        &mut cx,
15620    );
15621}
15622
15623#[gpui::test]
15624async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15625    init_test(cx, |_| {});
15626    let mut cx = EditorTestContext::new(cx).await;
15627    let text = indoc! {
15628        "
15629        impl A {
15630            fn b() {
15631                0;
15632                3;
15633                5;
15634                6;
15635                7;
15636            }
15637        }
15638        "
15639    };
15640    let base_text = indoc! {
15641        "
15642        impl A {
15643            fn b() {
15644                0;
15645                1;
15646                2;
15647                3;
15648                4;
15649            }
15650            fn c() {
15651                5;
15652                6;
15653                7;
15654            }
15655        }
15656        "
15657    };
15658
15659    cx.update_editor(|editor, window, cx| {
15660        editor.set_text(text, window, cx);
15661
15662        editor.buffer().update(cx, |multibuffer, cx| {
15663            let buffer = multibuffer.as_singleton().unwrap();
15664            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15665
15666            multibuffer.set_all_diff_hunks_expanded(cx);
15667            multibuffer.add_diff(diff, cx);
15668
15669            buffer.read(cx).remote_id()
15670        })
15671    });
15672    cx.run_until_parked();
15673
15674    cx.assert_state_with_diff(
15675        indoc! { "
15676          impl A {
15677              fn b() {
15678                  0;
15679        -         1;
15680        -         2;
15681                  3;
15682        -         4;
15683        -     }
15684        -     fn c() {
15685                  5;
15686                  6;
15687                  7;
15688              }
15689          }
15690          ˇ"
15691        }
15692        .to_string(),
15693    );
15694
15695    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15696        editor
15697            .snapshot(window, cx)
15698            .buffer_snapshot
15699            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15700            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15701            .collect::<Vec<_>>()
15702    });
15703    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15704    assert_eq!(
15705        actual_guides,
15706        vec![
15707            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15708            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15709            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15710        ]
15711    );
15712}
15713
15714#[gpui::test]
15715async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15716    init_test(cx, |_| {});
15717    let mut cx = EditorTestContext::new(cx).await;
15718
15719    let diff_base = r#"
15720        a
15721        b
15722        c
15723        "#
15724    .unindent();
15725
15726    cx.set_state(
15727        &r#"
15728        ˇA
15729        b
15730        C
15731        "#
15732        .unindent(),
15733    );
15734    cx.set_head_text(&diff_base);
15735    cx.update_editor(|editor, window, cx| {
15736        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15737    });
15738    executor.run_until_parked();
15739
15740    let both_hunks_expanded = r#"
15741        - a
15742        + ˇA
15743          b
15744        - c
15745        + C
15746        "#
15747    .unindent();
15748
15749    cx.assert_state_with_diff(both_hunks_expanded.clone());
15750
15751    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15752        let snapshot = editor.snapshot(window, cx);
15753        let hunks = editor
15754            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15755            .collect::<Vec<_>>();
15756        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15757        let buffer_id = hunks[0].buffer_id;
15758        hunks
15759            .into_iter()
15760            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15761            .collect::<Vec<_>>()
15762    });
15763    assert_eq!(hunk_ranges.len(), 2);
15764
15765    cx.update_editor(|editor, _, cx| {
15766        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15767    });
15768    executor.run_until_parked();
15769
15770    let second_hunk_expanded = r#"
15771          ˇA
15772          b
15773        - c
15774        + C
15775        "#
15776    .unindent();
15777
15778    cx.assert_state_with_diff(second_hunk_expanded);
15779
15780    cx.update_editor(|editor, _, cx| {
15781        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15782    });
15783    executor.run_until_parked();
15784
15785    cx.assert_state_with_diff(both_hunks_expanded.clone());
15786
15787    cx.update_editor(|editor, _, cx| {
15788        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15789    });
15790    executor.run_until_parked();
15791
15792    let first_hunk_expanded = r#"
15793        - a
15794        + ˇA
15795          b
15796          C
15797        "#
15798    .unindent();
15799
15800    cx.assert_state_with_diff(first_hunk_expanded);
15801
15802    cx.update_editor(|editor, _, cx| {
15803        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15804    });
15805    executor.run_until_parked();
15806
15807    cx.assert_state_with_diff(both_hunks_expanded);
15808
15809    cx.set_state(
15810        &r#"
15811        ˇA
15812        b
15813        "#
15814        .unindent(),
15815    );
15816    cx.run_until_parked();
15817
15818    // TODO this cursor position seems bad
15819    cx.assert_state_with_diff(
15820        r#"
15821        - ˇa
15822        + A
15823          b
15824        "#
15825        .unindent(),
15826    );
15827
15828    cx.update_editor(|editor, window, cx| {
15829        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15830    });
15831
15832    cx.assert_state_with_diff(
15833        r#"
15834            - ˇa
15835            + A
15836              b
15837            - c
15838            "#
15839        .unindent(),
15840    );
15841
15842    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15843        let snapshot = editor.snapshot(window, cx);
15844        let hunks = editor
15845            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15846            .collect::<Vec<_>>();
15847        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15848        let buffer_id = hunks[0].buffer_id;
15849        hunks
15850            .into_iter()
15851            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15852            .collect::<Vec<_>>()
15853    });
15854    assert_eq!(hunk_ranges.len(), 2);
15855
15856    cx.update_editor(|editor, _, cx| {
15857        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15858    });
15859    executor.run_until_parked();
15860
15861    cx.assert_state_with_diff(
15862        r#"
15863        - ˇa
15864        + A
15865          b
15866        "#
15867        .unindent(),
15868    );
15869}
15870
15871#[gpui::test]
15872async fn test_toggle_deletion_hunk_at_start_of_file(
15873    executor: BackgroundExecutor,
15874    cx: &mut TestAppContext,
15875) {
15876    init_test(cx, |_| {});
15877    let mut cx = EditorTestContext::new(cx).await;
15878
15879    let diff_base = r#"
15880        a
15881        b
15882        c
15883        "#
15884    .unindent();
15885
15886    cx.set_state(
15887        &r#"
15888        ˇb
15889        c
15890        "#
15891        .unindent(),
15892    );
15893    cx.set_head_text(&diff_base);
15894    cx.update_editor(|editor, window, cx| {
15895        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15896    });
15897    executor.run_until_parked();
15898
15899    let hunk_expanded = r#"
15900        - a
15901          ˇb
15902          c
15903        "#
15904    .unindent();
15905
15906    cx.assert_state_with_diff(hunk_expanded.clone());
15907
15908    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15909        let snapshot = editor.snapshot(window, cx);
15910        let hunks = editor
15911            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15912            .collect::<Vec<_>>();
15913        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15914        let buffer_id = hunks[0].buffer_id;
15915        hunks
15916            .into_iter()
15917            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15918            .collect::<Vec<_>>()
15919    });
15920    assert_eq!(hunk_ranges.len(), 1);
15921
15922    cx.update_editor(|editor, _, cx| {
15923        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15924    });
15925    executor.run_until_parked();
15926
15927    let hunk_collapsed = r#"
15928          ˇb
15929          c
15930        "#
15931    .unindent();
15932
15933    cx.assert_state_with_diff(hunk_collapsed);
15934
15935    cx.update_editor(|editor, _, cx| {
15936        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15937    });
15938    executor.run_until_parked();
15939
15940    cx.assert_state_with_diff(hunk_expanded.clone());
15941}
15942
15943#[gpui::test]
15944async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15945    init_test(cx, |_| {});
15946
15947    let fs = FakeFs::new(cx.executor());
15948    fs.insert_tree(
15949        path!("/test"),
15950        json!({
15951            ".git": {},
15952            "file-1": "ONE\n",
15953            "file-2": "TWO\n",
15954            "file-3": "THREE\n",
15955        }),
15956    )
15957    .await;
15958
15959    fs.set_head_for_repo(
15960        path!("/test/.git").as_ref(),
15961        &[
15962            ("file-1".into(), "one\n".into()),
15963            ("file-2".into(), "two\n".into()),
15964            ("file-3".into(), "three\n".into()),
15965        ],
15966    );
15967
15968    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15969    let mut buffers = vec![];
15970    for i in 1..=3 {
15971        let buffer = project
15972            .update(cx, |project, cx| {
15973                let path = format!(path!("/test/file-{}"), i);
15974                project.open_local_buffer(path, cx)
15975            })
15976            .await
15977            .unwrap();
15978        buffers.push(buffer);
15979    }
15980
15981    let multibuffer = cx.new(|cx| {
15982        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15983        multibuffer.set_all_diff_hunks_expanded(cx);
15984        for buffer in &buffers {
15985            let snapshot = buffer.read(cx).snapshot();
15986            multibuffer.set_excerpts_for_path(
15987                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15988                buffer.clone(),
15989                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15990                DEFAULT_MULTIBUFFER_CONTEXT,
15991                cx,
15992            );
15993        }
15994        multibuffer
15995    });
15996
15997    let editor = cx.add_window(|window, cx| {
15998        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15999    });
16000    cx.run_until_parked();
16001
16002    let snapshot = editor
16003        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16004        .unwrap();
16005    let hunks = snapshot
16006        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16007        .map(|hunk| match hunk {
16008            DisplayDiffHunk::Unfolded {
16009                display_row_range, ..
16010            } => display_row_range,
16011            DisplayDiffHunk::Folded { .. } => unreachable!(),
16012        })
16013        .collect::<Vec<_>>();
16014    assert_eq!(
16015        hunks,
16016        [
16017            DisplayRow(2)..DisplayRow(4),
16018            DisplayRow(7)..DisplayRow(9),
16019            DisplayRow(12)..DisplayRow(14),
16020        ]
16021    );
16022}
16023
16024#[gpui::test]
16025async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16026    init_test(cx, |_| {});
16027
16028    let mut cx = EditorTestContext::new(cx).await;
16029    cx.set_head_text(indoc! { "
16030        one
16031        two
16032        three
16033        four
16034        five
16035        "
16036    });
16037    cx.set_index_text(indoc! { "
16038        one
16039        two
16040        three
16041        four
16042        five
16043        "
16044    });
16045    cx.set_state(indoc! {"
16046        one
16047        TWO
16048        ˇTHREE
16049        FOUR
16050        five
16051    "});
16052    cx.run_until_parked();
16053    cx.update_editor(|editor, window, cx| {
16054        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16055    });
16056    cx.run_until_parked();
16057    cx.assert_index_text(Some(indoc! {"
16058        one
16059        TWO
16060        THREE
16061        FOUR
16062        five
16063    "}));
16064    cx.set_state(indoc! { "
16065        one
16066        TWO
16067        ˇTHREE-HUNDRED
16068        FOUR
16069        five
16070    "});
16071    cx.run_until_parked();
16072    cx.update_editor(|editor, window, cx| {
16073        let snapshot = editor.snapshot(window, cx);
16074        let hunks = editor
16075            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16076            .collect::<Vec<_>>();
16077        assert_eq!(hunks.len(), 1);
16078        assert_eq!(
16079            hunks[0].status(),
16080            DiffHunkStatus {
16081                kind: DiffHunkStatusKind::Modified,
16082                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16083            }
16084        );
16085
16086        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16087    });
16088    cx.run_until_parked();
16089    cx.assert_index_text(Some(indoc! {"
16090        one
16091        TWO
16092        THREE-HUNDRED
16093        FOUR
16094        five
16095    "}));
16096}
16097
16098#[gpui::test]
16099fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16100    init_test(cx, |_| {});
16101
16102    let editor = cx.add_window(|window, cx| {
16103        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16104        build_editor(buffer, window, cx)
16105    });
16106
16107    let render_args = Arc::new(Mutex::new(None));
16108    let snapshot = editor
16109        .update(cx, |editor, window, cx| {
16110            let snapshot = editor.buffer().read(cx).snapshot(cx);
16111            let range =
16112                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16113
16114            struct RenderArgs {
16115                row: MultiBufferRow,
16116                folded: bool,
16117                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16118            }
16119
16120            let crease = Crease::inline(
16121                range,
16122                FoldPlaceholder::test(),
16123                {
16124                    let toggle_callback = render_args.clone();
16125                    move |row, folded, callback, _window, _cx| {
16126                        *toggle_callback.lock() = Some(RenderArgs {
16127                            row,
16128                            folded,
16129                            callback,
16130                        });
16131                        div()
16132                    }
16133                },
16134                |_row, _folded, _window, _cx| div(),
16135            );
16136
16137            editor.insert_creases(Some(crease), cx);
16138            let snapshot = editor.snapshot(window, cx);
16139            let _div = snapshot.render_crease_toggle(
16140                MultiBufferRow(1),
16141                false,
16142                cx.entity().clone(),
16143                window,
16144                cx,
16145            );
16146            snapshot
16147        })
16148        .unwrap();
16149
16150    let render_args = render_args.lock().take().unwrap();
16151    assert_eq!(render_args.row, MultiBufferRow(1));
16152    assert!(!render_args.folded);
16153    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16154
16155    cx.update_window(*editor, |_, window, cx| {
16156        (render_args.callback)(true, window, cx)
16157    })
16158    .unwrap();
16159    let snapshot = editor
16160        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16161        .unwrap();
16162    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16163
16164    cx.update_window(*editor, |_, window, cx| {
16165        (render_args.callback)(false, window, cx)
16166    })
16167    .unwrap();
16168    let snapshot = editor
16169        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16170        .unwrap();
16171    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16172}
16173
16174#[gpui::test]
16175async fn test_input_text(cx: &mut TestAppContext) {
16176    init_test(cx, |_| {});
16177    let mut cx = EditorTestContext::new(cx).await;
16178
16179    cx.set_state(
16180        &r#"ˇone
16181        two
16182
16183        three
16184        fourˇ
16185        five
16186
16187        siˇx"#
16188            .unindent(),
16189    );
16190
16191    cx.dispatch_action(HandleInput(String::new()));
16192    cx.assert_editor_state(
16193        &r#"ˇone
16194        two
16195
16196        three
16197        fourˇ
16198        five
16199
16200        siˇx"#
16201            .unindent(),
16202    );
16203
16204    cx.dispatch_action(HandleInput("AAAA".to_string()));
16205    cx.assert_editor_state(
16206        &r#"AAAAˇone
16207        two
16208
16209        three
16210        fourAAAAˇ
16211        five
16212
16213        siAAAAˇx"#
16214            .unindent(),
16215    );
16216}
16217
16218#[gpui::test]
16219async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16220    init_test(cx, |_| {});
16221
16222    let mut cx = EditorTestContext::new(cx).await;
16223    cx.set_state(
16224        r#"let foo = 1;
16225let foo = 2;
16226let foo = 3;
16227let fooˇ = 4;
16228let foo = 5;
16229let foo = 6;
16230let foo = 7;
16231let foo = 8;
16232let foo = 9;
16233let foo = 10;
16234let foo = 11;
16235let foo = 12;
16236let foo = 13;
16237let foo = 14;
16238let foo = 15;"#,
16239    );
16240
16241    cx.update_editor(|e, window, cx| {
16242        assert_eq!(
16243            e.next_scroll_position,
16244            NextScrollCursorCenterTopBottom::Center,
16245            "Default next scroll direction is center",
16246        );
16247
16248        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16249        assert_eq!(
16250            e.next_scroll_position,
16251            NextScrollCursorCenterTopBottom::Top,
16252            "After center, next scroll direction should be top",
16253        );
16254
16255        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16256        assert_eq!(
16257            e.next_scroll_position,
16258            NextScrollCursorCenterTopBottom::Bottom,
16259            "After top, next scroll direction should be bottom",
16260        );
16261
16262        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16263        assert_eq!(
16264            e.next_scroll_position,
16265            NextScrollCursorCenterTopBottom::Center,
16266            "After bottom, scrolling should start over",
16267        );
16268
16269        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16270        assert_eq!(
16271            e.next_scroll_position,
16272            NextScrollCursorCenterTopBottom::Top,
16273            "Scrolling continues if retriggered fast enough"
16274        );
16275    });
16276
16277    cx.executor()
16278        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16279    cx.executor().run_until_parked();
16280    cx.update_editor(|e, _, _| {
16281        assert_eq!(
16282            e.next_scroll_position,
16283            NextScrollCursorCenterTopBottom::Center,
16284            "If scrolling is not triggered fast enough, it should reset"
16285        );
16286    });
16287}
16288
16289#[gpui::test]
16290async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16291    init_test(cx, |_| {});
16292    let mut cx = EditorLspTestContext::new_rust(
16293        lsp::ServerCapabilities {
16294            definition_provider: Some(lsp::OneOf::Left(true)),
16295            references_provider: Some(lsp::OneOf::Left(true)),
16296            ..lsp::ServerCapabilities::default()
16297        },
16298        cx,
16299    )
16300    .await;
16301
16302    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16303        let go_to_definition = cx
16304            .lsp
16305            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16306                move |params, _| async move {
16307                    if empty_go_to_definition {
16308                        Ok(None)
16309                    } else {
16310                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16311                            uri: params.text_document_position_params.text_document.uri,
16312                            range: lsp::Range::new(
16313                                lsp::Position::new(4, 3),
16314                                lsp::Position::new(4, 6),
16315                            ),
16316                        })))
16317                    }
16318                },
16319            );
16320        let references = cx
16321            .lsp
16322            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16323                Ok(Some(vec![lsp::Location {
16324                    uri: params.text_document_position.text_document.uri,
16325                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16326                }]))
16327            });
16328        (go_to_definition, references)
16329    };
16330
16331    cx.set_state(
16332        &r#"fn one() {
16333            let mut a = ˇtwo();
16334        }
16335
16336        fn two() {}"#
16337            .unindent(),
16338    );
16339    set_up_lsp_handlers(false, &mut cx);
16340    let navigated = cx
16341        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16342        .await
16343        .expect("Failed to navigate to definition");
16344    assert_eq!(
16345        navigated,
16346        Navigated::Yes,
16347        "Should have navigated to definition from the GetDefinition response"
16348    );
16349    cx.assert_editor_state(
16350        &r#"fn one() {
16351            let mut a = two();
16352        }
16353
16354        fn «twoˇ»() {}"#
16355            .unindent(),
16356    );
16357
16358    let editors = cx.update_workspace(|workspace, _, cx| {
16359        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16360    });
16361    cx.update_editor(|_, _, test_editor_cx| {
16362        assert_eq!(
16363            editors.len(),
16364            1,
16365            "Initially, only one, test, editor should be open in the workspace"
16366        );
16367        assert_eq!(
16368            test_editor_cx.entity(),
16369            editors.last().expect("Asserted len is 1").clone()
16370        );
16371    });
16372
16373    set_up_lsp_handlers(true, &mut cx);
16374    let navigated = cx
16375        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16376        .await
16377        .expect("Failed to navigate to lookup references");
16378    assert_eq!(
16379        navigated,
16380        Navigated::Yes,
16381        "Should have navigated to references as a fallback after empty GoToDefinition response"
16382    );
16383    // We should not change the selections in the existing file,
16384    // if opening another milti buffer with the references
16385    cx.assert_editor_state(
16386        &r#"fn one() {
16387            let mut a = two();
16388        }
16389
16390        fn «twoˇ»() {}"#
16391            .unindent(),
16392    );
16393    let editors = cx.update_workspace(|workspace, _, cx| {
16394        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16395    });
16396    cx.update_editor(|_, _, test_editor_cx| {
16397        assert_eq!(
16398            editors.len(),
16399            2,
16400            "After falling back to references search, we open a new editor with the results"
16401        );
16402        let references_fallback_text = editors
16403            .into_iter()
16404            .find(|new_editor| *new_editor != test_editor_cx.entity())
16405            .expect("Should have one non-test editor now")
16406            .read(test_editor_cx)
16407            .text(test_editor_cx);
16408        assert_eq!(
16409            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16410            "Should use the range from the references response and not the GoToDefinition one"
16411        );
16412    });
16413}
16414
16415#[gpui::test]
16416async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16417    init_test(cx, |_| {});
16418    cx.update(|cx| {
16419        let mut editor_settings = EditorSettings::get_global(cx).clone();
16420        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16421        EditorSettings::override_global(editor_settings, cx);
16422    });
16423    let mut cx = EditorLspTestContext::new_rust(
16424        lsp::ServerCapabilities {
16425            definition_provider: Some(lsp::OneOf::Left(true)),
16426            references_provider: Some(lsp::OneOf::Left(true)),
16427            ..lsp::ServerCapabilities::default()
16428        },
16429        cx,
16430    )
16431    .await;
16432    let original_state = r#"fn one() {
16433        let mut a = ˇtwo();
16434    }
16435
16436    fn two() {}"#
16437        .unindent();
16438    cx.set_state(&original_state);
16439
16440    let mut go_to_definition = cx
16441        .lsp
16442        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16443            move |_, _| async move { Ok(None) },
16444        );
16445    let _references = cx
16446        .lsp
16447        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16448            panic!("Should not call for references with no go to definition fallback")
16449        });
16450
16451    let navigated = cx
16452        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16453        .await
16454        .expect("Failed to navigate to lookup references");
16455    go_to_definition
16456        .next()
16457        .await
16458        .expect("Should have called the go_to_definition handler");
16459
16460    assert_eq!(
16461        navigated,
16462        Navigated::No,
16463        "Should have navigated to references as a fallback after empty GoToDefinition response"
16464    );
16465    cx.assert_editor_state(&original_state);
16466    let editors = cx.update_workspace(|workspace, _, cx| {
16467        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16468    });
16469    cx.update_editor(|_, _, _| {
16470        assert_eq!(
16471            editors.len(),
16472            1,
16473            "After unsuccessful fallback, no other editor should have been opened"
16474        );
16475    });
16476}
16477
16478#[gpui::test]
16479async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16480    init_test(cx, |_| {});
16481
16482    let language = Arc::new(Language::new(
16483        LanguageConfig::default(),
16484        Some(tree_sitter_rust::LANGUAGE.into()),
16485    ));
16486
16487    let text = r#"
16488        #[cfg(test)]
16489        mod tests() {
16490            #[test]
16491            fn runnable_1() {
16492                let a = 1;
16493            }
16494
16495            #[test]
16496            fn runnable_2() {
16497                let a = 1;
16498                let b = 2;
16499            }
16500        }
16501    "#
16502    .unindent();
16503
16504    let fs = FakeFs::new(cx.executor());
16505    fs.insert_file("/file.rs", Default::default()).await;
16506
16507    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16508    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16509    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16510    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16511    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16512
16513    let editor = cx.new_window_entity(|window, cx| {
16514        Editor::new(
16515            EditorMode::Full,
16516            multi_buffer,
16517            Some(project.clone()),
16518            window,
16519            cx,
16520        )
16521    });
16522
16523    editor.update_in(cx, |editor, window, cx| {
16524        let snapshot = editor.buffer().read(cx).snapshot(cx);
16525        editor.tasks.insert(
16526            (buffer.read(cx).remote_id(), 3),
16527            RunnableTasks {
16528                templates: vec![],
16529                offset: snapshot.anchor_before(43),
16530                column: 0,
16531                extra_variables: HashMap::default(),
16532                context_range: BufferOffset(43)..BufferOffset(85),
16533            },
16534        );
16535        editor.tasks.insert(
16536            (buffer.read(cx).remote_id(), 8),
16537            RunnableTasks {
16538                templates: vec![],
16539                offset: snapshot.anchor_before(86),
16540                column: 0,
16541                extra_variables: HashMap::default(),
16542                context_range: BufferOffset(86)..BufferOffset(191),
16543            },
16544        );
16545
16546        // Test finding task when cursor is inside function body
16547        editor.change_selections(None, window, cx, |s| {
16548            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16549        });
16550        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16551        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16552
16553        // Test finding task when cursor is on function name
16554        editor.change_selections(None, window, cx, |s| {
16555            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16556        });
16557        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16558        assert_eq!(row, 8, "Should find task when cursor is on function name");
16559    });
16560}
16561
16562#[gpui::test]
16563async fn test_folding_buffers(cx: &mut TestAppContext) {
16564    init_test(cx, |_| {});
16565
16566    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16567    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16568    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16569
16570    let fs = FakeFs::new(cx.executor());
16571    fs.insert_tree(
16572        path!("/a"),
16573        json!({
16574            "first.rs": sample_text_1,
16575            "second.rs": sample_text_2,
16576            "third.rs": sample_text_3,
16577        }),
16578    )
16579    .await;
16580    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16581    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16582    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16583    let worktree = project.update(cx, |project, cx| {
16584        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16585        assert_eq!(worktrees.len(), 1);
16586        worktrees.pop().unwrap()
16587    });
16588    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16589
16590    let buffer_1 = project
16591        .update(cx, |project, cx| {
16592            project.open_buffer((worktree_id, "first.rs"), cx)
16593        })
16594        .await
16595        .unwrap();
16596    let buffer_2 = project
16597        .update(cx, |project, cx| {
16598            project.open_buffer((worktree_id, "second.rs"), cx)
16599        })
16600        .await
16601        .unwrap();
16602    let buffer_3 = project
16603        .update(cx, |project, cx| {
16604            project.open_buffer((worktree_id, "third.rs"), cx)
16605        })
16606        .await
16607        .unwrap();
16608
16609    let multi_buffer = cx.new(|cx| {
16610        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16611        multi_buffer.push_excerpts(
16612            buffer_1.clone(),
16613            [
16614                ExcerptRange {
16615                    context: Point::new(0, 0)..Point::new(3, 0),
16616                    primary: None,
16617                },
16618                ExcerptRange {
16619                    context: Point::new(5, 0)..Point::new(7, 0),
16620                    primary: None,
16621                },
16622                ExcerptRange {
16623                    context: Point::new(9, 0)..Point::new(10, 4),
16624                    primary: None,
16625                },
16626            ],
16627            cx,
16628        );
16629        multi_buffer.push_excerpts(
16630            buffer_2.clone(),
16631            [
16632                ExcerptRange {
16633                    context: Point::new(0, 0)..Point::new(3, 0),
16634                    primary: None,
16635                },
16636                ExcerptRange {
16637                    context: Point::new(5, 0)..Point::new(7, 0),
16638                    primary: None,
16639                },
16640                ExcerptRange {
16641                    context: Point::new(9, 0)..Point::new(10, 4),
16642                    primary: None,
16643                },
16644            ],
16645            cx,
16646        );
16647        multi_buffer.push_excerpts(
16648            buffer_3.clone(),
16649            [
16650                ExcerptRange {
16651                    context: Point::new(0, 0)..Point::new(3, 0),
16652                    primary: None,
16653                },
16654                ExcerptRange {
16655                    context: Point::new(5, 0)..Point::new(7, 0),
16656                    primary: None,
16657                },
16658                ExcerptRange {
16659                    context: Point::new(9, 0)..Point::new(10, 4),
16660                    primary: None,
16661                },
16662            ],
16663            cx,
16664        );
16665        multi_buffer
16666    });
16667    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16668        Editor::new(
16669            EditorMode::Full,
16670            multi_buffer.clone(),
16671            Some(project.clone()),
16672            window,
16673            cx,
16674        )
16675    });
16676
16677    assert_eq!(
16678        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16679        "\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",
16680    );
16681
16682    multi_buffer_editor.update(cx, |editor, cx| {
16683        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16684    });
16685    assert_eq!(
16686        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16687        "\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",
16688        "After folding the first buffer, its text should not be displayed"
16689    );
16690
16691    multi_buffer_editor.update(cx, |editor, cx| {
16692        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16693    });
16694    assert_eq!(
16695        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16696        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16697        "After folding the second buffer, its text should not be displayed"
16698    );
16699
16700    multi_buffer_editor.update(cx, |editor, cx| {
16701        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16702    });
16703    assert_eq!(
16704        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16705        "\n\n\n\n\n",
16706        "After folding the third buffer, its text should not be displayed"
16707    );
16708
16709    // Emulate selection inside the fold logic, that should work
16710    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16711        editor
16712            .snapshot(window, cx)
16713            .next_line_boundary(Point::new(0, 4));
16714    });
16715
16716    multi_buffer_editor.update(cx, |editor, cx| {
16717        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16718    });
16719    assert_eq!(
16720        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16721        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16722        "After unfolding the second buffer, its text should be displayed"
16723    );
16724
16725    // Typing inside of buffer 1 causes that buffer to be unfolded.
16726    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16727        assert_eq!(
16728            multi_buffer
16729                .read(cx)
16730                .snapshot(cx)
16731                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16732                .collect::<String>(),
16733            "bbbb"
16734        );
16735        editor.change_selections(None, window, cx, |selections| {
16736            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16737        });
16738        editor.handle_input("B", window, cx);
16739    });
16740
16741    assert_eq!(
16742        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16743        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16744        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16745    );
16746
16747    multi_buffer_editor.update(cx, |editor, cx| {
16748        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16749    });
16750    assert_eq!(
16751        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16752        "\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",
16753        "After unfolding the all buffers, all original text should be displayed"
16754    );
16755}
16756
16757#[gpui::test]
16758async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16759    init_test(cx, |_| {});
16760
16761    let sample_text_1 = "1111\n2222\n3333".to_string();
16762    let sample_text_2 = "4444\n5555\n6666".to_string();
16763    let sample_text_3 = "7777\n8888\n9999".to_string();
16764
16765    let fs = FakeFs::new(cx.executor());
16766    fs.insert_tree(
16767        path!("/a"),
16768        json!({
16769            "first.rs": sample_text_1,
16770            "second.rs": sample_text_2,
16771            "third.rs": sample_text_3,
16772        }),
16773    )
16774    .await;
16775    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16776    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16777    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16778    let worktree = project.update(cx, |project, cx| {
16779        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16780        assert_eq!(worktrees.len(), 1);
16781        worktrees.pop().unwrap()
16782    });
16783    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16784
16785    let buffer_1 = project
16786        .update(cx, |project, cx| {
16787            project.open_buffer((worktree_id, "first.rs"), cx)
16788        })
16789        .await
16790        .unwrap();
16791    let buffer_2 = project
16792        .update(cx, |project, cx| {
16793            project.open_buffer((worktree_id, "second.rs"), cx)
16794        })
16795        .await
16796        .unwrap();
16797    let buffer_3 = project
16798        .update(cx, |project, cx| {
16799            project.open_buffer((worktree_id, "third.rs"), cx)
16800        })
16801        .await
16802        .unwrap();
16803
16804    let multi_buffer = cx.new(|cx| {
16805        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16806        multi_buffer.push_excerpts(
16807            buffer_1.clone(),
16808            [ExcerptRange {
16809                context: Point::new(0, 0)..Point::new(3, 0),
16810                primary: None,
16811            }],
16812            cx,
16813        );
16814        multi_buffer.push_excerpts(
16815            buffer_2.clone(),
16816            [ExcerptRange {
16817                context: Point::new(0, 0)..Point::new(3, 0),
16818                primary: None,
16819            }],
16820            cx,
16821        );
16822        multi_buffer.push_excerpts(
16823            buffer_3.clone(),
16824            [ExcerptRange {
16825                context: Point::new(0, 0)..Point::new(3, 0),
16826                primary: None,
16827            }],
16828            cx,
16829        );
16830        multi_buffer
16831    });
16832
16833    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16834        Editor::new(
16835            EditorMode::Full,
16836            multi_buffer,
16837            Some(project.clone()),
16838            window,
16839            cx,
16840        )
16841    });
16842
16843    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16844    assert_eq!(
16845        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16846        full_text,
16847    );
16848
16849    multi_buffer_editor.update(cx, |editor, cx| {
16850        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16851    });
16852    assert_eq!(
16853        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16854        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16855        "After folding the first buffer, its text should not be displayed"
16856    );
16857
16858    multi_buffer_editor.update(cx, |editor, cx| {
16859        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16860    });
16861
16862    assert_eq!(
16863        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16864        "\n\n\n\n\n\n7777\n8888\n9999",
16865        "After folding the second buffer, its text should not be displayed"
16866    );
16867
16868    multi_buffer_editor.update(cx, |editor, cx| {
16869        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16870    });
16871    assert_eq!(
16872        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16873        "\n\n\n\n\n",
16874        "After folding the third buffer, its text should not be displayed"
16875    );
16876
16877    multi_buffer_editor.update(cx, |editor, cx| {
16878        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16879    });
16880    assert_eq!(
16881        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16882        "\n\n\n\n4444\n5555\n6666\n\n",
16883        "After unfolding the second buffer, its text should be displayed"
16884    );
16885
16886    multi_buffer_editor.update(cx, |editor, cx| {
16887        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16888    });
16889    assert_eq!(
16890        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16891        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16892        "After unfolding the first buffer, its text should be displayed"
16893    );
16894
16895    multi_buffer_editor.update(cx, |editor, cx| {
16896        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16897    });
16898    assert_eq!(
16899        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16900        full_text,
16901        "After unfolding all buffers, all original text should be displayed"
16902    );
16903}
16904
16905#[gpui::test]
16906async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16907    init_test(cx, |_| {});
16908
16909    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16910
16911    let fs = FakeFs::new(cx.executor());
16912    fs.insert_tree(
16913        path!("/a"),
16914        json!({
16915            "main.rs": sample_text,
16916        }),
16917    )
16918    .await;
16919    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16920    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16921    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16922    let worktree = project.update(cx, |project, cx| {
16923        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16924        assert_eq!(worktrees.len(), 1);
16925        worktrees.pop().unwrap()
16926    });
16927    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16928
16929    let buffer_1 = project
16930        .update(cx, |project, cx| {
16931            project.open_buffer((worktree_id, "main.rs"), cx)
16932        })
16933        .await
16934        .unwrap();
16935
16936    let multi_buffer = cx.new(|cx| {
16937        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16938        multi_buffer.push_excerpts(
16939            buffer_1.clone(),
16940            [ExcerptRange {
16941                context: Point::new(0, 0)
16942                    ..Point::new(
16943                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16944                        0,
16945                    ),
16946                primary: None,
16947            }],
16948            cx,
16949        );
16950        multi_buffer
16951    });
16952    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16953        Editor::new(
16954            EditorMode::Full,
16955            multi_buffer,
16956            Some(project.clone()),
16957            window,
16958            cx,
16959        )
16960    });
16961
16962    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16963    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16964        enum TestHighlight {}
16965        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16966        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16967        editor.highlight_text::<TestHighlight>(
16968            vec![highlight_range.clone()],
16969            HighlightStyle::color(Hsla::green()),
16970            cx,
16971        );
16972        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16973    });
16974
16975    let full_text = format!("\n\n{sample_text}");
16976    assert_eq!(
16977        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16978        full_text,
16979    );
16980}
16981
16982#[gpui::test]
16983async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16984    init_test(cx, |_| {});
16985    cx.update(|cx| {
16986        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16987            "keymaps/default-linux.json",
16988            cx,
16989        )
16990        .unwrap();
16991        cx.bind_keys(default_key_bindings);
16992    });
16993
16994    let (editor, cx) = cx.add_window_view(|window, cx| {
16995        let multi_buffer = MultiBuffer::build_multi(
16996            [
16997                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16998                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16999                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17000                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17001            ],
17002            cx,
17003        );
17004        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17005
17006        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17007        // fold all but the second buffer, so that we test navigating between two
17008        // adjacent folded buffers, as well as folded buffers at the start and
17009        // end the multibuffer
17010        editor.fold_buffer(buffer_ids[0], cx);
17011        editor.fold_buffer(buffer_ids[2], cx);
17012        editor.fold_buffer(buffer_ids[3], cx);
17013
17014        editor
17015    });
17016    cx.simulate_resize(size(px(1000.), px(1000.)));
17017
17018    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17019    cx.assert_excerpts_with_selections(indoc! {"
17020        [EXCERPT]
17021        ˇ[FOLDED]
17022        [EXCERPT]
17023        a1
17024        b1
17025        [EXCERPT]
17026        [FOLDED]
17027        [EXCERPT]
17028        [FOLDED]
17029        "
17030    });
17031    cx.simulate_keystroke("down");
17032    cx.assert_excerpts_with_selections(indoc! {"
17033        [EXCERPT]
17034        [FOLDED]
17035        [EXCERPT]
17036        ˇa1
17037        b1
17038        [EXCERPT]
17039        [FOLDED]
17040        [EXCERPT]
17041        [FOLDED]
17042        "
17043    });
17044    cx.simulate_keystroke("down");
17045    cx.assert_excerpts_with_selections(indoc! {"
17046        [EXCERPT]
17047        [FOLDED]
17048        [EXCERPT]
17049        a1
17050        ˇb1
17051        [EXCERPT]
17052        [FOLDED]
17053        [EXCERPT]
17054        [FOLDED]
17055        "
17056    });
17057    cx.simulate_keystroke("down");
17058    cx.assert_excerpts_with_selections(indoc! {"
17059        [EXCERPT]
17060        [FOLDED]
17061        [EXCERPT]
17062        a1
17063        b1
17064        ˇ[EXCERPT]
17065        [FOLDED]
17066        [EXCERPT]
17067        [FOLDED]
17068        "
17069    });
17070    cx.simulate_keystroke("down");
17071    cx.assert_excerpts_with_selections(indoc! {"
17072        [EXCERPT]
17073        [FOLDED]
17074        [EXCERPT]
17075        a1
17076        b1
17077        [EXCERPT]
17078        ˇ[FOLDED]
17079        [EXCERPT]
17080        [FOLDED]
17081        "
17082    });
17083    for _ in 0..5 {
17084        cx.simulate_keystroke("down");
17085        cx.assert_excerpts_with_selections(indoc! {"
17086            [EXCERPT]
17087            [FOLDED]
17088            [EXCERPT]
17089            a1
17090            b1
17091            [EXCERPT]
17092            [FOLDED]
17093            [EXCERPT]
17094            ˇ[FOLDED]
17095            "
17096        });
17097    }
17098
17099    cx.simulate_keystroke("up");
17100    cx.assert_excerpts_with_selections(indoc! {"
17101        [EXCERPT]
17102        [FOLDED]
17103        [EXCERPT]
17104        a1
17105        b1
17106        [EXCERPT]
17107        ˇ[FOLDED]
17108        [EXCERPT]
17109        [FOLDED]
17110        "
17111    });
17112    cx.simulate_keystroke("up");
17113    cx.assert_excerpts_with_selections(indoc! {"
17114        [EXCERPT]
17115        [FOLDED]
17116        [EXCERPT]
17117        a1
17118        b1
17119        ˇ[EXCERPT]
17120        [FOLDED]
17121        [EXCERPT]
17122        [FOLDED]
17123        "
17124    });
17125    cx.simulate_keystroke("up");
17126    cx.assert_excerpts_with_selections(indoc! {"
17127        [EXCERPT]
17128        [FOLDED]
17129        [EXCERPT]
17130        a1
17131        ˇb1
17132        [EXCERPT]
17133        [FOLDED]
17134        [EXCERPT]
17135        [FOLDED]
17136        "
17137    });
17138    cx.simulate_keystroke("up");
17139    cx.assert_excerpts_with_selections(indoc! {"
17140        [EXCERPT]
17141        [FOLDED]
17142        [EXCERPT]
17143        ˇa1
17144        b1
17145        [EXCERPT]
17146        [FOLDED]
17147        [EXCERPT]
17148        [FOLDED]
17149        "
17150    });
17151    for _ in 0..5 {
17152        cx.simulate_keystroke("up");
17153        cx.assert_excerpts_with_selections(indoc! {"
17154            [EXCERPT]
17155            ˇ[FOLDED]
17156            [EXCERPT]
17157            a1
17158            b1
17159            [EXCERPT]
17160            [FOLDED]
17161            [EXCERPT]
17162            [FOLDED]
17163            "
17164        });
17165    }
17166}
17167
17168#[gpui::test]
17169async fn test_inline_completion_text(cx: &mut TestAppContext) {
17170    init_test(cx, |_| {});
17171
17172    // Simple insertion
17173    assert_highlighted_edits(
17174        "Hello, world!",
17175        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17176        true,
17177        cx,
17178        |highlighted_edits, cx| {
17179            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17180            assert_eq!(highlighted_edits.highlights.len(), 1);
17181            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17182            assert_eq!(
17183                highlighted_edits.highlights[0].1.background_color,
17184                Some(cx.theme().status().created_background)
17185            );
17186        },
17187    )
17188    .await;
17189
17190    // Replacement
17191    assert_highlighted_edits(
17192        "This is a test.",
17193        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17194        false,
17195        cx,
17196        |highlighted_edits, cx| {
17197            assert_eq!(highlighted_edits.text, "That is a test.");
17198            assert_eq!(highlighted_edits.highlights.len(), 1);
17199            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17200            assert_eq!(
17201                highlighted_edits.highlights[0].1.background_color,
17202                Some(cx.theme().status().created_background)
17203            );
17204        },
17205    )
17206    .await;
17207
17208    // Multiple edits
17209    assert_highlighted_edits(
17210        "Hello, world!",
17211        vec![
17212            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17213            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17214        ],
17215        false,
17216        cx,
17217        |highlighted_edits, cx| {
17218            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17219            assert_eq!(highlighted_edits.highlights.len(), 2);
17220            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17221            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17222            assert_eq!(
17223                highlighted_edits.highlights[0].1.background_color,
17224                Some(cx.theme().status().created_background)
17225            );
17226            assert_eq!(
17227                highlighted_edits.highlights[1].1.background_color,
17228                Some(cx.theme().status().created_background)
17229            );
17230        },
17231    )
17232    .await;
17233
17234    // Multiple lines with edits
17235    assert_highlighted_edits(
17236        "First line\nSecond line\nThird line\nFourth line",
17237        vec![
17238            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17239            (
17240                Point::new(2, 0)..Point::new(2, 10),
17241                "New third line".to_string(),
17242            ),
17243            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17244        ],
17245        false,
17246        cx,
17247        |highlighted_edits, cx| {
17248            assert_eq!(
17249                highlighted_edits.text,
17250                "Second modified\nNew third line\nFourth updated line"
17251            );
17252            assert_eq!(highlighted_edits.highlights.len(), 3);
17253            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17254            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17255            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17256            for highlight in &highlighted_edits.highlights {
17257                assert_eq!(
17258                    highlight.1.background_color,
17259                    Some(cx.theme().status().created_background)
17260                );
17261            }
17262        },
17263    )
17264    .await;
17265}
17266
17267#[gpui::test]
17268async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17269    init_test(cx, |_| {});
17270
17271    // Deletion
17272    assert_highlighted_edits(
17273        "Hello, world!",
17274        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17275        true,
17276        cx,
17277        |highlighted_edits, cx| {
17278            assert_eq!(highlighted_edits.text, "Hello, world!");
17279            assert_eq!(highlighted_edits.highlights.len(), 1);
17280            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17281            assert_eq!(
17282                highlighted_edits.highlights[0].1.background_color,
17283                Some(cx.theme().status().deleted_background)
17284            );
17285        },
17286    )
17287    .await;
17288
17289    // Insertion
17290    assert_highlighted_edits(
17291        "Hello, world!",
17292        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17293        true,
17294        cx,
17295        |highlighted_edits, cx| {
17296            assert_eq!(highlighted_edits.highlights.len(), 1);
17297            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17298            assert_eq!(
17299                highlighted_edits.highlights[0].1.background_color,
17300                Some(cx.theme().status().created_background)
17301            );
17302        },
17303    )
17304    .await;
17305}
17306
17307async fn assert_highlighted_edits(
17308    text: &str,
17309    edits: Vec<(Range<Point>, String)>,
17310    include_deletions: bool,
17311    cx: &mut TestAppContext,
17312    assertion_fn: impl Fn(HighlightedText, &App),
17313) {
17314    let window = cx.add_window(|window, cx| {
17315        let buffer = MultiBuffer::build_simple(text, cx);
17316        Editor::new(EditorMode::Full, buffer, None, window, cx)
17317    });
17318    let cx = &mut VisualTestContext::from_window(*window, cx);
17319
17320    let (buffer, snapshot) = window
17321        .update(cx, |editor, _window, cx| {
17322            (
17323                editor.buffer().clone(),
17324                editor.buffer().read(cx).snapshot(cx),
17325            )
17326        })
17327        .unwrap();
17328
17329    let edits = edits
17330        .into_iter()
17331        .map(|(range, edit)| {
17332            (
17333                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17334                edit,
17335            )
17336        })
17337        .collect::<Vec<_>>();
17338
17339    let text_anchor_edits = edits
17340        .clone()
17341        .into_iter()
17342        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17343        .collect::<Vec<_>>();
17344
17345    let edit_preview = window
17346        .update(cx, |_, _window, cx| {
17347            buffer
17348                .read(cx)
17349                .as_singleton()
17350                .unwrap()
17351                .read(cx)
17352                .preview_edits(text_anchor_edits.into(), cx)
17353        })
17354        .unwrap()
17355        .await;
17356
17357    cx.update(|_window, cx| {
17358        let highlighted_edits = inline_completion_edit_text(
17359            &snapshot.as_singleton().unwrap().2,
17360            &edits,
17361            &edit_preview,
17362            include_deletions,
17363            cx,
17364        );
17365        assertion_fn(highlighted_edits, cx)
17366    });
17367}
17368
17369#[track_caller]
17370fn assert_breakpoint(
17371    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17372    path: &Arc<Path>,
17373    expected: Vec<(u32, Breakpoint)>,
17374) {
17375    if expected.len() == 0usize {
17376        assert!(!breakpoints.contains_key(path), "{}", path.display());
17377    } else {
17378        let mut breakpoint = breakpoints
17379            .get(path)
17380            .unwrap()
17381            .into_iter()
17382            .map(|breakpoint| {
17383                (
17384                    breakpoint.row,
17385                    Breakpoint {
17386                        message: breakpoint.message.clone(),
17387                        state: breakpoint.state,
17388                    },
17389                )
17390            })
17391            .collect::<Vec<_>>();
17392
17393        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17394
17395        assert_eq!(expected, breakpoint);
17396    }
17397}
17398
17399fn add_log_breakpoint_at_cursor(
17400    editor: &mut Editor,
17401    log_message: &str,
17402    window: &mut Window,
17403    cx: &mut Context<Editor>,
17404) {
17405    let (anchor, bp) = editor
17406        .breakpoint_at_cursor_head(window, cx)
17407        .unwrap_or_else(|| {
17408            let cursor_position: Point = editor.selections.newest(cx).head();
17409
17410            let breakpoint_position = editor
17411                .snapshot(window, cx)
17412                .display_snapshot
17413                .buffer_snapshot
17414                .anchor_before(Point::new(cursor_position.row, 0));
17415
17416            (
17417                breakpoint_position,
17418                Breakpoint {
17419                    message: Some(Arc::from(log_message)),
17420                    state: BreakpointState::Enabled,
17421                },
17422            )
17423        });
17424
17425    editor.edit_breakpoint_at_anchor(
17426        anchor,
17427        bp,
17428        BreakpointEditAction::EditLogMessage(log_message.into()),
17429        cx,
17430    );
17431}
17432
17433#[gpui::test]
17434async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17435    init_test(cx, |_| {});
17436
17437    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17438    let fs = FakeFs::new(cx.executor());
17439    fs.insert_tree(
17440        path!("/a"),
17441        json!({
17442            "main.rs": sample_text,
17443        }),
17444    )
17445    .await;
17446    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17447    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17448    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17449
17450    let fs = FakeFs::new(cx.executor());
17451    fs.insert_tree(
17452        path!("/a"),
17453        json!({
17454            "main.rs": sample_text,
17455        }),
17456    )
17457    .await;
17458    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17459    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17460    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17461    let worktree_id = workspace
17462        .update(cx, |workspace, _window, cx| {
17463            workspace.project().update(cx, |project, cx| {
17464                project.worktrees(cx).next().unwrap().read(cx).id()
17465            })
17466        })
17467        .unwrap();
17468
17469    let buffer = project
17470        .update(cx, |project, cx| {
17471            project.open_buffer((worktree_id, "main.rs"), cx)
17472        })
17473        .await
17474        .unwrap();
17475
17476    let (editor, cx) = cx.add_window_view(|window, cx| {
17477        Editor::new(
17478            EditorMode::Full,
17479            MultiBuffer::build_from_buffer(buffer, cx),
17480            Some(project.clone()),
17481            window,
17482            cx,
17483        )
17484    });
17485
17486    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17487    let abs_path = project.read_with(cx, |project, cx| {
17488        project
17489            .absolute_path(&project_path, cx)
17490            .map(|path_buf| Arc::from(path_buf.to_owned()))
17491            .unwrap()
17492    });
17493
17494    // assert we can add breakpoint on the first line
17495    editor.update_in(cx, |editor, window, cx| {
17496        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17497        editor.move_to_end(&MoveToEnd, window, cx);
17498        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17499    });
17500
17501    let breakpoints = editor.update(cx, |editor, cx| {
17502        editor
17503            .breakpoint_store()
17504            .as_ref()
17505            .unwrap()
17506            .read(cx)
17507            .all_breakpoints(cx)
17508            .clone()
17509    });
17510
17511    assert_eq!(1, breakpoints.len());
17512    assert_breakpoint(
17513        &breakpoints,
17514        &abs_path,
17515        vec![
17516            (0, Breakpoint::new_standard()),
17517            (3, Breakpoint::new_standard()),
17518        ],
17519    );
17520
17521    editor.update_in(cx, |editor, window, cx| {
17522        editor.move_to_beginning(&MoveToBeginning, window, cx);
17523        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17524    });
17525
17526    let breakpoints = editor.update(cx, |editor, cx| {
17527        editor
17528            .breakpoint_store()
17529            .as_ref()
17530            .unwrap()
17531            .read(cx)
17532            .all_breakpoints(cx)
17533            .clone()
17534    });
17535
17536    assert_eq!(1, breakpoints.len());
17537    assert_breakpoint(
17538        &breakpoints,
17539        &abs_path,
17540        vec![(3, Breakpoint::new_standard())],
17541    );
17542
17543    editor.update_in(cx, |editor, window, cx| {
17544        editor.move_to_end(&MoveToEnd, window, cx);
17545        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17546    });
17547
17548    let breakpoints = editor.update(cx, |editor, cx| {
17549        editor
17550            .breakpoint_store()
17551            .as_ref()
17552            .unwrap()
17553            .read(cx)
17554            .all_breakpoints(cx)
17555            .clone()
17556    });
17557
17558    assert_eq!(0, breakpoints.len());
17559    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17560}
17561
17562#[gpui::test]
17563async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17564    init_test(cx, |_| {});
17565
17566    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17567
17568    let fs = FakeFs::new(cx.executor());
17569    fs.insert_tree(
17570        path!("/a"),
17571        json!({
17572            "main.rs": sample_text,
17573        }),
17574    )
17575    .await;
17576    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17577    let (workspace, cx) =
17578        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17579
17580    let worktree_id = workspace.update(cx, |workspace, cx| {
17581        workspace.project().update(cx, |project, cx| {
17582            project.worktrees(cx).next().unwrap().read(cx).id()
17583        })
17584    });
17585
17586    let buffer = project
17587        .update(cx, |project, cx| {
17588            project.open_buffer((worktree_id, "main.rs"), cx)
17589        })
17590        .await
17591        .unwrap();
17592
17593    let (editor, cx) = cx.add_window_view(|window, cx| {
17594        Editor::new(
17595            EditorMode::Full,
17596            MultiBuffer::build_from_buffer(buffer, cx),
17597            Some(project.clone()),
17598            window,
17599            cx,
17600        )
17601    });
17602
17603    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17604    let abs_path = project.read_with(cx, |project, cx| {
17605        project
17606            .absolute_path(&project_path, cx)
17607            .map(|path_buf| Arc::from(path_buf.to_owned()))
17608            .unwrap()
17609    });
17610
17611    editor.update_in(cx, |editor, window, cx| {
17612        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17613    });
17614
17615    let breakpoints = editor.update(cx, |editor, cx| {
17616        editor
17617            .breakpoint_store()
17618            .as_ref()
17619            .unwrap()
17620            .read(cx)
17621            .all_breakpoints(cx)
17622            .clone()
17623    });
17624
17625    assert_breakpoint(
17626        &breakpoints,
17627        &abs_path,
17628        vec![(0, Breakpoint::new_log("hello world"))],
17629    );
17630
17631    // Removing a log message from a log breakpoint should remove it
17632    editor.update_in(cx, |editor, window, cx| {
17633        add_log_breakpoint_at_cursor(editor, "", window, cx);
17634    });
17635
17636    let breakpoints = editor.update(cx, |editor, cx| {
17637        editor
17638            .breakpoint_store()
17639            .as_ref()
17640            .unwrap()
17641            .read(cx)
17642            .all_breakpoints(cx)
17643            .clone()
17644    });
17645
17646    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17647
17648    editor.update_in(cx, |editor, window, cx| {
17649        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17650        editor.move_to_end(&MoveToEnd, window, cx);
17651        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17652        // Not adding a log message to a standard breakpoint shouldn't remove it
17653        add_log_breakpoint_at_cursor(editor, "", window, cx);
17654    });
17655
17656    let breakpoints = editor.update(cx, |editor, cx| {
17657        editor
17658            .breakpoint_store()
17659            .as_ref()
17660            .unwrap()
17661            .read(cx)
17662            .all_breakpoints(cx)
17663            .clone()
17664    });
17665
17666    assert_breakpoint(
17667        &breakpoints,
17668        &abs_path,
17669        vec![
17670            (0, Breakpoint::new_standard()),
17671            (3, Breakpoint::new_standard()),
17672        ],
17673    );
17674
17675    editor.update_in(cx, |editor, window, cx| {
17676        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17677    });
17678
17679    let breakpoints = editor.update(cx, |editor, cx| {
17680        editor
17681            .breakpoint_store()
17682            .as_ref()
17683            .unwrap()
17684            .read(cx)
17685            .all_breakpoints(cx)
17686            .clone()
17687    });
17688
17689    assert_breakpoint(
17690        &breakpoints,
17691        &abs_path,
17692        vec![
17693            (0, Breakpoint::new_standard()),
17694            (3, Breakpoint::new_log("hello world")),
17695        ],
17696    );
17697
17698    editor.update_in(cx, |editor, window, cx| {
17699        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17700    });
17701
17702    let breakpoints = editor.update(cx, |editor, cx| {
17703        editor
17704            .breakpoint_store()
17705            .as_ref()
17706            .unwrap()
17707            .read(cx)
17708            .all_breakpoints(cx)
17709            .clone()
17710    });
17711
17712    assert_breakpoint(
17713        &breakpoints,
17714        &abs_path,
17715        vec![
17716            (0, Breakpoint::new_standard()),
17717            (3, Breakpoint::new_log("hello Earth!!")),
17718        ],
17719    );
17720}
17721
17722/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17723/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17724/// or when breakpoints were placed out of order. This tests for a regression too
17725#[gpui::test]
17726async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17727    init_test(cx, |_| {});
17728
17729    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17730    let fs = FakeFs::new(cx.executor());
17731    fs.insert_tree(
17732        path!("/a"),
17733        json!({
17734            "main.rs": sample_text,
17735        }),
17736    )
17737    .await;
17738    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17739    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17740    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17741
17742    let fs = FakeFs::new(cx.executor());
17743    fs.insert_tree(
17744        path!("/a"),
17745        json!({
17746            "main.rs": sample_text,
17747        }),
17748    )
17749    .await;
17750    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17751    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17752    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17753    let worktree_id = workspace
17754        .update(cx, |workspace, _window, cx| {
17755            workspace.project().update(cx, |project, cx| {
17756                project.worktrees(cx).next().unwrap().read(cx).id()
17757            })
17758        })
17759        .unwrap();
17760
17761    let buffer = project
17762        .update(cx, |project, cx| {
17763            project.open_buffer((worktree_id, "main.rs"), cx)
17764        })
17765        .await
17766        .unwrap();
17767
17768    let (editor, cx) = cx.add_window_view(|window, cx| {
17769        Editor::new(
17770            EditorMode::Full,
17771            MultiBuffer::build_from_buffer(buffer, cx),
17772            Some(project.clone()),
17773            window,
17774            cx,
17775        )
17776    });
17777
17778    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17779    let abs_path = project.read_with(cx, |project, cx| {
17780        project
17781            .absolute_path(&project_path, cx)
17782            .map(|path_buf| Arc::from(path_buf.to_owned()))
17783            .unwrap()
17784    });
17785
17786    // assert we can add breakpoint on the first line
17787    editor.update_in(cx, |editor, window, cx| {
17788        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17789        editor.move_to_end(&MoveToEnd, window, cx);
17790        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17791        editor.move_up(&MoveUp, window, cx);
17792        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17793    });
17794
17795    let breakpoints = editor.update(cx, |editor, cx| {
17796        editor
17797            .breakpoint_store()
17798            .as_ref()
17799            .unwrap()
17800            .read(cx)
17801            .all_breakpoints(cx)
17802            .clone()
17803    });
17804
17805    assert_eq!(1, breakpoints.len());
17806    assert_breakpoint(
17807        &breakpoints,
17808        &abs_path,
17809        vec![
17810            (0, Breakpoint::new_standard()),
17811            (2, Breakpoint::new_standard()),
17812            (3, Breakpoint::new_standard()),
17813        ],
17814    );
17815
17816    editor.update_in(cx, |editor, window, cx| {
17817        editor.move_to_beginning(&MoveToBeginning, window, cx);
17818        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17819        editor.move_to_end(&MoveToEnd, window, cx);
17820        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17821        // Disabling a breakpoint that doesn't exist should do nothing
17822        editor.move_up(&MoveUp, window, cx);
17823        editor.move_up(&MoveUp, window, cx);
17824        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17825    });
17826
17827    let breakpoints = editor.update(cx, |editor, cx| {
17828        editor
17829            .breakpoint_store()
17830            .as_ref()
17831            .unwrap()
17832            .read(cx)
17833            .all_breakpoints(cx)
17834            .clone()
17835    });
17836
17837    let disable_breakpoint = {
17838        let mut bp = Breakpoint::new_standard();
17839        bp.state = BreakpointState::Disabled;
17840        bp
17841    };
17842
17843    assert_eq!(1, breakpoints.len());
17844    assert_breakpoint(
17845        &breakpoints,
17846        &abs_path,
17847        vec![
17848            (0, disable_breakpoint.clone()),
17849            (2, Breakpoint::new_standard()),
17850            (3, disable_breakpoint.clone()),
17851        ],
17852    );
17853
17854    editor.update_in(cx, |editor, window, cx| {
17855        editor.move_to_beginning(&MoveToBeginning, window, cx);
17856        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17857        editor.move_to_end(&MoveToEnd, window, cx);
17858        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17859        editor.move_up(&MoveUp, window, cx);
17860        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17861    });
17862
17863    let breakpoints = editor.update(cx, |editor, cx| {
17864        editor
17865            .breakpoint_store()
17866            .as_ref()
17867            .unwrap()
17868            .read(cx)
17869            .all_breakpoints(cx)
17870            .clone()
17871    });
17872
17873    assert_eq!(1, breakpoints.len());
17874    assert_breakpoint(
17875        &breakpoints,
17876        &abs_path,
17877        vec![
17878            (0, Breakpoint::new_standard()),
17879            (2, disable_breakpoint),
17880            (3, Breakpoint::new_standard()),
17881        ],
17882    );
17883}
17884
17885#[gpui::test]
17886async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17887    init_test(cx, |_| {});
17888    let capabilities = lsp::ServerCapabilities {
17889        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17890            prepare_provider: Some(true),
17891            work_done_progress_options: Default::default(),
17892        })),
17893        ..Default::default()
17894    };
17895    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17896
17897    cx.set_state(indoc! {"
17898        struct Fˇoo {}
17899    "});
17900
17901    cx.update_editor(|editor, _, cx| {
17902        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17903        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17904        editor.highlight_background::<DocumentHighlightRead>(
17905            &[highlight_range],
17906            |c| c.editor_document_highlight_read_background,
17907            cx,
17908        );
17909    });
17910
17911    let mut prepare_rename_handler = cx
17912        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17913            move |_, _, _| async move {
17914                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17915                    start: lsp::Position {
17916                        line: 0,
17917                        character: 7,
17918                    },
17919                    end: lsp::Position {
17920                        line: 0,
17921                        character: 10,
17922                    },
17923                })))
17924            },
17925        );
17926    let prepare_rename_task = cx
17927        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17928        .expect("Prepare rename was not started");
17929    prepare_rename_handler.next().await.unwrap();
17930    prepare_rename_task.await.expect("Prepare rename failed");
17931
17932    let mut rename_handler =
17933        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17934            let edit = lsp::TextEdit {
17935                range: lsp::Range {
17936                    start: lsp::Position {
17937                        line: 0,
17938                        character: 7,
17939                    },
17940                    end: lsp::Position {
17941                        line: 0,
17942                        character: 10,
17943                    },
17944                },
17945                new_text: "FooRenamed".to_string(),
17946            };
17947            Ok(Some(lsp::WorkspaceEdit::new(
17948                // Specify the same edit twice
17949                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17950            )))
17951        });
17952    let rename_task = cx
17953        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17954        .expect("Confirm rename was not started");
17955    rename_handler.next().await.unwrap();
17956    rename_task.await.expect("Confirm rename failed");
17957    cx.run_until_parked();
17958
17959    // Despite two edits, only one is actually applied as those are identical
17960    cx.assert_editor_state(indoc! {"
17961        struct FooRenamedˇ {}
17962    "});
17963}
17964
17965#[gpui::test]
17966async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17967    init_test(cx, |_| {});
17968    // These capabilities indicate that the server does not support prepare rename.
17969    let capabilities = lsp::ServerCapabilities {
17970        rename_provider: Some(lsp::OneOf::Left(true)),
17971        ..Default::default()
17972    };
17973    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17974
17975    cx.set_state(indoc! {"
17976        struct Fˇoo {}
17977    "});
17978
17979    cx.update_editor(|editor, _window, cx| {
17980        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17981        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17982        editor.highlight_background::<DocumentHighlightRead>(
17983            &[highlight_range],
17984            |c| c.editor_document_highlight_read_background,
17985            cx,
17986        );
17987    });
17988
17989    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17990        .expect("Prepare rename was not started")
17991        .await
17992        .expect("Prepare rename failed");
17993
17994    let mut rename_handler =
17995        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17996            let edit = lsp::TextEdit {
17997                range: lsp::Range {
17998                    start: lsp::Position {
17999                        line: 0,
18000                        character: 7,
18001                    },
18002                    end: lsp::Position {
18003                        line: 0,
18004                        character: 10,
18005                    },
18006                },
18007                new_text: "FooRenamed".to_string(),
18008            };
18009            Ok(Some(lsp::WorkspaceEdit::new(
18010                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18011            )))
18012        });
18013    let rename_task = cx
18014        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18015        .expect("Confirm rename was not started");
18016    rename_handler.next().await.unwrap();
18017    rename_task.await.expect("Confirm rename failed");
18018    cx.run_until_parked();
18019
18020    // Correct range is renamed, as `surrounding_word` is used to find it.
18021    cx.assert_editor_state(indoc! {"
18022        struct FooRenamedˇ {}
18023    "});
18024}
18025
18026#[gpui::test]
18027async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18028    init_test(cx, |_| {});
18029    let mut cx = EditorTestContext::new(cx).await;
18030
18031    let language = Arc::new(
18032        Language::new(
18033            LanguageConfig::default(),
18034            Some(tree_sitter_html::LANGUAGE.into()),
18035        )
18036        .with_brackets_query(
18037            r#"
18038            ("<" @open "/>" @close)
18039            ("</" @open ">" @close)
18040            ("<" @open ">" @close)
18041            ("\"" @open "\"" @close)
18042            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18043        "#,
18044        )
18045        .unwrap(),
18046    );
18047    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18048
18049    cx.set_state(indoc! {"
18050        <span>ˇ</span>
18051    "});
18052    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18053    cx.assert_editor_state(indoc! {"
18054        <span>
18055        ˇ
18056        </span>
18057    "});
18058
18059    cx.set_state(indoc! {"
18060        <span><span></span>ˇ</span>
18061    "});
18062    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18063    cx.assert_editor_state(indoc! {"
18064        <span><span></span>
18065        ˇ</span>
18066    "});
18067
18068    cx.set_state(indoc! {"
18069        <span>ˇ
18070        </span>
18071    "});
18072    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18073    cx.assert_editor_state(indoc! {"
18074        <span>
18075        ˇ
18076        </span>
18077    "});
18078}
18079
18080#[gpui::test(iterations = 10)]
18081async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18082    init_test(cx, |_| {});
18083
18084    let fs = FakeFs::new(cx.executor());
18085    fs.insert_tree(
18086        path!("/dir"),
18087        json!({
18088            "a.ts": "a",
18089        }),
18090    )
18091    .await;
18092
18093    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18094    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18095    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18096
18097    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18098    language_registry.add(Arc::new(Language::new(
18099        LanguageConfig {
18100            name: "TypeScript".into(),
18101            matcher: LanguageMatcher {
18102                path_suffixes: vec!["ts".to_string()],
18103                ..Default::default()
18104            },
18105            ..Default::default()
18106        },
18107        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18108    )));
18109    let mut fake_language_servers = language_registry.register_fake_lsp(
18110        "TypeScript",
18111        FakeLspAdapter {
18112            capabilities: lsp::ServerCapabilities {
18113                code_lens_provider: Some(lsp::CodeLensOptions {
18114                    resolve_provider: Some(true),
18115                }),
18116                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18117                    commands: vec!["_the/command".to_string()],
18118                    ..lsp::ExecuteCommandOptions::default()
18119                }),
18120                ..lsp::ServerCapabilities::default()
18121            },
18122            ..FakeLspAdapter::default()
18123        },
18124    );
18125
18126    let (buffer, _handle) = project
18127        .update(cx, |p, cx| {
18128            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18129        })
18130        .await
18131        .unwrap();
18132    cx.executor().run_until_parked();
18133
18134    let fake_server = fake_language_servers.next().await.unwrap();
18135
18136    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18137    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18138    drop(buffer_snapshot);
18139    let actions = cx
18140        .update_window(*workspace, |_, window, cx| {
18141            project.code_actions(&buffer, anchor..anchor, window, cx)
18142        })
18143        .unwrap();
18144
18145    fake_server
18146        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18147            Ok(Some(vec![
18148                lsp::CodeLens {
18149                    range: lsp::Range::default(),
18150                    command: Some(lsp::Command {
18151                        title: "Code lens command".to_owned(),
18152                        command: "_the/command".to_owned(),
18153                        arguments: None,
18154                    }),
18155                    data: None,
18156                },
18157                lsp::CodeLens {
18158                    range: lsp::Range::default(),
18159                    command: Some(lsp::Command {
18160                        title: "Command not in capabilities".to_owned(),
18161                        command: "not in capabilities".to_owned(),
18162                        arguments: None,
18163                    }),
18164                    data: None,
18165                },
18166                lsp::CodeLens {
18167                    range: lsp::Range {
18168                        start: lsp::Position {
18169                            line: 1,
18170                            character: 1,
18171                        },
18172                        end: lsp::Position {
18173                            line: 1,
18174                            character: 1,
18175                        },
18176                    },
18177                    command: Some(lsp::Command {
18178                        title: "Command not in range".to_owned(),
18179                        command: "_the/command".to_owned(),
18180                        arguments: None,
18181                    }),
18182                    data: None,
18183                },
18184            ]))
18185        })
18186        .next()
18187        .await;
18188
18189    let actions = actions.await.unwrap();
18190    assert_eq!(
18191        actions.len(),
18192        1,
18193        "Should have only one valid action for the 0..0 range"
18194    );
18195    let action = actions[0].clone();
18196    let apply = project.update(cx, |project, cx| {
18197        project.apply_code_action(buffer.clone(), action, true, cx)
18198    });
18199
18200    // Resolving the code action does not populate its edits. In absence of
18201    // edits, we must execute the given command.
18202    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18203        |mut lens, _| async move {
18204            let lens_command = lens.command.as_mut().expect("should have a command");
18205            assert_eq!(lens_command.title, "Code lens command");
18206            lens_command.arguments = Some(vec![json!("the-argument")]);
18207            Ok(lens)
18208        },
18209    );
18210
18211    // While executing the command, the language server sends the editor
18212    // a `workspaceEdit` request.
18213    fake_server
18214        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18215            let fake = fake_server.clone();
18216            move |params, _| {
18217                assert_eq!(params.command, "_the/command");
18218                let fake = fake.clone();
18219                async move {
18220                    fake.server
18221                        .request::<lsp::request::ApplyWorkspaceEdit>(
18222                            lsp::ApplyWorkspaceEditParams {
18223                                label: None,
18224                                edit: lsp::WorkspaceEdit {
18225                                    changes: Some(
18226                                        [(
18227                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18228                                            vec![lsp::TextEdit {
18229                                                range: lsp::Range::new(
18230                                                    lsp::Position::new(0, 0),
18231                                                    lsp::Position::new(0, 0),
18232                                                ),
18233                                                new_text: "X".into(),
18234                                            }],
18235                                        )]
18236                                        .into_iter()
18237                                        .collect(),
18238                                    ),
18239                                    ..Default::default()
18240                                },
18241                            },
18242                        )
18243                        .await
18244                        .unwrap();
18245                    Ok(Some(json!(null)))
18246                }
18247            }
18248        })
18249        .next()
18250        .await;
18251
18252    // Applying the code lens command returns a project transaction containing the edits
18253    // sent by the language server in its `workspaceEdit` request.
18254    let transaction = apply.await.unwrap();
18255    assert!(transaction.0.contains_key(&buffer));
18256    buffer.update(cx, |buffer, cx| {
18257        assert_eq!(buffer.text(), "Xa");
18258        buffer.undo(cx);
18259        assert_eq!(buffer.text(), "a");
18260    });
18261}
18262
18263#[gpui::test]
18264async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18265    init_test(cx, |_| {});
18266
18267    let fs = FakeFs::new(cx.executor());
18268    let main_text = r#"fn main() {
18269println!("1");
18270println!("2");
18271println!("3");
18272println!("4");
18273println!("5");
18274}"#;
18275    let lib_text = "mod foo {}";
18276    fs.insert_tree(
18277        path!("/a"),
18278        json!({
18279            "lib.rs": lib_text,
18280            "main.rs": main_text,
18281        }),
18282    )
18283    .await;
18284
18285    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18286    let (workspace, cx) =
18287        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18288    let worktree_id = workspace.update(cx, |workspace, cx| {
18289        workspace.project().update(cx, |project, cx| {
18290            project.worktrees(cx).next().unwrap().read(cx).id()
18291        })
18292    });
18293
18294    let expected_ranges = vec![
18295        Point::new(0, 0)..Point::new(0, 0),
18296        Point::new(1, 0)..Point::new(1, 1),
18297        Point::new(2, 0)..Point::new(2, 2),
18298        Point::new(3, 0)..Point::new(3, 3),
18299    ];
18300
18301    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18302    let editor_1 = workspace
18303        .update_in(cx, |workspace, window, cx| {
18304            workspace.open_path(
18305                (worktree_id, "main.rs"),
18306                Some(pane_1.downgrade()),
18307                true,
18308                window,
18309                cx,
18310            )
18311        })
18312        .unwrap()
18313        .await
18314        .downcast::<Editor>()
18315        .unwrap();
18316    pane_1.update(cx, |pane, cx| {
18317        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18318        open_editor.update(cx, |editor, cx| {
18319            assert_eq!(
18320                editor.display_text(cx),
18321                main_text,
18322                "Original main.rs text on initial open",
18323            );
18324            assert_eq!(
18325                editor
18326                    .selections
18327                    .all::<Point>(cx)
18328                    .into_iter()
18329                    .map(|s| s.range())
18330                    .collect::<Vec<_>>(),
18331                vec![Point::zero()..Point::zero()],
18332                "Default selections on initial open",
18333            );
18334        })
18335    });
18336    editor_1.update_in(cx, |editor, window, cx| {
18337        editor.change_selections(None, window, cx, |s| {
18338            s.select_ranges(expected_ranges.clone());
18339        });
18340    });
18341
18342    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18343        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18344    });
18345    let editor_2 = workspace
18346        .update_in(cx, |workspace, window, cx| {
18347            workspace.open_path(
18348                (worktree_id, "main.rs"),
18349                Some(pane_2.downgrade()),
18350                true,
18351                window,
18352                cx,
18353            )
18354        })
18355        .unwrap()
18356        .await
18357        .downcast::<Editor>()
18358        .unwrap();
18359    pane_2.update(cx, |pane, cx| {
18360        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18361        open_editor.update(cx, |editor, cx| {
18362            assert_eq!(
18363                editor.display_text(cx),
18364                main_text,
18365                "Original main.rs text on initial open in another panel",
18366            );
18367            assert_eq!(
18368                editor
18369                    .selections
18370                    .all::<Point>(cx)
18371                    .into_iter()
18372                    .map(|s| s.range())
18373                    .collect::<Vec<_>>(),
18374                vec![Point::zero()..Point::zero()],
18375                "Default selections on initial open in another panel",
18376            );
18377        })
18378    });
18379
18380    editor_2.update_in(cx, |editor, window, cx| {
18381        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18382    });
18383
18384    let _other_editor_1 = workspace
18385        .update_in(cx, |workspace, window, cx| {
18386            workspace.open_path(
18387                (worktree_id, "lib.rs"),
18388                Some(pane_1.downgrade()),
18389                true,
18390                window,
18391                cx,
18392            )
18393        })
18394        .unwrap()
18395        .await
18396        .downcast::<Editor>()
18397        .unwrap();
18398    pane_1
18399        .update_in(cx, |pane, window, cx| {
18400            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18401                .unwrap()
18402        })
18403        .await
18404        .unwrap();
18405    drop(editor_1);
18406    pane_1.update(cx, |pane, cx| {
18407        pane.active_item()
18408            .unwrap()
18409            .downcast::<Editor>()
18410            .unwrap()
18411            .update(cx, |editor, cx| {
18412                assert_eq!(
18413                    editor.display_text(cx),
18414                    lib_text,
18415                    "Other file should be open and active",
18416                );
18417            });
18418        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18419    });
18420
18421    let _other_editor_2 = workspace
18422        .update_in(cx, |workspace, window, cx| {
18423            workspace.open_path(
18424                (worktree_id, "lib.rs"),
18425                Some(pane_2.downgrade()),
18426                true,
18427                window,
18428                cx,
18429            )
18430        })
18431        .unwrap()
18432        .await
18433        .downcast::<Editor>()
18434        .unwrap();
18435    pane_2
18436        .update_in(cx, |pane, window, cx| {
18437            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18438                .unwrap()
18439        })
18440        .await
18441        .unwrap();
18442    drop(editor_2);
18443    pane_2.update(cx, |pane, cx| {
18444        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18445        open_editor.update(cx, |editor, cx| {
18446            assert_eq!(
18447                editor.display_text(cx),
18448                lib_text,
18449                "Other file should be open and active in another panel too",
18450            );
18451        });
18452        assert_eq!(
18453            pane.items().count(),
18454            1,
18455            "No other editors should be open in another pane",
18456        );
18457    });
18458
18459    let _editor_1_reopened = workspace
18460        .update_in(cx, |workspace, window, cx| {
18461            workspace.open_path(
18462                (worktree_id, "main.rs"),
18463                Some(pane_1.downgrade()),
18464                true,
18465                window,
18466                cx,
18467            )
18468        })
18469        .unwrap()
18470        .await
18471        .downcast::<Editor>()
18472        .unwrap();
18473    let _editor_2_reopened = workspace
18474        .update_in(cx, |workspace, window, cx| {
18475            workspace.open_path(
18476                (worktree_id, "main.rs"),
18477                Some(pane_2.downgrade()),
18478                true,
18479                window,
18480                cx,
18481            )
18482        })
18483        .unwrap()
18484        .await
18485        .downcast::<Editor>()
18486        .unwrap();
18487    pane_1.update(cx, |pane, cx| {
18488        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18489        open_editor.update(cx, |editor, cx| {
18490            assert_eq!(
18491                editor.display_text(cx),
18492                main_text,
18493                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18494            );
18495            assert_eq!(
18496                editor
18497                    .selections
18498                    .all::<Point>(cx)
18499                    .into_iter()
18500                    .map(|s| s.range())
18501                    .collect::<Vec<_>>(),
18502                expected_ranges,
18503                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18504            );
18505        })
18506    });
18507    pane_2.update(cx, |pane, cx| {
18508        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18509        open_editor.update(cx, |editor, cx| {
18510            assert_eq!(
18511                editor.display_text(cx),
18512                r#"fn main() {
18513⋯rintln!("1");
18514⋯intln!("2");
18515⋯ntln!("3");
18516println!("4");
18517println!("5");
18518}"#,
18519                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18520            );
18521            assert_eq!(
18522                editor
18523                    .selections
18524                    .all::<Point>(cx)
18525                    .into_iter()
18526                    .map(|s| s.range())
18527                    .collect::<Vec<_>>(),
18528                vec![Point::zero()..Point::zero()],
18529                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18530            );
18531        })
18532    });
18533}
18534
18535#[gpui::test]
18536async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18537    init_test(cx, |_| {});
18538
18539    let fs = FakeFs::new(cx.executor());
18540    let main_text = r#"fn main() {
18541println!("1");
18542println!("2");
18543println!("3");
18544println!("4");
18545println!("5");
18546}"#;
18547    let lib_text = "mod foo {}";
18548    fs.insert_tree(
18549        path!("/a"),
18550        json!({
18551            "lib.rs": lib_text,
18552            "main.rs": main_text,
18553        }),
18554    )
18555    .await;
18556
18557    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18558    let (workspace, cx) =
18559        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18560    let worktree_id = workspace.update(cx, |workspace, cx| {
18561        workspace.project().update(cx, |project, cx| {
18562            project.worktrees(cx).next().unwrap().read(cx).id()
18563        })
18564    });
18565
18566    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18567    let editor = workspace
18568        .update_in(cx, |workspace, window, cx| {
18569            workspace.open_path(
18570                (worktree_id, "main.rs"),
18571                Some(pane.downgrade()),
18572                true,
18573                window,
18574                cx,
18575            )
18576        })
18577        .unwrap()
18578        .await
18579        .downcast::<Editor>()
18580        .unwrap();
18581    pane.update(cx, |pane, cx| {
18582        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18583        open_editor.update(cx, |editor, cx| {
18584            assert_eq!(
18585                editor.display_text(cx),
18586                main_text,
18587                "Original main.rs text on initial open",
18588            );
18589        })
18590    });
18591    editor.update_in(cx, |editor, window, cx| {
18592        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18593    });
18594
18595    cx.update_global(|store: &mut SettingsStore, cx| {
18596        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18597            s.restore_on_file_reopen = Some(false);
18598        });
18599    });
18600    editor.update_in(cx, |editor, window, cx| {
18601        editor.fold_ranges(
18602            vec![
18603                Point::new(1, 0)..Point::new(1, 1),
18604                Point::new(2, 0)..Point::new(2, 2),
18605                Point::new(3, 0)..Point::new(3, 3),
18606            ],
18607            false,
18608            window,
18609            cx,
18610        );
18611    });
18612    pane.update_in(cx, |pane, window, cx| {
18613        pane.close_all_items(&CloseAllItems::default(), window, cx)
18614            .unwrap()
18615    })
18616    .await
18617    .unwrap();
18618    pane.update(cx, |pane, _| {
18619        assert!(pane.active_item().is_none());
18620    });
18621    cx.update_global(|store: &mut SettingsStore, cx| {
18622        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18623            s.restore_on_file_reopen = Some(true);
18624        });
18625    });
18626
18627    let _editor_reopened = workspace
18628        .update_in(cx, |workspace, window, cx| {
18629            workspace.open_path(
18630                (worktree_id, "main.rs"),
18631                Some(pane.downgrade()),
18632                true,
18633                window,
18634                cx,
18635            )
18636        })
18637        .unwrap()
18638        .await
18639        .downcast::<Editor>()
18640        .unwrap();
18641    pane.update(cx, |pane, cx| {
18642        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18643        open_editor.update(cx, |editor, cx| {
18644            assert_eq!(
18645                editor.display_text(cx),
18646                main_text,
18647                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18648            );
18649        })
18650    });
18651}
18652
18653fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18654    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18655    point..point
18656}
18657
18658fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18659    let (text, ranges) = marked_text_ranges(marked_text, true);
18660    assert_eq!(editor.text(cx), text);
18661    assert_eq!(
18662        editor.selections.ranges(cx),
18663        ranges,
18664        "Assert selections are {}",
18665        marked_text
18666    );
18667}
18668
18669pub fn handle_signature_help_request(
18670    cx: &mut EditorLspTestContext,
18671    mocked_response: lsp::SignatureHelp,
18672) -> impl Future<Output = ()> {
18673    let mut request =
18674        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18675            let mocked_response = mocked_response.clone();
18676            async move { Ok(Some(mocked_response)) }
18677        });
18678
18679    async move {
18680        request.next().await;
18681    }
18682}
18683
18684/// Handle completion request passing a marked string specifying where the completion
18685/// should be triggered from using '|' character, what range should be replaced, and what completions
18686/// should be returned using '<' and '>' to delimit the range
18687pub fn handle_completion_request(
18688    cx: &mut EditorLspTestContext,
18689    marked_string: &str,
18690    completions: Vec<&'static str>,
18691    counter: Arc<AtomicUsize>,
18692) -> impl Future<Output = ()> {
18693    let complete_from_marker: TextRangeMarker = '|'.into();
18694    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18695    let (_, mut marked_ranges) = marked_text_ranges_by(
18696        marked_string,
18697        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18698    );
18699
18700    let complete_from_position =
18701        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18702    let replace_range =
18703        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18704
18705    let mut request =
18706        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18707            let completions = completions.clone();
18708            counter.fetch_add(1, atomic::Ordering::Release);
18709            async move {
18710                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18711                assert_eq!(
18712                    params.text_document_position.position,
18713                    complete_from_position
18714                );
18715                Ok(Some(lsp::CompletionResponse::Array(
18716                    completions
18717                        .iter()
18718                        .map(|completion_text| lsp::CompletionItem {
18719                            label: completion_text.to_string(),
18720                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18721                                range: replace_range,
18722                                new_text: completion_text.to_string(),
18723                            })),
18724                            ..Default::default()
18725                        })
18726                        .collect(),
18727                )))
18728            }
18729        });
18730
18731    async move {
18732        request.next().await;
18733    }
18734}
18735
18736fn handle_resolve_completion_request(
18737    cx: &mut EditorLspTestContext,
18738    edits: Option<Vec<(&'static str, &'static str)>>,
18739) -> impl Future<Output = ()> {
18740    let edits = edits.map(|edits| {
18741        edits
18742            .iter()
18743            .map(|(marked_string, new_text)| {
18744                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18745                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18746                lsp::TextEdit::new(replace_range, new_text.to_string())
18747            })
18748            .collect::<Vec<_>>()
18749    });
18750
18751    let mut request =
18752        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18753            let edits = edits.clone();
18754            async move {
18755                Ok(lsp::CompletionItem {
18756                    additional_text_edits: edits,
18757                    ..Default::default()
18758                })
18759            }
18760        });
18761
18762    async move {
18763        request.next().await;
18764    }
18765}
18766
18767pub(crate) fn update_test_language_settings(
18768    cx: &mut TestAppContext,
18769    f: impl Fn(&mut AllLanguageSettingsContent),
18770) {
18771    cx.update(|cx| {
18772        SettingsStore::update_global(cx, |store, cx| {
18773            store.update_user_settings::<AllLanguageSettings>(cx, f);
18774        });
18775    });
18776}
18777
18778pub(crate) fn update_test_project_settings(
18779    cx: &mut TestAppContext,
18780    f: impl Fn(&mut ProjectSettings),
18781) {
18782    cx.update(|cx| {
18783        SettingsStore::update_global(cx, |store, cx| {
18784            store.update_user_settings::<ProjectSettings>(cx, f);
18785        });
18786    });
18787}
18788
18789pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18790    cx.update(|cx| {
18791        assets::Assets.load_test_fonts(cx);
18792        let store = SettingsStore::test(cx);
18793        cx.set_global(store);
18794        theme::init(theme::LoadThemes::JustBase, cx);
18795        release_channel::init(SemanticVersion::default(), cx);
18796        client::init_settings(cx);
18797        language::init(cx);
18798        Project::init_settings(cx);
18799        workspace::init_settings(cx);
18800        crate::init(cx);
18801    });
18802
18803    update_test_language_settings(cx, f);
18804}
18805
18806#[track_caller]
18807fn assert_hunk_revert(
18808    not_reverted_text_with_selections: &str,
18809    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18810    expected_reverted_text_with_selections: &str,
18811    base_text: &str,
18812    cx: &mut EditorLspTestContext,
18813) {
18814    cx.set_state(not_reverted_text_with_selections);
18815    cx.set_head_text(base_text);
18816    cx.executor().run_until_parked();
18817
18818    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18819        let snapshot = editor.snapshot(window, cx);
18820        let reverted_hunk_statuses = snapshot
18821            .buffer_snapshot
18822            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18823            .map(|hunk| hunk.status().kind)
18824            .collect::<Vec<_>>();
18825
18826        editor.git_restore(&Default::default(), window, cx);
18827        reverted_hunk_statuses
18828    });
18829    cx.executor().run_until_parked();
18830    cx.assert_editor_state(expected_reverted_text_with_selections);
18831    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18832}