editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace(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!(
 7989            "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}"
 7990        ),
 7991    );
 7992    buffer_1.update(cx, |buffer, _| {
 7993        assert!(!buffer.is_dirty());
 7994        assert_eq!(
 7995            buffer.text(),
 7996            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7997        )
 7998    });
 7999    buffer_2.update(cx, |buffer, _| {
 8000        assert!(!buffer.is_dirty());
 8001        assert_eq!(
 8002            buffer.text(),
 8003            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8004        )
 8005    });
 8006    buffer_3.update(cx, |buffer, _| {
 8007        assert!(!buffer.is_dirty());
 8008        assert_eq!(buffer.text(), sample_text_3,)
 8009    });
 8010}
 8011
 8012#[gpui::test]
 8013async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8014    init_test(cx, |_| {});
 8015
 8016    let fs = FakeFs::new(cx.executor());
 8017    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8018
 8019    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8020
 8021    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8022    language_registry.add(rust_lang());
 8023    let mut fake_servers = language_registry.register_fake_lsp(
 8024        "Rust",
 8025        FakeLspAdapter {
 8026            capabilities: lsp::ServerCapabilities {
 8027                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8028                ..Default::default()
 8029            },
 8030            ..Default::default()
 8031        },
 8032    );
 8033
 8034    let buffer = project
 8035        .update(cx, |project, cx| {
 8036            project.open_local_buffer(path!("/file.rs"), cx)
 8037        })
 8038        .await
 8039        .unwrap();
 8040
 8041    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8042    let (editor, cx) = cx.add_window_view(|window, cx| {
 8043        build_editor_with_project(project.clone(), buffer, window, cx)
 8044    });
 8045    editor.update_in(cx, |editor, window, cx| {
 8046        editor.set_text("one\ntwo\nthree\n", window, cx)
 8047    });
 8048    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8049
 8050    cx.executor().start_waiting();
 8051    let fake_server = fake_servers.next().await.unwrap();
 8052
 8053    let save = editor
 8054        .update_in(cx, |editor, window, cx| {
 8055            editor.save(true, project.clone(), window, cx)
 8056        })
 8057        .unwrap();
 8058    fake_server
 8059        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8060            assert_eq!(
 8061                params.text_document.uri,
 8062                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8063            );
 8064            assert_eq!(params.options.tab_size, 4);
 8065            Ok(Some(vec![lsp::TextEdit::new(
 8066                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8067                ", ".to_string(),
 8068            )]))
 8069        })
 8070        .next()
 8071        .await;
 8072    cx.executor().start_waiting();
 8073    save.await;
 8074    assert_eq!(
 8075        editor.update(cx, |editor, cx| editor.text(cx)),
 8076        "one, two\nthree\n"
 8077    );
 8078    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8079
 8080    editor.update_in(cx, |editor, window, cx| {
 8081        editor.set_text("one\ntwo\nthree\n", window, cx)
 8082    });
 8083    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8084
 8085    // Ensure we can still save even if formatting hangs.
 8086    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8087        move |params, _| async move {
 8088            assert_eq!(
 8089                params.text_document.uri,
 8090                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8091            );
 8092            futures::future::pending::<()>().await;
 8093            unreachable!()
 8094        },
 8095    );
 8096    let save = editor
 8097        .update_in(cx, |editor, window, cx| {
 8098            editor.save(true, project.clone(), window, cx)
 8099        })
 8100        .unwrap();
 8101    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8102    cx.executor().start_waiting();
 8103    save.await;
 8104    assert_eq!(
 8105        editor.update(cx, |editor, cx| editor.text(cx)),
 8106        "one\ntwo\nthree\n"
 8107    );
 8108    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8109
 8110    // For non-dirty buffer, no formatting request should be sent
 8111    let save = editor
 8112        .update_in(cx, |editor, window, cx| {
 8113            editor.save(true, project.clone(), window, cx)
 8114        })
 8115        .unwrap();
 8116    let _pending_format_request = fake_server
 8117        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8118            panic!("Should not be invoked on non-dirty buffer");
 8119        })
 8120        .next();
 8121    cx.executor().start_waiting();
 8122    save.await;
 8123
 8124    // Set Rust language override and assert overridden tabsize is sent to language server
 8125    update_test_language_settings(cx, |settings| {
 8126        settings.languages.insert(
 8127            "Rust".into(),
 8128            LanguageSettingsContent {
 8129                tab_size: NonZeroU32::new(8),
 8130                ..Default::default()
 8131            },
 8132        );
 8133    });
 8134
 8135    editor.update_in(cx, |editor, window, cx| {
 8136        editor.set_text("somehting_new\n", window, cx)
 8137    });
 8138    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8139    let save = editor
 8140        .update_in(cx, |editor, window, cx| {
 8141            editor.save(true, project.clone(), window, cx)
 8142        })
 8143        .unwrap();
 8144    fake_server
 8145        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8146            assert_eq!(
 8147                params.text_document.uri,
 8148                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8149            );
 8150            assert_eq!(params.options.tab_size, 8);
 8151            Ok(Some(vec![]))
 8152        })
 8153        .next()
 8154        .await;
 8155    cx.executor().start_waiting();
 8156    save.await;
 8157}
 8158
 8159#[gpui::test]
 8160async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8161    init_test(cx, |settings| {
 8162        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8163            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8164        ))
 8165    });
 8166
 8167    let fs = FakeFs::new(cx.executor());
 8168    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8169
 8170    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8171
 8172    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8173    language_registry.add(Arc::new(Language::new(
 8174        LanguageConfig {
 8175            name: "Rust".into(),
 8176            matcher: LanguageMatcher {
 8177                path_suffixes: vec!["rs".to_string()],
 8178                ..Default::default()
 8179            },
 8180            ..LanguageConfig::default()
 8181        },
 8182        Some(tree_sitter_rust::LANGUAGE.into()),
 8183    )));
 8184    update_test_language_settings(cx, |settings| {
 8185        // Enable Prettier formatting for the same buffer, and ensure
 8186        // LSP is called instead of Prettier.
 8187        settings.defaults.prettier = Some(PrettierSettings {
 8188            allowed: true,
 8189            ..PrettierSettings::default()
 8190        });
 8191    });
 8192    let mut fake_servers = language_registry.register_fake_lsp(
 8193        "Rust",
 8194        FakeLspAdapter {
 8195            capabilities: lsp::ServerCapabilities {
 8196                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8197                ..Default::default()
 8198            },
 8199            ..Default::default()
 8200        },
 8201    );
 8202
 8203    let buffer = project
 8204        .update(cx, |project, cx| {
 8205            project.open_local_buffer(path!("/file.rs"), cx)
 8206        })
 8207        .await
 8208        .unwrap();
 8209
 8210    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8211    let (editor, cx) = cx.add_window_view(|window, cx| {
 8212        build_editor_with_project(project.clone(), buffer, window, cx)
 8213    });
 8214    editor.update_in(cx, |editor, window, cx| {
 8215        editor.set_text("one\ntwo\nthree\n", window, cx)
 8216    });
 8217
 8218    cx.executor().start_waiting();
 8219    let fake_server = fake_servers.next().await.unwrap();
 8220
 8221    let format = editor
 8222        .update_in(cx, |editor, window, cx| {
 8223            editor.perform_format(
 8224                project.clone(),
 8225                FormatTrigger::Manual,
 8226                FormatTarget::Buffers,
 8227                window,
 8228                cx,
 8229            )
 8230        })
 8231        .unwrap();
 8232    fake_server
 8233        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8234            assert_eq!(
 8235                params.text_document.uri,
 8236                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8237            );
 8238            assert_eq!(params.options.tab_size, 4);
 8239            Ok(Some(vec![lsp::TextEdit::new(
 8240                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8241                ", ".to_string(),
 8242            )]))
 8243        })
 8244        .next()
 8245        .await;
 8246    cx.executor().start_waiting();
 8247    format.await;
 8248    assert_eq!(
 8249        editor.update(cx, |editor, cx| editor.text(cx)),
 8250        "one, two\nthree\n"
 8251    );
 8252
 8253    editor.update_in(cx, |editor, window, cx| {
 8254        editor.set_text("one\ntwo\nthree\n", window, cx)
 8255    });
 8256    // Ensure we don't lock if formatting hangs.
 8257    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8258        move |params, _| async move {
 8259            assert_eq!(
 8260                params.text_document.uri,
 8261                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8262            );
 8263            futures::future::pending::<()>().await;
 8264            unreachable!()
 8265        },
 8266    );
 8267    let format = editor
 8268        .update_in(cx, |editor, window, cx| {
 8269            editor.perform_format(
 8270                project,
 8271                FormatTrigger::Manual,
 8272                FormatTarget::Buffers,
 8273                window,
 8274                cx,
 8275            )
 8276        })
 8277        .unwrap();
 8278    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8279    cx.executor().start_waiting();
 8280    format.await;
 8281    assert_eq!(
 8282        editor.update(cx, |editor, cx| editor.text(cx)),
 8283        "one\ntwo\nthree\n"
 8284    );
 8285}
 8286
 8287#[gpui::test]
 8288async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8289    init_test(cx, |settings| {
 8290        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8291            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8292        ))
 8293    });
 8294
 8295    let fs = FakeFs::new(cx.executor());
 8296    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8297
 8298    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8299
 8300    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8301    language_registry.add(Arc::new(Language::new(
 8302        LanguageConfig {
 8303            name: "TypeScript".into(),
 8304            matcher: LanguageMatcher {
 8305                path_suffixes: vec!["ts".to_string()],
 8306                ..Default::default()
 8307            },
 8308            ..LanguageConfig::default()
 8309        },
 8310        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8311    )));
 8312    update_test_language_settings(cx, |settings| {
 8313        settings.defaults.prettier = Some(PrettierSettings {
 8314            allowed: true,
 8315            ..PrettierSettings::default()
 8316        });
 8317    });
 8318    let mut fake_servers = language_registry.register_fake_lsp(
 8319        "TypeScript",
 8320        FakeLspAdapter {
 8321            capabilities: lsp::ServerCapabilities {
 8322                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8323                ..Default::default()
 8324            },
 8325            ..Default::default()
 8326        },
 8327    );
 8328
 8329    let buffer = project
 8330        .update(cx, |project, cx| {
 8331            project.open_local_buffer(path!("/file.ts"), cx)
 8332        })
 8333        .await
 8334        .unwrap();
 8335
 8336    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8337    let (editor, cx) = cx.add_window_view(|window, cx| {
 8338        build_editor_with_project(project.clone(), buffer, window, cx)
 8339    });
 8340    editor.update_in(cx, |editor, window, cx| {
 8341        editor.set_text(
 8342            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8343            window,
 8344            cx,
 8345        )
 8346    });
 8347
 8348    cx.executor().start_waiting();
 8349    let fake_server = fake_servers.next().await.unwrap();
 8350
 8351    let format = editor
 8352        .update_in(cx, |editor, window, cx| {
 8353            editor.perform_code_action_kind(
 8354                project.clone(),
 8355                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8356                window,
 8357                cx,
 8358            )
 8359        })
 8360        .unwrap();
 8361    fake_server
 8362        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8363            assert_eq!(
 8364                params.text_document.uri,
 8365                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8366            );
 8367            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8368                lsp::CodeAction {
 8369                    title: "Organize Imports".to_string(),
 8370                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8371                    edit: Some(lsp::WorkspaceEdit {
 8372                        changes: Some(
 8373                            [(
 8374                                params.text_document.uri.clone(),
 8375                                vec![lsp::TextEdit::new(
 8376                                    lsp::Range::new(
 8377                                        lsp::Position::new(1, 0),
 8378                                        lsp::Position::new(2, 0),
 8379                                    ),
 8380                                    "".to_string(),
 8381                                )],
 8382                            )]
 8383                            .into_iter()
 8384                            .collect(),
 8385                        ),
 8386                        ..Default::default()
 8387                    }),
 8388                    ..Default::default()
 8389                },
 8390            )]))
 8391        })
 8392        .next()
 8393        .await;
 8394    cx.executor().start_waiting();
 8395    format.await;
 8396    assert_eq!(
 8397        editor.update(cx, |editor, cx| editor.text(cx)),
 8398        "import { a } from 'module';\n\nconst x = a;\n"
 8399    );
 8400
 8401    editor.update_in(cx, |editor, window, cx| {
 8402        editor.set_text(
 8403            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8404            window,
 8405            cx,
 8406        )
 8407    });
 8408    // Ensure we don't lock if code action hangs.
 8409    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8410        move |params, _| async move {
 8411            assert_eq!(
 8412                params.text_document.uri,
 8413                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8414            );
 8415            futures::future::pending::<()>().await;
 8416            unreachable!()
 8417        },
 8418    );
 8419    let format = editor
 8420        .update_in(cx, |editor, window, cx| {
 8421            editor.perform_code_action_kind(
 8422                project,
 8423                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8424                window,
 8425                cx,
 8426            )
 8427        })
 8428        .unwrap();
 8429    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8430    cx.executor().start_waiting();
 8431    format.await;
 8432    assert_eq!(
 8433        editor.update(cx, |editor, cx| editor.text(cx)),
 8434        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8435    );
 8436}
 8437
 8438#[gpui::test]
 8439async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8440    init_test(cx, |_| {});
 8441
 8442    let mut cx = EditorLspTestContext::new_rust(
 8443        lsp::ServerCapabilities {
 8444            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8445            ..Default::default()
 8446        },
 8447        cx,
 8448    )
 8449    .await;
 8450
 8451    cx.set_state(indoc! {"
 8452        one.twoˇ
 8453    "});
 8454
 8455    // The format request takes a long time. When it completes, it inserts
 8456    // a newline and an indent before the `.`
 8457    cx.lsp
 8458        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8459            let executor = cx.background_executor().clone();
 8460            async move {
 8461                executor.timer(Duration::from_millis(100)).await;
 8462                Ok(Some(vec![lsp::TextEdit {
 8463                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8464                    new_text: "\n    ".into(),
 8465                }]))
 8466            }
 8467        });
 8468
 8469    // Submit a format request.
 8470    let format_1 = cx
 8471        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8472        .unwrap();
 8473    cx.executor().run_until_parked();
 8474
 8475    // Submit a second format request.
 8476    let format_2 = cx
 8477        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8478        .unwrap();
 8479    cx.executor().run_until_parked();
 8480
 8481    // Wait for both format requests to complete
 8482    cx.executor().advance_clock(Duration::from_millis(200));
 8483    cx.executor().start_waiting();
 8484    format_1.await.unwrap();
 8485    cx.executor().start_waiting();
 8486    format_2.await.unwrap();
 8487
 8488    // The formatting edits only happens once.
 8489    cx.assert_editor_state(indoc! {"
 8490        one
 8491            .twoˇ
 8492    "});
 8493}
 8494
 8495#[gpui::test]
 8496async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8497    init_test(cx, |settings| {
 8498        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8499    });
 8500
 8501    let mut cx = EditorLspTestContext::new_rust(
 8502        lsp::ServerCapabilities {
 8503            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8504            ..Default::default()
 8505        },
 8506        cx,
 8507    )
 8508    .await;
 8509
 8510    // Set up a buffer white some trailing whitespace and no trailing newline.
 8511    cx.set_state(
 8512        &[
 8513            "one ",   //
 8514            "twoˇ",   //
 8515            "three ", //
 8516            "four",   //
 8517        ]
 8518        .join("\n"),
 8519    );
 8520
 8521    // Submit a format request.
 8522    let format = cx
 8523        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8524        .unwrap();
 8525
 8526    // Record which buffer changes have been sent to the language server
 8527    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8528    cx.lsp
 8529        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8530            let buffer_changes = buffer_changes.clone();
 8531            move |params, _| {
 8532                buffer_changes.lock().extend(
 8533                    params
 8534                        .content_changes
 8535                        .into_iter()
 8536                        .map(|e| (e.range.unwrap(), e.text)),
 8537                );
 8538            }
 8539        });
 8540
 8541    // Handle formatting requests to the language server.
 8542    cx.lsp
 8543        .set_request_handler::<lsp::request::Formatting, _, _>({
 8544            let buffer_changes = buffer_changes.clone();
 8545            move |_, _| {
 8546                // When formatting is requested, trailing whitespace has already been stripped,
 8547                // and the trailing newline has already been added.
 8548                assert_eq!(
 8549                    &buffer_changes.lock()[1..],
 8550                    &[
 8551                        (
 8552                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8553                            "".into()
 8554                        ),
 8555                        (
 8556                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8557                            "".into()
 8558                        ),
 8559                        (
 8560                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8561                            "\n".into()
 8562                        ),
 8563                    ]
 8564                );
 8565
 8566                // Insert blank lines between each line of the buffer.
 8567                async move {
 8568                    Ok(Some(vec![
 8569                        lsp::TextEdit {
 8570                            range: lsp::Range::new(
 8571                                lsp::Position::new(1, 0),
 8572                                lsp::Position::new(1, 0),
 8573                            ),
 8574                            new_text: "\n".into(),
 8575                        },
 8576                        lsp::TextEdit {
 8577                            range: lsp::Range::new(
 8578                                lsp::Position::new(2, 0),
 8579                                lsp::Position::new(2, 0),
 8580                            ),
 8581                            new_text: "\n".into(),
 8582                        },
 8583                    ]))
 8584                }
 8585            }
 8586        });
 8587
 8588    // After formatting the buffer, the trailing whitespace is stripped,
 8589    // a newline is appended, and the edits provided by the language server
 8590    // have been applied.
 8591    format.await.unwrap();
 8592    cx.assert_editor_state(
 8593        &[
 8594            "one",   //
 8595            "",      //
 8596            "twoˇ",  //
 8597            "",      //
 8598            "three", //
 8599            "four",  //
 8600            "",      //
 8601        ]
 8602        .join("\n"),
 8603    );
 8604
 8605    // Undoing the formatting undoes the trailing whitespace removal, the
 8606    // trailing newline, and the LSP edits.
 8607    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8608    cx.assert_editor_state(
 8609        &[
 8610            "one ",   //
 8611            "twoˇ",   //
 8612            "three ", //
 8613            "four",   //
 8614        ]
 8615        .join("\n"),
 8616    );
 8617}
 8618
 8619#[gpui::test]
 8620async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8621    cx: &mut TestAppContext,
 8622) {
 8623    init_test(cx, |_| {});
 8624
 8625    cx.update(|cx| {
 8626        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8627            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8628                settings.auto_signature_help = Some(true);
 8629            });
 8630        });
 8631    });
 8632
 8633    let mut cx = EditorLspTestContext::new_rust(
 8634        lsp::ServerCapabilities {
 8635            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8636                ..Default::default()
 8637            }),
 8638            ..Default::default()
 8639        },
 8640        cx,
 8641    )
 8642    .await;
 8643
 8644    let language = Language::new(
 8645        LanguageConfig {
 8646            name: "Rust".into(),
 8647            brackets: BracketPairConfig {
 8648                pairs: vec![
 8649                    BracketPair {
 8650                        start: "{".to_string(),
 8651                        end: "}".to_string(),
 8652                        close: true,
 8653                        surround: true,
 8654                        newline: true,
 8655                    },
 8656                    BracketPair {
 8657                        start: "(".to_string(),
 8658                        end: ")".to_string(),
 8659                        close: true,
 8660                        surround: true,
 8661                        newline: true,
 8662                    },
 8663                    BracketPair {
 8664                        start: "/*".to_string(),
 8665                        end: " */".to_string(),
 8666                        close: true,
 8667                        surround: true,
 8668                        newline: true,
 8669                    },
 8670                    BracketPair {
 8671                        start: "[".to_string(),
 8672                        end: "]".to_string(),
 8673                        close: false,
 8674                        surround: false,
 8675                        newline: true,
 8676                    },
 8677                    BracketPair {
 8678                        start: "\"".to_string(),
 8679                        end: "\"".to_string(),
 8680                        close: true,
 8681                        surround: true,
 8682                        newline: false,
 8683                    },
 8684                    BracketPair {
 8685                        start: "<".to_string(),
 8686                        end: ">".to_string(),
 8687                        close: false,
 8688                        surround: true,
 8689                        newline: true,
 8690                    },
 8691                ],
 8692                ..Default::default()
 8693            },
 8694            autoclose_before: "})]".to_string(),
 8695            ..Default::default()
 8696        },
 8697        Some(tree_sitter_rust::LANGUAGE.into()),
 8698    );
 8699    let language = Arc::new(language);
 8700
 8701    cx.language_registry().add(language.clone());
 8702    cx.update_buffer(|buffer, cx| {
 8703        buffer.set_language(Some(language), cx);
 8704    });
 8705
 8706    cx.set_state(
 8707        &r#"
 8708            fn main() {
 8709                sampleˇ
 8710            }
 8711        "#
 8712        .unindent(),
 8713    );
 8714
 8715    cx.update_editor(|editor, window, cx| {
 8716        editor.handle_input("(", window, cx);
 8717    });
 8718    cx.assert_editor_state(
 8719        &"
 8720            fn main() {
 8721                sample(ˇ)
 8722            }
 8723        "
 8724        .unindent(),
 8725    );
 8726
 8727    let mocked_response = lsp::SignatureHelp {
 8728        signatures: vec![lsp::SignatureInformation {
 8729            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8730            documentation: None,
 8731            parameters: Some(vec![
 8732                lsp::ParameterInformation {
 8733                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8734                    documentation: None,
 8735                },
 8736                lsp::ParameterInformation {
 8737                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8738                    documentation: None,
 8739                },
 8740            ]),
 8741            active_parameter: None,
 8742        }],
 8743        active_signature: Some(0),
 8744        active_parameter: Some(0),
 8745    };
 8746    handle_signature_help_request(&mut cx, mocked_response).await;
 8747
 8748    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8749        .await;
 8750
 8751    cx.editor(|editor, _, _| {
 8752        let signature_help_state = editor.signature_help_state.popover().cloned();
 8753        assert_eq!(
 8754            signature_help_state.unwrap().label,
 8755            "param1: u8, param2: u8"
 8756        );
 8757    });
 8758}
 8759
 8760#[gpui::test]
 8761async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8762    init_test(cx, |_| {});
 8763
 8764    cx.update(|cx| {
 8765        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8766            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8767                settings.auto_signature_help = Some(false);
 8768                settings.show_signature_help_after_edits = Some(false);
 8769            });
 8770        });
 8771    });
 8772
 8773    let mut cx = EditorLspTestContext::new_rust(
 8774        lsp::ServerCapabilities {
 8775            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8776                ..Default::default()
 8777            }),
 8778            ..Default::default()
 8779        },
 8780        cx,
 8781    )
 8782    .await;
 8783
 8784    let language = Language::new(
 8785        LanguageConfig {
 8786            name: "Rust".into(),
 8787            brackets: BracketPairConfig {
 8788                pairs: vec![
 8789                    BracketPair {
 8790                        start: "{".to_string(),
 8791                        end: "}".to_string(),
 8792                        close: true,
 8793                        surround: true,
 8794                        newline: true,
 8795                    },
 8796                    BracketPair {
 8797                        start: "(".to_string(),
 8798                        end: ")".to_string(),
 8799                        close: true,
 8800                        surround: true,
 8801                        newline: true,
 8802                    },
 8803                    BracketPair {
 8804                        start: "/*".to_string(),
 8805                        end: " */".to_string(),
 8806                        close: true,
 8807                        surround: true,
 8808                        newline: true,
 8809                    },
 8810                    BracketPair {
 8811                        start: "[".to_string(),
 8812                        end: "]".to_string(),
 8813                        close: false,
 8814                        surround: false,
 8815                        newline: true,
 8816                    },
 8817                    BracketPair {
 8818                        start: "\"".to_string(),
 8819                        end: "\"".to_string(),
 8820                        close: true,
 8821                        surround: true,
 8822                        newline: false,
 8823                    },
 8824                    BracketPair {
 8825                        start: "<".to_string(),
 8826                        end: ">".to_string(),
 8827                        close: false,
 8828                        surround: true,
 8829                        newline: true,
 8830                    },
 8831                ],
 8832                ..Default::default()
 8833            },
 8834            autoclose_before: "})]".to_string(),
 8835            ..Default::default()
 8836        },
 8837        Some(tree_sitter_rust::LANGUAGE.into()),
 8838    );
 8839    let language = Arc::new(language);
 8840
 8841    cx.language_registry().add(language.clone());
 8842    cx.update_buffer(|buffer, cx| {
 8843        buffer.set_language(Some(language), cx);
 8844    });
 8845
 8846    // Ensure that signature_help is not called when no signature help is enabled.
 8847    cx.set_state(
 8848        &r#"
 8849            fn main() {
 8850                sampleˇ
 8851            }
 8852        "#
 8853        .unindent(),
 8854    );
 8855    cx.update_editor(|editor, window, cx| {
 8856        editor.handle_input("(", window, cx);
 8857    });
 8858    cx.assert_editor_state(
 8859        &"
 8860            fn main() {
 8861                sample(ˇ)
 8862            }
 8863        "
 8864        .unindent(),
 8865    );
 8866    cx.editor(|editor, _, _| {
 8867        assert!(editor.signature_help_state.task().is_none());
 8868    });
 8869
 8870    let mocked_response = lsp::SignatureHelp {
 8871        signatures: vec![lsp::SignatureInformation {
 8872            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8873            documentation: None,
 8874            parameters: Some(vec![
 8875                lsp::ParameterInformation {
 8876                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8877                    documentation: None,
 8878                },
 8879                lsp::ParameterInformation {
 8880                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8881                    documentation: None,
 8882                },
 8883            ]),
 8884            active_parameter: None,
 8885        }],
 8886        active_signature: Some(0),
 8887        active_parameter: Some(0),
 8888    };
 8889
 8890    // Ensure that signature_help is called when enabled afte edits
 8891    cx.update(|_, cx| {
 8892        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8893            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8894                settings.auto_signature_help = Some(false);
 8895                settings.show_signature_help_after_edits = Some(true);
 8896            });
 8897        });
 8898    });
 8899    cx.set_state(
 8900        &r#"
 8901            fn main() {
 8902                sampleˇ
 8903            }
 8904        "#
 8905        .unindent(),
 8906    );
 8907    cx.update_editor(|editor, window, cx| {
 8908        editor.handle_input("(", window, cx);
 8909    });
 8910    cx.assert_editor_state(
 8911        &"
 8912            fn main() {
 8913                sample(ˇ)
 8914            }
 8915        "
 8916        .unindent(),
 8917    );
 8918    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8919    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8920        .await;
 8921    cx.update_editor(|editor, _, _| {
 8922        let signature_help_state = editor.signature_help_state.popover().cloned();
 8923        assert!(signature_help_state.is_some());
 8924        assert_eq!(
 8925            signature_help_state.unwrap().label,
 8926            "param1: u8, param2: u8"
 8927        );
 8928        editor.signature_help_state = SignatureHelpState::default();
 8929    });
 8930
 8931    // Ensure that signature_help is called when auto signature help override is enabled
 8932    cx.update(|_, cx| {
 8933        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8934            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8935                settings.auto_signature_help = Some(true);
 8936                settings.show_signature_help_after_edits = Some(false);
 8937            });
 8938        });
 8939    });
 8940    cx.set_state(
 8941        &r#"
 8942            fn main() {
 8943                sampleˇ
 8944            }
 8945        "#
 8946        .unindent(),
 8947    );
 8948    cx.update_editor(|editor, window, cx| {
 8949        editor.handle_input("(", window, cx);
 8950    });
 8951    cx.assert_editor_state(
 8952        &"
 8953            fn main() {
 8954                sample(ˇ)
 8955            }
 8956        "
 8957        .unindent(),
 8958    );
 8959    handle_signature_help_request(&mut cx, mocked_response).await;
 8960    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8961        .await;
 8962    cx.editor(|editor, _, _| {
 8963        let signature_help_state = editor.signature_help_state.popover().cloned();
 8964        assert!(signature_help_state.is_some());
 8965        assert_eq!(
 8966            signature_help_state.unwrap().label,
 8967            "param1: u8, param2: u8"
 8968        );
 8969    });
 8970}
 8971
 8972#[gpui::test]
 8973async fn test_signature_help(cx: &mut TestAppContext) {
 8974    init_test(cx, |_| {});
 8975    cx.update(|cx| {
 8976        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8977            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8978                settings.auto_signature_help = Some(true);
 8979            });
 8980        });
 8981    });
 8982
 8983    let mut cx = EditorLspTestContext::new_rust(
 8984        lsp::ServerCapabilities {
 8985            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8986                ..Default::default()
 8987            }),
 8988            ..Default::default()
 8989        },
 8990        cx,
 8991    )
 8992    .await;
 8993
 8994    // A test that directly calls `show_signature_help`
 8995    cx.update_editor(|editor, window, cx| {
 8996        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8997    });
 8998
 8999    let mocked_response = lsp::SignatureHelp {
 9000        signatures: vec![lsp::SignatureInformation {
 9001            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9002            documentation: None,
 9003            parameters: Some(vec![
 9004                lsp::ParameterInformation {
 9005                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9006                    documentation: None,
 9007                },
 9008                lsp::ParameterInformation {
 9009                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9010                    documentation: None,
 9011                },
 9012            ]),
 9013            active_parameter: None,
 9014        }],
 9015        active_signature: Some(0),
 9016        active_parameter: Some(0),
 9017    };
 9018    handle_signature_help_request(&mut cx, mocked_response).await;
 9019
 9020    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9021        .await;
 9022
 9023    cx.editor(|editor, _, _| {
 9024        let signature_help_state = editor.signature_help_state.popover().cloned();
 9025        assert!(signature_help_state.is_some());
 9026        assert_eq!(
 9027            signature_help_state.unwrap().label,
 9028            "param1: u8, param2: u8"
 9029        );
 9030    });
 9031
 9032    // When exiting outside from inside the brackets, `signature_help` is closed.
 9033    cx.set_state(indoc! {"
 9034        fn main() {
 9035            sample(ˇ);
 9036        }
 9037
 9038        fn sample(param1: u8, param2: u8) {}
 9039    "});
 9040
 9041    cx.update_editor(|editor, window, cx| {
 9042        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9043    });
 9044
 9045    let mocked_response = lsp::SignatureHelp {
 9046        signatures: Vec::new(),
 9047        active_signature: None,
 9048        active_parameter: None,
 9049    };
 9050    handle_signature_help_request(&mut cx, mocked_response).await;
 9051
 9052    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9053        .await;
 9054
 9055    cx.editor(|editor, _, _| {
 9056        assert!(!editor.signature_help_state.is_shown());
 9057    });
 9058
 9059    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9060    cx.set_state(indoc! {"
 9061        fn main() {
 9062            sample(ˇ);
 9063        }
 9064
 9065        fn sample(param1: u8, param2: u8) {}
 9066    "});
 9067
 9068    let mocked_response = lsp::SignatureHelp {
 9069        signatures: vec![lsp::SignatureInformation {
 9070            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9071            documentation: None,
 9072            parameters: Some(vec![
 9073                lsp::ParameterInformation {
 9074                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9075                    documentation: None,
 9076                },
 9077                lsp::ParameterInformation {
 9078                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9079                    documentation: None,
 9080                },
 9081            ]),
 9082            active_parameter: None,
 9083        }],
 9084        active_signature: Some(0),
 9085        active_parameter: Some(0),
 9086    };
 9087    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9088    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9089        .await;
 9090    cx.editor(|editor, _, _| {
 9091        assert!(editor.signature_help_state.is_shown());
 9092    });
 9093
 9094    // Restore the popover with more parameter input
 9095    cx.set_state(indoc! {"
 9096        fn main() {
 9097            sample(param1, param2ˇ);
 9098        }
 9099
 9100        fn sample(param1: u8, param2: u8) {}
 9101    "});
 9102
 9103    let mocked_response = lsp::SignatureHelp {
 9104        signatures: vec![lsp::SignatureInformation {
 9105            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9106            documentation: None,
 9107            parameters: Some(vec![
 9108                lsp::ParameterInformation {
 9109                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9110                    documentation: None,
 9111                },
 9112                lsp::ParameterInformation {
 9113                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9114                    documentation: None,
 9115                },
 9116            ]),
 9117            active_parameter: None,
 9118        }],
 9119        active_signature: Some(0),
 9120        active_parameter: Some(1),
 9121    };
 9122    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9123    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9124        .await;
 9125
 9126    // When selecting a range, the popover is gone.
 9127    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9128    cx.update_editor(|editor, window, cx| {
 9129        editor.change_selections(None, window, cx, |s| {
 9130            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9131        })
 9132    });
 9133    cx.assert_editor_state(indoc! {"
 9134        fn main() {
 9135            sample(param1, «ˇparam2»);
 9136        }
 9137
 9138        fn sample(param1: u8, param2: u8) {}
 9139    "});
 9140    cx.editor(|editor, _, _| {
 9141        assert!(!editor.signature_help_state.is_shown());
 9142    });
 9143
 9144    // When unselecting again, the popover is back if within the brackets.
 9145    cx.update_editor(|editor, window, cx| {
 9146        editor.change_selections(None, window, cx, |s| {
 9147            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9148        })
 9149    });
 9150    cx.assert_editor_state(indoc! {"
 9151        fn main() {
 9152            sample(param1, ˇparam2);
 9153        }
 9154
 9155        fn sample(param1: u8, param2: u8) {}
 9156    "});
 9157    handle_signature_help_request(&mut cx, mocked_response).await;
 9158    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9159        .await;
 9160    cx.editor(|editor, _, _| {
 9161        assert!(editor.signature_help_state.is_shown());
 9162    });
 9163
 9164    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9165    cx.update_editor(|editor, window, cx| {
 9166        editor.change_selections(None, window, cx, |s| {
 9167            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9168            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9169        })
 9170    });
 9171    cx.assert_editor_state(indoc! {"
 9172        fn main() {
 9173            sample(param1, ˇparam2);
 9174        }
 9175
 9176        fn sample(param1: u8, param2: u8) {}
 9177    "});
 9178
 9179    let mocked_response = lsp::SignatureHelp {
 9180        signatures: vec![lsp::SignatureInformation {
 9181            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9182            documentation: None,
 9183            parameters: Some(vec![
 9184                lsp::ParameterInformation {
 9185                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9186                    documentation: None,
 9187                },
 9188                lsp::ParameterInformation {
 9189                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9190                    documentation: None,
 9191                },
 9192            ]),
 9193            active_parameter: None,
 9194        }],
 9195        active_signature: Some(0),
 9196        active_parameter: Some(1),
 9197    };
 9198    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9199    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9200        .await;
 9201    cx.update_editor(|editor, _, cx| {
 9202        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9203    });
 9204    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9205        .await;
 9206    cx.update_editor(|editor, window, cx| {
 9207        editor.change_selections(None, window, cx, |s| {
 9208            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9209        })
 9210    });
 9211    cx.assert_editor_state(indoc! {"
 9212        fn main() {
 9213            sample(param1, «ˇparam2»);
 9214        }
 9215
 9216        fn sample(param1: u8, param2: u8) {}
 9217    "});
 9218    cx.update_editor(|editor, window, cx| {
 9219        editor.change_selections(None, window, cx, |s| {
 9220            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9221        })
 9222    });
 9223    cx.assert_editor_state(indoc! {"
 9224        fn main() {
 9225            sample(param1, ˇparam2);
 9226        }
 9227
 9228        fn sample(param1: u8, param2: u8) {}
 9229    "});
 9230    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9231        .await;
 9232}
 9233
 9234#[gpui::test]
 9235async fn test_completion(cx: &mut TestAppContext) {
 9236    init_test(cx, |_| {});
 9237
 9238    let mut cx = EditorLspTestContext::new_rust(
 9239        lsp::ServerCapabilities {
 9240            completion_provider: Some(lsp::CompletionOptions {
 9241                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9242                resolve_provider: Some(true),
 9243                ..Default::default()
 9244            }),
 9245            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9246            ..Default::default()
 9247        },
 9248        cx,
 9249    )
 9250    .await;
 9251    let counter = Arc::new(AtomicUsize::new(0));
 9252
 9253    cx.set_state(indoc! {"
 9254        oneˇ
 9255        two
 9256        three
 9257    "});
 9258    cx.simulate_keystroke(".");
 9259    handle_completion_request(
 9260        &mut cx,
 9261        indoc! {"
 9262            one.|<>
 9263            two
 9264            three
 9265        "},
 9266        vec!["first_completion", "second_completion"],
 9267        counter.clone(),
 9268    )
 9269    .await;
 9270    cx.condition(|editor, _| editor.context_menu_visible())
 9271        .await;
 9272    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9273
 9274    let _handler = handle_signature_help_request(
 9275        &mut cx,
 9276        lsp::SignatureHelp {
 9277            signatures: vec![lsp::SignatureInformation {
 9278                label: "test signature".to_string(),
 9279                documentation: None,
 9280                parameters: Some(vec![lsp::ParameterInformation {
 9281                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9282                    documentation: None,
 9283                }]),
 9284                active_parameter: None,
 9285            }],
 9286            active_signature: None,
 9287            active_parameter: None,
 9288        },
 9289    );
 9290    cx.update_editor(|editor, window, cx| {
 9291        assert!(
 9292            !editor.signature_help_state.is_shown(),
 9293            "No signature help was called for"
 9294        );
 9295        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9296    });
 9297    cx.run_until_parked();
 9298    cx.update_editor(|editor, _, _| {
 9299        assert!(
 9300            !editor.signature_help_state.is_shown(),
 9301            "No signature help should be shown when completions menu is open"
 9302        );
 9303    });
 9304
 9305    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9306        editor.context_menu_next(&Default::default(), window, cx);
 9307        editor
 9308            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9309            .unwrap()
 9310    });
 9311    cx.assert_editor_state(indoc! {"
 9312        one.second_completionˇ
 9313        two
 9314        three
 9315    "});
 9316
 9317    handle_resolve_completion_request(
 9318        &mut cx,
 9319        Some(vec![
 9320            (
 9321                //This overlaps with the primary completion edit which is
 9322                //misbehavior from the LSP spec, test that we filter it out
 9323                indoc! {"
 9324                    one.second_ˇcompletion
 9325                    two
 9326                    threeˇ
 9327                "},
 9328                "overlapping additional edit",
 9329            ),
 9330            (
 9331                indoc! {"
 9332                    one.second_completion
 9333                    two
 9334                    threeˇ
 9335                "},
 9336                "\nadditional edit",
 9337            ),
 9338        ]),
 9339    )
 9340    .await;
 9341    apply_additional_edits.await.unwrap();
 9342    cx.assert_editor_state(indoc! {"
 9343        one.second_completionˇ
 9344        two
 9345        three
 9346        additional edit
 9347    "});
 9348
 9349    cx.set_state(indoc! {"
 9350        one.second_completion
 9351        twoˇ
 9352        threeˇ
 9353        additional edit
 9354    "});
 9355    cx.simulate_keystroke(" ");
 9356    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9357    cx.simulate_keystroke("s");
 9358    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9359
 9360    cx.assert_editor_state(indoc! {"
 9361        one.second_completion
 9362        two sˇ
 9363        three sˇ
 9364        additional edit
 9365    "});
 9366    handle_completion_request(
 9367        &mut cx,
 9368        indoc! {"
 9369            one.second_completion
 9370            two s
 9371            three <s|>
 9372            additional edit
 9373        "},
 9374        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9375        counter.clone(),
 9376    )
 9377    .await;
 9378    cx.condition(|editor, _| editor.context_menu_visible())
 9379        .await;
 9380    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9381
 9382    cx.simulate_keystroke("i");
 9383
 9384    handle_completion_request(
 9385        &mut cx,
 9386        indoc! {"
 9387            one.second_completion
 9388            two si
 9389            three <si|>
 9390            additional edit
 9391        "},
 9392        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9393        counter.clone(),
 9394    )
 9395    .await;
 9396    cx.condition(|editor, _| editor.context_menu_visible())
 9397        .await;
 9398    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9399
 9400    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9401        editor
 9402            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9403            .unwrap()
 9404    });
 9405    cx.assert_editor_state(indoc! {"
 9406        one.second_completion
 9407        two sixth_completionˇ
 9408        three sixth_completionˇ
 9409        additional edit
 9410    "});
 9411
 9412    apply_additional_edits.await.unwrap();
 9413
 9414    update_test_language_settings(&mut cx, |settings| {
 9415        settings.defaults.show_completions_on_input = Some(false);
 9416    });
 9417    cx.set_state("editorˇ");
 9418    cx.simulate_keystroke(".");
 9419    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9420    cx.simulate_keystrokes("c l o");
 9421    cx.assert_editor_state("editor.cloˇ");
 9422    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9423    cx.update_editor(|editor, window, cx| {
 9424        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9425    });
 9426    handle_completion_request(
 9427        &mut cx,
 9428        "editor.<clo|>",
 9429        vec!["close", "clobber"],
 9430        counter.clone(),
 9431    )
 9432    .await;
 9433    cx.condition(|editor, _| editor.context_menu_visible())
 9434        .await;
 9435    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9436
 9437    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9438        editor
 9439            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9440            .unwrap()
 9441    });
 9442    cx.assert_editor_state("editor.closeˇ");
 9443    handle_resolve_completion_request(&mut cx, None).await;
 9444    apply_additional_edits.await.unwrap();
 9445}
 9446
 9447#[gpui::test]
 9448async fn test_word_completion(cx: &mut TestAppContext) {
 9449    let lsp_fetch_timeout_ms = 10;
 9450    init_test(cx, |language_settings| {
 9451        language_settings.defaults.completions = Some(CompletionSettings {
 9452            words: WordsCompletionMode::Fallback,
 9453            lsp: true,
 9454            lsp_fetch_timeout_ms: 10,
 9455        });
 9456    });
 9457
 9458    let mut cx = EditorLspTestContext::new_rust(
 9459        lsp::ServerCapabilities {
 9460            completion_provider: Some(lsp::CompletionOptions {
 9461                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9462                ..lsp::CompletionOptions::default()
 9463            }),
 9464            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9465            ..lsp::ServerCapabilities::default()
 9466        },
 9467        cx,
 9468    )
 9469    .await;
 9470
 9471    let throttle_completions = Arc::new(AtomicBool::new(false));
 9472
 9473    let lsp_throttle_completions = throttle_completions.clone();
 9474    let _completion_requests_handler =
 9475        cx.lsp
 9476            .server
 9477            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9478                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9479                let cx = cx.clone();
 9480                async move {
 9481                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9482                        cx.background_executor()
 9483                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9484                            .await;
 9485                    }
 9486                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9487                        lsp::CompletionItem {
 9488                            label: "first".into(),
 9489                            ..lsp::CompletionItem::default()
 9490                        },
 9491                        lsp::CompletionItem {
 9492                            label: "last".into(),
 9493                            ..lsp::CompletionItem::default()
 9494                        },
 9495                    ])))
 9496                }
 9497            });
 9498
 9499    cx.set_state(indoc! {"
 9500        oneˇ
 9501        two
 9502        three
 9503    "});
 9504    cx.simulate_keystroke(".");
 9505    cx.executor().run_until_parked();
 9506    cx.condition(|editor, _| editor.context_menu_visible())
 9507        .await;
 9508    cx.update_editor(|editor, window, cx| {
 9509        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9510        {
 9511            assert_eq!(
 9512                completion_menu_entries(&menu),
 9513                &["first", "last"],
 9514                "When LSP server is fast to reply, no fallback word completions are used"
 9515            );
 9516        } else {
 9517            panic!("expected completion menu to be open");
 9518        }
 9519        editor.cancel(&Cancel, window, cx);
 9520    });
 9521    cx.executor().run_until_parked();
 9522    cx.condition(|editor, _| !editor.context_menu_visible())
 9523        .await;
 9524
 9525    throttle_completions.store(true, atomic::Ordering::Release);
 9526    cx.simulate_keystroke(".");
 9527    cx.executor()
 9528        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9529    cx.executor().run_until_parked();
 9530    cx.condition(|editor, _| editor.context_menu_visible())
 9531        .await;
 9532    cx.update_editor(|editor, _, _| {
 9533        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9534        {
 9535            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9536                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9537        } else {
 9538            panic!("expected completion menu to be open");
 9539        }
 9540    });
 9541}
 9542
 9543#[gpui::test]
 9544async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9545    init_test(cx, |language_settings| {
 9546        language_settings.defaults.completions = Some(CompletionSettings {
 9547            words: WordsCompletionMode::Enabled,
 9548            lsp: true,
 9549            lsp_fetch_timeout_ms: 0,
 9550        });
 9551    });
 9552
 9553    let mut cx = EditorLspTestContext::new_rust(
 9554        lsp::ServerCapabilities {
 9555            completion_provider: Some(lsp::CompletionOptions {
 9556                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9557                ..lsp::CompletionOptions::default()
 9558            }),
 9559            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9560            ..lsp::ServerCapabilities::default()
 9561        },
 9562        cx,
 9563    )
 9564    .await;
 9565
 9566    let _completion_requests_handler =
 9567        cx.lsp
 9568            .server
 9569            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9570                Ok(Some(lsp::CompletionResponse::Array(vec![
 9571                    lsp::CompletionItem {
 9572                        label: "first".into(),
 9573                        ..lsp::CompletionItem::default()
 9574                    },
 9575                    lsp::CompletionItem {
 9576                        label: "last".into(),
 9577                        ..lsp::CompletionItem::default()
 9578                    },
 9579                ])))
 9580            });
 9581
 9582    cx.set_state(indoc! {"ˇ
 9583        first
 9584        last
 9585        second
 9586    "});
 9587    cx.simulate_keystroke(".");
 9588    cx.executor().run_until_parked();
 9589    cx.condition(|editor, _| editor.context_menu_visible())
 9590        .await;
 9591    cx.update_editor(|editor, _, _| {
 9592        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9593        {
 9594            assert_eq!(
 9595                completion_menu_entries(&menu),
 9596                &["first", "last", "second"],
 9597                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9598            );
 9599        } else {
 9600            panic!("expected completion menu to be open");
 9601        }
 9602    });
 9603}
 9604
 9605#[gpui::test]
 9606async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9607    init_test(cx, |language_settings| {
 9608        language_settings.defaults.completions = Some(CompletionSettings {
 9609            words: WordsCompletionMode::Disabled,
 9610            lsp: true,
 9611            lsp_fetch_timeout_ms: 0,
 9612        });
 9613    });
 9614
 9615    let mut cx = EditorLspTestContext::new_rust(
 9616        lsp::ServerCapabilities {
 9617            completion_provider: Some(lsp::CompletionOptions {
 9618                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9619                ..lsp::CompletionOptions::default()
 9620            }),
 9621            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9622            ..lsp::ServerCapabilities::default()
 9623        },
 9624        cx,
 9625    )
 9626    .await;
 9627
 9628    let _completion_requests_handler =
 9629        cx.lsp
 9630            .server
 9631            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9632                panic!("LSP completions should not be queried when dealing with word completions")
 9633            });
 9634
 9635    cx.set_state(indoc! {"ˇ
 9636        first
 9637        last
 9638        second
 9639    "});
 9640    cx.update_editor(|editor, window, cx| {
 9641        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9642    });
 9643    cx.executor().run_until_parked();
 9644    cx.condition(|editor, _| editor.context_menu_visible())
 9645        .await;
 9646    cx.update_editor(|editor, _, _| {
 9647        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9648        {
 9649            assert_eq!(
 9650                completion_menu_entries(&menu),
 9651                &["first", "last", "second"],
 9652                "`ShowWordCompletions` action should show word completions"
 9653            );
 9654        } else {
 9655            panic!("expected completion menu to be open");
 9656        }
 9657    });
 9658
 9659    cx.simulate_keystroke("l");
 9660    cx.executor().run_until_parked();
 9661    cx.condition(|editor, _| editor.context_menu_visible())
 9662        .await;
 9663    cx.update_editor(|editor, _, _| {
 9664        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9665        {
 9666            assert_eq!(
 9667                completion_menu_entries(&menu),
 9668                &["last"],
 9669                "After showing word completions, further editing should filter them and not query the LSP"
 9670            );
 9671        } else {
 9672            panic!("expected completion menu to be open");
 9673        }
 9674    });
 9675}
 9676
 9677#[gpui::test]
 9678async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9679    init_test(cx, |language_settings| {
 9680        language_settings.defaults.completions = Some(CompletionSettings {
 9681            words: WordsCompletionMode::Fallback,
 9682            lsp: false,
 9683            lsp_fetch_timeout_ms: 0,
 9684        });
 9685    });
 9686
 9687    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9688
 9689    cx.set_state(indoc! {"ˇ
 9690        0_usize
 9691        let
 9692        33
 9693        4.5f32
 9694    "});
 9695    cx.update_editor(|editor, window, cx| {
 9696        editor.show_completions(&ShowCompletions::default(), window, cx);
 9697    });
 9698    cx.executor().run_until_parked();
 9699    cx.condition(|editor, _| editor.context_menu_visible())
 9700        .await;
 9701    cx.update_editor(|editor, window, cx| {
 9702        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9703        {
 9704            assert_eq!(
 9705                completion_menu_entries(&menu),
 9706                &["let"],
 9707                "With no digits in the completion query, no digits should be in the word completions"
 9708            );
 9709        } else {
 9710            panic!("expected completion menu to be open");
 9711        }
 9712        editor.cancel(&Cancel, window, cx);
 9713    });
 9714
 9715    cx.set_state(indoc! {" 9716        0_usize
 9717        let
 9718        3
 9719        33.35f32
 9720    "});
 9721    cx.update_editor(|editor, window, cx| {
 9722        editor.show_completions(&ShowCompletions::default(), window, cx);
 9723    });
 9724    cx.executor().run_until_parked();
 9725    cx.condition(|editor, _| editor.context_menu_visible())
 9726        .await;
 9727    cx.update_editor(|editor, _, _| {
 9728        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9729        {
 9730            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9731                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9732        } else {
 9733            panic!("expected completion menu to be open");
 9734        }
 9735    });
 9736}
 9737
 9738fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9739    let position = || lsp::Position {
 9740        line: params.text_document_position.position.line,
 9741        character: params.text_document_position.position.character,
 9742    };
 9743    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9744        range: lsp::Range {
 9745            start: position(),
 9746            end: position(),
 9747        },
 9748        new_text: text.to_string(),
 9749    }))
 9750}
 9751
 9752#[gpui::test]
 9753async fn test_multiline_completion(cx: &mut TestAppContext) {
 9754    init_test(cx, |_| {});
 9755
 9756    let fs = FakeFs::new(cx.executor());
 9757    fs.insert_tree(
 9758        path!("/a"),
 9759        json!({
 9760            "main.ts": "a",
 9761        }),
 9762    )
 9763    .await;
 9764
 9765    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9766    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9767    let typescript_language = Arc::new(Language::new(
 9768        LanguageConfig {
 9769            name: "TypeScript".into(),
 9770            matcher: LanguageMatcher {
 9771                path_suffixes: vec!["ts".to_string()],
 9772                ..LanguageMatcher::default()
 9773            },
 9774            line_comments: vec!["// ".into()],
 9775            ..LanguageConfig::default()
 9776        },
 9777        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9778    ));
 9779    language_registry.add(typescript_language.clone());
 9780    let mut fake_servers = language_registry.register_fake_lsp(
 9781        "TypeScript",
 9782        FakeLspAdapter {
 9783            capabilities: lsp::ServerCapabilities {
 9784                completion_provider: Some(lsp::CompletionOptions {
 9785                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9786                    ..lsp::CompletionOptions::default()
 9787                }),
 9788                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9789                ..lsp::ServerCapabilities::default()
 9790            },
 9791            // Emulate vtsls label generation
 9792            label_for_completion: Some(Box::new(|item, _| {
 9793                let text = if let Some(description) = item
 9794                    .label_details
 9795                    .as_ref()
 9796                    .and_then(|label_details| label_details.description.as_ref())
 9797                {
 9798                    format!("{} {}", item.label, description)
 9799                } else if let Some(detail) = &item.detail {
 9800                    format!("{} {}", item.label, detail)
 9801                } else {
 9802                    item.label.clone()
 9803                };
 9804                let len = text.len();
 9805                Some(language::CodeLabel {
 9806                    text,
 9807                    runs: Vec::new(),
 9808                    filter_range: 0..len,
 9809                })
 9810            })),
 9811            ..FakeLspAdapter::default()
 9812        },
 9813    );
 9814    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9815    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9816    let worktree_id = workspace
 9817        .update(cx, |workspace, _window, cx| {
 9818            workspace.project().update(cx, |project, cx| {
 9819                project.worktrees(cx).next().unwrap().read(cx).id()
 9820            })
 9821        })
 9822        .unwrap();
 9823    let _buffer = project
 9824        .update(cx, |project, cx| {
 9825            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9826        })
 9827        .await
 9828        .unwrap();
 9829    let editor = workspace
 9830        .update(cx, |workspace, window, cx| {
 9831            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9832        })
 9833        .unwrap()
 9834        .await
 9835        .unwrap()
 9836        .downcast::<Editor>()
 9837        .unwrap();
 9838    let fake_server = fake_servers.next().await.unwrap();
 9839
 9840    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9841    let multiline_label_2 = "a\nb\nc\n";
 9842    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9843    let multiline_description = "d\ne\nf\n";
 9844    let multiline_detail_2 = "g\nh\ni\n";
 9845
 9846    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
 9847        move |params, _| async move {
 9848            Ok(Some(lsp::CompletionResponse::Array(vec![
 9849                lsp::CompletionItem {
 9850                    label: multiline_label.to_string(),
 9851                    text_edit: gen_text_edit(&params, "new_text_1"),
 9852                    ..lsp::CompletionItem::default()
 9853                },
 9854                lsp::CompletionItem {
 9855                    label: "single line label 1".to_string(),
 9856                    detail: Some(multiline_detail.to_string()),
 9857                    text_edit: gen_text_edit(&params, "new_text_2"),
 9858                    ..lsp::CompletionItem::default()
 9859                },
 9860                lsp::CompletionItem {
 9861                    label: "single line label 2".to_string(),
 9862                    label_details: Some(lsp::CompletionItemLabelDetails {
 9863                        description: Some(multiline_description.to_string()),
 9864                        detail: None,
 9865                    }),
 9866                    text_edit: gen_text_edit(&params, "new_text_2"),
 9867                    ..lsp::CompletionItem::default()
 9868                },
 9869                lsp::CompletionItem {
 9870                    label: multiline_label_2.to_string(),
 9871                    detail: Some(multiline_detail_2.to_string()),
 9872                    text_edit: gen_text_edit(&params, "new_text_3"),
 9873                    ..lsp::CompletionItem::default()
 9874                },
 9875                lsp::CompletionItem {
 9876                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9877                    detail: Some(
 9878                        "Details with many     spaces and \t but without newlines".to_string(),
 9879                    ),
 9880                    text_edit: gen_text_edit(&params, "new_text_4"),
 9881                    ..lsp::CompletionItem::default()
 9882                },
 9883            ])))
 9884        },
 9885    );
 9886
 9887    editor.update_in(cx, |editor, window, cx| {
 9888        cx.focus_self(window);
 9889        editor.move_to_end(&MoveToEnd, window, cx);
 9890        editor.handle_input(".", window, cx);
 9891    });
 9892    cx.run_until_parked();
 9893    completion_handle.next().await.unwrap();
 9894
 9895    editor.update(cx, |editor, _| {
 9896        assert!(editor.context_menu_visible());
 9897        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9898        {
 9899            let completion_labels = menu
 9900                .completions
 9901                .borrow()
 9902                .iter()
 9903                .map(|c| c.label.text.clone())
 9904                .collect::<Vec<_>>();
 9905            assert_eq!(
 9906                completion_labels,
 9907                &[
 9908                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9909                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9910                    "single line label 2 d e f ",
 9911                    "a b c g h i ",
 9912                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9913                ],
 9914                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9915            );
 9916
 9917            for completion in menu
 9918                .completions
 9919                .borrow()
 9920                .iter() {
 9921                    assert_eq!(
 9922                        completion.label.filter_range,
 9923                        0..completion.label.text.len(),
 9924                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9925                    );
 9926                }
 9927        } else {
 9928            panic!("expected completion menu to be open");
 9929        }
 9930    });
 9931}
 9932
 9933#[gpui::test]
 9934async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9935    init_test(cx, |_| {});
 9936    let mut cx = EditorLspTestContext::new_rust(
 9937        lsp::ServerCapabilities {
 9938            completion_provider: Some(lsp::CompletionOptions {
 9939                trigger_characters: Some(vec![".".to_string()]),
 9940                ..Default::default()
 9941            }),
 9942            ..Default::default()
 9943        },
 9944        cx,
 9945    )
 9946    .await;
 9947    cx.lsp
 9948        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 9949            Ok(Some(lsp::CompletionResponse::Array(vec![
 9950                lsp::CompletionItem {
 9951                    label: "first".into(),
 9952                    ..Default::default()
 9953                },
 9954                lsp::CompletionItem {
 9955                    label: "last".into(),
 9956                    ..Default::default()
 9957                },
 9958            ])))
 9959        });
 9960    cx.set_state("variableˇ");
 9961    cx.simulate_keystroke(".");
 9962    cx.executor().run_until_parked();
 9963
 9964    cx.update_editor(|editor, _, _| {
 9965        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9966        {
 9967            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9968        } else {
 9969            panic!("expected completion menu to be open");
 9970        }
 9971    });
 9972
 9973    cx.update_editor(|editor, window, cx| {
 9974        editor.move_page_down(&MovePageDown::default(), window, cx);
 9975        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9976        {
 9977            assert!(
 9978                menu.selected_item == 1,
 9979                "expected PageDown to select the last item from the context menu"
 9980            );
 9981        } else {
 9982            panic!("expected completion menu to stay open after PageDown");
 9983        }
 9984    });
 9985
 9986    cx.update_editor(|editor, window, cx| {
 9987        editor.move_page_up(&MovePageUp::default(), window, cx);
 9988        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9989        {
 9990            assert!(
 9991                menu.selected_item == 0,
 9992                "expected PageUp to select the first item from the context menu"
 9993            );
 9994        } else {
 9995            panic!("expected completion menu to stay open after PageUp");
 9996        }
 9997    });
 9998}
 9999
10000#[gpui::test]
10001async fn test_completion_sort(cx: &mut TestAppContext) {
10002    init_test(cx, |_| {});
10003    let mut cx = EditorLspTestContext::new_rust(
10004        lsp::ServerCapabilities {
10005            completion_provider: Some(lsp::CompletionOptions {
10006                trigger_characters: Some(vec![".".to_string()]),
10007                ..Default::default()
10008            }),
10009            ..Default::default()
10010        },
10011        cx,
10012    )
10013    .await;
10014    cx.lsp
10015        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10016            Ok(Some(lsp::CompletionResponse::Array(vec![
10017                lsp::CompletionItem {
10018                    label: "Range".into(),
10019                    sort_text: Some("a".into()),
10020                    ..Default::default()
10021                },
10022                lsp::CompletionItem {
10023                    label: "r".into(),
10024                    sort_text: Some("b".into()),
10025                    ..Default::default()
10026                },
10027                lsp::CompletionItem {
10028                    label: "ret".into(),
10029                    sort_text: Some("c".into()),
10030                    ..Default::default()
10031                },
10032                lsp::CompletionItem {
10033                    label: "return".into(),
10034                    sort_text: Some("d".into()),
10035                    ..Default::default()
10036                },
10037                lsp::CompletionItem {
10038                    label: "slice".into(),
10039                    sort_text: Some("d".into()),
10040                    ..Default::default()
10041                },
10042            ])))
10043        });
10044    cx.set_state("");
10045    cx.executor().run_until_parked();
10046    cx.update_editor(|editor, window, cx| {
10047        editor.show_completions(
10048            &ShowCompletions {
10049                trigger: Some("r".into()),
10050            },
10051            window,
10052            cx,
10053        );
10054    });
10055    cx.executor().run_until_parked();
10056
10057    cx.update_editor(|editor, _, _| {
10058        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10059        {
10060            assert_eq!(
10061                completion_menu_entries(&menu),
10062                &["r", "ret", "Range", "return"]
10063            );
10064        } else {
10065            panic!("expected completion menu to be open");
10066        }
10067    });
10068}
10069
10070#[gpui::test]
10071async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10072    init_test(cx, |_| {});
10073
10074    let mut cx = EditorLspTestContext::new_rust(
10075        lsp::ServerCapabilities {
10076            completion_provider: Some(lsp::CompletionOptions {
10077                trigger_characters: Some(vec![".".to_string()]),
10078                resolve_provider: Some(true),
10079                ..Default::default()
10080            }),
10081            ..Default::default()
10082        },
10083        cx,
10084    )
10085    .await;
10086
10087    cx.set_state("fn main() { let a = 2ˇ; }");
10088    cx.simulate_keystroke(".");
10089    let completion_item = lsp::CompletionItem {
10090        label: "Some".into(),
10091        kind: Some(lsp::CompletionItemKind::SNIPPET),
10092        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10093        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10094            kind: lsp::MarkupKind::Markdown,
10095            value: "```rust\nSome(2)\n```".to_string(),
10096        })),
10097        deprecated: Some(false),
10098        sort_text: Some("Some".to_string()),
10099        filter_text: Some("Some".to_string()),
10100        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10101        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10102            range: lsp::Range {
10103                start: lsp::Position {
10104                    line: 0,
10105                    character: 22,
10106                },
10107                end: lsp::Position {
10108                    line: 0,
10109                    character: 22,
10110                },
10111            },
10112            new_text: "Some(2)".to_string(),
10113        })),
10114        additional_text_edits: Some(vec![lsp::TextEdit {
10115            range: lsp::Range {
10116                start: lsp::Position {
10117                    line: 0,
10118                    character: 20,
10119                },
10120                end: lsp::Position {
10121                    line: 0,
10122                    character: 22,
10123                },
10124            },
10125            new_text: "".to_string(),
10126        }]),
10127        ..Default::default()
10128    };
10129
10130    let closure_completion_item = completion_item.clone();
10131    let counter = Arc::new(AtomicUsize::new(0));
10132    let counter_clone = counter.clone();
10133    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10134        let task_completion_item = closure_completion_item.clone();
10135        counter_clone.fetch_add(1, atomic::Ordering::Release);
10136        async move {
10137            Ok(Some(lsp::CompletionResponse::Array(vec![
10138                task_completion_item,
10139            ])))
10140        }
10141    });
10142
10143    cx.condition(|editor, _| editor.context_menu_visible())
10144        .await;
10145    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10146    assert!(request.next().await.is_some());
10147    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10148
10149    cx.simulate_keystrokes("S o m");
10150    cx.condition(|editor, _| editor.context_menu_visible())
10151        .await;
10152    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10153    assert!(request.next().await.is_some());
10154    assert!(request.next().await.is_some());
10155    assert!(request.next().await.is_some());
10156    request.close();
10157    assert!(request.next().await.is_none());
10158    assert_eq!(
10159        counter.load(atomic::Ordering::Acquire),
10160        4,
10161        "With the completions menu open, only one LSP request should happen per input"
10162    );
10163}
10164
10165#[gpui::test]
10166async fn test_toggle_comment(cx: &mut TestAppContext) {
10167    init_test(cx, |_| {});
10168    let mut cx = EditorTestContext::new(cx).await;
10169    let language = Arc::new(Language::new(
10170        LanguageConfig {
10171            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10172            ..Default::default()
10173        },
10174        Some(tree_sitter_rust::LANGUAGE.into()),
10175    ));
10176    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10177
10178    // If multiple selections intersect a line, the line is only toggled once.
10179    cx.set_state(indoc! {"
10180        fn a() {
10181            «//b();
10182            ˇ»// «c();
10183            //ˇ»  d();
10184        }
10185    "});
10186
10187    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10188
10189    cx.assert_editor_state(indoc! {"
10190        fn a() {
10191            «b();
10192            c();
10193            ˇ» d();
10194        }
10195    "});
10196
10197    // The comment prefix is inserted at the same column for every line in a
10198    // selection.
10199    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10200
10201    cx.assert_editor_state(indoc! {"
10202        fn a() {
10203            // «b();
10204            // c();
10205            ˇ»//  d();
10206        }
10207    "});
10208
10209    // If a selection ends at the beginning of a line, that line is not toggled.
10210    cx.set_selections_state(indoc! {"
10211        fn a() {
10212            // b();
10213            «// c();
10214        ˇ»    //  d();
10215        }
10216    "});
10217
10218    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10219
10220    cx.assert_editor_state(indoc! {"
10221        fn a() {
10222            // b();
10223            «c();
10224        ˇ»    //  d();
10225        }
10226    "});
10227
10228    // If a selection span a single line and is empty, the line is toggled.
10229    cx.set_state(indoc! {"
10230        fn a() {
10231            a();
10232            b();
10233        ˇ
10234        }
10235    "});
10236
10237    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10238
10239    cx.assert_editor_state(indoc! {"
10240        fn a() {
10241            a();
10242            b();
10243        //•ˇ
10244        }
10245    "});
10246
10247    // If a selection span multiple lines, empty lines are not toggled.
10248    cx.set_state(indoc! {"
10249        fn a() {
10250            «a();
10251
10252            c();ˇ»
10253        }
10254    "});
10255
10256    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10257
10258    cx.assert_editor_state(indoc! {"
10259        fn a() {
10260            // «a();
10261
10262            // c();ˇ»
10263        }
10264    "});
10265
10266    // If a selection includes multiple comment prefixes, all lines are uncommented.
10267    cx.set_state(indoc! {"
10268        fn a() {
10269            «// a();
10270            /// b();
10271            //! c();ˇ»
10272        }
10273    "});
10274
10275    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10276
10277    cx.assert_editor_state(indoc! {"
10278        fn a() {
10279            «a();
10280            b();
10281            c();ˇ»
10282        }
10283    "});
10284}
10285
10286#[gpui::test]
10287async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10288    init_test(cx, |_| {});
10289    let mut cx = EditorTestContext::new(cx).await;
10290    let language = Arc::new(Language::new(
10291        LanguageConfig {
10292            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10293            ..Default::default()
10294        },
10295        Some(tree_sitter_rust::LANGUAGE.into()),
10296    ));
10297    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10298
10299    let toggle_comments = &ToggleComments {
10300        advance_downwards: false,
10301        ignore_indent: true,
10302    };
10303
10304    // If multiple selections intersect a line, the line is only toggled once.
10305    cx.set_state(indoc! {"
10306        fn a() {
10307        //    «b();
10308        //    c();
10309        //    ˇ» d();
10310        }
10311    "});
10312
10313    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10314
10315    cx.assert_editor_state(indoc! {"
10316        fn a() {
10317            «b();
10318            c();
10319            ˇ» d();
10320        }
10321    "});
10322
10323    // The comment prefix is inserted at the beginning of each line
10324    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10325
10326    cx.assert_editor_state(indoc! {"
10327        fn a() {
10328        //    «b();
10329        //    c();
10330        //    ˇ» d();
10331        }
10332    "});
10333
10334    // If a selection ends at the beginning of a line, that line is not toggled.
10335    cx.set_selections_state(indoc! {"
10336        fn a() {
10337        //    b();
10338        //    «c();
10339        ˇ»//     d();
10340        }
10341    "});
10342
10343    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10344
10345    cx.assert_editor_state(indoc! {"
10346        fn a() {
10347        //    b();
10348            «c();
10349        ˇ»//     d();
10350        }
10351    "});
10352
10353    // If a selection span a single line and is empty, the line is toggled.
10354    cx.set_state(indoc! {"
10355        fn a() {
10356            a();
10357            b();
10358        ˇ
10359        }
10360    "});
10361
10362    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10363
10364    cx.assert_editor_state(indoc! {"
10365        fn a() {
10366            a();
10367            b();
10368        //ˇ
10369        }
10370    "});
10371
10372    // If a selection span multiple lines, empty lines are not toggled.
10373    cx.set_state(indoc! {"
10374        fn a() {
10375            «a();
10376
10377            c();ˇ»
10378        }
10379    "});
10380
10381    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10382
10383    cx.assert_editor_state(indoc! {"
10384        fn a() {
10385        //    «a();
10386
10387        //    c();ˇ»
10388        }
10389    "});
10390
10391    // If a selection includes multiple comment prefixes, all lines are uncommented.
10392    cx.set_state(indoc! {"
10393        fn a() {
10394        //    «a();
10395        ///    b();
10396        //!    c();ˇ»
10397        }
10398    "});
10399
10400    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10401
10402    cx.assert_editor_state(indoc! {"
10403        fn a() {
10404            «a();
10405            b();
10406            c();ˇ»
10407        }
10408    "});
10409}
10410
10411#[gpui::test]
10412async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10413    init_test(cx, |_| {});
10414
10415    let language = Arc::new(Language::new(
10416        LanguageConfig {
10417            line_comments: vec!["// ".into()],
10418            ..Default::default()
10419        },
10420        Some(tree_sitter_rust::LANGUAGE.into()),
10421    ));
10422
10423    let mut cx = EditorTestContext::new(cx).await;
10424
10425    cx.language_registry().add(language.clone());
10426    cx.update_buffer(|buffer, cx| {
10427        buffer.set_language(Some(language), cx);
10428    });
10429
10430    let toggle_comments = &ToggleComments {
10431        advance_downwards: true,
10432        ignore_indent: false,
10433    };
10434
10435    // Single cursor on one line -> advance
10436    // Cursor moves horizontally 3 characters as well on non-blank line
10437    cx.set_state(indoc!(
10438        "fn a() {
10439             ˇdog();
10440             cat();
10441        }"
10442    ));
10443    cx.update_editor(|editor, window, cx| {
10444        editor.toggle_comments(toggle_comments, window, cx);
10445    });
10446    cx.assert_editor_state(indoc!(
10447        "fn a() {
10448             // dog();
10449             catˇ();
10450        }"
10451    ));
10452
10453    // Single selection on one line -> don't advance
10454    cx.set_state(indoc!(
10455        "fn a() {
10456             «dog()ˇ»;
10457             cat();
10458        }"
10459    ));
10460    cx.update_editor(|editor, window, cx| {
10461        editor.toggle_comments(toggle_comments, window, cx);
10462    });
10463    cx.assert_editor_state(indoc!(
10464        "fn a() {
10465             // «dog()ˇ»;
10466             cat();
10467        }"
10468    ));
10469
10470    // Multiple cursors on one line -> advance
10471    cx.set_state(indoc!(
10472        "fn a() {
10473             ˇdˇog();
10474             cat();
10475        }"
10476    ));
10477    cx.update_editor(|editor, window, cx| {
10478        editor.toggle_comments(toggle_comments, window, cx);
10479    });
10480    cx.assert_editor_state(indoc!(
10481        "fn a() {
10482             // dog();
10483             catˇ(ˇ);
10484        }"
10485    ));
10486
10487    // Multiple cursors on one line, with selection -> don't advance
10488    cx.set_state(indoc!(
10489        "fn a() {
10490             ˇdˇog«()ˇ»;
10491             cat();
10492        }"
10493    ));
10494    cx.update_editor(|editor, window, cx| {
10495        editor.toggle_comments(toggle_comments, window, cx);
10496    });
10497    cx.assert_editor_state(indoc!(
10498        "fn a() {
10499             // ˇdˇog«()ˇ»;
10500             cat();
10501        }"
10502    ));
10503
10504    // Single cursor on one line -> advance
10505    // Cursor moves to column 0 on blank line
10506    cx.set_state(indoc!(
10507        "fn a() {
10508             ˇdog();
10509
10510             cat();
10511        }"
10512    ));
10513    cx.update_editor(|editor, window, cx| {
10514        editor.toggle_comments(toggle_comments, window, cx);
10515    });
10516    cx.assert_editor_state(indoc!(
10517        "fn a() {
10518             // dog();
10519        ˇ
10520             cat();
10521        }"
10522    ));
10523
10524    // Single cursor on one line -> advance
10525    // Cursor starts and ends at column 0
10526    cx.set_state(indoc!(
10527        "fn a() {
10528         ˇ    dog();
10529             cat();
10530        }"
10531    ));
10532    cx.update_editor(|editor, window, cx| {
10533        editor.toggle_comments(toggle_comments, window, cx);
10534    });
10535    cx.assert_editor_state(indoc!(
10536        "fn a() {
10537             // dog();
10538         ˇ    cat();
10539        }"
10540    ));
10541}
10542
10543#[gpui::test]
10544async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10545    init_test(cx, |_| {});
10546
10547    let mut cx = EditorTestContext::new(cx).await;
10548
10549    let html_language = Arc::new(
10550        Language::new(
10551            LanguageConfig {
10552                name: "HTML".into(),
10553                block_comment: Some(("<!-- ".into(), " -->".into())),
10554                ..Default::default()
10555            },
10556            Some(tree_sitter_html::LANGUAGE.into()),
10557        )
10558        .with_injection_query(
10559            r#"
10560            (script_element
10561                (raw_text) @injection.content
10562                (#set! injection.language "javascript"))
10563            "#,
10564        )
10565        .unwrap(),
10566    );
10567
10568    let javascript_language = Arc::new(Language::new(
10569        LanguageConfig {
10570            name: "JavaScript".into(),
10571            line_comments: vec!["// ".into()],
10572            ..Default::default()
10573        },
10574        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10575    ));
10576
10577    cx.language_registry().add(html_language.clone());
10578    cx.language_registry().add(javascript_language.clone());
10579    cx.update_buffer(|buffer, cx| {
10580        buffer.set_language(Some(html_language), cx);
10581    });
10582
10583    // Toggle comments for empty selections
10584    cx.set_state(
10585        &r#"
10586            <p>A</p>ˇ
10587            <p>B</p>ˇ
10588            <p>C</p>ˇ
10589        "#
10590        .unindent(),
10591    );
10592    cx.update_editor(|editor, window, cx| {
10593        editor.toggle_comments(&ToggleComments::default(), window, cx)
10594    });
10595    cx.assert_editor_state(
10596        &r#"
10597            <!-- <p>A</p>ˇ -->
10598            <!-- <p>B</p>ˇ -->
10599            <!-- <p>C</p>ˇ -->
10600        "#
10601        .unindent(),
10602    );
10603    cx.update_editor(|editor, window, cx| {
10604        editor.toggle_comments(&ToggleComments::default(), window, cx)
10605    });
10606    cx.assert_editor_state(
10607        &r#"
10608            <p>A</p>ˇ
10609            <p>B</p>ˇ
10610            <p>C</p>ˇ
10611        "#
10612        .unindent(),
10613    );
10614
10615    // Toggle comments for mixture of empty and non-empty selections, where
10616    // multiple selections occupy a given line.
10617    cx.set_state(
10618        &r#"
10619            <p>A«</p>
10620            <p>ˇ»B</p>ˇ
10621            <p>C«</p>
10622            <p>ˇ»D</p>ˇ
10623        "#
10624        .unindent(),
10625    );
10626
10627    cx.update_editor(|editor, window, cx| {
10628        editor.toggle_comments(&ToggleComments::default(), window, cx)
10629    });
10630    cx.assert_editor_state(
10631        &r#"
10632            <!-- <p>A«</p>
10633            <p>ˇ»B</p>ˇ -->
10634            <!-- <p>C«</p>
10635            <p>ˇ»D</p>ˇ -->
10636        "#
10637        .unindent(),
10638    );
10639    cx.update_editor(|editor, window, cx| {
10640        editor.toggle_comments(&ToggleComments::default(), window, cx)
10641    });
10642    cx.assert_editor_state(
10643        &r#"
10644            <p>A«</p>
10645            <p>ˇ»B</p>ˇ
10646            <p>C«</p>
10647            <p>ˇ»D</p>ˇ
10648        "#
10649        .unindent(),
10650    );
10651
10652    // Toggle comments when different languages are active for different
10653    // selections.
10654    cx.set_state(
10655        &r#"
10656            ˇ<script>
10657                ˇvar x = new Y();
10658            ˇ</script>
10659        "#
10660        .unindent(),
10661    );
10662    cx.executor().run_until_parked();
10663    cx.update_editor(|editor, window, cx| {
10664        editor.toggle_comments(&ToggleComments::default(), window, cx)
10665    });
10666    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10667    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10668    cx.assert_editor_state(
10669        &r#"
10670            <!-- ˇ<script> -->
10671                // ˇvar x = new Y();
10672            <!-- ˇ</script> -->
10673        "#
10674        .unindent(),
10675    );
10676}
10677
10678#[gpui::test]
10679fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10680    init_test(cx, |_| {});
10681
10682    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10683    let multibuffer = cx.new(|cx| {
10684        let mut multibuffer = MultiBuffer::new(ReadWrite);
10685        multibuffer.push_excerpts(
10686            buffer.clone(),
10687            [
10688                ExcerptRange {
10689                    context: Point::new(0, 0)..Point::new(0, 4),
10690                    primary: None,
10691                },
10692                ExcerptRange {
10693                    context: Point::new(1, 0)..Point::new(1, 4),
10694                    primary: None,
10695                },
10696            ],
10697            cx,
10698        );
10699        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10700        multibuffer
10701    });
10702
10703    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10704    editor.update_in(cx, |editor, window, cx| {
10705        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10706        editor.change_selections(None, window, cx, |s| {
10707            s.select_ranges([
10708                Point::new(0, 0)..Point::new(0, 0),
10709                Point::new(1, 0)..Point::new(1, 0),
10710            ])
10711        });
10712
10713        editor.handle_input("X", window, cx);
10714        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10715        assert_eq!(
10716            editor.selections.ranges(cx),
10717            [
10718                Point::new(0, 1)..Point::new(0, 1),
10719                Point::new(1, 1)..Point::new(1, 1),
10720            ]
10721        );
10722
10723        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10724        editor.change_selections(None, window, cx, |s| {
10725            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10726        });
10727        editor.backspace(&Default::default(), window, cx);
10728        assert_eq!(editor.text(cx), "Xa\nbbb");
10729        assert_eq!(
10730            editor.selections.ranges(cx),
10731            [Point::new(1, 0)..Point::new(1, 0)]
10732        );
10733
10734        editor.change_selections(None, window, cx, |s| {
10735            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10736        });
10737        editor.backspace(&Default::default(), window, cx);
10738        assert_eq!(editor.text(cx), "X\nbb");
10739        assert_eq!(
10740            editor.selections.ranges(cx),
10741            [Point::new(0, 1)..Point::new(0, 1)]
10742        );
10743    });
10744}
10745
10746#[gpui::test]
10747fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10748    init_test(cx, |_| {});
10749
10750    let markers = vec![('[', ']').into(), ('(', ')').into()];
10751    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10752        indoc! {"
10753            [aaaa
10754            (bbbb]
10755            cccc)",
10756        },
10757        markers.clone(),
10758    );
10759    let excerpt_ranges = markers.into_iter().map(|marker| {
10760        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10761        ExcerptRange {
10762            context,
10763            primary: None,
10764        }
10765    });
10766    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10767    let multibuffer = cx.new(|cx| {
10768        let mut multibuffer = MultiBuffer::new(ReadWrite);
10769        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10770        multibuffer
10771    });
10772
10773    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10774    editor.update_in(cx, |editor, window, cx| {
10775        let (expected_text, selection_ranges) = marked_text_ranges(
10776            indoc! {"
10777                aaaa
10778                bˇbbb
10779                bˇbbˇb
10780                cccc"
10781            },
10782            true,
10783        );
10784        assert_eq!(editor.text(cx), expected_text);
10785        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10786
10787        editor.handle_input("X", window, cx);
10788
10789        let (expected_text, expected_selections) = marked_text_ranges(
10790            indoc! {"
10791                aaaa
10792                bXˇbbXb
10793                bXˇbbXˇb
10794                cccc"
10795            },
10796            false,
10797        );
10798        assert_eq!(editor.text(cx), expected_text);
10799        assert_eq!(editor.selections.ranges(cx), expected_selections);
10800
10801        editor.newline(&Newline, window, cx);
10802        let (expected_text, expected_selections) = marked_text_ranges(
10803            indoc! {"
10804                aaaa
10805                bX
10806                ˇbbX
10807                b
10808                bX
10809                ˇbbX
10810                ˇb
10811                cccc"
10812            },
10813            false,
10814        );
10815        assert_eq!(editor.text(cx), expected_text);
10816        assert_eq!(editor.selections.ranges(cx), expected_selections);
10817    });
10818}
10819
10820#[gpui::test]
10821fn test_refresh_selections(cx: &mut TestAppContext) {
10822    init_test(cx, |_| {});
10823
10824    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10825    let mut excerpt1_id = None;
10826    let multibuffer = cx.new(|cx| {
10827        let mut multibuffer = MultiBuffer::new(ReadWrite);
10828        excerpt1_id = multibuffer
10829            .push_excerpts(
10830                buffer.clone(),
10831                [
10832                    ExcerptRange {
10833                        context: Point::new(0, 0)..Point::new(1, 4),
10834                        primary: None,
10835                    },
10836                    ExcerptRange {
10837                        context: Point::new(1, 0)..Point::new(2, 4),
10838                        primary: None,
10839                    },
10840                ],
10841                cx,
10842            )
10843            .into_iter()
10844            .next();
10845        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10846        multibuffer
10847    });
10848
10849    let editor = cx.add_window(|window, cx| {
10850        let mut editor = build_editor(multibuffer.clone(), window, cx);
10851        let snapshot = editor.snapshot(window, cx);
10852        editor.change_selections(None, window, cx, |s| {
10853            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10854        });
10855        editor.begin_selection(
10856            Point::new(2, 1).to_display_point(&snapshot),
10857            true,
10858            1,
10859            window,
10860            cx,
10861        );
10862        assert_eq!(
10863            editor.selections.ranges(cx),
10864            [
10865                Point::new(1, 3)..Point::new(1, 3),
10866                Point::new(2, 1)..Point::new(2, 1),
10867            ]
10868        );
10869        editor
10870    });
10871
10872    // Refreshing selections is a no-op when excerpts haven't changed.
10873    _ = editor.update(cx, |editor, window, cx| {
10874        editor.change_selections(None, window, cx, |s| s.refresh());
10875        assert_eq!(
10876            editor.selections.ranges(cx),
10877            [
10878                Point::new(1, 3)..Point::new(1, 3),
10879                Point::new(2, 1)..Point::new(2, 1),
10880            ]
10881        );
10882    });
10883
10884    multibuffer.update(cx, |multibuffer, cx| {
10885        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10886    });
10887    _ = editor.update(cx, |editor, window, cx| {
10888        // Removing an excerpt causes the first selection to become degenerate.
10889        assert_eq!(
10890            editor.selections.ranges(cx),
10891            [
10892                Point::new(0, 0)..Point::new(0, 0),
10893                Point::new(0, 1)..Point::new(0, 1)
10894            ]
10895        );
10896
10897        // Refreshing selections will relocate the first selection to the original buffer
10898        // location.
10899        editor.change_selections(None, window, cx, |s| s.refresh());
10900        assert_eq!(
10901            editor.selections.ranges(cx),
10902            [
10903                Point::new(0, 1)..Point::new(0, 1),
10904                Point::new(0, 3)..Point::new(0, 3)
10905            ]
10906        );
10907        assert!(editor.selections.pending_anchor().is_some());
10908    });
10909}
10910
10911#[gpui::test]
10912fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10913    init_test(cx, |_| {});
10914
10915    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10916    let mut excerpt1_id = None;
10917    let multibuffer = cx.new(|cx| {
10918        let mut multibuffer = MultiBuffer::new(ReadWrite);
10919        excerpt1_id = multibuffer
10920            .push_excerpts(
10921                buffer.clone(),
10922                [
10923                    ExcerptRange {
10924                        context: Point::new(0, 0)..Point::new(1, 4),
10925                        primary: None,
10926                    },
10927                    ExcerptRange {
10928                        context: Point::new(1, 0)..Point::new(2, 4),
10929                        primary: None,
10930                    },
10931                ],
10932                cx,
10933            )
10934            .into_iter()
10935            .next();
10936        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10937        multibuffer
10938    });
10939
10940    let editor = cx.add_window(|window, cx| {
10941        let mut editor = build_editor(multibuffer.clone(), window, cx);
10942        let snapshot = editor.snapshot(window, cx);
10943        editor.begin_selection(
10944            Point::new(1, 3).to_display_point(&snapshot),
10945            false,
10946            1,
10947            window,
10948            cx,
10949        );
10950        assert_eq!(
10951            editor.selections.ranges(cx),
10952            [Point::new(1, 3)..Point::new(1, 3)]
10953        );
10954        editor
10955    });
10956
10957    multibuffer.update(cx, |multibuffer, cx| {
10958        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10959    });
10960    _ = editor.update(cx, |editor, window, cx| {
10961        assert_eq!(
10962            editor.selections.ranges(cx),
10963            [Point::new(0, 0)..Point::new(0, 0)]
10964        );
10965
10966        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10967        editor.change_selections(None, window, cx, |s| s.refresh());
10968        assert_eq!(
10969            editor.selections.ranges(cx),
10970            [Point::new(0, 3)..Point::new(0, 3)]
10971        );
10972        assert!(editor.selections.pending_anchor().is_some());
10973    });
10974}
10975
10976#[gpui::test]
10977async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10978    init_test(cx, |_| {});
10979
10980    let language = Arc::new(
10981        Language::new(
10982            LanguageConfig {
10983                brackets: BracketPairConfig {
10984                    pairs: vec![
10985                        BracketPair {
10986                            start: "{".to_string(),
10987                            end: "}".to_string(),
10988                            close: true,
10989                            surround: true,
10990                            newline: true,
10991                        },
10992                        BracketPair {
10993                            start: "/* ".to_string(),
10994                            end: " */".to_string(),
10995                            close: true,
10996                            surround: true,
10997                            newline: true,
10998                        },
10999                    ],
11000                    ..Default::default()
11001                },
11002                ..Default::default()
11003            },
11004            Some(tree_sitter_rust::LANGUAGE.into()),
11005        )
11006        .with_indents_query("")
11007        .unwrap(),
11008    );
11009
11010    let text = concat!(
11011        "{   }\n",     //
11012        "  x\n",       //
11013        "  /*   */\n", //
11014        "x\n",         //
11015        "{{} }\n",     //
11016    );
11017
11018    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11019    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11020    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11021    editor
11022        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11023        .await;
11024
11025    editor.update_in(cx, |editor, window, cx| {
11026        editor.change_selections(None, window, cx, |s| {
11027            s.select_display_ranges([
11028                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11029                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11030                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11031            ])
11032        });
11033        editor.newline(&Newline, window, cx);
11034
11035        assert_eq!(
11036            editor.buffer().read(cx).read(cx).text(),
11037            concat!(
11038                "{ \n",    // Suppress rustfmt
11039                "\n",      //
11040                "}\n",     //
11041                "  x\n",   //
11042                "  /* \n", //
11043                "  \n",    //
11044                "  */\n",  //
11045                "x\n",     //
11046                "{{} \n",  //
11047                "}\n",     //
11048            )
11049        );
11050    });
11051}
11052
11053#[gpui::test]
11054fn test_highlighted_ranges(cx: &mut TestAppContext) {
11055    init_test(cx, |_| {});
11056
11057    let editor = cx.add_window(|window, cx| {
11058        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11059        build_editor(buffer.clone(), window, cx)
11060    });
11061
11062    _ = editor.update(cx, |editor, window, cx| {
11063        struct Type1;
11064        struct Type2;
11065
11066        let buffer = editor.buffer.read(cx).snapshot(cx);
11067
11068        let anchor_range =
11069            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11070
11071        editor.highlight_background::<Type1>(
11072            &[
11073                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11074                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11075                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11076                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11077            ],
11078            |_| Hsla::red(),
11079            cx,
11080        );
11081        editor.highlight_background::<Type2>(
11082            &[
11083                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11084                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11085                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11086                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11087            ],
11088            |_| Hsla::green(),
11089            cx,
11090        );
11091
11092        let snapshot = editor.snapshot(window, cx);
11093        let mut highlighted_ranges = editor.background_highlights_in_range(
11094            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11095            &snapshot,
11096            cx.theme().colors(),
11097        );
11098        // Enforce a consistent ordering based on color without relying on the ordering of the
11099        // highlight's `TypeId` which is non-executor.
11100        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11101        assert_eq!(
11102            highlighted_ranges,
11103            &[
11104                (
11105                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11106                    Hsla::red(),
11107                ),
11108                (
11109                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11110                    Hsla::red(),
11111                ),
11112                (
11113                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11114                    Hsla::green(),
11115                ),
11116                (
11117                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11118                    Hsla::green(),
11119                ),
11120            ]
11121        );
11122        assert_eq!(
11123            editor.background_highlights_in_range(
11124                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11125                &snapshot,
11126                cx.theme().colors(),
11127            ),
11128            &[(
11129                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11130                Hsla::red(),
11131            )]
11132        );
11133    });
11134}
11135
11136#[gpui::test]
11137async fn test_following(cx: &mut TestAppContext) {
11138    init_test(cx, |_| {});
11139
11140    let fs = FakeFs::new(cx.executor());
11141    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11142
11143    let buffer = project.update(cx, |project, cx| {
11144        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11145        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11146    });
11147    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11148    let follower = cx.update(|cx| {
11149        cx.open_window(
11150            WindowOptions {
11151                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11152                    gpui::Point::new(px(0.), px(0.)),
11153                    gpui::Point::new(px(10.), px(80.)),
11154                ))),
11155                ..Default::default()
11156            },
11157            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11158        )
11159        .unwrap()
11160    });
11161
11162    let is_still_following = Rc::new(RefCell::new(true));
11163    let follower_edit_event_count = Rc::new(RefCell::new(0));
11164    let pending_update = Rc::new(RefCell::new(None));
11165    let leader_entity = leader.root(cx).unwrap();
11166    let follower_entity = follower.root(cx).unwrap();
11167    _ = follower.update(cx, {
11168        let update = pending_update.clone();
11169        let is_still_following = is_still_following.clone();
11170        let follower_edit_event_count = follower_edit_event_count.clone();
11171        |_, window, cx| {
11172            cx.subscribe_in(
11173                &leader_entity,
11174                window,
11175                move |_, leader, event, window, cx| {
11176                    leader.read(cx).add_event_to_update_proto(
11177                        event,
11178                        &mut update.borrow_mut(),
11179                        window,
11180                        cx,
11181                    );
11182                },
11183            )
11184            .detach();
11185
11186            cx.subscribe_in(
11187                &follower_entity,
11188                window,
11189                move |_, _, event: &EditorEvent, _window, _cx| {
11190                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11191                        *is_still_following.borrow_mut() = false;
11192                    }
11193
11194                    if let EditorEvent::BufferEdited = event {
11195                        *follower_edit_event_count.borrow_mut() += 1;
11196                    }
11197                },
11198            )
11199            .detach();
11200        }
11201    });
11202
11203    // Update the selections only
11204    _ = leader.update(cx, |leader, window, cx| {
11205        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11206    });
11207    follower
11208        .update(cx, |follower, window, cx| {
11209            follower.apply_update_proto(
11210                &project,
11211                pending_update.borrow_mut().take().unwrap(),
11212                window,
11213                cx,
11214            )
11215        })
11216        .unwrap()
11217        .await
11218        .unwrap();
11219    _ = follower.update(cx, |follower, _, cx| {
11220        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11221    });
11222    assert!(*is_still_following.borrow());
11223    assert_eq!(*follower_edit_event_count.borrow(), 0);
11224
11225    // Update the scroll position only
11226    _ = leader.update(cx, |leader, window, cx| {
11227        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11228    });
11229    follower
11230        .update(cx, |follower, window, cx| {
11231            follower.apply_update_proto(
11232                &project,
11233                pending_update.borrow_mut().take().unwrap(),
11234                window,
11235                cx,
11236            )
11237        })
11238        .unwrap()
11239        .await
11240        .unwrap();
11241    assert_eq!(
11242        follower
11243            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11244            .unwrap(),
11245        gpui::Point::new(1.5, 3.5)
11246    );
11247    assert!(*is_still_following.borrow());
11248    assert_eq!(*follower_edit_event_count.borrow(), 0);
11249
11250    // Update the selections and scroll position. The follower's scroll position is updated
11251    // via autoscroll, not via the leader's exact scroll position.
11252    _ = leader.update(cx, |leader, window, cx| {
11253        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11254        leader.request_autoscroll(Autoscroll::newest(), cx);
11255        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11256    });
11257    follower
11258        .update(cx, |follower, window, cx| {
11259            follower.apply_update_proto(
11260                &project,
11261                pending_update.borrow_mut().take().unwrap(),
11262                window,
11263                cx,
11264            )
11265        })
11266        .unwrap()
11267        .await
11268        .unwrap();
11269    _ = follower.update(cx, |follower, _, cx| {
11270        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11271        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11272    });
11273    assert!(*is_still_following.borrow());
11274
11275    // Creating a pending selection that precedes another selection
11276    _ = leader.update(cx, |leader, window, cx| {
11277        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11278        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11279    });
11280    follower
11281        .update(cx, |follower, window, cx| {
11282            follower.apply_update_proto(
11283                &project,
11284                pending_update.borrow_mut().take().unwrap(),
11285                window,
11286                cx,
11287            )
11288        })
11289        .unwrap()
11290        .await
11291        .unwrap();
11292    _ = follower.update(cx, |follower, _, cx| {
11293        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11294    });
11295    assert!(*is_still_following.borrow());
11296
11297    // Extend the pending selection so that it surrounds another selection
11298    _ = leader.update(cx, |leader, window, cx| {
11299        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11300    });
11301    follower
11302        .update(cx, |follower, window, cx| {
11303            follower.apply_update_proto(
11304                &project,
11305                pending_update.borrow_mut().take().unwrap(),
11306                window,
11307                cx,
11308            )
11309        })
11310        .unwrap()
11311        .await
11312        .unwrap();
11313    _ = follower.update(cx, |follower, _, cx| {
11314        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11315    });
11316
11317    // Scrolling locally breaks the follow
11318    _ = follower.update(cx, |follower, window, cx| {
11319        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11320        follower.set_scroll_anchor(
11321            ScrollAnchor {
11322                anchor: top_anchor,
11323                offset: gpui::Point::new(0.0, 0.5),
11324            },
11325            window,
11326            cx,
11327        );
11328    });
11329    assert!(!(*is_still_following.borrow()));
11330}
11331
11332#[gpui::test]
11333async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11334    init_test(cx, |_| {});
11335
11336    let fs = FakeFs::new(cx.executor());
11337    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11338    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11339    let pane = workspace
11340        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11341        .unwrap();
11342
11343    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11344
11345    let leader = pane.update_in(cx, |_, window, cx| {
11346        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11347        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11348    });
11349
11350    // Start following the editor when it has no excerpts.
11351    let mut state_message =
11352        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11353    let workspace_entity = workspace.root(cx).unwrap();
11354    let follower_1 = cx
11355        .update_window(*workspace.deref(), |_, window, cx| {
11356            Editor::from_state_proto(
11357                workspace_entity,
11358                ViewId {
11359                    creator: Default::default(),
11360                    id: 0,
11361                },
11362                &mut state_message,
11363                window,
11364                cx,
11365            )
11366        })
11367        .unwrap()
11368        .unwrap()
11369        .await
11370        .unwrap();
11371
11372    let update_message = Rc::new(RefCell::new(None));
11373    follower_1.update_in(cx, {
11374        let update = update_message.clone();
11375        |_, window, cx| {
11376            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11377                leader.read(cx).add_event_to_update_proto(
11378                    event,
11379                    &mut update.borrow_mut(),
11380                    window,
11381                    cx,
11382                );
11383            })
11384            .detach();
11385        }
11386    });
11387
11388    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11389        (
11390            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11391            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11392        )
11393    });
11394
11395    // Insert some excerpts.
11396    leader.update(cx, |leader, cx| {
11397        leader.buffer.update(cx, |multibuffer, cx| {
11398            let excerpt_ids = multibuffer.push_excerpts(
11399                buffer_1.clone(),
11400                [
11401                    ExcerptRange {
11402                        context: 1..6,
11403                        primary: None,
11404                    },
11405                    ExcerptRange {
11406                        context: 12..15,
11407                        primary: None,
11408                    },
11409                    ExcerptRange {
11410                        context: 0..3,
11411                        primary: None,
11412                    },
11413                ],
11414                cx,
11415            );
11416            multibuffer.insert_excerpts_after(
11417                excerpt_ids[0],
11418                buffer_2.clone(),
11419                [
11420                    ExcerptRange {
11421                        context: 8..12,
11422                        primary: None,
11423                    },
11424                    ExcerptRange {
11425                        context: 0..6,
11426                        primary: None,
11427                    },
11428                ],
11429                cx,
11430            );
11431        });
11432    });
11433
11434    // Apply the update of adding the excerpts.
11435    follower_1
11436        .update_in(cx, |follower, window, cx| {
11437            follower.apply_update_proto(
11438                &project,
11439                update_message.borrow().clone().unwrap(),
11440                window,
11441                cx,
11442            )
11443        })
11444        .await
11445        .unwrap();
11446    assert_eq!(
11447        follower_1.update(cx, |editor, cx| editor.text(cx)),
11448        leader.update(cx, |editor, cx| editor.text(cx))
11449    );
11450    update_message.borrow_mut().take();
11451
11452    // Start following separately after it already has excerpts.
11453    let mut state_message =
11454        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11455    let workspace_entity = workspace.root(cx).unwrap();
11456    let follower_2 = cx
11457        .update_window(*workspace.deref(), |_, window, cx| {
11458            Editor::from_state_proto(
11459                workspace_entity,
11460                ViewId {
11461                    creator: Default::default(),
11462                    id: 0,
11463                },
11464                &mut state_message,
11465                window,
11466                cx,
11467            )
11468        })
11469        .unwrap()
11470        .unwrap()
11471        .await
11472        .unwrap();
11473    assert_eq!(
11474        follower_2.update(cx, |editor, cx| editor.text(cx)),
11475        leader.update(cx, |editor, cx| editor.text(cx))
11476    );
11477
11478    // Remove some excerpts.
11479    leader.update(cx, |leader, cx| {
11480        leader.buffer.update(cx, |multibuffer, cx| {
11481            let excerpt_ids = multibuffer.excerpt_ids();
11482            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11483            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11484        });
11485    });
11486
11487    // Apply the update of removing the excerpts.
11488    follower_1
11489        .update_in(cx, |follower, window, cx| {
11490            follower.apply_update_proto(
11491                &project,
11492                update_message.borrow().clone().unwrap(),
11493                window,
11494                cx,
11495            )
11496        })
11497        .await
11498        .unwrap();
11499    follower_2
11500        .update_in(cx, |follower, window, cx| {
11501            follower.apply_update_proto(
11502                &project,
11503                update_message.borrow().clone().unwrap(),
11504                window,
11505                cx,
11506            )
11507        })
11508        .await
11509        .unwrap();
11510    update_message.borrow_mut().take();
11511    assert_eq!(
11512        follower_1.update(cx, |editor, cx| editor.text(cx)),
11513        leader.update(cx, |editor, cx| editor.text(cx))
11514    );
11515}
11516
11517#[gpui::test]
11518async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11519    init_test(cx, |_| {});
11520
11521    let mut cx = EditorTestContext::new(cx).await;
11522    let lsp_store =
11523        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11524
11525    cx.set_state(indoc! {"
11526        ˇfn func(abc def: i32) -> u32 {
11527        }
11528    "});
11529
11530    cx.update(|_, cx| {
11531        lsp_store.update(cx, |lsp_store, cx| {
11532            lsp_store
11533                .update_diagnostics(
11534                    LanguageServerId(0),
11535                    lsp::PublishDiagnosticsParams {
11536                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11537                        version: None,
11538                        diagnostics: vec![
11539                            lsp::Diagnostic {
11540                                range: lsp::Range::new(
11541                                    lsp::Position::new(0, 11),
11542                                    lsp::Position::new(0, 12),
11543                                ),
11544                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11545                                ..Default::default()
11546                            },
11547                            lsp::Diagnostic {
11548                                range: lsp::Range::new(
11549                                    lsp::Position::new(0, 12),
11550                                    lsp::Position::new(0, 15),
11551                                ),
11552                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11553                                ..Default::default()
11554                            },
11555                            lsp::Diagnostic {
11556                                range: lsp::Range::new(
11557                                    lsp::Position::new(0, 25),
11558                                    lsp::Position::new(0, 28),
11559                                ),
11560                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11561                                ..Default::default()
11562                            },
11563                        ],
11564                    },
11565                    &[],
11566                    cx,
11567                )
11568                .unwrap()
11569        });
11570    });
11571
11572    executor.run_until_parked();
11573
11574    cx.update_editor(|editor, window, cx| {
11575        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11576    });
11577
11578    cx.assert_editor_state(indoc! {"
11579        fn func(abc def: i32) -> ˇu32 {
11580        }
11581    "});
11582
11583    cx.update_editor(|editor, window, cx| {
11584        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11585    });
11586
11587    cx.assert_editor_state(indoc! {"
11588        fn func(abc ˇdef: i32) -> u32 {
11589        }
11590    "});
11591
11592    cx.update_editor(|editor, window, cx| {
11593        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11594    });
11595
11596    cx.assert_editor_state(indoc! {"
11597        fn func(abcˇ def: i32) -> u32 {
11598        }
11599    "});
11600
11601    cx.update_editor(|editor, window, cx| {
11602        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11603    });
11604
11605    cx.assert_editor_state(indoc! {"
11606        fn func(abc def: i32) -> ˇu32 {
11607        }
11608    "});
11609}
11610
11611#[gpui::test]
11612async fn cycle_through_same_place_diagnostics(
11613    executor: BackgroundExecutor,
11614    cx: &mut TestAppContext,
11615) {
11616    init_test(cx, |_| {});
11617
11618    let mut cx = EditorTestContext::new(cx).await;
11619    let lsp_store =
11620        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11621
11622    cx.set_state(indoc! {"
11623        ˇfn func(abc def: i32) -> u32 {
11624        }
11625    "});
11626
11627    cx.update(|_, cx| {
11628        lsp_store.update(cx, |lsp_store, cx| {
11629            lsp_store
11630                .update_diagnostics(
11631                    LanguageServerId(0),
11632                    lsp::PublishDiagnosticsParams {
11633                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11634                        version: None,
11635                        diagnostics: vec![
11636                            lsp::Diagnostic {
11637                                range: lsp::Range::new(
11638                                    lsp::Position::new(0, 11),
11639                                    lsp::Position::new(0, 12),
11640                                ),
11641                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11642                                ..Default::default()
11643                            },
11644                            lsp::Diagnostic {
11645                                range: lsp::Range::new(
11646                                    lsp::Position::new(0, 12),
11647                                    lsp::Position::new(0, 15),
11648                                ),
11649                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11650                                ..Default::default()
11651                            },
11652                            lsp::Diagnostic {
11653                                range: lsp::Range::new(
11654                                    lsp::Position::new(0, 12),
11655                                    lsp::Position::new(0, 15),
11656                                ),
11657                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11658                                ..Default::default()
11659                            },
11660                            lsp::Diagnostic {
11661                                range: lsp::Range::new(
11662                                    lsp::Position::new(0, 25),
11663                                    lsp::Position::new(0, 28),
11664                                ),
11665                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11666                                ..Default::default()
11667                            },
11668                        ],
11669                    },
11670                    &[],
11671                    cx,
11672                )
11673                .unwrap()
11674        });
11675    });
11676    executor.run_until_parked();
11677
11678    //// Backward
11679
11680    // Fourth diagnostic
11681    cx.update_editor(|editor, window, cx| {
11682        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11683    });
11684    cx.assert_editor_state(indoc! {"
11685        fn func(abc def: i32) -> ˇu32 {
11686        }
11687    "});
11688
11689    // Third diagnostic
11690    cx.update_editor(|editor, window, cx| {
11691        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11692    });
11693    cx.assert_editor_state(indoc! {"
11694        fn func(abc ˇdef: i32) -> u32 {
11695        }
11696    "});
11697
11698    // Second diagnostic, same place
11699    cx.update_editor(|editor, window, cx| {
11700        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11701    });
11702    cx.assert_editor_state(indoc! {"
11703        fn func(abc ˇdef: i32) -> u32 {
11704        }
11705    "});
11706
11707    // First diagnostic
11708    cx.update_editor(|editor, window, cx| {
11709        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11710    });
11711    cx.assert_editor_state(indoc! {"
11712        fn func(abcˇ def: i32) -> u32 {
11713        }
11714    "});
11715
11716    // Wrapped over, fourth diagnostic
11717    cx.update_editor(|editor, window, cx| {
11718        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11719    });
11720    cx.assert_editor_state(indoc! {"
11721        fn func(abc def: i32) -> ˇu32 {
11722        }
11723    "});
11724
11725    cx.update_editor(|editor, window, cx| {
11726        editor.move_to_beginning(&MoveToBeginning, window, cx);
11727    });
11728    cx.assert_editor_state(indoc! {"
11729        ˇfn func(abc def: i32) -> u32 {
11730        }
11731    "});
11732
11733    //// Forward
11734
11735    // First diagnostic
11736    cx.update_editor(|editor, window, cx| {
11737        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11738    });
11739    cx.assert_editor_state(indoc! {"
11740        fn func(abcˇ def: i32) -> u32 {
11741        }
11742    "});
11743
11744    // Second diagnostic
11745    cx.update_editor(|editor, window, cx| {
11746        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11747    });
11748    cx.assert_editor_state(indoc! {"
11749        fn func(abc ˇdef: i32) -> u32 {
11750        }
11751    "});
11752
11753    // Third diagnostic, same place
11754    cx.update_editor(|editor, window, cx| {
11755        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11756    });
11757    cx.assert_editor_state(indoc! {"
11758        fn func(abc ˇdef: i32) -> u32 {
11759        }
11760    "});
11761
11762    // Fourth diagnostic
11763    cx.update_editor(|editor, window, cx| {
11764        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11765    });
11766    cx.assert_editor_state(indoc! {"
11767        fn func(abc def: i32) -> ˇu32 {
11768        }
11769    "});
11770
11771    // Wrapped around, first diagnostic
11772    cx.update_editor(|editor, window, cx| {
11773        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11774    });
11775    cx.assert_editor_state(indoc! {"
11776        fn func(abcˇ def: i32) -> u32 {
11777        }
11778    "});
11779}
11780
11781#[gpui::test]
11782async fn active_diagnostics_dismiss_after_invalidation(
11783    executor: BackgroundExecutor,
11784    cx: &mut TestAppContext,
11785) {
11786    init_test(cx, |_| {});
11787
11788    let mut cx = EditorTestContext::new(cx).await;
11789    let lsp_store =
11790        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11791
11792    cx.set_state(indoc! {"
11793        ˇfn func(abc def: i32) -> u32 {
11794        }
11795    "});
11796
11797    let message = "Something's wrong!";
11798    cx.update(|_, cx| {
11799        lsp_store.update(cx, |lsp_store, cx| {
11800            lsp_store
11801                .update_diagnostics(
11802                    LanguageServerId(0),
11803                    lsp::PublishDiagnosticsParams {
11804                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11805                        version: None,
11806                        diagnostics: vec![lsp::Diagnostic {
11807                            range: lsp::Range::new(
11808                                lsp::Position::new(0, 11),
11809                                lsp::Position::new(0, 12),
11810                            ),
11811                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11812                            message: message.to_string(),
11813                            ..Default::default()
11814                        }],
11815                    },
11816                    &[],
11817                    cx,
11818                )
11819                .unwrap()
11820        });
11821    });
11822    executor.run_until_parked();
11823
11824    cx.update_editor(|editor, window, cx| {
11825        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11826        assert_eq!(
11827            editor
11828                .active_diagnostics
11829                .as_ref()
11830                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11831            Some(message),
11832            "Should have a diagnostics group activated"
11833        );
11834    });
11835    cx.assert_editor_state(indoc! {"
11836        fn func(abcˇ def: i32) -> u32 {
11837        }
11838    "});
11839
11840    cx.update(|_, cx| {
11841        lsp_store.update(cx, |lsp_store, cx| {
11842            lsp_store
11843                .update_diagnostics(
11844                    LanguageServerId(0),
11845                    lsp::PublishDiagnosticsParams {
11846                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11847                        version: None,
11848                        diagnostics: Vec::new(),
11849                    },
11850                    &[],
11851                    cx,
11852                )
11853                .unwrap()
11854        });
11855    });
11856    executor.run_until_parked();
11857    cx.update_editor(|editor, _, _| {
11858        assert_eq!(
11859            editor.active_diagnostics, None,
11860            "After no diagnostics set to the editor, no diagnostics should be active"
11861        );
11862    });
11863    cx.assert_editor_state(indoc! {"
11864        fn func(abcˇ def: i32) -> u32 {
11865        }
11866    "});
11867
11868    cx.update_editor(|editor, window, cx| {
11869        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11870        assert_eq!(
11871            editor.active_diagnostics, None,
11872            "Should be no diagnostics to go to and activate"
11873        );
11874    });
11875    cx.assert_editor_state(indoc! {"
11876        fn func(abcˇ def: i32) -> u32 {
11877        }
11878    "});
11879}
11880
11881#[gpui::test]
11882async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11883    init_test(cx, |_| {});
11884
11885    let mut cx = EditorTestContext::new(cx).await;
11886
11887    cx.set_state(indoc! {"
11888        fn func(abˇc def: i32) -> u32 {
11889        }
11890    "});
11891    let lsp_store =
11892        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11893
11894    cx.update(|_, cx| {
11895        lsp_store.update(cx, |lsp_store, cx| {
11896            lsp_store.update_diagnostics(
11897                LanguageServerId(0),
11898                lsp::PublishDiagnosticsParams {
11899                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11900                    version: None,
11901                    diagnostics: vec![lsp::Diagnostic {
11902                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11903                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11904                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11905                        ..Default::default()
11906                    }],
11907                },
11908                &[],
11909                cx,
11910            )
11911        })
11912    }).unwrap();
11913    cx.run_until_parked();
11914    cx.update_editor(|editor, window, cx| {
11915        hover_popover::hover(editor, &Default::default(), window, cx)
11916    });
11917    cx.run_until_parked();
11918    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11919}
11920
11921#[gpui::test]
11922async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11923    init_test(cx, |_| {});
11924
11925    let mut cx = EditorTestContext::new(cx).await;
11926
11927    let diff_base = r#"
11928        use some::mod;
11929
11930        const A: u32 = 42;
11931
11932        fn main() {
11933            println!("hello");
11934
11935            println!("world");
11936        }
11937        "#
11938    .unindent();
11939
11940    // Edits are modified, removed, modified, added
11941    cx.set_state(
11942        &r#"
11943        use some::modified;
11944
11945        ˇ
11946        fn main() {
11947            println!("hello there");
11948
11949            println!("around the");
11950            println!("world");
11951        }
11952        "#
11953        .unindent(),
11954    );
11955
11956    cx.set_head_text(&diff_base);
11957    executor.run_until_parked();
11958
11959    cx.update_editor(|editor, window, cx| {
11960        //Wrap around the bottom of the buffer
11961        for _ in 0..3 {
11962            editor.go_to_next_hunk(&GoToHunk, window, cx);
11963        }
11964    });
11965
11966    cx.assert_editor_state(
11967        &r#"
11968        ˇuse some::modified;
11969
11970
11971        fn main() {
11972            println!("hello there");
11973
11974            println!("around the");
11975            println!("world");
11976        }
11977        "#
11978        .unindent(),
11979    );
11980
11981    cx.update_editor(|editor, window, cx| {
11982        //Wrap around the top of the buffer
11983        for _ in 0..2 {
11984            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11985        }
11986    });
11987
11988    cx.assert_editor_state(
11989        &r#"
11990        use some::modified;
11991
11992
11993        fn main() {
11994        ˇ    println!("hello there");
11995
11996            println!("around the");
11997            println!("world");
11998        }
11999        "#
12000        .unindent(),
12001    );
12002
12003    cx.update_editor(|editor, window, cx| {
12004        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12005    });
12006
12007    cx.assert_editor_state(
12008        &r#"
12009        use some::modified;
12010
12011        ˇ
12012        fn main() {
12013            println!("hello there");
12014
12015            println!("around the");
12016            println!("world");
12017        }
12018        "#
12019        .unindent(),
12020    );
12021
12022    cx.update_editor(|editor, window, cx| {
12023        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12024    });
12025
12026    cx.assert_editor_state(
12027        &r#"
12028        ˇuse some::modified;
12029
12030
12031        fn main() {
12032            println!("hello there");
12033
12034            println!("around the");
12035            println!("world");
12036        }
12037        "#
12038        .unindent(),
12039    );
12040
12041    cx.update_editor(|editor, window, cx| {
12042        for _ in 0..2 {
12043            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12044        }
12045    });
12046
12047    cx.assert_editor_state(
12048        &r#"
12049        use some::modified;
12050
12051
12052        fn main() {
12053        ˇ    println!("hello there");
12054
12055            println!("around the");
12056            println!("world");
12057        }
12058        "#
12059        .unindent(),
12060    );
12061
12062    cx.update_editor(|editor, window, cx| {
12063        editor.fold(&Fold, window, cx);
12064    });
12065
12066    cx.update_editor(|editor, window, cx| {
12067        editor.go_to_next_hunk(&GoToHunk, window, cx);
12068    });
12069
12070    cx.assert_editor_state(
12071        &r#"
12072        ˇuse some::modified;
12073
12074
12075        fn main() {
12076            println!("hello there");
12077
12078            println!("around the");
12079            println!("world");
12080        }
12081        "#
12082        .unindent(),
12083    );
12084}
12085
12086#[test]
12087fn test_split_words() {
12088    fn split(text: &str) -> Vec<&str> {
12089        split_words(text).collect()
12090    }
12091
12092    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12093    assert_eq!(split("hello_world"), &["hello_", "world"]);
12094    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12095    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12096    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12097    assert_eq!(split("helloworld"), &["helloworld"]);
12098
12099    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12100}
12101
12102#[gpui::test]
12103async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12104    init_test(cx, |_| {});
12105
12106    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12107    let mut assert = |before, after| {
12108        let _state_context = cx.set_state(before);
12109        cx.run_until_parked();
12110        cx.update_editor(|editor, window, cx| {
12111            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12112        });
12113        cx.run_until_parked();
12114        cx.assert_editor_state(after);
12115    };
12116
12117    // Outside bracket jumps to outside of matching bracket
12118    assert("console.logˇ(var);", "console.log(var)ˇ;");
12119    assert("console.log(var)ˇ;", "console.logˇ(var);");
12120
12121    // Inside bracket jumps to inside of matching bracket
12122    assert("console.log(ˇvar);", "console.log(varˇ);");
12123    assert("console.log(varˇ);", "console.log(ˇvar);");
12124
12125    // When outside a bracket and inside, favor jumping to the inside bracket
12126    assert(
12127        "console.log('foo', [1, 2, 3]ˇ);",
12128        "console.log(ˇ'foo', [1, 2, 3]);",
12129    );
12130    assert(
12131        "console.log(ˇ'foo', [1, 2, 3]);",
12132        "console.log('foo', [1, 2, 3]ˇ);",
12133    );
12134
12135    // Bias forward if two options are equally likely
12136    assert(
12137        "let result = curried_fun()ˇ();",
12138        "let result = curried_fun()()ˇ;",
12139    );
12140
12141    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12142    assert(
12143        indoc! {"
12144            function test() {
12145                console.log('test')ˇ
12146            }"},
12147        indoc! {"
12148            function test() {
12149                console.logˇ('test')
12150            }"},
12151    );
12152}
12153
12154#[gpui::test]
12155async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12156    init_test(cx, |_| {});
12157
12158    let fs = FakeFs::new(cx.executor());
12159    fs.insert_tree(
12160        path!("/a"),
12161        json!({
12162            "main.rs": "fn main() { let a = 5; }",
12163            "other.rs": "// Test file",
12164        }),
12165    )
12166    .await;
12167    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12168
12169    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12170    language_registry.add(Arc::new(Language::new(
12171        LanguageConfig {
12172            name: "Rust".into(),
12173            matcher: LanguageMatcher {
12174                path_suffixes: vec!["rs".to_string()],
12175                ..Default::default()
12176            },
12177            brackets: BracketPairConfig {
12178                pairs: vec![BracketPair {
12179                    start: "{".to_string(),
12180                    end: "}".to_string(),
12181                    close: true,
12182                    surround: true,
12183                    newline: true,
12184                }],
12185                disabled_scopes_by_bracket_ix: Vec::new(),
12186            },
12187            ..Default::default()
12188        },
12189        Some(tree_sitter_rust::LANGUAGE.into()),
12190    )));
12191    let mut fake_servers = language_registry.register_fake_lsp(
12192        "Rust",
12193        FakeLspAdapter {
12194            capabilities: lsp::ServerCapabilities {
12195                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12196                    first_trigger_character: "{".to_string(),
12197                    more_trigger_character: None,
12198                }),
12199                ..Default::default()
12200            },
12201            ..Default::default()
12202        },
12203    );
12204
12205    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12206
12207    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12208
12209    let worktree_id = workspace
12210        .update(cx, |workspace, _, cx| {
12211            workspace.project().update(cx, |project, cx| {
12212                project.worktrees(cx).next().unwrap().read(cx).id()
12213            })
12214        })
12215        .unwrap();
12216
12217    let buffer = project
12218        .update(cx, |project, cx| {
12219            project.open_local_buffer(path!("/a/main.rs"), cx)
12220        })
12221        .await
12222        .unwrap();
12223    let editor_handle = workspace
12224        .update(cx, |workspace, window, cx| {
12225            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12226        })
12227        .unwrap()
12228        .await
12229        .unwrap()
12230        .downcast::<Editor>()
12231        .unwrap();
12232
12233    cx.executor().start_waiting();
12234    let fake_server = fake_servers.next().await.unwrap();
12235
12236    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12237        |params, _| async move {
12238            assert_eq!(
12239                params.text_document_position.text_document.uri,
12240                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12241            );
12242            assert_eq!(
12243                params.text_document_position.position,
12244                lsp::Position::new(0, 21),
12245            );
12246
12247            Ok(Some(vec![lsp::TextEdit {
12248                new_text: "]".to_string(),
12249                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12250            }]))
12251        },
12252    );
12253
12254    editor_handle.update_in(cx, |editor, window, cx| {
12255        window.focus(&editor.focus_handle(cx));
12256        editor.change_selections(None, window, cx, |s| {
12257            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12258        });
12259        editor.handle_input("{", window, cx);
12260    });
12261
12262    cx.executor().run_until_parked();
12263
12264    buffer.update(cx, |buffer, _| {
12265        assert_eq!(
12266            buffer.text(),
12267            "fn main() { let a = {5}; }",
12268            "No extra braces from on type formatting should appear in the buffer"
12269        )
12270    });
12271}
12272
12273#[gpui::test]
12274async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12275    init_test(cx, |_| {});
12276
12277    let fs = FakeFs::new(cx.executor());
12278    fs.insert_tree(
12279        path!("/a"),
12280        json!({
12281            "main.rs": "fn main() { let a = 5; }",
12282            "other.rs": "// Test file",
12283        }),
12284    )
12285    .await;
12286
12287    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12288
12289    let server_restarts = Arc::new(AtomicUsize::new(0));
12290    let closure_restarts = Arc::clone(&server_restarts);
12291    let language_server_name = "test language server";
12292    let language_name: LanguageName = "Rust".into();
12293
12294    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12295    language_registry.add(Arc::new(Language::new(
12296        LanguageConfig {
12297            name: language_name.clone(),
12298            matcher: LanguageMatcher {
12299                path_suffixes: vec!["rs".to_string()],
12300                ..Default::default()
12301            },
12302            ..Default::default()
12303        },
12304        Some(tree_sitter_rust::LANGUAGE.into()),
12305    )));
12306    let mut fake_servers = language_registry.register_fake_lsp(
12307        "Rust",
12308        FakeLspAdapter {
12309            name: language_server_name,
12310            initialization_options: Some(json!({
12311                "testOptionValue": true
12312            })),
12313            initializer: Some(Box::new(move |fake_server| {
12314                let task_restarts = Arc::clone(&closure_restarts);
12315                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12316                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12317                    futures::future::ready(Ok(()))
12318                });
12319            })),
12320            ..Default::default()
12321        },
12322    );
12323
12324    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12325    let _buffer = project
12326        .update(cx, |project, cx| {
12327            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12328        })
12329        .await
12330        .unwrap();
12331    let _fake_server = fake_servers.next().await.unwrap();
12332    update_test_language_settings(cx, |language_settings| {
12333        language_settings.languages.insert(
12334            language_name.clone(),
12335            LanguageSettingsContent {
12336                tab_size: NonZeroU32::new(8),
12337                ..Default::default()
12338            },
12339        );
12340    });
12341    cx.executor().run_until_parked();
12342    assert_eq!(
12343        server_restarts.load(atomic::Ordering::Acquire),
12344        0,
12345        "Should not restart LSP server on an unrelated change"
12346    );
12347
12348    update_test_project_settings(cx, |project_settings| {
12349        project_settings.lsp.insert(
12350            "Some other server name".into(),
12351            LspSettings {
12352                binary: None,
12353                settings: None,
12354                initialization_options: Some(json!({
12355                    "some other init value": false
12356                })),
12357            },
12358        );
12359    });
12360    cx.executor().run_until_parked();
12361    assert_eq!(
12362        server_restarts.load(atomic::Ordering::Acquire),
12363        0,
12364        "Should not restart LSP server on an unrelated LSP settings change"
12365    );
12366
12367    update_test_project_settings(cx, |project_settings| {
12368        project_settings.lsp.insert(
12369            language_server_name.into(),
12370            LspSettings {
12371                binary: None,
12372                settings: None,
12373                initialization_options: Some(json!({
12374                    "anotherInitValue": false
12375                })),
12376            },
12377        );
12378    });
12379    cx.executor().run_until_parked();
12380    assert_eq!(
12381        server_restarts.load(atomic::Ordering::Acquire),
12382        1,
12383        "Should restart LSP server on a related LSP settings change"
12384    );
12385
12386    update_test_project_settings(cx, |project_settings| {
12387        project_settings.lsp.insert(
12388            language_server_name.into(),
12389            LspSettings {
12390                binary: None,
12391                settings: None,
12392                initialization_options: Some(json!({
12393                    "anotherInitValue": false
12394                })),
12395            },
12396        );
12397    });
12398    cx.executor().run_until_parked();
12399    assert_eq!(
12400        server_restarts.load(atomic::Ordering::Acquire),
12401        1,
12402        "Should not restart LSP server on a related LSP settings change that is the same"
12403    );
12404
12405    update_test_project_settings(cx, |project_settings| {
12406        project_settings.lsp.insert(
12407            language_server_name.into(),
12408            LspSettings {
12409                binary: None,
12410                settings: None,
12411                initialization_options: None,
12412            },
12413        );
12414    });
12415    cx.executor().run_until_parked();
12416    assert_eq!(
12417        server_restarts.load(atomic::Ordering::Acquire),
12418        2,
12419        "Should restart LSP server on another related LSP settings change"
12420    );
12421}
12422
12423#[gpui::test]
12424async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12425    init_test(cx, |_| {});
12426
12427    let mut cx = EditorLspTestContext::new_rust(
12428        lsp::ServerCapabilities {
12429            completion_provider: Some(lsp::CompletionOptions {
12430                trigger_characters: Some(vec![".".to_string()]),
12431                resolve_provider: Some(true),
12432                ..Default::default()
12433            }),
12434            ..Default::default()
12435        },
12436        cx,
12437    )
12438    .await;
12439
12440    cx.set_state("fn main() { let a = 2ˇ; }");
12441    cx.simulate_keystroke(".");
12442    let completion_item = lsp::CompletionItem {
12443        label: "some".into(),
12444        kind: Some(lsp::CompletionItemKind::SNIPPET),
12445        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12446        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12447            kind: lsp::MarkupKind::Markdown,
12448            value: "```rust\nSome(2)\n```".to_string(),
12449        })),
12450        deprecated: Some(false),
12451        sort_text: Some("fffffff2".to_string()),
12452        filter_text: Some("some".to_string()),
12453        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12454        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12455            range: lsp::Range {
12456                start: lsp::Position {
12457                    line: 0,
12458                    character: 22,
12459                },
12460                end: lsp::Position {
12461                    line: 0,
12462                    character: 22,
12463                },
12464            },
12465            new_text: "Some(2)".to_string(),
12466        })),
12467        additional_text_edits: Some(vec![lsp::TextEdit {
12468            range: lsp::Range {
12469                start: lsp::Position {
12470                    line: 0,
12471                    character: 20,
12472                },
12473                end: lsp::Position {
12474                    line: 0,
12475                    character: 22,
12476                },
12477            },
12478            new_text: "".to_string(),
12479        }]),
12480        ..Default::default()
12481    };
12482
12483    let closure_completion_item = completion_item.clone();
12484    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12485        let task_completion_item = closure_completion_item.clone();
12486        async move {
12487            Ok(Some(lsp::CompletionResponse::Array(vec![
12488                task_completion_item,
12489            ])))
12490        }
12491    });
12492
12493    request.next().await;
12494
12495    cx.condition(|editor, _| editor.context_menu_visible())
12496        .await;
12497    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12498        editor
12499            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12500            .unwrap()
12501    });
12502    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12503
12504    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12505        let task_completion_item = completion_item.clone();
12506        async move { Ok(task_completion_item) }
12507    })
12508    .next()
12509    .await
12510    .unwrap();
12511    apply_additional_edits.await.unwrap();
12512    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12513}
12514
12515#[gpui::test]
12516async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12517    init_test(cx, |_| {});
12518
12519    let mut cx = EditorLspTestContext::new_rust(
12520        lsp::ServerCapabilities {
12521            completion_provider: Some(lsp::CompletionOptions {
12522                trigger_characters: Some(vec![".".to_string()]),
12523                resolve_provider: Some(true),
12524                ..Default::default()
12525            }),
12526            ..Default::default()
12527        },
12528        cx,
12529    )
12530    .await;
12531
12532    cx.set_state("fn main() { let a = 2ˇ; }");
12533    cx.simulate_keystroke(".");
12534
12535    let item1 = lsp::CompletionItem {
12536        label: "method id()".to_string(),
12537        filter_text: Some("id".to_string()),
12538        detail: None,
12539        documentation: None,
12540        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12541            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12542            new_text: ".id".to_string(),
12543        })),
12544        ..lsp::CompletionItem::default()
12545    };
12546
12547    let item2 = lsp::CompletionItem {
12548        label: "other".to_string(),
12549        filter_text: Some("other".to_string()),
12550        detail: None,
12551        documentation: None,
12552        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12553            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12554            new_text: ".other".to_string(),
12555        })),
12556        ..lsp::CompletionItem::default()
12557    };
12558
12559    let item1 = item1.clone();
12560    cx.set_request_handler::<lsp::request::Completion, _, _>({
12561        let item1 = item1.clone();
12562        move |_, _, _| {
12563            let item1 = item1.clone();
12564            let item2 = item2.clone();
12565            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12566        }
12567    })
12568    .next()
12569    .await;
12570
12571    cx.condition(|editor, _| editor.context_menu_visible())
12572        .await;
12573    cx.update_editor(|editor, _, _| {
12574        let context_menu = editor.context_menu.borrow_mut();
12575        let context_menu = context_menu
12576            .as_ref()
12577            .expect("Should have the context menu deployed");
12578        match context_menu {
12579            CodeContextMenu::Completions(completions_menu) => {
12580                let completions = completions_menu.completions.borrow_mut();
12581                assert_eq!(
12582                    completions
12583                        .iter()
12584                        .map(|completion| &completion.label.text)
12585                        .collect::<Vec<_>>(),
12586                    vec!["method id()", "other"]
12587                )
12588            }
12589            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12590        }
12591    });
12592
12593    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12594        let item1 = item1.clone();
12595        move |_, item_to_resolve, _| {
12596            let item1 = item1.clone();
12597            async move {
12598                if item1 == item_to_resolve {
12599                    Ok(lsp::CompletionItem {
12600                        label: "method id()".to_string(),
12601                        filter_text: Some("id".to_string()),
12602                        detail: Some("Now resolved!".to_string()),
12603                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12604                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12605                            range: lsp::Range::new(
12606                                lsp::Position::new(0, 22),
12607                                lsp::Position::new(0, 22),
12608                            ),
12609                            new_text: ".id".to_string(),
12610                        })),
12611                        ..lsp::CompletionItem::default()
12612                    })
12613                } else {
12614                    Ok(item_to_resolve)
12615                }
12616            }
12617        }
12618    })
12619    .next()
12620    .await
12621    .unwrap();
12622    cx.run_until_parked();
12623
12624    cx.update_editor(|editor, window, cx| {
12625        editor.context_menu_next(&Default::default(), window, cx);
12626    });
12627
12628    cx.update_editor(|editor, _, _| {
12629        let context_menu = editor.context_menu.borrow_mut();
12630        let context_menu = context_menu
12631            .as_ref()
12632            .expect("Should have the context menu deployed");
12633        match context_menu {
12634            CodeContextMenu::Completions(completions_menu) => {
12635                let completions = completions_menu.completions.borrow_mut();
12636                assert_eq!(
12637                    completions
12638                        .iter()
12639                        .map(|completion| &completion.label.text)
12640                        .collect::<Vec<_>>(),
12641                    vec!["method id() Now resolved!", "other"],
12642                    "Should update first completion label, but not second as the filter text did not match."
12643                );
12644            }
12645            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12646        }
12647    });
12648}
12649
12650#[gpui::test]
12651async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12652    init_test(cx, |_| {});
12653
12654    let mut cx = EditorLspTestContext::new_rust(
12655        lsp::ServerCapabilities {
12656            completion_provider: Some(lsp::CompletionOptions {
12657                trigger_characters: Some(vec![".".to_string()]),
12658                resolve_provider: Some(true),
12659                ..Default::default()
12660            }),
12661            ..Default::default()
12662        },
12663        cx,
12664    )
12665    .await;
12666
12667    cx.set_state("fn main() { let a = 2ˇ; }");
12668    cx.simulate_keystroke(".");
12669
12670    let unresolved_item_1 = lsp::CompletionItem {
12671        label: "id".to_string(),
12672        filter_text: Some("id".to_string()),
12673        detail: None,
12674        documentation: None,
12675        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12676            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12677            new_text: ".id".to_string(),
12678        })),
12679        ..lsp::CompletionItem::default()
12680    };
12681    let resolved_item_1 = lsp::CompletionItem {
12682        additional_text_edits: Some(vec![lsp::TextEdit {
12683            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12684            new_text: "!!".to_string(),
12685        }]),
12686        ..unresolved_item_1.clone()
12687    };
12688    let unresolved_item_2 = lsp::CompletionItem {
12689        label: "other".to_string(),
12690        filter_text: Some("other".to_string()),
12691        detail: None,
12692        documentation: None,
12693        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12694            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12695            new_text: ".other".to_string(),
12696        })),
12697        ..lsp::CompletionItem::default()
12698    };
12699    let resolved_item_2 = lsp::CompletionItem {
12700        additional_text_edits: Some(vec![lsp::TextEdit {
12701            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12702            new_text: "??".to_string(),
12703        }]),
12704        ..unresolved_item_2.clone()
12705    };
12706
12707    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12708    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12709    cx.lsp
12710        .server
12711        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12712            let unresolved_item_1 = unresolved_item_1.clone();
12713            let resolved_item_1 = resolved_item_1.clone();
12714            let unresolved_item_2 = unresolved_item_2.clone();
12715            let resolved_item_2 = resolved_item_2.clone();
12716            let resolve_requests_1 = resolve_requests_1.clone();
12717            let resolve_requests_2 = resolve_requests_2.clone();
12718            move |unresolved_request, _| {
12719                let unresolved_item_1 = unresolved_item_1.clone();
12720                let resolved_item_1 = resolved_item_1.clone();
12721                let unresolved_item_2 = unresolved_item_2.clone();
12722                let resolved_item_2 = resolved_item_2.clone();
12723                let resolve_requests_1 = resolve_requests_1.clone();
12724                let resolve_requests_2 = resolve_requests_2.clone();
12725                async move {
12726                    if unresolved_request == unresolved_item_1 {
12727                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12728                        Ok(resolved_item_1.clone())
12729                    } else if unresolved_request == unresolved_item_2 {
12730                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12731                        Ok(resolved_item_2.clone())
12732                    } else {
12733                        panic!("Unexpected completion item {unresolved_request:?}")
12734                    }
12735                }
12736            }
12737        })
12738        .detach();
12739
12740    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12741        let unresolved_item_1 = unresolved_item_1.clone();
12742        let unresolved_item_2 = unresolved_item_2.clone();
12743        async move {
12744            Ok(Some(lsp::CompletionResponse::Array(vec![
12745                unresolved_item_1,
12746                unresolved_item_2,
12747            ])))
12748        }
12749    })
12750    .next()
12751    .await;
12752
12753    cx.condition(|editor, _| editor.context_menu_visible())
12754        .await;
12755    cx.update_editor(|editor, _, _| {
12756        let context_menu = editor.context_menu.borrow_mut();
12757        let context_menu = context_menu
12758            .as_ref()
12759            .expect("Should have the context menu deployed");
12760        match context_menu {
12761            CodeContextMenu::Completions(completions_menu) => {
12762                let completions = completions_menu.completions.borrow_mut();
12763                assert_eq!(
12764                    completions
12765                        .iter()
12766                        .map(|completion| &completion.label.text)
12767                        .collect::<Vec<_>>(),
12768                    vec!["id", "other"]
12769                )
12770            }
12771            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12772        }
12773    });
12774    cx.run_until_parked();
12775
12776    cx.update_editor(|editor, window, cx| {
12777        editor.context_menu_next(&ContextMenuNext, window, cx);
12778    });
12779    cx.run_until_parked();
12780    cx.update_editor(|editor, window, cx| {
12781        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12782    });
12783    cx.run_until_parked();
12784    cx.update_editor(|editor, window, cx| {
12785        editor.context_menu_next(&ContextMenuNext, window, cx);
12786    });
12787    cx.run_until_parked();
12788    cx.update_editor(|editor, window, cx| {
12789        editor
12790            .compose_completion(&ComposeCompletion::default(), window, cx)
12791            .expect("No task returned")
12792    })
12793    .await
12794    .expect("Completion failed");
12795    cx.run_until_parked();
12796
12797    cx.update_editor(|editor, _, cx| {
12798        assert_eq!(
12799            resolve_requests_1.load(atomic::Ordering::Acquire),
12800            1,
12801            "Should always resolve once despite multiple selections"
12802        );
12803        assert_eq!(
12804            resolve_requests_2.load(atomic::Ordering::Acquire),
12805            1,
12806            "Should always resolve once after multiple selections and applying the completion"
12807        );
12808        assert_eq!(
12809            editor.text(cx),
12810            "fn main() { let a = ??.other; }",
12811            "Should use resolved data when applying the completion"
12812        );
12813    });
12814}
12815
12816#[gpui::test]
12817async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12818    init_test(cx, |_| {});
12819
12820    let item_0 = lsp::CompletionItem {
12821        label: "abs".into(),
12822        insert_text: Some("abs".into()),
12823        data: Some(json!({ "very": "special"})),
12824        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12825        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12826            lsp::InsertReplaceEdit {
12827                new_text: "abs".to_string(),
12828                insert: lsp::Range::default(),
12829                replace: lsp::Range::default(),
12830            },
12831        )),
12832        ..lsp::CompletionItem::default()
12833    };
12834    let items = iter::once(item_0.clone())
12835        .chain((11..51).map(|i| lsp::CompletionItem {
12836            label: format!("item_{}", i),
12837            insert_text: Some(format!("item_{}", i)),
12838            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12839            ..lsp::CompletionItem::default()
12840        }))
12841        .collect::<Vec<_>>();
12842
12843    let default_commit_characters = vec!["?".to_string()];
12844    let default_data = json!({ "default": "data"});
12845    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12846    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12847    let default_edit_range = lsp::Range {
12848        start: lsp::Position {
12849            line: 0,
12850            character: 5,
12851        },
12852        end: lsp::Position {
12853            line: 0,
12854            character: 5,
12855        },
12856    };
12857
12858    let mut cx = EditorLspTestContext::new_rust(
12859        lsp::ServerCapabilities {
12860            completion_provider: Some(lsp::CompletionOptions {
12861                trigger_characters: Some(vec![".".to_string()]),
12862                resolve_provider: Some(true),
12863                ..Default::default()
12864            }),
12865            ..Default::default()
12866        },
12867        cx,
12868    )
12869    .await;
12870
12871    cx.set_state("fn main() { let a = 2ˇ; }");
12872    cx.simulate_keystroke(".");
12873
12874    let completion_data = default_data.clone();
12875    let completion_characters = default_commit_characters.clone();
12876    let completion_items = items.clone();
12877    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12878        let default_data = completion_data.clone();
12879        let default_commit_characters = completion_characters.clone();
12880        let items = completion_items.clone();
12881        async move {
12882            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12883                items,
12884                item_defaults: Some(lsp::CompletionListItemDefaults {
12885                    data: Some(default_data.clone()),
12886                    commit_characters: Some(default_commit_characters.clone()),
12887                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12888                        default_edit_range,
12889                    )),
12890                    insert_text_format: Some(default_insert_text_format),
12891                    insert_text_mode: Some(default_insert_text_mode),
12892                }),
12893                ..lsp::CompletionList::default()
12894            })))
12895        }
12896    })
12897    .next()
12898    .await;
12899
12900    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12901    cx.lsp
12902        .server
12903        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12904            let closure_resolved_items = resolved_items.clone();
12905            move |item_to_resolve, _| {
12906                let closure_resolved_items = closure_resolved_items.clone();
12907                async move {
12908                    closure_resolved_items.lock().push(item_to_resolve.clone());
12909                    Ok(item_to_resolve)
12910                }
12911            }
12912        })
12913        .detach();
12914
12915    cx.condition(|editor, _| editor.context_menu_visible())
12916        .await;
12917    cx.run_until_parked();
12918    cx.update_editor(|editor, _, _| {
12919        let menu = editor.context_menu.borrow_mut();
12920        match menu.as_ref().expect("should have the completions menu") {
12921            CodeContextMenu::Completions(completions_menu) => {
12922                assert_eq!(
12923                    completions_menu
12924                        .entries
12925                        .borrow()
12926                        .iter()
12927                        .map(|mat| mat.string.clone())
12928                        .collect::<Vec<String>>(),
12929                    items
12930                        .iter()
12931                        .map(|completion| completion.label.clone())
12932                        .collect::<Vec<String>>()
12933                );
12934            }
12935            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12936        }
12937    });
12938    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12939    // with 4 from the end.
12940    assert_eq!(
12941        *resolved_items.lock(),
12942        [&items[0..16], &items[items.len() - 4..items.len()]]
12943            .concat()
12944            .iter()
12945            .cloned()
12946            .map(|mut item| {
12947                if item.data.is_none() {
12948                    item.data = Some(default_data.clone());
12949                }
12950                item
12951            })
12952            .collect::<Vec<lsp::CompletionItem>>(),
12953        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12954    );
12955    resolved_items.lock().clear();
12956
12957    cx.update_editor(|editor, window, cx| {
12958        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12959    });
12960    cx.run_until_parked();
12961    // Completions that have already been resolved are skipped.
12962    assert_eq!(
12963        *resolved_items.lock(),
12964        items[items.len() - 16..items.len() - 4]
12965            .iter()
12966            .cloned()
12967            .map(|mut item| {
12968                if item.data.is_none() {
12969                    item.data = Some(default_data.clone());
12970                }
12971                item
12972            })
12973            .collect::<Vec<lsp::CompletionItem>>()
12974    );
12975    resolved_items.lock().clear();
12976}
12977
12978#[gpui::test]
12979async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12980    init_test(cx, |_| {});
12981
12982    let mut cx = EditorLspTestContext::new(
12983        Language::new(
12984            LanguageConfig {
12985                matcher: LanguageMatcher {
12986                    path_suffixes: vec!["jsx".into()],
12987                    ..Default::default()
12988                },
12989                overrides: [(
12990                    "element".into(),
12991                    LanguageConfigOverride {
12992                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
12993                        ..Default::default()
12994                    },
12995                )]
12996                .into_iter()
12997                .collect(),
12998                ..Default::default()
12999            },
13000            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13001        )
13002        .with_override_query("(jsx_self_closing_element) @element")
13003        .unwrap(),
13004        lsp::ServerCapabilities {
13005            completion_provider: Some(lsp::CompletionOptions {
13006                trigger_characters: Some(vec![":".to_string()]),
13007                ..Default::default()
13008            }),
13009            ..Default::default()
13010        },
13011        cx,
13012    )
13013    .await;
13014
13015    cx.lsp
13016        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13017            Ok(Some(lsp::CompletionResponse::Array(vec![
13018                lsp::CompletionItem {
13019                    label: "bg-blue".into(),
13020                    ..Default::default()
13021                },
13022                lsp::CompletionItem {
13023                    label: "bg-red".into(),
13024                    ..Default::default()
13025                },
13026                lsp::CompletionItem {
13027                    label: "bg-yellow".into(),
13028                    ..Default::default()
13029                },
13030            ])))
13031        });
13032
13033    cx.set_state(r#"<p class="bgˇ" />"#);
13034
13035    // Trigger completion when typing a dash, because the dash is an extra
13036    // word character in the 'element' scope, which contains the cursor.
13037    cx.simulate_keystroke("-");
13038    cx.executor().run_until_parked();
13039    cx.update_editor(|editor, _, _| {
13040        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13041        {
13042            assert_eq!(
13043                completion_menu_entries(&menu),
13044                &["bg-red", "bg-blue", "bg-yellow"]
13045            );
13046        } else {
13047            panic!("expected completion menu to be open");
13048        }
13049    });
13050
13051    cx.simulate_keystroke("l");
13052    cx.executor().run_until_parked();
13053    cx.update_editor(|editor, _, _| {
13054        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13055        {
13056            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13057        } else {
13058            panic!("expected completion menu to be open");
13059        }
13060    });
13061
13062    // When filtering completions, consider the character after the '-' to
13063    // be the start of a subword.
13064    cx.set_state(r#"<p class="yelˇ" />"#);
13065    cx.simulate_keystroke("l");
13066    cx.executor().run_until_parked();
13067    cx.update_editor(|editor, _, _| {
13068        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13069        {
13070            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13071        } else {
13072            panic!("expected completion menu to be open");
13073        }
13074    });
13075}
13076
13077fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13078    let entries = menu.entries.borrow();
13079    entries.iter().map(|mat| mat.string.clone()).collect()
13080}
13081
13082#[gpui::test]
13083async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13084    init_test(cx, |settings| {
13085        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13086            FormatterList(vec![Formatter::Prettier].into()),
13087        ))
13088    });
13089
13090    let fs = FakeFs::new(cx.executor());
13091    fs.insert_file(path!("/file.ts"), Default::default()).await;
13092
13093    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13094    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13095
13096    language_registry.add(Arc::new(Language::new(
13097        LanguageConfig {
13098            name: "TypeScript".into(),
13099            matcher: LanguageMatcher {
13100                path_suffixes: vec!["ts".to_string()],
13101                ..Default::default()
13102            },
13103            ..Default::default()
13104        },
13105        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13106    )));
13107    update_test_language_settings(cx, |settings| {
13108        settings.defaults.prettier = Some(PrettierSettings {
13109            allowed: true,
13110            ..PrettierSettings::default()
13111        });
13112    });
13113
13114    let test_plugin = "test_plugin";
13115    let _ = language_registry.register_fake_lsp(
13116        "TypeScript",
13117        FakeLspAdapter {
13118            prettier_plugins: vec![test_plugin],
13119            ..Default::default()
13120        },
13121    );
13122
13123    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13124    let buffer = project
13125        .update(cx, |project, cx| {
13126            project.open_local_buffer(path!("/file.ts"), cx)
13127        })
13128        .await
13129        .unwrap();
13130
13131    let buffer_text = "one\ntwo\nthree\n";
13132    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13133    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13134    editor.update_in(cx, |editor, window, cx| {
13135        editor.set_text(buffer_text, window, cx)
13136    });
13137
13138    editor
13139        .update_in(cx, |editor, window, cx| {
13140            editor.perform_format(
13141                project.clone(),
13142                FormatTrigger::Manual,
13143                FormatTarget::Buffers,
13144                window,
13145                cx,
13146            )
13147        })
13148        .unwrap()
13149        .await;
13150    assert_eq!(
13151        editor.update(cx, |editor, cx| editor.text(cx)),
13152        buffer_text.to_string() + prettier_format_suffix,
13153        "Test prettier formatting was not applied to the original buffer text",
13154    );
13155
13156    update_test_language_settings(cx, |settings| {
13157        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13158    });
13159    let format = editor.update_in(cx, |editor, window, cx| {
13160        editor.perform_format(
13161            project.clone(),
13162            FormatTrigger::Manual,
13163            FormatTarget::Buffers,
13164            window,
13165            cx,
13166        )
13167    });
13168    format.await.unwrap();
13169    assert_eq!(
13170        editor.update(cx, |editor, cx| editor.text(cx)),
13171        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13172        "Autoformatting (via test prettier) was not applied to the original buffer text",
13173    );
13174}
13175
13176#[gpui::test]
13177async fn test_addition_reverts(cx: &mut TestAppContext) {
13178    init_test(cx, |_| {});
13179    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13180    let base_text = indoc! {r#"
13181        struct Row;
13182        struct Row1;
13183        struct Row2;
13184
13185        struct Row4;
13186        struct Row5;
13187        struct Row6;
13188
13189        struct Row8;
13190        struct Row9;
13191        struct Row10;"#};
13192
13193    // When addition hunks are not adjacent to carets, no hunk revert is performed
13194    assert_hunk_revert(
13195        indoc! {r#"struct Row;
13196                   struct Row1;
13197                   struct Row1.1;
13198                   struct Row1.2;
13199                   struct Row2;ˇ
13200
13201                   struct Row4;
13202                   struct Row5;
13203                   struct Row6;
13204
13205                   struct Row8;
13206                   ˇstruct Row9;
13207                   struct Row9.1;
13208                   struct Row9.2;
13209                   struct Row9.3;
13210                   struct Row10;"#},
13211        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13212        indoc! {r#"struct Row;
13213                   struct Row1;
13214                   struct Row1.1;
13215                   struct Row1.2;
13216                   struct Row2;ˇ
13217
13218                   struct Row4;
13219                   struct Row5;
13220                   struct Row6;
13221
13222                   struct Row8;
13223                   ˇstruct Row9;
13224                   struct Row9.1;
13225                   struct Row9.2;
13226                   struct Row9.3;
13227                   struct Row10;"#},
13228        base_text,
13229        &mut cx,
13230    );
13231    // Same for selections
13232    assert_hunk_revert(
13233        indoc! {r#"struct Row;
13234                   struct Row1;
13235                   struct Row2;
13236                   struct Row2.1;
13237                   struct Row2.2;
13238                   «ˇ
13239                   struct Row4;
13240                   struct» Row5;
13241                   «struct Row6;
13242                   ˇ»
13243                   struct Row9.1;
13244                   struct Row9.2;
13245                   struct Row9.3;
13246                   struct Row8;
13247                   struct Row9;
13248                   struct Row10;"#},
13249        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13250        indoc! {r#"struct Row;
13251                   struct Row1;
13252                   struct Row2;
13253                   struct Row2.1;
13254                   struct Row2.2;
13255                   «ˇ
13256                   struct Row4;
13257                   struct» Row5;
13258                   «struct Row6;
13259                   ˇ»
13260                   struct Row9.1;
13261                   struct Row9.2;
13262                   struct Row9.3;
13263                   struct Row8;
13264                   struct Row9;
13265                   struct Row10;"#},
13266        base_text,
13267        &mut cx,
13268    );
13269
13270    // When carets and selections intersect the addition hunks, those are reverted.
13271    // Adjacent carets got merged.
13272    assert_hunk_revert(
13273        indoc! {r#"struct Row;
13274                   ˇ// something on the top
13275                   struct Row1;
13276                   struct Row2;
13277                   struct Roˇw3.1;
13278                   struct Row2.2;
13279                   struct Row2.3;ˇ
13280
13281                   struct Row4;
13282                   struct ˇRow5.1;
13283                   struct Row5.2;
13284                   struct «Rowˇ»5.3;
13285                   struct Row5;
13286                   struct Row6;
13287                   ˇ
13288                   struct Row9.1;
13289                   struct «Rowˇ»9.2;
13290                   struct «ˇRow»9.3;
13291                   struct Row8;
13292                   struct Row9;
13293                   «ˇ// something on bottom»
13294                   struct Row10;"#},
13295        vec![
13296            DiffHunkStatusKind::Added,
13297            DiffHunkStatusKind::Added,
13298            DiffHunkStatusKind::Added,
13299            DiffHunkStatusKind::Added,
13300            DiffHunkStatusKind::Added,
13301        ],
13302        indoc! {r#"struct Row;
13303                   ˇstruct Row1;
13304                   struct Row2;
13305                   ˇ
13306                   struct Row4;
13307                   ˇstruct Row5;
13308                   struct Row6;
13309                   ˇ
13310                   ˇstruct Row8;
13311                   struct Row9;
13312                   ˇstruct Row10;"#},
13313        base_text,
13314        &mut cx,
13315    );
13316}
13317
13318#[gpui::test]
13319async fn test_modification_reverts(cx: &mut TestAppContext) {
13320    init_test(cx, |_| {});
13321    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13322    let base_text = indoc! {r#"
13323        struct Row;
13324        struct Row1;
13325        struct Row2;
13326
13327        struct Row4;
13328        struct Row5;
13329        struct Row6;
13330
13331        struct Row8;
13332        struct Row9;
13333        struct Row10;"#};
13334
13335    // Modification hunks behave the same as the addition ones.
13336    assert_hunk_revert(
13337        indoc! {r#"struct Row;
13338                   struct Row1;
13339                   struct Row33;
13340                   ˇ
13341                   struct Row4;
13342                   struct Row5;
13343                   struct Row6;
13344                   ˇ
13345                   struct Row99;
13346                   struct Row9;
13347                   struct Row10;"#},
13348        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13349        indoc! {r#"struct Row;
13350                   struct Row1;
13351                   struct Row33;
13352                   ˇ
13353                   struct Row4;
13354                   struct Row5;
13355                   struct Row6;
13356                   ˇ
13357                   struct Row99;
13358                   struct Row9;
13359                   struct Row10;"#},
13360        base_text,
13361        &mut cx,
13362    );
13363    assert_hunk_revert(
13364        indoc! {r#"struct Row;
13365                   struct Row1;
13366                   struct Row33;
13367                   «ˇ
13368                   struct Row4;
13369                   struct» Row5;
13370                   «struct Row6;
13371                   ˇ»
13372                   struct Row99;
13373                   struct Row9;
13374                   struct Row10;"#},
13375        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13376        indoc! {r#"struct Row;
13377                   struct Row1;
13378                   struct Row33;
13379                   «ˇ
13380                   struct Row4;
13381                   struct» Row5;
13382                   «struct Row6;
13383                   ˇ»
13384                   struct Row99;
13385                   struct Row9;
13386                   struct Row10;"#},
13387        base_text,
13388        &mut cx,
13389    );
13390
13391    assert_hunk_revert(
13392        indoc! {r#"ˇstruct Row1.1;
13393                   struct Row1;
13394                   «ˇstr»uct Row22;
13395
13396                   struct ˇRow44;
13397                   struct Row5;
13398                   struct «Rˇ»ow66;ˇ
13399
13400                   «struˇ»ct Row88;
13401                   struct Row9;
13402                   struct Row1011;ˇ"#},
13403        vec![
13404            DiffHunkStatusKind::Modified,
13405            DiffHunkStatusKind::Modified,
13406            DiffHunkStatusKind::Modified,
13407            DiffHunkStatusKind::Modified,
13408            DiffHunkStatusKind::Modified,
13409            DiffHunkStatusKind::Modified,
13410        ],
13411        indoc! {r#"struct Row;
13412                   ˇstruct Row1;
13413                   struct Row2;
13414                   ˇ
13415                   struct Row4;
13416                   ˇstruct Row5;
13417                   struct Row6;
13418                   ˇ
13419                   struct Row8;
13420                   ˇstruct Row9;
13421                   struct Row10;ˇ"#},
13422        base_text,
13423        &mut cx,
13424    );
13425}
13426
13427#[gpui::test]
13428async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13429    init_test(cx, |_| {});
13430    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13431    let base_text = indoc! {r#"
13432        one
13433
13434        two
13435        three
13436        "#};
13437
13438    cx.set_head_text(base_text);
13439    cx.set_state("\nˇ\n");
13440    cx.executor().run_until_parked();
13441    cx.update_editor(|editor, _window, cx| {
13442        editor.expand_selected_diff_hunks(cx);
13443    });
13444    cx.executor().run_until_parked();
13445    cx.update_editor(|editor, window, cx| {
13446        editor.backspace(&Default::default(), window, cx);
13447    });
13448    cx.run_until_parked();
13449    cx.assert_state_with_diff(
13450        indoc! {r#"
13451
13452        - two
13453        - threeˇ
13454        +
13455        "#}
13456        .to_string(),
13457    );
13458}
13459
13460#[gpui::test]
13461async fn test_deletion_reverts(cx: &mut TestAppContext) {
13462    init_test(cx, |_| {});
13463    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13464    let base_text = indoc! {r#"struct Row;
13465struct Row1;
13466struct Row2;
13467
13468struct Row4;
13469struct Row5;
13470struct Row6;
13471
13472struct Row8;
13473struct Row9;
13474struct Row10;"#};
13475
13476    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13477    assert_hunk_revert(
13478        indoc! {r#"struct Row;
13479                   struct Row2;
13480
13481                   ˇstruct Row4;
13482                   struct Row5;
13483                   struct Row6;
13484                   ˇ
13485                   struct Row8;
13486                   struct Row10;"#},
13487        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13488        indoc! {r#"struct Row;
13489                   struct Row2;
13490
13491                   ˇstruct Row4;
13492                   struct Row5;
13493                   struct Row6;
13494                   ˇ
13495                   struct Row8;
13496                   struct Row10;"#},
13497        base_text,
13498        &mut cx,
13499    );
13500    assert_hunk_revert(
13501        indoc! {r#"struct Row;
13502                   struct Row2;
13503
13504                   «ˇstruct Row4;
13505                   struct» Row5;
13506                   «struct Row6;
13507                   ˇ»
13508                   struct Row8;
13509                   struct Row10;"#},
13510        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13511        indoc! {r#"struct Row;
13512                   struct Row2;
13513
13514                   «ˇstruct Row4;
13515                   struct» Row5;
13516                   «struct Row6;
13517                   ˇ»
13518                   struct Row8;
13519                   struct Row10;"#},
13520        base_text,
13521        &mut cx,
13522    );
13523
13524    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13525    assert_hunk_revert(
13526        indoc! {r#"struct Row;
13527                   ˇstruct Row2;
13528
13529                   struct Row4;
13530                   struct Row5;
13531                   struct Row6;
13532
13533                   struct Row8;ˇ
13534                   struct Row10;"#},
13535        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13536        indoc! {r#"struct Row;
13537                   struct Row1;
13538                   ˇstruct Row2;
13539
13540                   struct Row4;
13541                   struct Row5;
13542                   struct Row6;
13543
13544                   struct Row8;ˇ
13545                   struct Row9;
13546                   struct Row10;"#},
13547        base_text,
13548        &mut cx,
13549    );
13550    assert_hunk_revert(
13551        indoc! {r#"struct Row;
13552                   struct Row2«ˇ;
13553                   struct Row4;
13554                   struct» Row5;
13555                   «struct Row6;
13556
13557                   struct Row8;ˇ»
13558                   struct Row10;"#},
13559        vec![
13560            DiffHunkStatusKind::Deleted,
13561            DiffHunkStatusKind::Deleted,
13562            DiffHunkStatusKind::Deleted,
13563        ],
13564        indoc! {r#"struct Row;
13565                   struct Row1;
13566                   struct Row2«ˇ;
13567
13568                   struct Row4;
13569                   struct» Row5;
13570                   «struct Row6;
13571
13572                   struct Row8;ˇ»
13573                   struct Row9;
13574                   struct Row10;"#},
13575        base_text,
13576        &mut cx,
13577    );
13578}
13579
13580#[gpui::test]
13581async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13582    init_test(cx, |_| {});
13583
13584    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13585    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13586    let base_text_3 =
13587        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13588
13589    let text_1 = edit_first_char_of_every_line(base_text_1);
13590    let text_2 = edit_first_char_of_every_line(base_text_2);
13591    let text_3 = edit_first_char_of_every_line(base_text_3);
13592
13593    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13594    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13595    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13596
13597    let multibuffer = cx.new(|cx| {
13598        let mut multibuffer = MultiBuffer::new(ReadWrite);
13599        multibuffer.push_excerpts(
13600            buffer_1.clone(),
13601            [
13602                ExcerptRange {
13603                    context: Point::new(0, 0)..Point::new(3, 0),
13604                    primary: None,
13605                },
13606                ExcerptRange {
13607                    context: Point::new(5, 0)..Point::new(7, 0),
13608                    primary: None,
13609                },
13610                ExcerptRange {
13611                    context: Point::new(9, 0)..Point::new(10, 4),
13612                    primary: None,
13613                },
13614            ],
13615            cx,
13616        );
13617        multibuffer.push_excerpts(
13618            buffer_2.clone(),
13619            [
13620                ExcerptRange {
13621                    context: Point::new(0, 0)..Point::new(3, 0),
13622                    primary: None,
13623                },
13624                ExcerptRange {
13625                    context: Point::new(5, 0)..Point::new(7, 0),
13626                    primary: None,
13627                },
13628                ExcerptRange {
13629                    context: Point::new(9, 0)..Point::new(10, 4),
13630                    primary: None,
13631                },
13632            ],
13633            cx,
13634        );
13635        multibuffer.push_excerpts(
13636            buffer_3.clone(),
13637            [
13638                ExcerptRange {
13639                    context: Point::new(0, 0)..Point::new(3, 0),
13640                    primary: None,
13641                },
13642                ExcerptRange {
13643                    context: Point::new(5, 0)..Point::new(7, 0),
13644                    primary: None,
13645                },
13646                ExcerptRange {
13647                    context: Point::new(9, 0)..Point::new(10, 4),
13648                    primary: None,
13649                },
13650            ],
13651            cx,
13652        );
13653        multibuffer
13654    });
13655
13656    let fs = FakeFs::new(cx.executor());
13657    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13658    let (editor, cx) = cx
13659        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13660    editor.update_in(cx, |editor, _window, cx| {
13661        for (buffer, diff_base) in [
13662            (buffer_1.clone(), base_text_1),
13663            (buffer_2.clone(), base_text_2),
13664            (buffer_3.clone(), base_text_3),
13665        ] {
13666            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13667            editor
13668                .buffer
13669                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13670        }
13671    });
13672    cx.executor().run_until_parked();
13673
13674    editor.update_in(cx, |editor, window, cx| {
13675        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}");
13676        editor.select_all(&SelectAll, window, cx);
13677        editor.git_restore(&Default::default(), window, cx);
13678    });
13679    cx.executor().run_until_parked();
13680
13681    // When all ranges are selected, all buffer hunks are reverted.
13682    editor.update(cx, |editor, cx| {
13683        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");
13684    });
13685    buffer_1.update(cx, |buffer, _| {
13686        assert_eq!(buffer.text(), base_text_1);
13687    });
13688    buffer_2.update(cx, |buffer, _| {
13689        assert_eq!(buffer.text(), base_text_2);
13690    });
13691    buffer_3.update(cx, |buffer, _| {
13692        assert_eq!(buffer.text(), base_text_3);
13693    });
13694
13695    editor.update_in(cx, |editor, window, cx| {
13696        editor.undo(&Default::default(), window, cx);
13697    });
13698
13699    editor.update_in(cx, |editor, window, cx| {
13700        editor.change_selections(None, window, cx, |s| {
13701            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13702        });
13703        editor.git_restore(&Default::default(), window, cx);
13704    });
13705
13706    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13707    // but not affect buffer_2 and its related excerpts.
13708    editor.update(cx, |editor, cx| {
13709        assert_eq!(
13710            editor.text(cx),
13711            "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}"
13712        );
13713    });
13714    buffer_1.update(cx, |buffer, _| {
13715        assert_eq!(buffer.text(), base_text_1);
13716    });
13717    buffer_2.update(cx, |buffer, _| {
13718        assert_eq!(
13719            buffer.text(),
13720            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13721        );
13722    });
13723    buffer_3.update(cx, |buffer, _| {
13724        assert_eq!(
13725            buffer.text(),
13726            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13727        );
13728    });
13729
13730    fn edit_first_char_of_every_line(text: &str) -> String {
13731        text.split('\n')
13732            .map(|line| format!("X{}", &line[1..]))
13733            .collect::<Vec<_>>()
13734            .join("\n")
13735    }
13736}
13737
13738#[gpui::test]
13739async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13740    init_test(cx, |_| {});
13741
13742    let cols = 4;
13743    let rows = 10;
13744    let sample_text_1 = sample_text(rows, cols, 'a');
13745    assert_eq!(
13746        sample_text_1,
13747        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13748    );
13749    let sample_text_2 = sample_text(rows, cols, 'l');
13750    assert_eq!(
13751        sample_text_2,
13752        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13753    );
13754    let sample_text_3 = sample_text(rows, cols, 'v');
13755    assert_eq!(
13756        sample_text_3,
13757        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13758    );
13759
13760    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13761    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13762    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13763
13764    let multi_buffer = cx.new(|cx| {
13765        let mut multibuffer = MultiBuffer::new(ReadWrite);
13766        multibuffer.push_excerpts(
13767            buffer_1.clone(),
13768            [
13769                ExcerptRange {
13770                    context: Point::new(0, 0)..Point::new(3, 0),
13771                    primary: None,
13772                },
13773                ExcerptRange {
13774                    context: Point::new(5, 0)..Point::new(7, 0),
13775                    primary: None,
13776                },
13777                ExcerptRange {
13778                    context: Point::new(9, 0)..Point::new(10, 4),
13779                    primary: None,
13780                },
13781            ],
13782            cx,
13783        );
13784        multibuffer.push_excerpts(
13785            buffer_2.clone(),
13786            [
13787                ExcerptRange {
13788                    context: Point::new(0, 0)..Point::new(3, 0),
13789                    primary: None,
13790                },
13791                ExcerptRange {
13792                    context: Point::new(5, 0)..Point::new(7, 0),
13793                    primary: None,
13794                },
13795                ExcerptRange {
13796                    context: Point::new(9, 0)..Point::new(10, 4),
13797                    primary: None,
13798                },
13799            ],
13800            cx,
13801        );
13802        multibuffer.push_excerpts(
13803            buffer_3.clone(),
13804            [
13805                ExcerptRange {
13806                    context: Point::new(0, 0)..Point::new(3, 0),
13807                    primary: None,
13808                },
13809                ExcerptRange {
13810                    context: Point::new(5, 0)..Point::new(7, 0),
13811                    primary: None,
13812                },
13813                ExcerptRange {
13814                    context: Point::new(9, 0)..Point::new(10, 4),
13815                    primary: None,
13816                },
13817            ],
13818            cx,
13819        );
13820        multibuffer
13821    });
13822
13823    let fs = FakeFs::new(cx.executor());
13824    fs.insert_tree(
13825        "/a",
13826        json!({
13827            "main.rs": sample_text_1,
13828            "other.rs": sample_text_2,
13829            "lib.rs": sample_text_3,
13830        }),
13831    )
13832    .await;
13833    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13834    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13835    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13836    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13837        Editor::new(
13838            EditorMode::Full,
13839            multi_buffer,
13840            Some(project.clone()),
13841            window,
13842            cx,
13843        )
13844    });
13845    let multibuffer_item_id = workspace
13846        .update(cx, |workspace, window, cx| {
13847            assert!(
13848                workspace.active_item(cx).is_none(),
13849                "active item should be None before the first item is added"
13850            );
13851            workspace.add_item_to_active_pane(
13852                Box::new(multi_buffer_editor.clone()),
13853                None,
13854                true,
13855                window,
13856                cx,
13857            );
13858            let active_item = workspace
13859                .active_item(cx)
13860                .expect("should have an active item after adding the multi buffer");
13861            assert!(
13862                !active_item.is_singleton(cx),
13863                "A multi buffer was expected to active after adding"
13864            );
13865            active_item.item_id()
13866        })
13867        .unwrap();
13868    cx.executor().run_until_parked();
13869
13870    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13871        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13872            s.select_ranges(Some(1..2))
13873        });
13874        editor.open_excerpts(&OpenExcerpts, window, cx);
13875    });
13876    cx.executor().run_until_parked();
13877    let first_item_id = workspace
13878        .update(cx, |workspace, window, cx| {
13879            let active_item = workspace
13880                .active_item(cx)
13881                .expect("should have an active item after navigating into the 1st buffer");
13882            let first_item_id = active_item.item_id();
13883            assert_ne!(
13884                first_item_id, multibuffer_item_id,
13885                "Should navigate into the 1st buffer and activate it"
13886            );
13887            assert!(
13888                active_item.is_singleton(cx),
13889                "New active item should be a singleton buffer"
13890            );
13891            assert_eq!(
13892                active_item
13893                    .act_as::<Editor>(cx)
13894                    .expect("should have navigated into an editor for the 1st buffer")
13895                    .read(cx)
13896                    .text(cx),
13897                sample_text_1
13898            );
13899
13900            workspace
13901                .go_back(workspace.active_pane().downgrade(), window, cx)
13902                .detach_and_log_err(cx);
13903
13904            first_item_id
13905        })
13906        .unwrap();
13907    cx.executor().run_until_parked();
13908    workspace
13909        .update(cx, |workspace, _, cx| {
13910            let active_item = workspace
13911                .active_item(cx)
13912                .expect("should have an active item after navigating back");
13913            assert_eq!(
13914                active_item.item_id(),
13915                multibuffer_item_id,
13916                "Should navigate back to the multi buffer"
13917            );
13918            assert!(!active_item.is_singleton(cx));
13919        })
13920        .unwrap();
13921
13922    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13923        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13924            s.select_ranges(Some(39..40))
13925        });
13926        editor.open_excerpts(&OpenExcerpts, window, cx);
13927    });
13928    cx.executor().run_until_parked();
13929    let second_item_id = workspace
13930        .update(cx, |workspace, window, cx| {
13931            let active_item = workspace
13932                .active_item(cx)
13933                .expect("should have an active item after navigating into the 2nd buffer");
13934            let second_item_id = active_item.item_id();
13935            assert_ne!(
13936                second_item_id, multibuffer_item_id,
13937                "Should navigate away from the multibuffer"
13938            );
13939            assert_ne!(
13940                second_item_id, first_item_id,
13941                "Should navigate into the 2nd buffer and activate it"
13942            );
13943            assert!(
13944                active_item.is_singleton(cx),
13945                "New active item should be a singleton buffer"
13946            );
13947            assert_eq!(
13948                active_item
13949                    .act_as::<Editor>(cx)
13950                    .expect("should have navigated into an editor")
13951                    .read(cx)
13952                    .text(cx),
13953                sample_text_2
13954            );
13955
13956            workspace
13957                .go_back(workspace.active_pane().downgrade(), window, cx)
13958                .detach_and_log_err(cx);
13959
13960            second_item_id
13961        })
13962        .unwrap();
13963    cx.executor().run_until_parked();
13964    workspace
13965        .update(cx, |workspace, _, cx| {
13966            let active_item = workspace
13967                .active_item(cx)
13968                .expect("should have an active item after navigating back from the 2nd buffer");
13969            assert_eq!(
13970                active_item.item_id(),
13971                multibuffer_item_id,
13972                "Should navigate back from the 2nd buffer to the multi buffer"
13973            );
13974            assert!(!active_item.is_singleton(cx));
13975        })
13976        .unwrap();
13977
13978    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13979        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13980            s.select_ranges(Some(70..70))
13981        });
13982        editor.open_excerpts(&OpenExcerpts, window, cx);
13983    });
13984    cx.executor().run_until_parked();
13985    workspace
13986        .update(cx, |workspace, window, cx| {
13987            let active_item = workspace
13988                .active_item(cx)
13989                .expect("should have an active item after navigating into the 3rd buffer");
13990            let third_item_id = active_item.item_id();
13991            assert_ne!(
13992                third_item_id, multibuffer_item_id,
13993                "Should navigate into the 3rd buffer and activate it"
13994            );
13995            assert_ne!(third_item_id, first_item_id);
13996            assert_ne!(third_item_id, second_item_id);
13997            assert!(
13998                active_item.is_singleton(cx),
13999                "New active item should be a singleton buffer"
14000            );
14001            assert_eq!(
14002                active_item
14003                    .act_as::<Editor>(cx)
14004                    .expect("should have navigated into an editor")
14005                    .read(cx)
14006                    .text(cx),
14007                sample_text_3
14008            );
14009
14010            workspace
14011                .go_back(workspace.active_pane().downgrade(), window, cx)
14012                .detach_and_log_err(cx);
14013        })
14014        .unwrap();
14015    cx.executor().run_until_parked();
14016    workspace
14017        .update(cx, |workspace, _, cx| {
14018            let active_item = workspace
14019                .active_item(cx)
14020                .expect("should have an active item after navigating back from the 3rd buffer");
14021            assert_eq!(
14022                active_item.item_id(),
14023                multibuffer_item_id,
14024                "Should navigate back from the 3rd buffer to the multi buffer"
14025            );
14026            assert!(!active_item.is_singleton(cx));
14027        })
14028        .unwrap();
14029}
14030
14031#[gpui::test]
14032async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14033    init_test(cx, |_| {});
14034
14035    let mut cx = EditorTestContext::new(cx).await;
14036
14037    let diff_base = r#"
14038        use some::mod;
14039
14040        const A: u32 = 42;
14041
14042        fn main() {
14043            println!("hello");
14044
14045            println!("world");
14046        }
14047        "#
14048    .unindent();
14049
14050    cx.set_state(
14051        &r#"
14052        use some::modified;
14053
14054        ˇ
14055        fn main() {
14056            println!("hello there");
14057
14058            println!("around the");
14059            println!("world");
14060        }
14061        "#
14062        .unindent(),
14063    );
14064
14065    cx.set_head_text(&diff_base);
14066    executor.run_until_parked();
14067
14068    cx.update_editor(|editor, window, cx| {
14069        editor.go_to_next_hunk(&GoToHunk, window, cx);
14070        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14071    });
14072    executor.run_until_parked();
14073    cx.assert_state_with_diff(
14074        r#"
14075          use some::modified;
14076
14077
14078          fn main() {
14079        -     println!("hello");
14080        + ˇ    println!("hello there");
14081
14082              println!("around the");
14083              println!("world");
14084          }
14085        "#
14086        .unindent(),
14087    );
14088
14089    cx.update_editor(|editor, window, cx| {
14090        for _ in 0..2 {
14091            editor.go_to_next_hunk(&GoToHunk, window, cx);
14092            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14093        }
14094    });
14095    executor.run_until_parked();
14096    cx.assert_state_with_diff(
14097        r#"
14098        - use some::mod;
14099        + ˇuse some::modified;
14100
14101
14102          fn main() {
14103        -     println!("hello");
14104        +     println!("hello there");
14105
14106        +     println!("around the");
14107              println!("world");
14108          }
14109        "#
14110        .unindent(),
14111    );
14112
14113    cx.update_editor(|editor, window, cx| {
14114        editor.go_to_next_hunk(&GoToHunk, window, cx);
14115        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14116    });
14117    executor.run_until_parked();
14118    cx.assert_state_with_diff(
14119        r#"
14120        - use some::mod;
14121        + use some::modified;
14122
14123        - const A: u32 = 42;
14124          ˇ
14125          fn main() {
14126        -     println!("hello");
14127        +     println!("hello there");
14128
14129        +     println!("around the");
14130              println!("world");
14131          }
14132        "#
14133        .unindent(),
14134    );
14135
14136    cx.update_editor(|editor, window, cx| {
14137        editor.cancel(&Cancel, window, cx);
14138    });
14139
14140    cx.assert_state_with_diff(
14141        r#"
14142          use some::modified;
14143
14144          ˇ
14145          fn main() {
14146              println!("hello there");
14147
14148              println!("around the");
14149              println!("world");
14150          }
14151        "#
14152        .unindent(),
14153    );
14154}
14155
14156#[gpui::test]
14157async fn test_diff_base_change_with_expanded_diff_hunks(
14158    executor: BackgroundExecutor,
14159    cx: &mut TestAppContext,
14160) {
14161    init_test(cx, |_| {});
14162
14163    let mut cx = EditorTestContext::new(cx).await;
14164
14165    let diff_base = r#"
14166        use some::mod1;
14167        use some::mod2;
14168
14169        const A: u32 = 42;
14170        const B: u32 = 42;
14171        const C: u32 = 42;
14172
14173        fn main() {
14174            println!("hello");
14175
14176            println!("world");
14177        }
14178        "#
14179    .unindent();
14180
14181    cx.set_state(
14182        &r#"
14183        use some::mod2;
14184
14185        const A: u32 = 42;
14186        const C: u32 = 42;
14187
14188        fn main(ˇ) {
14189            //println!("hello");
14190
14191            println!("world");
14192            //
14193            //
14194        }
14195        "#
14196        .unindent(),
14197    );
14198
14199    cx.set_head_text(&diff_base);
14200    executor.run_until_parked();
14201
14202    cx.update_editor(|editor, window, cx| {
14203        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14204    });
14205    executor.run_until_parked();
14206    cx.assert_state_with_diff(
14207        r#"
14208        - use some::mod1;
14209          use some::mod2;
14210
14211          const A: u32 = 42;
14212        - const B: u32 = 42;
14213          const C: u32 = 42;
14214
14215          fn main(ˇ) {
14216        -     println!("hello");
14217        +     //println!("hello");
14218
14219              println!("world");
14220        +     //
14221        +     //
14222          }
14223        "#
14224        .unindent(),
14225    );
14226
14227    cx.set_head_text("new diff base!");
14228    executor.run_until_parked();
14229    cx.assert_state_with_diff(
14230        r#"
14231        - new diff base!
14232        + use some::mod2;
14233        +
14234        + const A: u32 = 42;
14235        + const C: u32 = 42;
14236        +
14237        + fn main(ˇ) {
14238        +     //println!("hello");
14239        +
14240        +     println!("world");
14241        +     //
14242        +     //
14243        + }
14244        "#
14245        .unindent(),
14246    );
14247}
14248
14249#[gpui::test]
14250async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14251    init_test(cx, |_| {});
14252
14253    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14254    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14255    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14256    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14257    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14258    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14259
14260    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14261    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14262    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14263
14264    let multi_buffer = cx.new(|cx| {
14265        let mut multibuffer = MultiBuffer::new(ReadWrite);
14266        multibuffer.push_excerpts(
14267            buffer_1.clone(),
14268            [
14269                ExcerptRange {
14270                    context: Point::new(0, 0)..Point::new(3, 0),
14271                    primary: None,
14272                },
14273                ExcerptRange {
14274                    context: Point::new(5, 0)..Point::new(7, 0),
14275                    primary: None,
14276                },
14277                ExcerptRange {
14278                    context: Point::new(9, 0)..Point::new(10, 3),
14279                    primary: None,
14280                },
14281            ],
14282            cx,
14283        );
14284        multibuffer.push_excerpts(
14285            buffer_2.clone(),
14286            [
14287                ExcerptRange {
14288                    context: Point::new(0, 0)..Point::new(3, 0),
14289                    primary: None,
14290                },
14291                ExcerptRange {
14292                    context: Point::new(5, 0)..Point::new(7, 0),
14293                    primary: None,
14294                },
14295                ExcerptRange {
14296                    context: Point::new(9, 0)..Point::new(10, 3),
14297                    primary: None,
14298                },
14299            ],
14300            cx,
14301        );
14302        multibuffer.push_excerpts(
14303            buffer_3.clone(),
14304            [
14305                ExcerptRange {
14306                    context: Point::new(0, 0)..Point::new(3, 0),
14307                    primary: None,
14308                },
14309                ExcerptRange {
14310                    context: Point::new(5, 0)..Point::new(7, 0),
14311                    primary: None,
14312                },
14313                ExcerptRange {
14314                    context: Point::new(9, 0)..Point::new(10, 3),
14315                    primary: None,
14316                },
14317            ],
14318            cx,
14319        );
14320        multibuffer
14321    });
14322
14323    let editor =
14324        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14325    editor
14326        .update(cx, |editor, _window, cx| {
14327            for (buffer, diff_base) in [
14328                (buffer_1.clone(), file_1_old),
14329                (buffer_2.clone(), file_2_old),
14330                (buffer_3.clone(), file_3_old),
14331            ] {
14332                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14333                editor
14334                    .buffer
14335                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14336            }
14337        })
14338        .unwrap();
14339
14340    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14341    cx.run_until_parked();
14342
14343    cx.assert_editor_state(
14344        &"
14345            ˇaaa
14346            ccc
14347            ddd
14348
14349            ggg
14350            hhh
14351
14352
14353            lll
14354            mmm
14355            NNN
14356
14357            qqq
14358            rrr
14359
14360            uuu
14361            111
14362            222
14363            333
14364
14365            666
14366            777
14367
14368            000
14369            !!!"
14370        .unindent(),
14371    );
14372
14373    cx.update_editor(|editor, window, cx| {
14374        editor.select_all(&SelectAll, window, cx);
14375        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14376    });
14377    cx.executor().run_until_parked();
14378
14379    cx.assert_state_with_diff(
14380        "
14381            «aaa
14382          - bbb
14383            ccc
14384            ddd
14385
14386            ggg
14387            hhh
14388
14389
14390            lll
14391            mmm
14392          - nnn
14393          + NNN
14394
14395            qqq
14396            rrr
14397
14398            uuu
14399            111
14400            222
14401            333
14402
14403          + 666
14404            777
14405
14406            000
14407            !!!ˇ»"
14408            .unindent(),
14409    );
14410}
14411
14412#[gpui::test]
14413async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14414    init_test(cx, |_| {});
14415
14416    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14417    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14418
14419    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14420    let multi_buffer = cx.new(|cx| {
14421        let mut multibuffer = MultiBuffer::new(ReadWrite);
14422        multibuffer.push_excerpts(
14423            buffer.clone(),
14424            [
14425                ExcerptRange {
14426                    context: Point::new(0, 0)..Point::new(2, 0),
14427                    primary: None,
14428                },
14429                ExcerptRange {
14430                    context: Point::new(4, 0)..Point::new(7, 0),
14431                    primary: None,
14432                },
14433                ExcerptRange {
14434                    context: Point::new(9, 0)..Point::new(10, 0),
14435                    primary: None,
14436                },
14437            ],
14438            cx,
14439        );
14440        multibuffer
14441    });
14442
14443    let editor =
14444        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14445    editor
14446        .update(cx, |editor, _window, cx| {
14447            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14448            editor
14449                .buffer
14450                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14451        })
14452        .unwrap();
14453
14454    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14455    cx.run_until_parked();
14456
14457    cx.update_editor(|editor, window, cx| {
14458        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14459    });
14460    cx.executor().run_until_parked();
14461
14462    // When the start of a hunk coincides with the start of its excerpt,
14463    // the hunk is expanded. When the start of a a hunk is earlier than
14464    // the start of its excerpt, the hunk is not expanded.
14465    cx.assert_state_with_diff(
14466        "
14467            ˇaaa
14468          - bbb
14469          + BBB
14470
14471          - ddd
14472          - eee
14473          + DDD
14474          + EEE
14475            fff
14476
14477            iii
14478        "
14479        .unindent(),
14480    );
14481}
14482
14483#[gpui::test]
14484async fn test_edits_around_expanded_insertion_hunks(
14485    executor: BackgroundExecutor,
14486    cx: &mut TestAppContext,
14487) {
14488    init_test(cx, |_| {});
14489
14490    let mut cx = EditorTestContext::new(cx).await;
14491
14492    let diff_base = r#"
14493        use some::mod1;
14494        use some::mod2;
14495
14496        const A: u32 = 42;
14497
14498        fn main() {
14499            println!("hello");
14500
14501            println!("world");
14502        }
14503        "#
14504    .unindent();
14505    executor.run_until_parked();
14506    cx.set_state(
14507        &r#"
14508        use some::mod1;
14509        use some::mod2;
14510
14511        const A: u32 = 42;
14512        const B: u32 = 42;
14513        const C: u32 = 42;
14514        ˇ
14515
14516        fn main() {
14517            println!("hello");
14518
14519            println!("world");
14520        }
14521        "#
14522        .unindent(),
14523    );
14524
14525    cx.set_head_text(&diff_base);
14526    executor.run_until_parked();
14527
14528    cx.update_editor(|editor, window, cx| {
14529        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14530    });
14531    executor.run_until_parked();
14532
14533    cx.assert_state_with_diff(
14534        r#"
14535        use some::mod1;
14536        use some::mod2;
14537
14538        const A: u32 = 42;
14539      + const B: u32 = 42;
14540      + const C: u32 = 42;
14541      + ˇ
14542
14543        fn main() {
14544            println!("hello");
14545
14546            println!("world");
14547        }
14548      "#
14549        .unindent(),
14550    );
14551
14552    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14553    executor.run_until_parked();
14554
14555    cx.assert_state_with_diff(
14556        r#"
14557        use some::mod1;
14558        use some::mod2;
14559
14560        const A: u32 = 42;
14561      + const B: u32 = 42;
14562      + const C: u32 = 42;
14563      + const D: u32 = 42;
14564      + ˇ
14565
14566        fn main() {
14567            println!("hello");
14568
14569            println!("world");
14570        }
14571      "#
14572        .unindent(),
14573    );
14574
14575    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14576    executor.run_until_parked();
14577
14578    cx.assert_state_with_diff(
14579        r#"
14580        use some::mod1;
14581        use some::mod2;
14582
14583        const A: u32 = 42;
14584      + const B: u32 = 42;
14585      + const C: u32 = 42;
14586      + const D: u32 = 42;
14587      + const E: u32 = 42;
14588      + ˇ
14589
14590        fn main() {
14591            println!("hello");
14592
14593            println!("world");
14594        }
14595      "#
14596        .unindent(),
14597    );
14598
14599    cx.update_editor(|editor, window, cx| {
14600        editor.delete_line(&DeleteLine, window, cx);
14601    });
14602    executor.run_until_parked();
14603
14604    cx.assert_state_with_diff(
14605        r#"
14606        use some::mod1;
14607        use some::mod2;
14608
14609        const A: u32 = 42;
14610      + const B: u32 = 42;
14611      + const C: u32 = 42;
14612      + const D: u32 = 42;
14613      + const E: u32 = 42;
14614        ˇ
14615        fn main() {
14616            println!("hello");
14617
14618            println!("world");
14619        }
14620      "#
14621        .unindent(),
14622    );
14623
14624    cx.update_editor(|editor, 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        editor.move_up(&MoveUp, window, cx);
14630        editor.delete_line(&DeleteLine, window, cx);
14631    });
14632    executor.run_until_parked();
14633    cx.assert_state_with_diff(
14634        r#"
14635        use some::mod1;
14636        use some::mod2;
14637
14638        const A: u32 = 42;
14639      + const B: u32 = 42;
14640        ˇ
14641        fn main() {
14642            println!("hello");
14643
14644            println!("world");
14645        }
14646      "#
14647        .unindent(),
14648    );
14649
14650    cx.update_editor(|editor, window, cx| {
14651        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14652        editor.delete_line(&DeleteLine, window, cx);
14653    });
14654    executor.run_until_parked();
14655    cx.assert_state_with_diff(
14656        r#"
14657        ˇ
14658        fn main() {
14659            println!("hello");
14660
14661            println!("world");
14662        }
14663      "#
14664        .unindent(),
14665    );
14666}
14667
14668#[gpui::test]
14669async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14670    init_test(cx, |_| {});
14671
14672    let mut cx = EditorTestContext::new(cx).await;
14673    cx.set_head_text(indoc! { "
14674        one
14675        two
14676        three
14677        four
14678        five
14679        "
14680    });
14681    cx.set_state(indoc! { "
14682        one
14683        ˇthree
14684        five
14685    "});
14686    cx.run_until_parked();
14687    cx.update_editor(|editor, window, cx| {
14688        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14689    });
14690    cx.assert_state_with_diff(
14691        indoc! { "
14692        one
14693      - two
14694        ˇthree
14695      - four
14696        five
14697    "}
14698        .to_string(),
14699    );
14700    cx.update_editor(|editor, window, cx| {
14701        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14702    });
14703
14704    cx.assert_state_with_diff(
14705        indoc! { "
14706        one
14707        ˇthree
14708        five
14709    "}
14710        .to_string(),
14711    );
14712
14713    cx.set_state(indoc! { "
14714        one
14715        ˇTWO
14716        three
14717        four
14718        five
14719    "});
14720    cx.run_until_parked();
14721    cx.update_editor(|editor, window, cx| {
14722        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14723    });
14724
14725    cx.assert_state_with_diff(
14726        indoc! { "
14727            one
14728          - two
14729          + ˇTWO
14730            three
14731            four
14732            five
14733        "}
14734        .to_string(),
14735    );
14736    cx.update_editor(|editor, window, cx| {
14737        editor.move_up(&Default::default(), window, cx);
14738        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14739    });
14740    cx.assert_state_with_diff(
14741        indoc! { "
14742            one
14743            ˇTWO
14744            three
14745            four
14746            five
14747        "}
14748        .to_string(),
14749    );
14750}
14751
14752#[gpui::test]
14753async fn test_edits_around_expanded_deletion_hunks(
14754    executor: BackgroundExecutor,
14755    cx: &mut TestAppContext,
14756) {
14757    init_test(cx, |_| {});
14758
14759    let mut cx = EditorTestContext::new(cx).await;
14760
14761    let diff_base = r#"
14762        use some::mod1;
14763        use some::mod2;
14764
14765        const A: u32 = 42;
14766        const B: u32 = 42;
14767        const C: u32 = 42;
14768
14769
14770        fn main() {
14771            println!("hello");
14772
14773            println!("world");
14774        }
14775    "#
14776    .unindent();
14777    executor.run_until_parked();
14778    cx.set_state(
14779        &r#"
14780        use some::mod1;
14781        use some::mod2;
14782
14783        ˇconst B: u32 = 42;
14784        const C: u32 = 42;
14785
14786
14787        fn main() {
14788            println!("hello");
14789
14790            println!("world");
14791        }
14792        "#
14793        .unindent(),
14794    );
14795
14796    cx.set_head_text(&diff_base);
14797    executor.run_until_parked();
14798
14799    cx.update_editor(|editor, window, cx| {
14800        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14801    });
14802    executor.run_until_parked();
14803
14804    cx.assert_state_with_diff(
14805        r#"
14806        use some::mod1;
14807        use some::mod2;
14808
14809      - const A: u32 = 42;
14810        ˇconst B: u32 = 42;
14811        const C: u32 = 42;
14812
14813
14814        fn main() {
14815            println!("hello");
14816
14817            println!("world");
14818        }
14819      "#
14820        .unindent(),
14821    );
14822
14823    cx.update_editor(|editor, window, cx| {
14824        editor.delete_line(&DeleteLine, window, cx);
14825    });
14826    executor.run_until_parked();
14827    cx.assert_state_with_diff(
14828        r#"
14829        use some::mod1;
14830        use some::mod2;
14831
14832      - const A: u32 = 42;
14833      - const B: u32 = 42;
14834        ˇconst C: u32 = 42;
14835
14836
14837        fn main() {
14838            println!("hello");
14839
14840            println!("world");
14841        }
14842      "#
14843        .unindent(),
14844    );
14845
14846    cx.update_editor(|editor, window, cx| {
14847        editor.delete_line(&DeleteLine, window, cx);
14848    });
14849    executor.run_until_parked();
14850    cx.assert_state_with_diff(
14851        r#"
14852        use some::mod1;
14853        use some::mod2;
14854
14855      - const A: u32 = 42;
14856      - const B: u32 = 42;
14857      - const C: u32 = 42;
14858        ˇ
14859
14860        fn main() {
14861            println!("hello");
14862
14863            println!("world");
14864        }
14865      "#
14866        .unindent(),
14867    );
14868
14869    cx.update_editor(|editor, window, cx| {
14870        editor.handle_input("replacement", window, cx);
14871    });
14872    executor.run_until_parked();
14873    cx.assert_state_with_diff(
14874        r#"
14875        use some::mod1;
14876        use some::mod2;
14877
14878      - const A: u32 = 42;
14879      - const B: u32 = 42;
14880      - const C: u32 = 42;
14881      -
14882      + replacementˇ
14883
14884        fn main() {
14885            println!("hello");
14886
14887            println!("world");
14888        }
14889      "#
14890        .unindent(),
14891    );
14892}
14893
14894#[gpui::test]
14895async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14896    init_test(cx, |_| {});
14897
14898    let mut cx = EditorTestContext::new(cx).await;
14899
14900    let base_text = r#"
14901        one
14902        two
14903        three
14904        four
14905        five
14906    "#
14907    .unindent();
14908    executor.run_until_parked();
14909    cx.set_state(
14910        &r#"
14911        one
14912        two
14913        fˇour
14914        five
14915        "#
14916        .unindent(),
14917    );
14918
14919    cx.set_head_text(&base_text);
14920    executor.run_until_parked();
14921
14922    cx.update_editor(|editor, window, cx| {
14923        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14924    });
14925    executor.run_until_parked();
14926
14927    cx.assert_state_with_diff(
14928        r#"
14929          one
14930          two
14931        - three
14932          fˇour
14933          five
14934        "#
14935        .unindent(),
14936    );
14937
14938    cx.update_editor(|editor, window, cx| {
14939        editor.backspace(&Backspace, window, cx);
14940        editor.backspace(&Backspace, window, cx);
14941    });
14942    executor.run_until_parked();
14943    cx.assert_state_with_diff(
14944        r#"
14945          one
14946          two
14947        - threeˇ
14948        - four
14949        + our
14950          five
14951        "#
14952        .unindent(),
14953    );
14954}
14955
14956#[gpui::test]
14957async fn test_edit_after_expanded_modification_hunk(
14958    executor: BackgroundExecutor,
14959    cx: &mut TestAppContext,
14960) {
14961    init_test(cx, |_| {});
14962
14963    let mut cx = EditorTestContext::new(cx).await;
14964
14965    let diff_base = r#"
14966        use some::mod1;
14967        use some::mod2;
14968
14969        const A: u32 = 42;
14970        const B: u32 = 42;
14971        const C: u32 = 42;
14972        const D: u32 = 42;
14973
14974
14975        fn main() {
14976            println!("hello");
14977
14978            println!("world");
14979        }"#
14980    .unindent();
14981
14982    cx.set_state(
14983        &r#"
14984        use some::mod1;
14985        use some::mod2;
14986
14987        const A: u32 = 42;
14988        const B: u32 = 42;
14989        const C: u32 = 43ˇ
14990        const D: u32 = 42;
14991
14992
14993        fn main() {
14994            println!("hello");
14995
14996            println!("world");
14997        }"#
14998        .unindent(),
14999    );
15000
15001    cx.set_head_text(&diff_base);
15002    executor.run_until_parked();
15003    cx.update_editor(|editor, window, cx| {
15004        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15005    });
15006    executor.run_until_parked();
15007
15008    cx.assert_state_with_diff(
15009        r#"
15010        use some::mod1;
15011        use some::mod2;
15012
15013        const A: u32 = 42;
15014        const B: u32 = 42;
15015      - const C: u32 = 42;
15016      + const C: u32 = 43ˇ
15017        const D: u32 = 42;
15018
15019
15020        fn main() {
15021            println!("hello");
15022
15023            println!("world");
15024        }"#
15025        .unindent(),
15026    );
15027
15028    cx.update_editor(|editor, window, cx| {
15029        editor.handle_input("\nnew_line\n", window, cx);
15030    });
15031    executor.run_until_parked();
15032
15033    cx.assert_state_with_diff(
15034        r#"
15035        use some::mod1;
15036        use some::mod2;
15037
15038        const A: u32 = 42;
15039        const B: u32 = 42;
15040      - const C: u32 = 42;
15041      + const C: u32 = 43
15042      + new_line
15043      + ˇ
15044        const D: u32 = 42;
15045
15046
15047        fn main() {
15048            println!("hello");
15049
15050            println!("world");
15051        }"#
15052        .unindent(),
15053    );
15054}
15055
15056#[gpui::test]
15057async fn test_stage_and_unstage_added_file_hunk(
15058    executor: BackgroundExecutor,
15059    cx: &mut TestAppContext,
15060) {
15061    init_test(cx, |_| {});
15062
15063    let mut cx = EditorTestContext::new(cx).await;
15064    cx.update_editor(|editor, _, cx| {
15065        editor.set_expand_all_diff_hunks(cx);
15066    });
15067
15068    let working_copy = r#"
15069            ˇfn main() {
15070                println!("hello, world!");
15071            }
15072        "#
15073    .unindent();
15074
15075    cx.set_state(&working_copy);
15076    executor.run_until_parked();
15077
15078    cx.assert_state_with_diff(
15079        r#"
15080            + ˇfn main() {
15081            +     println!("hello, world!");
15082            + }
15083        "#
15084        .unindent(),
15085    );
15086    cx.assert_index_text(None);
15087
15088    cx.update_editor(|editor, window, cx| {
15089        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15090    });
15091    executor.run_until_parked();
15092    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15093    cx.assert_state_with_diff(
15094        r#"
15095            + ˇfn main() {
15096            +     println!("hello, world!");
15097            + }
15098        "#
15099        .unindent(),
15100    );
15101
15102    cx.update_editor(|editor, window, cx| {
15103        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15104    });
15105    executor.run_until_parked();
15106    cx.assert_index_text(None);
15107}
15108
15109async fn setup_indent_guides_editor(
15110    text: &str,
15111    cx: &mut TestAppContext,
15112) -> (BufferId, EditorTestContext) {
15113    init_test(cx, |_| {});
15114
15115    let mut cx = EditorTestContext::new(cx).await;
15116
15117    let buffer_id = cx.update_editor(|editor, window, cx| {
15118        editor.set_text(text, window, cx);
15119        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15120
15121        buffer_ids[0]
15122    });
15123
15124    (buffer_id, cx)
15125}
15126
15127fn assert_indent_guides(
15128    range: Range<u32>,
15129    expected: Vec<IndentGuide>,
15130    active_indices: Option<Vec<usize>>,
15131    cx: &mut EditorTestContext,
15132) {
15133    let indent_guides = cx.update_editor(|editor, window, cx| {
15134        let snapshot = editor.snapshot(window, cx).display_snapshot;
15135        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15136            editor,
15137            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15138            true,
15139            &snapshot,
15140            cx,
15141        );
15142
15143        indent_guides.sort_by(|a, b| {
15144            a.depth.cmp(&b.depth).then(
15145                a.start_row
15146                    .cmp(&b.start_row)
15147                    .then(a.end_row.cmp(&b.end_row)),
15148            )
15149        });
15150        indent_guides
15151    });
15152
15153    if let Some(expected) = active_indices {
15154        let active_indices = cx.update_editor(|editor, window, cx| {
15155            let snapshot = editor.snapshot(window, cx).display_snapshot;
15156            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15157        });
15158
15159        assert_eq!(
15160            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15161            expected,
15162            "Active indent guide indices do not match"
15163        );
15164    }
15165
15166    assert_eq!(indent_guides, expected, "Indent guides do not match");
15167}
15168
15169fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15170    IndentGuide {
15171        buffer_id,
15172        start_row: MultiBufferRow(start_row),
15173        end_row: MultiBufferRow(end_row),
15174        depth,
15175        tab_size: 4,
15176        settings: IndentGuideSettings {
15177            enabled: true,
15178            line_width: 1,
15179            active_line_width: 1,
15180            ..Default::default()
15181        },
15182    }
15183}
15184
15185#[gpui::test]
15186async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15187    let (buffer_id, mut cx) = setup_indent_guides_editor(
15188        &"
15189    fn main() {
15190        let a = 1;
15191    }"
15192        .unindent(),
15193        cx,
15194    )
15195    .await;
15196
15197    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15198}
15199
15200#[gpui::test]
15201async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15202    let (buffer_id, mut cx) = setup_indent_guides_editor(
15203        &"
15204    fn main() {
15205        let a = 1;
15206        let b = 2;
15207    }"
15208        .unindent(),
15209        cx,
15210    )
15211    .await;
15212
15213    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15214}
15215
15216#[gpui::test]
15217async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15218    let (buffer_id, mut cx) = setup_indent_guides_editor(
15219        &"
15220    fn main() {
15221        let a = 1;
15222        if a == 3 {
15223            let b = 2;
15224        } else {
15225            let c = 3;
15226        }
15227    }"
15228        .unindent(),
15229        cx,
15230    )
15231    .await;
15232
15233    assert_indent_guides(
15234        0..8,
15235        vec![
15236            indent_guide(buffer_id, 1, 6, 0),
15237            indent_guide(buffer_id, 3, 3, 1),
15238            indent_guide(buffer_id, 5, 5, 1),
15239        ],
15240        None,
15241        &mut cx,
15242    );
15243}
15244
15245#[gpui::test]
15246async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15247    let (buffer_id, mut cx) = setup_indent_guides_editor(
15248        &"
15249    fn main() {
15250        let a = 1;
15251            let b = 2;
15252        let c = 3;
15253    }"
15254        .unindent(),
15255        cx,
15256    )
15257    .await;
15258
15259    assert_indent_guides(
15260        0..5,
15261        vec![
15262            indent_guide(buffer_id, 1, 3, 0),
15263            indent_guide(buffer_id, 2, 2, 1),
15264        ],
15265        None,
15266        &mut cx,
15267    );
15268}
15269
15270#[gpui::test]
15271async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15272    let (buffer_id, mut cx) = setup_indent_guides_editor(
15273        &"
15274        fn main() {
15275            let a = 1;
15276
15277            let c = 3;
15278        }"
15279        .unindent(),
15280        cx,
15281    )
15282    .await;
15283
15284    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15285}
15286
15287#[gpui::test]
15288async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15289    let (buffer_id, mut cx) = setup_indent_guides_editor(
15290        &"
15291        fn main() {
15292            let a = 1;
15293
15294            let c = 3;
15295
15296            if a == 3 {
15297                let b = 2;
15298            } else {
15299                let c = 3;
15300            }
15301        }"
15302        .unindent(),
15303        cx,
15304    )
15305    .await;
15306
15307    assert_indent_guides(
15308        0..11,
15309        vec![
15310            indent_guide(buffer_id, 1, 9, 0),
15311            indent_guide(buffer_id, 6, 6, 1),
15312            indent_guide(buffer_id, 8, 8, 1),
15313        ],
15314        None,
15315        &mut cx,
15316    );
15317}
15318
15319#[gpui::test]
15320async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15321    let (buffer_id, mut cx) = setup_indent_guides_editor(
15322        &"
15323        fn main() {
15324            let a = 1;
15325
15326            let c = 3;
15327
15328            if a == 3 {
15329                let b = 2;
15330            } else {
15331                let c = 3;
15332            }
15333        }"
15334        .unindent(),
15335        cx,
15336    )
15337    .await;
15338
15339    assert_indent_guides(
15340        1..11,
15341        vec![
15342            indent_guide(buffer_id, 1, 9, 0),
15343            indent_guide(buffer_id, 6, 6, 1),
15344            indent_guide(buffer_id, 8, 8, 1),
15345        ],
15346        None,
15347        &mut cx,
15348    );
15349}
15350
15351#[gpui::test]
15352async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15353    let (buffer_id, mut cx) = setup_indent_guides_editor(
15354        &"
15355        fn main() {
15356            let a = 1;
15357
15358            let c = 3;
15359
15360            if a == 3 {
15361                let b = 2;
15362            } else {
15363                let c = 3;
15364            }
15365        }"
15366        .unindent(),
15367        cx,
15368    )
15369    .await;
15370
15371    assert_indent_guides(
15372        1..10,
15373        vec![
15374            indent_guide(buffer_id, 1, 9, 0),
15375            indent_guide(buffer_id, 6, 6, 1),
15376            indent_guide(buffer_id, 8, 8, 1),
15377        ],
15378        None,
15379        &mut cx,
15380    );
15381}
15382
15383#[gpui::test]
15384async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15385    let (buffer_id, mut cx) = setup_indent_guides_editor(
15386        &"
15387        block1
15388            block2
15389                block3
15390                    block4
15391            block2
15392        block1
15393        block1"
15394            .unindent(),
15395        cx,
15396    )
15397    .await;
15398
15399    assert_indent_guides(
15400        1..10,
15401        vec![
15402            indent_guide(buffer_id, 1, 4, 0),
15403            indent_guide(buffer_id, 2, 3, 1),
15404            indent_guide(buffer_id, 3, 3, 2),
15405        ],
15406        None,
15407        &mut cx,
15408    );
15409}
15410
15411#[gpui::test]
15412async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15413    let (buffer_id, mut cx) = setup_indent_guides_editor(
15414        &"
15415        block1
15416            block2
15417                block3
15418
15419        block1
15420        block1"
15421            .unindent(),
15422        cx,
15423    )
15424    .await;
15425
15426    assert_indent_guides(
15427        0..6,
15428        vec![
15429            indent_guide(buffer_id, 1, 2, 0),
15430            indent_guide(buffer_id, 2, 2, 1),
15431        ],
15432        None,
15433        &mut cx,
15434    );
15435}
15436
15437#[gpui::test]
15438async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15439    let (buffer_id, mut cx) = setup_indent_guides_editor(
15440        &"
15441        block1
15442
15443
15444
15445            block2
15446        "
15447        .unindent(),
15448        cx,
15449    )
15450    .await;
15451
15452    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15453}
15454
15455#[gpui::test]
15456async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15457    let (buffer_id, mut cx) = setup_indent_guides_editor(
15458        &"
15459        def a:
15460        \tb = 3
15461        \tif True:
15462        \t\tc = 4
15463        \t\td = 5
15464        \tprint(b)
15465        "
15466        .unindent(),
15467        cx,
15468    )
15469    .await;
15470
15471    assert_indent_guides(
15472        0..6,
15473        vec![
15474            indent_guide(buffer_id, 1, 6, 0),
15475            indent_guide(buffer_id, 3, 4, 1),
15476        ],
15477        None,
15478        &mut cx,
15479    );
15480}
15481
15482#[gpui::test]
15483async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15484    let (buffer_id, mut cx) = setup_indent_guides_editor(
15485        &"
15486    fn main() {
15487        let a = 1;
15488    }"
15489        .unindent(),
15490        cx,
15491    )
15492    .await;
15493
15494    cx.update_editor(|editor, window, cx| {
15495        editor.change_selections(None, window, cx, |s| {
15496            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15497        });
15498    });
15499
15500    assert_indent_guides(
15501        0..3,
15502        vec![indent_guide(buffer_id, 1, 1, 0)],
15503        Some(vec![0]),
15504        &mut cx,
15505    );
15506}
15507
15508#[gpui::test]
15509async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15510    let (buffer_id, mut cx) = setup_indent_guides_editor(
15511        &"
15512    fn main() {
15513        if 1 == 2 {
15514            let a = 1;
15515        }
15516    }"
15517        .unindent(),
15518        cx,
15519    )
15520    .await;
15521
15522    cx.update_editor(|editor, window, cx| {
15523        editor.change_selections(None, window, cx, |s| {
15524            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15525        });
15526    });
15527
15528    assert_indent_guides(
15529        0..4,
15530        vec![
15531            indent_guide(buffer_id, 1, 3, 0),
15532            indent_guide(buffer_id, 2, 2, 1),
15533        ],
15534        Some(vec![1]),
15535        &mut cx,
15536    );
15537
15538    cx.update_editor(|editor, window, cx| {
15539        editor.change_selections(None, window, cx, |s| {
15540            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15541        });
15542    });
15543
15544    assert_indent_guides(
15545        0..4,
15546        vec![
15547            indent_guide(buffer_id, 1, 3, 0),
15548            indent_guide(buffer_id, 2, 2, 1),
15549        ],
15550        Some(vec![1]),
15551        &mut cx,
15552    );
15553
15554    cx.update_editor(|editor, window, cx| {
15555        editor.change_selections(None, window, cx, |s| {
15556            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15557        });
15558    });
15559
15560    assert_indent_guides(
15561        0..4,
15562        vec![
15563            indent_guide(buffer_id, 1, 3, 0),
15564            indent_guide(buffer_id, 2, 2, 1),
15565        ],
15566        Some(vec![0]),
15567        &mut cx,
15568    );
15569}
15570
15571#[gpui::test]
15572async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15573    let (buffer_id, mut cx) = setup_indent_guides_editor(
15574        &"
15575    fn main() {
15576        let a = 1;
15577
15578        let b = 2;
15579    }"
15580        .unindent(),
15581        cx,
15582    )
15583    .await;
15584
15585    cx.update_editor(|editor, window, cx| {
15586        editor.change_selections(None, window, cx, |s| {
15587            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15588        });
15589    });
15590
15591    assert_indent_guides(
15592        0..5,
15593        vec![indent_guide(buffer_id, 1, 3, 0)],
15594        Some(vec![0]),
15595        &mut cx,
15596    );
15597}
15598
15599#[gpui::test]
15600async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15601    let (buffer_id, mut cx) = setup_indent_guides_editor(
15602        &"
15603    def m:
15604        a = 1
15605        pass"
15606            .unindent(),
15607        cx,
15608    )
15609    .await;
15610
15611    cx.update_editor(|editor, window, cx| {
15612        editor.change_selections(None, window, cx, |s| {
15613            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15614        });
15615    });
15616
15617    assert_indent_guides(
15618        0..3,
15619        vec![indent_guide(buffer_id, 1, 2, 0)],
15620        Some(vec![0]),
15621        &mut cx,
15622    );
15623}
15624
15625#[gpui::test]
15626async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15627    init_test(cx, |_| {});
15628    let mut cx = EditorTestContext::new(cx).await;
15629    let text = indoc! {
15630        "
15631        impl A {
15632            fn b() {
15633                0;
15634                3;
15635                5;
15636                6;
15637                7;
15638            }
15639        }
15640        "
15641    };
15642    let base_text = indoc! {
15643        "
15644        impl A {
15645            fn b() {
15646                0;
15647                1;
15648                2;
15649                3;
15650                4;
15651            }
15652            fn c() {
15653                5;
15654                6;
15655                7;
15656            }
15657        }
15658        "
15659    };
15660
15661    cx.update_editor(|editor, window, cx| {
15662        editor.set_text(text, window, cx);
15663
15664        editor.buffer().update(cx, |multibuffer, cx| {
15665            let buffer = multibuffer.as_singleton().unwrap();
15666            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15667
15668            multibuffer.set_all_diff_hunks_expanded(cx);
15669            multibuffer.add_diff(diff, cx);
15670
15671            buffer.read(cx).remote_id()
15672        })
15673    });
15674    cx.run_until_parked();
15675
15676    cx.assert_state_with_diff(
15677        indoc! { "
15678          impl A {
15679              fn b() {
15680                  0;
15681        -         1;
15682        -         2;
15683                  3;
15684        -         4;
15685        -     }
15686        -     fn c() {
15687                  5;
15688                  6;
15689                  7;
15690              }
15691          }
15692          ˇ"
15693        }
15694        .to_string(),
15695    );
15696
15697    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15698        editor
15699            .snapshot(window, cx)
15700            .buffer_snapshot
15701            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15702            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15703            .collect::<Vec<_>>()
15704    });
15705    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15706    assert_eq!(
15707        actual_guides,
15708        vec![
15709            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15710            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15711            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15712        ]
15713    );
15714}
15715
15716#[gpui::test]
15717async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15718    init_test(cx, |_| {});
15719    let mut cx = EditorTestContext::new(cx).await;
15720
15721    let diff_base = r#"
15722        a
15723        b
15724        c
15725        "#
15726    .unindent();
15727
15728    cx.set_state(
15729        &r#"
15730        ˇA
15731        b
15732        C
15733        "#
15734        .unindent(),
15735    );
15736    cx.set_head_text(&diff_base);
15737    cx.update_editor(|editor, window, cx| {
15738        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15739    });
15740    executor.run_until_parked();
15741
15742    let both_hunks_expanded = r#"
15743        - a
15744        + ˇA
15745          b
15746        - c
15747        + C
15748        "#
15749    .unindent();
15750
15751    cx.assert_state_with_diff(both_hunks_expanded.clone());
15752
15753    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15754        let snapshot = editor.snapshot(window, cx);
15755        let hunks = editor
15756            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15757            .collect::<Vec<_>>();
15758        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15759        let buffer_id = hunks[0].buffer_id;
15760        hunks
15761            .into_iter()
15762            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15763            .collect::<Vec<_>>()
15764    });
15765    assert_eq!(hunk_ranges.len(), 2);
15766
15767    cx.update_editor(|editor, _, cx| {
15768        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15769    });
15770    executor.run_until_parked();
15771
15772    let second_hunk_expanded = r#"
15773          ˇA
15774          b
15775        - c
15776        + C
15777        "#
15778    .unindent();
15779
15780    cx.assert_state_with_diff(second_hunk_expanded);
15781
15782    cx.update_editor(|editor, _, cx| {
15783        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15784    });
15785    executor.run_until_parked();
15786
15787    cx.assert_state_with_diff(both_hunks_expanded.clone());
15788
15789    cx.update_editor(|editor, _, cx| {
15790        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15791    });
15792    executor.run_until_parked();
15793
15794    let first_hunk_expanded = r#"
15795        - a
15796        + ˇA
15797          b
15798          C
15799        "#
15800    .unindent();
15801
15802    cx.assert_state_with_diff(first_hunk_expanded);
15803
15804    cx.update_editor(|editor, _, cx| {
15805        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15806    });
15807    executor.run_until_parked();
15808
15809    cx.assert_state_with_diff(both_hunks_expanded);
15810
15811    cx.set_state(
15812        &r#"
15813        ˇA
15814        b
15815        "#
15816        .unindent(),
15817    );
15818    cx.run_until_parked();
15819
15820    // TODO this cursor position seems bad
15821    cx.assert_state_with_diff(
15822        r#"
15823        - ˇa
15824        + A
15825          b
15826        "#
15827        .unindent(),
15828    );
15829
15830    cx.update_editor(|editor, window, cx| {
15831        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15832    });
15833
15834    cx.assert_state_with_diff(
15835        r#"
15836            - ˇa
15837            + A
15838              b
15839            - c
15840            "#
15841        .unindent(),
15842    );
15843
15844    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15845        let snapshot = editor.snapshot(window, cx);
15846        let hunks = editor
15847            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15848            .collect::<Vec<_>>();
15849        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15850        let buffer_id = hunks[0].buffer_id;
15851        hunks
15852            .into_iter()
15853            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15854            .collect::<Vec<_>>()
15855    });
15856    assert_eq!(hunk_ranges.len(), 2);
15857
15858    cx.update_editor(|editor, _, cx| {
15859        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15860    });
15861    executor.run_until_parked();
15862
15863    cx.assert_state_with_diff(
15864        r#"
15865        - ˇa
15866        + A
15867          b
15868        "#
15869        .unindent(),
15870    );
15871}
15872
15873#[gpui::test]
15874async fn test_toggle_deletion_hunk_at_start_of_file(
15875    executor: BackgroundExecutor,
15876    cx: &mut TestAppContext,
15877) {
15878    init_test(cx, |_| {});
15879    let mut cx = EditorTestContext::new(cx).await;
15880
15881    let diff_base = r#"
15882        a
15883        b
15884        c
15885        "#
15886    .unindent();
15887
15888    cx.set_state(
15889        &r#"
15890        ˇb
15891        c
15892        "#
15893        .unindent(),
15894    );
15895    cx.set_head_text(&diff_base);
15896    cx.update_editor(|editor, window, cx| {
15897        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15898    });
15899    executor.run_until_parked();
15900
15901    let hunk_expanded = r#"
15902        - a
15903          ˇb
15904          c
15905        "#
15906    .unindent();
15907
15908    cx.assert_state_with_diff(hunk_expanded.clone());
15909
15910    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15911        let snapshot = editor.snapshot(window, cx);
15912        let hunks = editor
15913            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15914            .collect::<Vec<_>>();
15915        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15916        let buffer_id = hunks[0].buffer_id;
15917        hunks
15918            .into_iter()
15919            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15920            .collect::<Vec<_>>()
15921    });
15922    assert_eq!(hunk_ranges.len(), 1);
15923
15924    cx.update_editor(|editor, _, cx| {
15925        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15926    });
15927    executor.run_until_parked();
15928
15929    let hunk_collapsed = r#"
15930          ˇb
15931          c
15932        "#
15933    .unindent();
15934
15935    cx.assert_state_with_diff(hunk_collapsed);
15936
15937    cx.update_editor(|editor, _, cx| {
15938        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15939    });
15940    executor.run_until_parked();
15941
15942    cx.assert_state_with_diff(hunk_expanded.clone());
15943}
15944
15945#[gpui::test]
15946async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15947    init_test(cx, |_| {});
15948
15949    let fs = FakeFs::new(cx.executor());
15950    fs.insert_tree(
15951        path!("/test"),
15952        json!({
15953            ".git": {},
15954            "file-1": "ONE\n",
15955            "file-2": "TWO\n",
15956            "file-3": "THREE\n",
15957        }),
15958    )
15959    .await;
15960
15961    fs.set_head_for_repo(
15962        path!("/test/.git").as_ref(),
15963        &[
15964            ("file-1".into(), "one\n".into()),
15965            ("file-2".into(), "two\n".into()),
15966            ("file-3".into(), "three\n".into()),
15967        ],
15968    );
15969
15970    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15971    let mut buffers = vec![];
15972    for i in 1..=3 {
15973        let buffer = project
15974            .update(cx, |project, cx| {
15975                let path = format!(path!("/test/file-{}"), i);
15976                project.open_local_buffer(path, cx)
15977            })
15978            .await
15979            .unwrap();
15980        buffers.push(buffer);
15981    }
15982
15983    let multibuffer = cx.new(|cx| {
15984        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15985        multibuffer.set_all_diff_hunks_expanded(cx);
15986        for buffer in &buffers {
15987            let snapshot = buffer.read(cx).snapshot();
15988            multibuffer.set_excerpts_for_path(
15989                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
15990                buffer.clone(),
15991                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15992                DEFAULT_MULTIBUFFER_CONTEXT,
15993                cx,
15994            );
15995        }
15996        multibuffer
15997    });
15998
15999    let editor = cx.add_window(|window, cx| {
16000        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16001    });
16002    cx.run_until_parked();
16003
16004    let snapshot = editor
16005        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16006        .unwrap();
16007    let hunks = snapshot
16008        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16009        .map(|hunk| match hunk {
16010            DisplayDiffHunk::Unfolded {
16011                display_row_range, ..
16012            } => display_row_range,
16013            DisplayDiffHunk::Folded { .. } => unreachable!(),
16014        })
16015        .collect::<Vec<_>>();
16016    assert_eq!(
16017        hunks,
16018        [
16019            DisplayRow(2)..DisplayRow(4),
16020            DisplayRow(7)..DisplayRow(9),
16021            DisplayRow(12)..DisplayRow(14),
16022        ]
16023    );
16024}
16025
16026#[gpui::test]
16027async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16028    init_test(cx, |_| {});
16029
16030    let mut cx = EditorTestContext::new(cx).await;
16031    cx.set_head_text(indoc! { "
16032        one
16033        two
16034        three
16035        four
16036        five
16037        "
16038    });
16039    cx.set_index_text(indoc! { "
16040        one
16041        two
16042        three
16043        four
16044        five
16045        "
16046    });
16047    cx.set_state(indoc! {"
16048        one
16049        TWO
16050        ˇTHREE
16051        FOUR
16052        five
16053    "});
16054    cx.run_until_parked();
16055    cx.update_editor(|editor, window, cx| {
16056        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16057    });
16058    cx.run_until_parked();
16059    cx.assert_index_text(Some(indoc! {"
16060        one
16061        TWO
16062        THREE
16063        FOUR
16064        five
16065    "}));
16066    cx.set_state(indoc! { "
16067        one
16068        TWO
16069        ˇTHREE-HUNDRED
16070        FOUR
16071        five
16072    "});
16073    cx.run_until_parked();
16074    cx.update_editor(|editor, window, cx| {
16075        let snapshot = editor.snapshot(window, cx);
16076        let hunks = editor
16077            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16078            .collect::<Vec<_>>();
16079        assert_eq!(hunks.len(), 1);
16080        assert_eq!(
16081            hunks[0].status(),
16082            DiffHunkStatus {
16083                kind: DiffHunkStatusKind::Modified,
16084                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16085            }
16086        );
16087
16088        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16089    });
16090    cx.run_until_parked();
16091    cx.assert_index_text(Some(indoc! {"
16092        one
16093        TWO
16094        THREE-HUNDRED
16095        FOUR
16096        five
16097    "}));
16098}
16099
16100#[gpui::test]
16101fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16102    init_test(cx, |_| {});
16103
16104    let editor = cx.add_window(|window, cx| {
16105        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16106        build_editor(buffer, window, cx)
16107    });
16108
16109    let render_args = Arc::new(Mutex::new(None));
16110    let snapshot = editor
16111        .update(cx, |editor, window, cx| {
16112            let snapshot = editor.buffer().read(cx).snapshot(cx);
16113            let range =
16114                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16115
16116            struct RenderArgs {
16117                row: MultiBufferRow,
16118                folded: bool,
16119                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16120            }
16121
16122            let crease = Crease::inline(
16123                range,
16124                FoldPlaceholder::test(),
16125                {
16126                    let toggle_callback = render_args.clone();
16127                    move |row, folded, callback, _window, _cx| {
16128                        *toggle_callback.lock() = Some(RenderArgs {
16129                            row,
16130                            folded,
16131                            callback,
16132                        });
16133                        div()
16134                    }
16135                },
16136                |_row, _folded, _window, _cx| div(),
16137            );
16138
16139            editor.insert_creases(Some(crease), cx);
16140            let snapshot = editor.snapshot(window, cx);
16141            let _div = snapshot.render_crease_toggle(
16142                MultiBufferRow(1),
16143                false,
16144                cx.entity().clone(),
16145                window,
16146                cx,
16147            );
16148            snapshot
16149        })
16150        .unwrap();
16151
16152    let render_args = render_args.lock().take().unwrap();
16153    assert_eq!(render_args.row, MultiBufferRow(1));
16154    assert!(!render_args.folded);
16155    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16156
16157    cx.update_window(*editor, |_, window, cx| {
16158        (render_args.callback)(true, window, cx)
16159    })
16160    .unwrap();
16161    let snapshot = editor
16162        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16163        .unwrap();
16164    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16165
16166    cx.update_window(*editor, |_, window, cx| {
16167        (render_args.callback)(false, window, cx)
16168    })
16169    .unwrap();
16170    let snapshot = editor
16171        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16172        .unwrap();
16173    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16174}
16175
16176#[gpui::test]
16177async fn test_input_text(cx: &mut TestAppContext) {
16178    init_test(cx, |_| {});
16179    let mut cx = EditorTestContext::new(cx).await;
16180
16181    cx.set_state(
16182        &r#"ˇone
16183        two
16184
16185        three
16186        fourˇ
16187        five
16188
16189        siˇx"#
16190            .unindent(),
16191    );
16192
16193    cx.dispatch_action(HandleInput(String::new()));
16194    cx.assert_editor_state(
16195        &r#"ˇone
16196        two
16197
16198        three
16199        fourˇ
16200        five
16201
16202        siˇx"#
16203            .unindent(),
16204    );
16205
16206    cx.dispatch_action(HandleInput("AAAA".to_string()));
16207    cx.assert_editor_state(
16208        &r#"AAAAˇone
16209        two
16210
16211        three
16212        fourAAAAˇ
16213        five
16214
16215        siAAAAˇx"#
16216            .unindent(),
16217    );
16218}
16219
16220#[gpui::test]
16221async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16222    init_test(cx, |_| {});
16223
16224    let mut cx = EditorTestContext::new(cx).await;
16225    cx.set_state(
16226        r#"let foo = 1;
16227let foo = 2;
16228let foo = 3;
16229let fooˇ = 4;
16230let foo = 5;
16231let foo = 6;
16232let foo = 7;
16233let foo = 8;
16234let foo = 9;
16235let foo = 10;
16236let foo = 11;
16237let foo = 12;
16238let foo = 13;
16239let foo = 14;
16240let foo = 15;"#,
16241    );
16242
16243    cx.update_editor(|e, window, cx| {
16244        assert_eq!(
16245            e.next_scroll_position,
16246            NextScrollCursorCenterTopBottom::Center,
16247            "Default next scroll direction is center",
16248        );
16249
16250        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16251        assert_eq!(
16252            e.next_scroll_position,
16253            NextScrollCursorCenterTopBottom::Top,
16254            "After center, next scroll direction should be top",
16255        );
16256
16257        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16258        assert_eq!(
16259            e.next_scroll_position,
16260            NextScrollCursorCenterTopBottom::Bottom,
16261            "After top, next scroll direction should be bottom",
16262        );
16263
16264        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16265        assert_eq!(
16266            e.next_scroll_position,
16267            NextScrollCursorCenterTopBottom::Center,
16268            "After bottom, scrolling should start over",
16269        );
16270
16271        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16272        assert_eq!(
16273            e.next_scroll_position,
16274            NextScrollCursorCenterTopBottom::Top,
16275            "Scrolling continues if retriggered fast enough"
16276        );
16277    });
16278
16279    cx.executor()
16280        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16281    cx.executor().run_until_parked();
16282    cx.update_editor(|e, _, _| {
16283        assert_eq!(
16284            e.next_scroll_position,
16285            NextScrollCursorCenterTopBottom::Center,
16286            "If scrolling is not triggered fast enough, it should reset"
16287        );
16288    });
16289}
16290
16291#[gpui::test]
16292async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16293    init_test(cx, |_| {});
16294    let mut cx = EditorLspTestContext::new_rust(
16295        lsp::ServerCapabilities {
16296            definition_provider: Some(lsp::OneOf::Left(true)),
16297            references_provider: Some(lsp::OneOf::Left(true)),
16298            ..lsp::ServerCapabilities::default()
16299        },
16300        cx,
16301    )
16302    .await;
16303
16304    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16305        let go_to_definition = cx
16306            .lsp
16307            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16308                move |params, _| async move {
16309                    if empty_go_to_definition {
16310                        Ok(None)
16311                    } else {
16312                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16313                            uri: params.text_document_position_params.text_document.uri,
16314                            range: lsp::Range::new(
16315                                lsp::Position::new(4, 3),
16316                                lsp::Position::new(4, 6),
16317                            ),
16318                        })))
16319                    }
16320                },
16321            );
16322        let references = cx
16323            .lsp
16324            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16325                Ok(Some(vec![lsp::Location {
16326                    uri: params.text_document_position.text_document.uri,
16327                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16328                }]))
16329            });
16330        (go_to_definition, references)
16331    };
16332
16333    cx.set_state(
16334        &r#"fn one() {
16335            let mut a = ˇtwo();
16336        }
16337
16338        fn two() {}"#
16339            .unindent(),
16340    );
16341    set_up_lsp_handlers(false, &mut cx);
16342    let navigated = cx
16343        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16344        .await
16345        .expect("Failed to navigate to definition");
16346    assert_eq!(
16347        navigated,
16348        Navigated::Yes,
16349        "Should have navigated to definition from the GetDefinition response"
16350    );
16351    cx.assert_editor_state(
16352        &r#"fn one() {
16353            let mut a = two();
16354        }
16355
16356        fn «twoˇ»() {}"#
16357            .unindent(),
16358    );
16359
16360    let editors = cx.update_workspace(|workspace, _, cx| {
16361        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16362    });
16363    cx.update_editor(|_, _, test_editor_cx| {
16364        assert_eq!(
16365            editors.len(),
16366            1,
16367            "Initially, only one, test, editor should be open in the workspace"
16368        );
16369        assert_eq!(
16370            test_editor_cx.entity(),
16371            editors.last().expect("Asserted len is 1").clone()
16372        );
16373    });
16374
16375    set_up_lsp_handlers(true, &mut cx);
16376    let navigated = cx
16377        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16378        .await
16379        .expect("Failed to navigate to lookup references");
16380    assert_eq!(
16381        navigated,
16382        Navigated::Yes,
16383        "Should have navigated to references as a fallback after empty GoToDefinition response"
16384    );
16385    // We should not change the selections in the existing file,
16386    // if opening another milti buffer with the references
16387    cx.assert_editor_state(
16388        &r#"fn one() {
16389            let mut a = two();
16390        }
16391
16392        fn «twoˇ»() {}"#
16393            .unindent(),
16394    );
16395    let editors = cx.update_workspace(|workspace, _, cx| {
16396        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16397    });
16398    cx.update_editor(|_, _, test_editor_cx| {
16399        assert_eq!(
16400            editors.len(),
16401            2,
16402            "After falling back to references search, we open a new editor with the results"
16403        );
16404        let references_fallback_text = editors
16405            .into_iter()
16406            .find(|new_editor| *new_editor != test_editor_cx.entity())
16407            .expect("Should have one non-test editor now")
16408            .read(test_editor_cx)
16409            .text(test_editor_cx);
16410        assert_eq!(
16411            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16412            "Should use the range from the references response and not the GoToDefinition one"
16413        );
16414    });
16415}
16416
16417#[gpui::test]
16418async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16419    init_test(cx, |_| {});
16420    cx.update(|cx| {
16421        let mut editor_settings = EditorSettings::get_global(cx).clone();
16422        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16423        EditorSettings::override_global(editor_settings, cx);
16424    });
16425    let mut cx = EditorLspTestContext::new_rust(
16426        lsp::ServerCapabilities {
16427            definition_provider: Some(lsp::OneOf::Left(true)),
16428            references_provider: Some(lsp::OneOf::Left(true)),
16429            ..lsp::ServerCapabilities::default()
16430        },
16431        cx,
16432    )
16433    .await;
16434    let original_state = r#"fn one() {
16435        let mut a = ˇtwo();
16436    }
16437
16438    fn two() {}"#
16439        .unindent();
16440    cx.set_state(&original_state);
16441
16442    let mut go_to_definition = cx
16443        .lsp
16444        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16445            move |_, _| async move { Ok(None) },
16446        );
16447    let _references = cx
16448        .lsp
16449        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16450            panic!("Should not call for references with no go to definition fallback")
16451        });
16452
16453    let navigated = cx
16454        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16455        .await
16456        .expect("Failed to navigate to lookup references");
16457    go_to_definition
16458        .next()
16459        .await
16460        .expect("Should have called the go_to_definition handler");
16461
16462    assert_eq!(
16463        navigated,
16464        Navigated::No,
16465        "Should have navigated to references as a fallback after empty GoToDefinition response"
16466    );
16467    cx.assert_editor_state(&original_state);
16468    let editors = cx.update_workspace(|workspace, _, cx| {
16469        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16470    });
16471    cx.update_editor(|_, _, _| {
16472        assert_eq!(
16473            editors.len(),
16474            1,
16475            "After unsuccessful fallback, no other editor should have been opened"
16476        );
16477    });
16478}
16479
16480#[gpui::test]
16481async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16482    init_test(cx, |_| {});
16483
16484    let language = Arc::new(Language::new(
16485        LanguageConfig::default(),
16486        Some(tree_sitter_rust::LANGUAGE.into()),
16487    ));
16488
16489    let text = r#"
16490        #[cfg(test)]
16491        mod tests() {
16492            #[test]
16493            fn runnable_1() {
16494                let a = 1;
16495            }
16496
16497            #[test]
16498            fn runnable_2() {
16499                let a = 1;
16500                let b = 2;
16501            }
16502        }
16503    "#
16504    .unindent();
16505
16506    let fs = FakeFs::new(cx.executor());
16507    fs.insert_file("/file.rs", Default::default()).await;
16508
16509    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16510    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16511    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16512    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16513    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16514
16515    let editor = cx.new_window_entity(|window, cx| {
16516        Editor::new(
16517            EditorMode::Full,
16518            multi_buffer,
16519            Some(project.clone()),
16520            window,
16521            cx,
16522        )
16523    });
16524
16525    editor.update_in(cx, |editor, window, cx| {
16526        let snapshot = editor.buffer().read(cx).snapshot(cx);
16527        editor.tasks.insert(
16528            (buffer.read(cx).remote_id(), 3),
16529            RunnableTasks {
16530                templates: vec![],
16531                offset: snapshot.anchor_before(43),
16532                column: 0,
16533                extra_variables: HashMap::default(),
16534                context_range: BufferOffset(43)..BufferOffset(85),
16535            },
16536        );
16537        editor.tasks.insert(
16538            (buffer.read(cx).remote_id(), 8),
16539            RunnableTasks {
16540                templates: vec![],
16541                offset: snapshot.anchor_before(86),
16542                column: 0,
16543                extra_variables: HashMap::default(),
16544                context_range: BufferOffset(86)..BufferOffset(191),
16545            },
16546        );
16547
16548        // Test finding task when cursor is inside function body
16549        editor.change_selections(None, window, cx, |s| {
16550            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16551        });
16552        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16553        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16554
16555        // Test finding task when cursor is on function name
16556        editor.change_selections(None, window, cx, |s| {
16557            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16558        });
16559        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16560        assert_eq!(row, 8, "Should find task when cursor is on function name");
16561    });
16562}
16563
16564#[gpui::test]
16565async fn test_folding_buffers(cx: &mut TestAppContext) {
16566    init_test(cx, |_| {});
16567
16568    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16569    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16570    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16571
16572    let fs = FakeFs::new(cx.executor());
16573    fs.insert_tree(
16574        path!("/a"),
16575        json!({
16576            "first.rs": sample_text_1,
16577            "second.rs": sample_text_2,
16578            "third.rs": sample_text_3,
16579        }),
16580    )
16581    .await;
16582    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16583    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16584    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16585    let worktree = project.update(cx, |project, cx| {
16586        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16587        assert_eq!(worktrees.len(), 1);
16588        worktrees.pop().unwrap()
16589    });
16590    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16591
16592    let buffer_1 = project
16593        .update(cx, |project, cx| {
16594            project.open_buffer((worktree_id, "first.rs"), cx)
16595        })
16596        .await
16597        .unwrap();
16598    let buffer_2 = project
16599        .update(cx, |project, cx| {
16600            project.open_buffer((worktree_id, "second.rs"), cx)
16601        })
16602        .await
16603        .unwrap();
16604    let buffer_3 = project
16605        .update(cx, |project, cx| {
16606            project.open_buffer((worktree_id, "third.rs"), cx)
16607        })
16608        .await
16609        .unwrap();
16610
16611    let multi_buffer = cx.new(|cx| {
16612        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16613        multi_buffer.push_excerpts(
16614            buffer_1.clone(),
16615            [
16616                ExcerptRange {
16617                    context: Point::new(0, 0)..Point::new(3, 0),
16618                    primary: None,
16619                },
16620                ExcerptRange {
16621                    context: Point::new(5, 0)..Point::new(7, 0),
16622                    primary: None,
16623                },
16624                ExcerptRange {
16625                    context: Point::new(9, 0)..Point::new(10, 4),
16626                    primary: None,
16627                },
16628            ],
16629            cx,
16630        );
16631        multi_buffer.push_excerpts(
16632            buffer_2.clone(),
16633            [
16634                ExcerptRange {
16635                    context: Point::new(0, 0)..Point::new(3, 0),
16636                    primary: None,
16637                },
16638                ExcerptRange {
16639                    context: Point::new(5, 0)..Point::new(7, 0),
16640                    primary: None,
16641                },
16642                ExcerptRange {
16643                    context: Point::new(9, 0)..Point::new(10, 4),
16644                    primary: None,
16645                },
16646            ],
16647            cx,
16648        );
16649        multi_buffer.push_excerpts(
16650            buffer_3.clone(),
16651            [
16652                ExcerptRange {
16653                    context: Point::new(0, 0)..Point::new(3, 0),
16654                    primary: None,
16655                },
16656                ExcerptRange {
16657                    context: Point::new(5, 0)..Point::new(7, 0),
16658                    primary: None,
16659                },
16660                ExcerptRange {
16661                    context: Point::new(9, 0)..Point::new(10, 4),
16662                    primary: None,
16663                },
16664            ],
16665            cx,
16666        );
16667        multi_buffer
16668    });
16669    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16670        Editor::new(
16671            EditorMode::Full,
16672            multi_buffer.clone(),
16673            Some(project.clone()),
16674            window,
16675            cx,
16676        )
16677    });
16678
16679    assert_eq!(
16680        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16681        "\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",
16682    );
16683
16684    multi_buffer_editor.update(cx, |editor, cx| {
16685        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16686    });
16687    assert_eq!(
16688        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16689        "\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",
16690        "After folding the first buffer, its text should not be displayed"
16691    );
16692
16693    multi_buffer_editor.update(cx, |editor, cx| {
16694        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16695    });
16696    assert_eq!(
16697        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16698        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16699        "After folding the second buffer, its text should not be displayed"
16700    );
16701
16702    multi_buffer_editor.update(cx, |editor, cx| {
16703        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16704    });
16705    assert_eq!(
16706        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16707        "\n\n\n\n\n",
16708        "After folding the third buffer, its text should not be displayed"
16709    );
16710
16711    // Emulate selection inside the fold logic, that should work
16712    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16713        editor
16714            .snapshot(window, cx)
16715            .next_line_boundary(Point::new(0, 4));
16716    });
16717
16718    multi_buffer_editor.update(cx, |editor, cx| {
16719        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16720    });
16721    assert_eq!(
16722        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16723        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16724        "After unfolding the second buffer, its text should be displayed"
16725    );
16726
16727    // Typing inside of buffer 1 causes that buffer to be unfolded.
16728    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16729        assert_eq!(
16730            multi_buffer
16731                .read(cx)
16732                .snapshot(cx)
16733                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16734                .collect::<String>(),
16735            "bbbb"
16736        );
16737        editor.change_selections(None, window, cx, |selections| {
16738            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16739        });
16740        editor.handle_input("B", window, cx);
16741    });
16742
16743    assert_eq!(
16744        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16745        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16746        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16747    );
16748
16749    multi_buffer_editor.update(cx, |editor, cx| {
16750        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16751    });
16752    assert_eq!(
16753        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16754        "\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",
16755        "After unfolding the all buffers, all original text should be displayed"
16756    );
16757}
16758
16759#[gpui::test]
16760async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16761    init_test(cx, |_| {});
16762
16763    let sample_text_1 = "1111\n2222\n3333".to_string();
16764    let sample_text_2 = "4444\n5555\n6666".to_string();
16765    let sample_text_3 = "7777\n8888\n9999".to_string();
16766
16767    let fs = FakeFs::new(cx.executor());
16768    fs.insert_tree(
16769        path!("/a"),
16770        json!({
16771            "first.rs": sample_text_1,
16772            "second.rs": sample_text_2,
16773            "third.rs": sample_text_3,
16774        }),
16775    )
16776    .await;
16777    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16778    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16779    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16780    let worktree = project.update(cx, |project, cx| {
16781        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16782        assert_eq!(worktrees.len(), 1);
16783        worktrees.pop().unwrap()
16784    });
16785    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16786
16787    let buffer_1 = project
16788        .update(cx, |project, cx| {
16789            project.open_buffer((worktree_id, "first.rs"), cx)
16790        })
16791        .await
16792        .unwrap();
16793    let buffer_2 = project
16794        .update(cx, |project, cx| {
16795            project.open_buffer((worktree_id, "second.rs"), cx)
16796        })
16797        .await
16798        .unwrap();
16799    let buffer_3 = project
16800        .update(cx, |project, cx| {
16801            project.open_buffer((worktree_id, "third.rs"), cx)
16802        })
16803        .await
16804        .unwrap();
16805
16806    let multi_buffer = cx.new(|cx| {
16807        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16808        multi_buffer.push_excerpts(
16809            buffer_1.clone(),
16810            [ExcerptRange {
16811                context: Point::new(0, 0)..Point::new(3, 0),
16812                primary: None,
16813            }],
16814            cx,
16815        );
16816        multi_buffer.push_excerpts(
16817            buffer_2.clone(),
16818            [ExcerptRange {
16819                context: Point::new(0, 0)..Point::new(3, 0),
16820                primary: None,
16821            }],
16822            cx,
16823        );
16824        multi_buffer.push_excerpts(
16825            buffer_3.clone(),
16826            [ExcerptRange {
16827                context: Point::new(0, 0)..Point::new(3, 0),
16828                primary: None,
16829            }],
16830            cx,
16831        );
16832        multi_buffer
16833    });
16834
16835    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16836        Editor::new(
16837            EditorMode::Full,
16838            multi_buffer,
16839            Some(project.clone()),
16840            window,
16841            cx,
16842        )
16843    });
16844
16845    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16846    assert_eq!(
16847        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16848        full_text,
16849    );
16850
16851    multi_buffer_editor.update(cx, |editor, cx| {
16852        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16853    });
16854    assert_eq!(
16855        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16856        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16857        "After folding the first buffer, its text should not be displayed"
16858    );
16859
16860    multi_buffer_editor.update(cx, |editor, cx| {
16861        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16862    });
16863
16864    assert_eq!(
16865        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16866        "\n\n\n\n\n\n7777\n8888\n9999",
16867        "After folding the second buffer, its text should not be displayed"
16868    );
16869
16870    multi_buffer_editor.update(cx, |editor, cx| {
16871        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16872    });
16873    assert_eq!(
16874        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16875        "\n\n\n\n\n",
16876        "After folding the third buffer, its text should not be displayed"
16877    );
16878
16879    multi_buffer_editor.update(cx, |editor, cx| {
16880        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16881    });
16882    assert_eq!(
16883        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16884        "\n\n\n\n4444\n5555\n6666\n\n",
16885        "After unfolding the second buffer, its text should be displayed"
16886    );
16887
16888    multi_buffer_editor.update(cx, |editor, cx| {
16889        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16890    });
16891    assert_eq!(
16892        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16893        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16894        "After unfolding the first buffer, its text should be displayed"
16895    );
16896
16897    multi_buffer_editor.update(cx, |editor, cx| {
16898        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16899    });
16900    assert_eq!(
16901        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16902        full_text,
16903        "After unfolding all buffers, all original text should be displayed"
16904    );
16905}
16906
16907#[gpui::test]
16908async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16909    init_test(cx, |_| {});
16910
16911    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16912
16913    let fs = FakeFs::new(cx.executor());
16914    fs.insert_tree(
16915        path!("/a"),
16916        json!({
16917            "main.rs": sample_text,
16918        }),
16919    )
16920    .await;
16921    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16922    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16923    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16924    let worktree = project.update(cx, |project, cx| {
16925        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16926        assert_eq!(worktrees.len(), 1);
16927        worktrees.pop().unwrap()
16928    });
16929    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16930
16931    let buffer_1 = project
16932        .update(cx, |project, cx| {
16933            project.open_buffer((worktree_id, "main.rs"), cx)
16934        })
16935        .await
16936        .unwrap();
16937
16938    let multi_buffer = cx.new(|cx| {
16939        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16940        multi_buffer.push_excerpts(
16941            buffer_1.clone(),
16942            [ExcerptRange {
16943                context: Point::new(0, 0)
16944                    ..Point::new(
16945                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16946                        0,
16947                    ),
16948                primary: None,
16949            }],
16950            cx,
16951        );
16952        multi_buffer
16953    });
16954    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16955        Editor::new(
16956            EditorMode::Full,
16957            multi_buffer,
16958            Some(project.clone()),
16959            window,
16960            cx,
16961        )
16962    });
16963
16964    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16965    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16966        enum TestHighlight {}
16967        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16968        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16969        editor.highlight_text::<TestHighlight>(
16970            vec![highlight_range.clone()],
16971            HighlightStyle::color(Hsla::green()),
16972            cx,
16973        );
16974        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16975    });
16976
16977    let full_text = format!("\n\n{sample_text}");
16978    assert_eq!(
16979        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16980        full_text,
16981    );
16982}
16983
16984#[gpui::test]
16985async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16986    init_test(cx, |_| {});
16987    cx.update(|cx| {
16988        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16989            "keymaps/default-linux.json",
16990            cx,
16991        )
16992        .unwrap();
16993        cx.bind_keys(default_key_bindings);
16994    });
16995
16996    let (editor, cx) = cx.add_window_view(|window, cx| {
16997        let multi_buffer = MultiBuffer::build_multi(
16998            [
16999                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17000                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17001                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17002                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17003            ],
17004            cx,
17005        );
17006        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17007
17008        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17009        // fold all but the second buffer, so that we test navigating between two
17010        // adjacent folded buffers, as well as folded buffers at the start and
17011        // end the multibuffer
17012        editor.fold_buffer(buffer_ids[0], cx);
17013        editor.fold_buffer(buffer_ids[2], cx);
17014        editor.fold_buffer(buffer_ids[3], cx);
17015
17016        editor
17017    });
17018    cx.simulate_resize(size(px(1000.), px(1000.)));
17019
17020    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17021    cx.assert_excerpts_with_selections(indoc! {"
17022        [EXCERPT]
17023        ˇ[FOLDED]
17024        [EXCERPT]
17025        a1
17026        b1
17027        [EXCERPT]
17028        [FOLDED]
17029        [EXCERPT]
17030        [FOLDED]
17031        "
17032    });
17033    cx.simulate_keystroke("down");
17034    cx.assert_excerpts_with_selections(indoc! {"
17035        [EXCERPT]
17036        [FOLDED]
17037        [EXCERPT]
17038        ˇa1
17039        b1
17040        [EXCERPT]
17041        [FOLDED]
17042        [EXCERPT]
17043        [FOLDED]
17044        "
17045    });
17046    cx.simulate_keystroke("down");
17047    cx.assert_excerpts_with_selections(indoc! {"
17048        [EXCERPT]
17049        [FOLDED]
17050        [EXCERPT]
17051        a1
17052        ˇb1
17053        [EXCERPT]
17054        [FOLDED]
17055        [EXCERPT]
17056        [FOLDED]
17057        "
17058    });
17059    cx.simulate_keystroke("down");
17060    cx.assert_excerpts_with_selections(indoc! {"
17061        [EXCERPT]
17062        [FOLDED]
17063        [EXCERPT]
17064        a1
17065        b1
17066        ˇ[EXCERPT]
17067        [FOLDED]
17068        [EXCERPT]
17069        [FOLDED]
17070        "
17071    });
17072    cx.simulate_keystroke("down");
17073    cx.assert_excerpts_with_selections(indoc! {"
17074        [EXCERPT]
17075        [FOLDED]
17076        [EXCERPT]
17077        a1
17078        b1
17079        [EXCERPT]
17080        ˇ[FOLDED]
17081        [EXCERPT]
17082        [FOLDED]
17083        "
17084    });
17085    for _ in 0..5 {
17086        cx.simulate_keystroke("down");
17087        cx.assert_excerpts_with_selections(indoc! {"
17088            [EXCERPT]
17089            [FOLDED]
17090            [EXCERPT]
17091            a1
17092            b1
17093            [EXCERPT]
17094            [FOLDED]
17095            [EXCERPT]
17096            ˇ[FOLDED]
17097            "
17098        });
17099    }
17100
17101    cx.simulate_keystroke("up");
17102    cx.assert_excerpts_with_selections(indoc! {"
17103        [EXCERPT]
17104        [FOLDED]
17105        [EXCERPT]
17106        a1
17107        b1
17108        [EXCERPT]
17109        ˇ[FOLDED]
17110        [EXCERPT]
17111        [FOLDED]
17112        "
17113    });
17114    cx.simulate_keystroke("up");
17115    cx.assert_excerpts_with_selections(indoc! {"
17116        [EXCERPT]
17117        [FOLDED]
17118        [EXCERPT]
17119        a1
17120        b1
17121        ˇ[EXCERPT]
17122        [FOLDED]
17123        [EXCERPT]
17124        [FOLDED]
17125        "
17126    });
17127    cx.simulate_keystroke("up");
17128    cx.assert_excerpts_with_selections(indoc! {"
17129        [EXCERPT]
17130        [FOLDED]
17131        [EXCERPT]
17132        a1
17133        ˇb1
17134        [EXCERPT]
17135        [FOLDED]
17136        [EXCERPT]
17137        [FOLDED]
17138        "
17139    });
17140    cx.simulate_keystroke("up");
17141    cx.assert_excerpts_with_selections(indoc! {"
17142        [EXCERPT]
17143        [FOLDED]
17144        [EXCERPT]
17145        ˇa1
17146        b1
17147        [EXCERPT]
17148        [FOLDED]
17149        [EXCERPT]
17150        [FOLDED]
17151        "
17152    });
17153    for _ in 0..5 {
17154        cx.simulate_keystroke("up");
17155        cx.assert_excerpts_with_selections(indoc! {"
17156            [EXCERPT]
17157            ˇ[FOLDED]
17158            [EXCERPT]
17159            a1
17160            b1
17161            [EXCERPT]
17162            [FOLDED]
17163            [EXCERPT]
17164            [FOLDED]
17165            "
17166        });
17167    }
17168}
17169
17170#[gpui::test]
17171async fn test_inline_completion_text(cx: &mut TestAppContext) {
17172    init_test(cx, |_| {});
17173
17174    // Simple insertion
17175    assert_highlighted_edits(
17176        "Hello, world!",
17177        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17178        true,
17179        cx,
17180        |highlighted_edits, cx| {
17181            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17182            assert_eq!(highlighted_edits.highlights.len(), 1);
17183            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17184            assert_eq!(
17185                highlighted_edits.highlights[0].1.background_color,
17186                Some(cx.theme().status().created_background)
17187            );
17188        },
17189    )
17190    .await;
17191
17192    // Replacement
17193    assert_highlighted_edits(
17194        "This is a test.",
17195        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17196        false,
17197        cx,
17198        |highlighted_edits, cx| {
17199            assert_eq!(highlighted_edits.text, "That is a test.");
17200            assert_eq!(highlighted_edits.highlights.len(), 1);
17201            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17202            assert_eq!(
17203                highlighted_edits.highlights[0].1.background_color,
17204                Some(cx.theme().status().created_background)
17205            );
17206        },
17207    )
17208    .await;
17209
17210    // Multiple edits
17211    assert_highlighted_edits(
17212        "Hello, world!",
17213        vec![
17214            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17215            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17216        ],
17217        false,
17218        cx,
17219        |highlighted_edits, cx| {
17220            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17221            assert_eq!(highlighted_edits.highlights.len(), 2);
17222            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17223            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17224            assert_eq!(
17225                highlighted_edits.highlights[0].1.background_color,
17226                Some(cx.theme().status().created_background)
17227            );
17228            assert_eq!(
17229                highlighted_edits.highlights[1].1.background_color,
17230                Some(cx.theme().status().created_background)
17231            );
17232        },
17233    )
17234    .await;
17235
17236    // Multiple lines with edits
17237    assert_highlighted_edits(
17238        "First line\nSecond line\nThird line\nFourth line",
17239        vec![
17240            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17241            (
17242                Point::new(2, 0)..Point::new(2, 10),
17243                "New third line".to_string(),
17244            ),
17245            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17246        ],
17247        false,
17248        cx,
17249        |highlighted_edits, cx| {
17250            assert_eq!(
17251                highlighted_edits.text,
17252                "Second modified\nNew third line\nFourth updated line"
17253            );
17254            assert_eq!(highlighted_edits.highlights.len(), 3);
17255            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17256            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17257            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17258            for highlight in &highlighted_edits.highlights {
17259                assert_eq!(
17260                    highlight.1.background_color,
17261                    Some(cx.theme().status().created_background)
17262                );
17263            }
17264        },
17265    )
17266    .await;
17267}
17268
17269#[gpui::test]
17270async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17271    init_test(cx, |_| {});
17272
17273    // Deletion
17274    assert_highlighted_edits(
17275        "Hello, world!",
17276        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17277        true,
17278        cx,
17279        |highlighted_edits, cx| {
17280            assert_eq!(highlighted_edits.text, "Hello, world!");
17281            assert_eq!(highlighted_edits.highlights.len(), 1);
17282            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17283            assert_eq!(
17284                highlighted_edits.highlights[0].1.background_color,
17285                Some(cx.theme().status().deleted_background)
17286            );
17287        },
17288    )
17289    .await;
17290
17291    // Insertion
17292    assert_highlighted_edits(
17293        "Hello, world!",
17294        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17295        true,
17296        cx,
17297        |highlighted_edits, cx| {
17298            assert_eq!(highlighted_edits.highlights.len(), 1);
17299            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17300            assert_eq!(
17301                highlighted_edits.highlights[0].1.background_color,
17302                Some(cx.theme().status().created_background)
17303            );
17304        },
17305    )
17306    .await;
17307}
17308
17309async fn assert_highlighted_edits(
17310    text: &str,
17311    edits: Vec<(Range<Point>, String)>,
17312    include_deletions: bool,
17313    cx: &mut TestAppContext,
17314    assertion_fn: impl Fn(HighlightedText, &App),
17315) {
17316    let window = cx.add_window(|window, cx| {
17317        let buffer = MultiBuffer::build_simple(text, cx);
17318        Editor::new(EditorMode::Full, buffer, None, window, cx)
17319    });
17320    let cx = &mut VisualTestContext::from_window(*window, cx);
17321
17322    let (buffer, snapshot) = window
17323        .update(cx, |editor, _window, cx| {
17324            (
17325                editor.buffer().clone(),
17326                editor.buffer().read(cx).snapshot(cx),
17327            )
17328        })
17329        .unwrap();
17330
17331    let edits = edits
17332        .into_iter()
17333        .map(|(range, edit)| {
17334            (
17335                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17336                edit,
17337            )
17338        })
17339        .collect::<Vec<_>>();
17340
17341    let text_anchor_edits = edits
17342        .clone()
17343        .into_iter()
17344        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17345        .collect::<Vec<_>>();
17346
17347    let edit_preview = window
17348        .update(cx, |_, _window, cx| {
17349            buffer
17350                .read(cx)
17351                .as_singleton()
17352                .unwrap()
17353                .read(cx)
17354                .preview_edits(text_anchor_edits.into(), cx)
17355        })
17356        .unwrap()
17357        .await;
17358
17359    cx.update(|_window, cx| {
17360        let highlighted_edits = inline_completion_edit_text(
17361            &snapshot.as_singleton().unwrap().2,
17362            &edits,
17363            &edit_preview,
17364            include_deletions,
17365            cx,
17366        );
17367        assertion_fn(highlighted_edits, cx)
17368    });
17369}
17370
17371#[track_caller]
17372fn assert_breakpoint(
17373    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17374    path: &Arc<Path>,
17375    expected: Vec<(u32, Breakpoint)>,
17376) {
17377    if expected.len() == 0usize {
17378        assert!(!breakpoints.contains_key(path), "{}", path.display());
17379    } else {
17380        let mut breakpoint = breakpoints
17381            .get(path)
17382            .unwrap()
17383            .into_iter()
17384            .map(|breakpoint| {
17385                (
17386                    breakpoint.row,
17387                    Breakpoint {
17388                        message: breakpoint.message.clone(),
17389                        state: breakpoint.state,
17390                    },
17391                )
17392            })
17393            .collect::<Vec<_>>();
17394
17395        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17396
17397        assert_eq!(expected, breakpoint);
17398    }
17399}
17400
17401fn add_log_breakpoint_at_cursor(
17402    editor: &mut Editor,
17403    log_message: &str,
17404    window: &mut Window,
17405    cx: &mut Context<Editor>,
17406) {
17407    let (anchor, bp) = editor
17408        .breakpoint_at_cursor_head(window, cx)
17409        .unwrap_or_else(|| {
17410            let cursor_position: Point = editor.selections.newest(cx).head();
17411
17412            let breakpoint_position = editor
17413                .snapshot(window, cx)
17414                .display_snapshot
17415                .buffer_snapshot
17416                .anchor_before(Point::new(cursor_position.row, 0));
17417
17418            (
17419                breakpoint_position,
17420                Breakpoint {
17421                    message: Some(Arc::from(log_message)),
17422                    state: BreakpointState::Enabled,
17423                },
17424            )
17425        });
17426
17427    editor.edit_breakpoint_at_anchor(
17428        anchor,
17429        bp,
17430        BreakpointEditAction::EditLogMessage(log_message.into()),
17431        cx,
17432    );
17433}
17434
17435#[gpui::test]
17436async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17437    init_test(cx, |_| {});
17438
17439    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17440    let fs = FakeFs::new(cx.executor());
17441    fs.insert_tree(
17442        path!("/a"),
17443        json!({
17444            "main.rs": sample_text,
17445        }),
17446    )
17447    .await;
17448    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17449    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17450    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17451
17452    let fs = FakeFs::new(cx.executor());
17453    fs.insert_tree(
17454        path!("/a"),
17455        json!({
17456            "main.rs": sample_text,
17457        }),
17458    )
17459    .await;
17460    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17461    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17462    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17463    let worktree_id = workspace
17464        .update(cx, |workspace, _window, cx| {
17465            workspace.project().update(cx, |project, cx| {
17466                project.worktrees(cx).next().unwrap().read(cx).id()
17467            })
17468        })
17469        .unwrap();
17470
17471    let buffer = project
17472        .update(cx, |project, cx| {
17473            project.open_buffer((worktree_id, "main.rs"), cx)
17474        })
17475        .await
17476        .unwrap();
17477
17478    let (editor, cx) = cx.add_window_view(|window, cx| {
17479        Editor::new(
17480            EditorMode::Full,
17481            MultiBuffer::build_from_buffer(buffer, cx),
17482            Some(project.clone()),
17483            window,
17484            cx,
17485        )
17486    });
17487
17488    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17489    let abs_path = project.read_with(cx, |project, cx| {
17490        project
17491            .absolute_path(&project_path, cx)
17492            .map(|path_buf| Arc::from(path_buf.to_owned()))
17493            .unwrap()
17494    });
17495
17496    // assert we can add breakpoint on the first line
17497    editor.update_in(cx, |editor, window, cx| {
17498        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17499        editor.move_to_end(&MoveToEnd, window, cx);
17500        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17501    });
17502
17503    let breakpoints = editor.update(cx, |editor, cx| {
17504        editor
17505            .breakpoint_store()
17506            .as_ref()
17507            .unwrap()
17508            .read(cx)
17509            .all_breakpoints(cx)
17510            .clone()
17511    });
17512
17513    assert_eq!(1, breakpoints.len());
17514    assert_breakpoint(
17515        &breakpoints,
17516        &abs_path,
17517        vec![
17518            (0, Breakpoint::new_standard()),
17519            (3, Breakpoint::new_standard()),
17520        ],
17521    );
17522
17523    editor.update_in(cx, |editor, window, cx| {
17524        editor.move_to_beginning(&MoveToBeginning, window, cx);
17525        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17526    });
17527
17528    let breakpoints = editor.update(cx, |editor, cx| {
17529        editor
17530            .breakpoint_store()
17531            .as_ref()
17532            .unwrap()
17533            .read(cx)
17534            .all_breakpoints(cx)
17535            .clone()
17536    });
17537
17538    assert_eq!(1, breakpoints.len());
17539    assert_breakpoint(
17540        &breakpoints,
17541        &abs_path,
17542        vec![(3, Breakpoint::new_standard())],
17543    );
17544
17545    editor.update_in(cx, |editor, window, cx| {
17546        editor.move_to_end(&MoveToEnd, window, cx);
17547        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17548    });
17549
17550    let breakpoints = editor.update(cx, |editor, cx| {
17551        editor
17552            .breakpoint_store()
17553            .as_ref()
17554            .unwrap()
17555            .read(cx)
17556            .all_breakpoints(cx)
17557            .clone()
17558    });
17559
17560    assert_eq!(0, breakpoints.len());
17561    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17562}
17563
17564#[gpui::test]
17565async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17566    init_test(cx, |_| {});
17567
17568    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17569
17570    let fs = FakeFs::new(cx.executor());
17571    fs.insert_tree(
17572        path!("/a"),
17573        json!({
17574            "main.rs": sample_text,
17575        }),
17576    )
17577    .await;
17578    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17579    let (workspace, cx) =
17580        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17581
17582    let worktree_id = workspace.update(cx, |workspace, cx| {
17583        workspace.project().update(cx, |project, cx| {
17584            project.worktrees(cx).next().unwrap().read(cx).id()
17585        })
17586    });
17587
17588    let buffer = project
17589        .update(cx, |project, cx| {
17590            project.open_buffer((worktree_id, "main.rs"), cx)
17591        })
17592        .await
17593        .unwrap();
17594
17595    let (editor, cx) = cx.add_window_view(|window, cx| {
17596        Editor::new(
17597            EditorMode::Full,
17598            MultiBuffer::build_from_buffer(buffer, cx),
17599            Some(project.clone()),
17600            window,
17601            cx,
17602        )
17603    });
17604
17605    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17606    let abs_path = project.read_with(cx, |project, cx| {
17607        project
17608            .absolute_path(&project_path, cx)
17609            .map(|path_buf| Arc::from(path_buf.to_owned()))
17610            .unwrap()
17611    });
17612
17613    editor.update_in(cx, |editor, window, cx| {
17614        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17615    });
17616
17617    let breakpoints = editor.update(cx, |editor, cx| {
17618        editor
17619            .breakpoint_store()
17620            .as_ref()
17621            .unwrap()
17622            .read(cx)
17623            .all_breakpoints(cx)
17624            .clone()
17625    });
17626
17627    assert_breakpoint(
17628        &breakpoints,
17629        &abs_path,
17630        vec![(0, Breakpoint::new_log("hello world"))],
17631    );
17632
17633    // Removing a log message from a log breakpoint should remove it
17634    editor.update_in(cx, |editor, window, cx| {
17635        add_log_breakpoint_at_cursor(editor, "", window, cx);
17636    });
17637
17638    let breakpoints = editor.update(cx, |editor, cx| {
17639        editor
17640            .breakpoint_store()
17641            .as_ref()
17642            .unwrap()
17643            .read(cx)
17644            .all_breakpoints(cx)
17645            .clone()
17646    });
17647
17648    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17649
17650    editor.update_in(cx, |editor, window, cx| {
17651        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17652        editor.move_to_end(&MoveToEnd, window, cx);
17653        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17654        // Not adding a log message to a standard breakpoint shouldn't remove it
17655        add_log_breakpoint_at_cursor(editor, "", window, cx);
17656    });
17657
17658    let breakpoints = editor.update(cx, |editor, cx| {
17659        editor
17660            .breakpoint_store()
17661            .as_ref()
17662            .unwrap()
17663            .read(cx)
17664            .all_breakpoints(cx)
17665            .clone()
17666    });
17667
17668    assert_breakpoint(
17669        &breakpoints,
17670        &abs_path,
17671        vec![
17672            (0, Breakpoint::new_standard()),
17673            (3, Breakpoint::new_standard()),
17674        ],
17675    );
17676
17677    editor.update_in(cx, |editor, window, cx| {
17678        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17679    });
17680
17681    let breakpoints = editor.update(cx, |editor, cx| {
17682        editor
17683            .breakpoint_store()
17684            .as_ref()
17685            .unwrap()
17686            .read(cx)
17687            .all_breakpoints(cx)
17688            .clone()
17689    });
17690
17691    assert_breakpoint(
17692        &breakpoints,
17693        &abs_path,
17694        vec![
17695            (0, Breakpoint::new_standard()),
17696            (3, Breakpoint::new_log("hello world")),
17697        ],
17698    );
17699
17700    editor.update_in(cx, |editor, window, cx| {
17701        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17702    });
17703
17704    let breakpoints = editor.update(cx, |editor, cx| {
17705        editor
17706            .breakpoint_store()
17707            .as_ref()
17708            .unwrap()
17709            .read(cx)
17710            .all_breakpoints(cx)
17711            .clone()
17712    });
17713
17714    assert_breakpoint(
17715        &breakpoints,
17716        &abs_path,
17717        vec![
17718            (0, Breakpoint::new_standard()),
17719            (3, Breakpoint::new_log("hello Earth!!")),
17720        ],
17721    );
17722}
17723
17724/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17725/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17726/// or when breakpoints were placed out of order. This tests for a regression too
17727#[gpui::test]
17728async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17729    init_test(cx, |_| {});
17730
17731    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17732    let fs = FakeFs::new(cx.executor());
17733    fs.insert_tree(
17734        path!("/a"),
17735        json!({
17736            "main.rs": sample_text,
17737        }),
17738    )
17739    .await;
17740    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17741    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17742    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17743
17744    let fs = FakeFs::new(cx.executor());
17745    fs.insert_tree(
17746        path!("/a"),
17747        json!({
17748            "main.rs": sample_text,
17749        }),
17750    )
17751    .await;
17752    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17753    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17754    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17755    let worktree_id = workspace
17756        .update(cx, |workspace, _window, cx| {
17757            workspace.project().update(cx, |project, cx| {
17758                project.worktrees(cx).next().unwrap().read(cx).id()
17759            })
17760        })
17761        .unwrap();
17762
17763    let buffer = project
17764        .update(cx, |project, cx| {
17765            project.open_buffer((worktree_id, "main.rs"), cx)
17766        })
17767        .await
17768        .unwrap();
17769
17770    let (editor, cx) = cx.add_window_view(|window, cx| {
17771        Editor::new(
17772            EditorMode::Full,
17773            MultiBuffer::build_from_buffer(buffer, cx),
17774            Some(project.clone()),
17775            window,
17776            cx,
17777        )
17778    });
17779
17780    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17781    let abs_path = project.read_with(cx, |project, cx| {
17782        project
17783            .absolute_path(&project_path, cx)
17784            .map(|path_buf| Arc::from(path_buf.to_owned()))
17785            .unwrap()
17786    });
17787
17788    // assert we can add breakpoint on the first line
17789    editor.update_in(cx, |editor, window, cx| {
17790        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17791        editor.move_to_end(&MoveToEnd, window, cx);
17792        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17793        editor.move_up(&MoveUp, window, cx);
17794        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17795    });
17796
17797    let breakpoints = editor.update(cx, |editor, cx| {
17798        editor
17799            .breakpoint_store()
17800            .as_ref()
17801            .unwrap()
17802            .read(cx)
17803            .all_breakpoints(cx)
17804            .clone()
17805    });
17806
17807    assert_eq!(1, breakpoints.len());
17808    assert_breakpoint(
17809        &breakpoints,
17810        &abs_path,
17811        vec![
17812            (0, Breakpoint::new_standard()),
17813            (2, Breakpoint::new_standard()),
17814            (3, Breakpoint::new_standard()),
17815        ],
17816    );
17817
17818    editor.update_in(cx, |editor, window, cx| {
17819        editor.move_to_beginning(&MoveToBeginning, window, cx);
17820        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17821        editor.move_to_end(&MoveToEnd, window, cx);
17822        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17823        // Disabling a breakpoint that doesn't exist should do nothing
17824        editor.move_up(&MoveUp, window, cx);
17825        editor.move_up(&MoveUp, window, cx);
17826        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17827    });
17828
17829    let breakpoints = editor.update(cx, |editor, cx| {
17830        editor
17831            .breakpoint_store()
17832            .as_ref()
17833            .unwrap()
17834            .read(cx)
17835            .all_breakpoints(cx)
17836            .clone()
17837    });
17838
17839    let disable_breakpoint = {
17840        let mut bp = Breakpoint::new_standard();
17841        bp.state = BreakpointState::Disabled;
17842        bp
17843    };
17844
17845    assert_eq!(1, breakpoints.len());
17846    assert_breakpoint(
17847        &breakpoints,
17848        &abs_path,
17849        vec![
17850            (0, disable_breakpoint.clone()),
17851            (2, Breakpoint::new_standard()),
17852            (3, disable_breakpoint.clone()),
17853        ],
17854    );
17855
17856    editor.update_in(cx, |editor, window, cx| {
17857        editor.move_to_beginning(&MoveToBeginning, window, cx);
17858        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17859        editor.move_to_end(&MoveToEnd, window, cx);
17860        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17861        editor.move_up(&MoveUp, window, cx);
17862        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17863    });
17864
17865    let breakpoints = editor.update(cx, |editor, cx| {
17866        editor
17867            .breakpoint_store()
17868            .as_ref()
17869            .unwrap()
17870            .read(cx)
17871            .all_breakpoints(cx)
17872            .clone()
17873    });
17874
17875    assert_eq!(1, breakpoints.len());
17876    assert_breakpoint(
17877        &breakpoints,
17878        &abs_path,
17879        vec![
17880            (0, Breakpoint::new_standard()),
17881            (2, disable_breakpoint),
17882            (3, Breakpoint::new_standard()),
17883        ],
17884    );
17885}
17886
17887#[gpui::test]
17888async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17889    init_test(cx, |_| {});
17890    let capabilities = lsp::ServerCapabilities {
17891        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17892            prepare_provider: Some(true),
17893            work_done_progress_options: Default::default(),
17894        })),
17895        ..Default::default()
17896    };
17897    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17898
17899    cx.set_state(indoc! {"
17900        struct Fˇoo {}
17901    "});
17902
17903    cx.update_editor(|editor, _, cx| {
17904        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17905        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17906        editor.highlight_background::<DocumentHighlightRead>(
17907            &[highlight_range],
17908            |c| c.editor_document_highlight_read_background,
17909            cx,
17910        );
17911    });
17912
17913    let mut prepare_rename_handler = cx
17914        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17915            move |_, _, _| async move {
17916                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17917                    start: lsp::Position {
17918                        line: 0,
17919                        character: 7,
17920                    },
17921                    end: lsp::Position {
17922                        line: 0,
17923                        character: 10,
17924                    },
17925                })))
17926            },
17927        );
17928    let prepare_rename_task = cx
17929        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17930        .expect("Prepare rename was not started");
17931    prepare_rename_handler.next().await.unwrap();
17932    prepare_rename_task.await.expect("Prepare rename failed");
17933
17934    let mut rename_handler =
17935        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17936            let edit = lsp::TextEdit {
17937                range: lsp::Range {
17938                    start: lsp::Position {
17939                        line: 0,
17940                        character: 7,
17941                    },
17942                    end: lsp::Position {
17943                        line: 0,
17944                        character: 10,
17945                    },
17946                },
17947                new_text: "FooRenamed".to_string(),
17948            };
17949            Ok(Some(lsp::WorkspaceEdit::new(
17950                // Specify the same edit twice
17951                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17952            )))
17953        });
17954    let rename_task = cx
17955        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17956        .expect("Confirm rename was not started");
17957    rename_handler.next().await.unwrap();
17958    rename_task.await.expect("Confirm rename failed");
17959    cx.run_until_parked();
17960
17961    // Despite two edits, only one is actually applied as those are identical
17962    cx.assert_editor_state(indoc! {"
17963        struct FooRenamedˇ {}
17964    "});
17965}
17966
17967#[gpui::test]
17968async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17969    init_test(cx, |_| {});
17970    // These capabilities indicate that the server does not support prepare rename.
17971    let capabilities = lsp::ServerCapabilities {
17972        rename_provider: Some(lsp::OneOf::Left(true)),
17973        ..Default::default()
17974    };
17975    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17976
17977    cx.set_state(indoc! {"
17978        struct Fˇoo {}
17979    "});
17980
17981    cx.update_editor(|editor, _window, cx| {
17982        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17983        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17984        editor.highlight_background::<DocumentHighlightRead>(
17985            &[highlight_range],
17986            |c| c.editor_document_highlight_read_background,
17987            cx,
17988        );
17989    });
17990
17991    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17992        .expect("Prepare rename was not started")
17993        .await
17994        .expect("Prepare rename failed");
17995
17996    let mut rename_handler =
17997        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17998            let edit = lsp::TextEdit {
17999                range: lsp::Range {
18000                    start: lsp::Position {
18001                        line: 0,
18002                        character: 7,
18003                    },
18004                    end: lsp::Position {
18005                        line: 0,
18006                        character: 10,
18007                    },
18008                },
18009                new_text: "FooRenamed".to_string(),
18010            };
18011            Ok(Some(lsp::WorkspaceEdit::new(
18012                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18013            )))
18014        });
18015    let rename_task = cx
18016        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18017        .expect("Confirm rename was not started");
18018    rename_handler.next().await.unwrap();
18019    rename_task.await.expect("Confirm rename failed");
18020    cx.run_until_parked();
18021
18022    // Correct range is renamed, as `surrounding_word` is used to find it.
18023    cx.assert_editor_state(indoc! {"
18024        struct FooRenamedˇ {}
18025    "});
18026}
18027
18028#[gpui::test]
18029async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18030    init_test(cx, |_| {});
18031    let mut cx = EditorTestContext::new(cx).await;
18032
18033    let language = Arc::new(
18034        Language::new(
18035            LanguageConfig::default(),
18036            Some(tree_sitter_html::LANGUAGE.into()),
18037        )
18038        .with_brackets_query(
18039            r#"
18040            ("<" @open "/>" @close)
18041            ("</" @open ">" @close)
18042            ("<" @open ">" @close)
18043            ("\"" @open "\"" @close)
18044            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18045        "#,
18046        )
18047        .unwrap(),
18048    );
18049    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18050
18051    cx.set_state(indoc! {"
18052        <span>ˇ</span>
18053    "});
18054    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18055    cx.assert_editor_state(indoc! {"
18056        <span>
18057        ˇ
18058        </span>
18059    "});
18060
18061    cx.set_state(indoc! {"
18062        <span><span></span>ˇ</span>
18063    "});
18064    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18065    cx.assert_editor_state(indoc! {"
18066        <span><span></span>
18067        ˇ</span>
18068    "});
18069
18070    cx.set_state(indoc! {"
18071        <span>ˇ
18072        </span>
18073    "});
18074    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18075    cx.assert_editor_state(indoc! {"
18076        <span>
18077        ˇ
18078        </span>
18079    "});
18080}
18081
18082#[gpui::test(iterations = 10)]
18083async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18084    init_test(cx, |_| {});
18085
18086    let fs = FakeFs::new(cx.executor());
18087    fs.insert_tree(
18088        path!("/dir"),
18089        json!({
18090            "a.ts": "a",
18091        }),
18092    )
18093    .await;
18094
18095    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18096    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18097    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18098
18099    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18100    language_registry.add(Arc::new(Language::new(
18101        LanguageConfig {
18102            name: "TypeScript".into(),
18103            matcher: LanguageMatcher {
18104                path_suffixes: vec!["ts".to_string()],
18105                ..Default::default()
18106            },
18107            ..Default::default()
18108        },
18109        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18110    )));
18111    let mut fake_language_servers = language_registry.register_fake_lsp(
18112        "TypeScript",
18113        FakeLspAdapter {
18114            capabilities: lsp::ServerCapabilities {
18115                code_lens_provider: Some(lsp::CodeLensOptions {
18116                    resolve_provider: Some(true),
18117                }),
18118                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18119                    commands: vec!["_the/command".to_string()],
18120                    ..lsp::ExecuteCommandOptions::default()
18121                }),
18122                ..lsp::ServerCapabilities::default()
18123            },
18124            ..FakeLspAdapter::default()
18125        },
18126    );
18127
18128    let (buffer, _handle) = project
18129        .update(cx, |p, cx| {
18130            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18131        })
18132        .await
18133        .unwrap();
18134    cx.executor().run_until_parked();
18135
18136    let fake_server = fake_language_servers.next().await.unwrap();
18137
18138    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18139    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18140    drop(buffer_snapshot);
18141    let actions = cx
18142        .update_window(*workspace, |_, window, cx| {
18143            project.code_actions(&buffer, anchor..anchor, window, cx)
18144        })
18145        .unwrap();
18146
18147    fake_server
18148        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18149            Ok(Some(vec![
18150                lsp::CodeLens {
18151                    range: lsp::Range::default(),
18152                    command: Some(lsp::Command {
18153                        title: "Code lens command".to_owned(),
18154                        command: "_the/command".to_owned(),
18155                        arguments: None,
18156                    }),
18157                    data: None,
18158                },
18159                lsp::CodeLens {
18160                    range: lsp::Range::default(),
18161                    command: Some(lsp::Command {
18162                        title: "Command not in capabilities".to_owned(),
18163                        command: "not in capabilities".to_owned(),
18164                        arguments: None,
18165                    }),
18166                    data: None,
18167                },
18168                lsp::CodeLens {
18169                    range: lsp::Range {
18170                        start: lsp::Position {
18171                            line: 1,
18172                            character: 1,
18173                        },
18174                        end: lsp::Position {
18175                            line: 1,
18176                            character: 1,
18177                        },
18178                    },
18179                    command: Some(lsp::Command {
18180                        title: "Command not in range".to_owned(),
18181                        command: "_the/command".to_owned(),
18182                        arguments: None,
18183                    }),
18184                    data: None,
18185                },
18186            ]))
18187        })
18188        .next()
18189        .await;
18190
18191    let actions = actions.await.unwrap();
18192    assert_eq!(
18193        actions.len(),
18194        1,
18195        "Should have only one valid action for the 0..0 range"
18196    );
18197    let action = actions[0].clone();
18198    let apply = project.update(cx, |project, cx| {
18199        project.apply_code_action(buffer.clone(), action, true, cx)
18200    });
18201
18202    // Resolving the code action does not populate its edits. In absence of
18203    // edits, we must execute the given command.
18204    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18205        |mut lens, _| async move {
18206            let lens_command = lens.command.as_mut().expect("should have a command");
18207            assert_eq!(lens_command.title, "Code lens command");
18208            lens_command.arguments = Some(vec![json!("the-argument")]);
18209            Ok(lens)
18210        },
18211    );
18212
18213    // While executing the command, the language server sends the editor
18214    // a `workspaceEdit` request.
18215    fake_server
18216        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18217            let fake = fake_server.clone();
18218            move |params, _| {
18219                assert_eq!(params.command, "_the/command");
18220                let fake = fake.clone();
18221                async move {
18222                    fake.server
18223                        .request::<lsp::request::ApplyWorkspaceEdit>(
18224                            lsp::ApplyWorkspaceEditParams {
18225                                label: None,
18226                                edit: lsp::WorkspaceEdit {
18227                                    changes: Some(
18228                                        [(
18229                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18230                                            vec![lsp::TextEdit {
18231                                                range: lsp::Range::new(
18232                                                    lsp::Position::new(0, 0),
18233                                                    lsp::Position::new(0, 0),
18234                                                ),
18235                                                new_text: "X".into(),
18236                                            }],
18237                                        )]
18238                                        .into_iter()
18239                                        .collect(),
18240                                    ),
18241                                    ..Default::default()
18242                                },
18243                            },
18244                        )
18245                        .await
18246                        .unwrap();
18247                    Ok(Some(json!(null)))
18248                }
18249            }
18250        })
18251        .next()
18252        .await;
18253
18254    // Applying the code lens command returns a project transaction containing the edits
18255    // sent by the language server in its `workspaceEdit` request.
18256    let transaction = apply.await.unwrap();
18257    assert!(transaction.0.contains_key(&buffer));
18258    buffer.update(cx, |buffer, cx| {
18259        assert_eq!(buffer.text(), "Xa");
18260        buffer.undo(cx);
18261        assert_eq!(buffer.text(), "a");
18262    });
18263}
18264
18265#[gpui::test]
18266async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18267    init_test(cx, |_| {});
18268
18269    let fs = FakeFs::new(cx.executor());
18270    let main_text = r#"fn main() {
18271println!("1");
18272println!("2");
18273println!("3");
18274println!("4");
18275println!("5");
18276}"#;
18277    let lib_text = "mod foo {}";
18278    fs.insert_tree(
18279        path!("/a"),
18280        json!({
18281            "lib.rs": lib_text,
18282            "main.rs": main_text,
18283        }),
18284    )
18285    .await;
18286
18287    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18288    let (workspace, cx) =
18289        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18290    let worktree_id = workspace.update(cx, |workspace, cx| {
18291        workspace.project().update(cx, |project, cx| {
18292            project.worktrees(cx).next().unwrap().read(cx).id()
18293        })
18294    });
18295
18296    let expected_ranges = vec![
18297        Point::new(0, 0)..Point::new(0, 0),
18298        Point::new(1, 0)..Point::new(1, 1),
18299        Point::new(2, 0)..Point::new(2, 2),
18300        Point::new(3, 0)..Point::new(3, 3),
18301    ];
18302
18303    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18304    let editor_1 = workspace
18305        .update_in(cx, |workspace, window, cx| {
18306            workspace.open_path(
18307                (worktree_id, "main.rs"),
18308                Some(pane_1.downgrade()),
18309                true,
18310                window,
18311                cx,
18312            )
18313        })
18314        .unwrap()
18315        .await
18316        .downcast::<Editor>()
18317        .unwrap();
18318    pane_1.update(cx, |pane, cx| {
18319        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18320        open_editor.update(cx, |editor, cx| {
18321            assert_eq!(
18322                editor.display_text(cx),
18323                main_text,
18324                "Original main.rs text on initial open",
18325            );
18326            assert_eq!(
18327                editor
18328                    .selections
18329                    .all::<Point>(cx)
18330                    .into_iter()
18331                    .map(|s| s.range())
18332                    .collect::<Vec<_>>(),
18333                vec![Point::zero()..Point::zero()],
18334                "Default selections on initial open",
18335            );
18336        })
18337    });
18338    editor_1.update_in(cx, |editor, window, cx| {
18339        editor.change_selections(None, window, cx, |s| {
18340            s.select_ranges(expected_ranges.clone());
18341        });
18342    });
18343
18344    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18345        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18346    });
18347    let editor_2 = workspace
18348        .update_in(cx, |workspace, window, cx| {
18349            workspace.open_path(
18350                (worktree_id, "main.rs"),
18351                Some(pane_2.downgrade()),
18352                true,
18353                window,
18354                cx,
18355            )
18356        })
18357        .unwrap()
18358        .await
18359        .downcast::<Editor>()
18360        .unwrap();
18361    pane_2.update(cx, |pane, cx| {
18362        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18363        open_editor.update(cx, |editor, cx| {
18364            assert_eq!(
18365                editor.display_text(cx),
18366                main_text,
18367                "Original main.rs text on initial open in another panel",
18368            );
18369            assert_eq!(
18370                editor
18371                    .selections
18372                    .all::<Point>(cx)
18373                    .into_iter()
18374                    .map(|s| s.range())
18375                    .collect::<Vec<_>>(),
18376                vec![Point::zero()..Point::zero()],
18377                "Default selections on initial open in another panel",
18378            );
18379        })
18380    });
18381
18382    editor_2.update_in(cx, |editor, window, cx| {
18383        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18384    });
18385
18386    let _other_editor_1 = workspace
18387        .update_in(cx, |workspace, window, cx| {
18388            workspace.open_path(
18389                (worktree_id, "lib.rs"),
18390                Some(pane_1.downgrade()),
18391                true,
18392                window,
18393                cx,
18394            )
18395        })
18396        .unwrap()
18397        .await
18398        .downcast::<Editor>()
18399        .unwrap();
18400    pane_1
18401        .update_in(cx, |pane, window, cx| {
18402            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18403                .unwrap()
18404        })
18405        .await
18406        .unwrap();
18407    drop(editor_1);
18408    pane_1.update(cx, |pane, cx| {
18409        pane.active_item()
18410            .unwrap()
18411            .downcast::<Editor>()
18412            .unwrap()
18413            .update(cx, |editor, cx| {
18414                assert_eq!(
18415                    editor.display_text(cx),
18416                    lib_text,
18417                    "Other file should be open and active",
18418                );
18419            });
18420        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18421    });
18422
18423    let _other_editor_2 = workspace
18424        .update_in(cx, |workspace, window, cx| {
18425            workspace.open_path(
18426                (worktree_id, "lib.rs"),
18427                Some(pane_2.downgrade()),
18428                true,
18429                window,
18430                cx,
18431            )
18432        })
18433        .unwrap()
18434        .await
18435        .downcast::<Editor>()
18436        .unwrap();
18437    pane_2
18438        .update_in(cx, |pane, window, cx| {
18439            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18440                .unwrap()
18441        })
18442        .await
18443        .unwrap();
18444    drop(editor_2);
18445    pane_2.update(cx, |pane, cx| {
18446        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18447        open_editor.update(cx, |editor, cx| {
18448            assert_eq!(
18449                editor.display_text(cx),
18450                lib_text,
18451                "Other file should be open and active in another panel too",
18452            );
18453        });
18454        assert_eq!(
18455            pane.items().count(),
18456            1,
18457            "No other editors should be open in another pane",
18458        );
18459    });
18460
18461    let _editor_1_reopened = workspace
18462        .update_in(cx, |workspace, window, cx| {
18463            workspace.open_path(
18464                (worktree_id, "main.rs"),
18465                Some(pane_1.downgrade()),
18466                true,
18467                window,
18468                cx,
18469            )
18470        })
18471        .unwrap()
18472        .await
18473        .downcast::<Editor>()
18474        .unwrap();
18475    let _editor_2_reopened = workspace
18476        .update_in(cx, |workspace, window, cx| {
18477            workspace.open_path(
18478                (worktree_id, "main.rs"),
18479                Some(pane_2.downgrade()),
18480                true,
18481                window,
18482                cx,
18483            )
18484        })
18485        .unwrap()
18486        .await
18487        .downcast::<Editor>()
18488        .unwrap();
18489    pane_1.update(cx, |pane, cx| {
18490        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18491        open_editor.update(cx, |editor, cx| {
18492            assert_eq!(
18493                editor.display_text(cx),
18494                main_text,
18495                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18496            );
18497            assert_eq!(
18498                editor
18499                    .selections
18500                    .all::<Point>(cx)
18501                    .into_iter()
18502                    .map(|s| s.range())
18503                    .collect::<Vec<_>>(),
18504                expected_ranges,
18505                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18506            );
18507        })
18508    });
18509    pane_2.update(cx, |pane, cx| {
18510        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18511        open_editor.update(cx, |editor, cx| {
18512            assert_eq!(
18513                editor.display_text(cx),
18514                r#"fn main() {
18515⋯rintln!("1");
18516⋯intln!("2");
18517⋯ntln!("3");
18518println!("4");
18519println!("5");
18520}"#,
18521                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18522            );
18523            assert_eq!(
18524                editor
18525                    .selections
18526                    .all::<Point>(cx)
18527                    .into_iter()
18528                    .map(|s| s.range())
18529                    .collect::<Vec<_>>(),
18530                vec![Point::zero()..Point::zero()],
18531                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18532            );
18533        })
18534    });
18535}
18536
18537#[gpui::test]
18538async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18539    init_test(cx, |_| {});
18540
18541    let fs = FakeFs::new(cx.executor());
18542    let main_text = r#"fn main() {
18543println!("1");
18544println!("2");
18545println!("3");
18546println!("4");
18547println!("5");
18548}"#;
18549    let lib_text = "mod foo {}";
18550    fs.insert_tree(
18551        path!("/a"),
18552        json!({
18553            "lib.rs": lib_text,
18554            "main.rs": main_text,
18555        }),
18556    )
18557    .await;
18558
18559    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18560    let (workspace, cx) =
18561        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18562    let worktree_id = workspace.update(cx, |workspace, cx| {
18563        workspace.project().update(cx, |project, cx| {
18564            project.worktrees(cx).next().unwrap().read(cx).id()
18565        })
18566    });
18567
18568    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18569    let editor = workspace
18570        .update_in(cx, |workspace, window, cx| {
18571            workspace.open_path(
18572                (worktree_id, "main.rs"),
18573                Some(pane.downgrade()),
18574                true,
18575                window,
18576                cx,
18577            )
18578        })
18579        .unwrap()
18580        .await
18581        .downcast::<Editor>()
18582        .unwrap();
18583    pane.update(cx, |pane, cx| {
18584        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18585        open_editor.update(cx, |editor, cx| {
18586            assert_eq!(
18587                editor.display_text(cx),
18588                main_text,
18589                "Original main.rs text on initial open",
18590            );
18591        })
18592    });
18593    editor.update_in(cx, |editor, window, cx| {
18594        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18595    });
18596
18597    cx.update_global(|store: &mut SettingsStore, cx| {
18598        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18599            s.restore_on_file_reopen = Some(false);
18600        });
18601    });
18602    editor.update_in(cx, |editor, window, cx| {
18603        editor.fold_ranges(
18604            vec![
18605                Point::new(1, 0)..Point::new(1, 1),
18606                Point::new(2, 0)..Point::new(2, 2),
18607                Point::new(3, 0)..Point::new(3, 3),
18608            ],
18609            false,
18610            window,
18611            cx,
18612        );
18613    });
18614    pane.update_in(cx, |pane, window, cx| {
18615        pane.close_all_items(&CloseAllItems::default(), window, cx)
18616            .unwrap()
18617    })
18618    .await
18619    .unwrap();
18620    pane.update(cx, |pane, _| {
18621        assert!(pane.active_item().is_none());
18622    });
18623    cx.update_global(|store: &mut SettingsStore, cx| {
18624        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18625            s.restore_on_file_reopen = Some(true);
18626        });
18627    });
18628
18629    let _editor_reopened = workspace
18630        .update_in(cx, |workspace, window, cx| {
18631            workspace.open_path(
18632                (worktree_id, "main.rs"),
18633                Some(pane.downgrade()),
18634                true,
18635                window,
18636                cx,
18637            )
18638        })
18639        .unwrap()
18640        .await
18641        .downcast::<Editor>()
18642        .unwrap();
18643    pane.update(cx, |pane, cx| {
18644        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18645        open_editor.update(cx, |editor, cx| {
18646            assert_eq!(
18647                editor.display_text(cx),
18648                main_text,
18649                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18650            );
18651        })
18652    });
18653}
18654
18655fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18656    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18657    point..point
18658}
18659
18660fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18661    let (text, ranges) = marked_text_ranges(marked_text, true);
18662    assert_eq!(editor.text(cx), text);
18663    assert_eq!(
18664        editor.selections.ranges(cx),
18665        ranges,
18666        "Assert selections are {}",
18667        marked_text
18668    );
18669}
18670
18671pub fn handle_signature_help_request(
18672    cx: &mut EditorLspTestContext,
18673    mocked_response: lsp::SignatureHelp,
18674) -> impl Future<Output = ()> + use<> {
18675    let mut request =
18676        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18677            let mocked_response = mocked_response.clone();
18678            async move { Ok(Some(mocked_response)) }
18679        });
18680
18681    async move {
18682        request.next().await;
18683    }
18684}
18685
18686/// Handle completion request passing a marked string specifying where the completion
18687/// should be triggered from using '|' character, what range should be replaced, and what completions
18688/// should be returned using '<' and '>' to delimit the range
18689pub fn handle_completion_request(
18690    cx: &mut EditorLspTestContext,
18691    marked_string: &str,
18692    completions: Vec<&'static str>,
18693    counter: Arc<AtomicUsize>,
18694) -> impl Future<Output = ()> {
18695    let complete_from_marker: TextRangeMarker = '|'.into();
18696    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18697    let (_, mut marked_ranges) = marked_text_ranges_by(
18698        marked_string,
18699        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18700    );
18701
18702    let complete_from_position =
18703        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18704    let replace_range =
18705        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18706
18707    let mut request =
18708        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18709            let completions = completions.clone();
18710            counter.fetch_add(1, atomic::Ordering::Release);
18711            async move {
18712                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18713                assert_eq!(
18714                    params.text_document_position.position,
18715                    complete_from_position
18716                );
18717                Ok(Some(lsp::CompletionResponse::Array(
18718                    completions
18719                        .iter()
18720                        .map(|completion_text| lsp::CompletionItem {
18721                            label: completion_text.to_string(),
18722                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18723                                range: replace_range,
18724                                new_text: completion_text.to_string(),
18725                            })),
18726                            ..Default::default()
18727                        })
18728                        .collect(),
18729                )))
18730            }
18731        });
18732
18733    async move {
18734        request.next().await;
18735    }
18736}
18737
18738fn handle_resolve_completion_request(
18739    cx: &mut EditorLspTestContext,
18740    edits: Option<Vec<(&'static str, &'static str)>>,
18741) -> impl Future<Output = ()> {
18742    let edits = edits.map(|edits| {
18743        edits
18744            .iter()
18745            .map(|(marked_string, new_text)| {
18746                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18747                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18748                lsp::TextEdit::new(replace_range, new_text.to_string())
18749            })
18750            .collect::<Vec<_>>()
18751    });
18752
18753    let mut request =
18754        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18755            let edits = edits.clone();
18756            async move {
18757                Ok(lsp::CompletionItem {
18758                    additional_text_edits: edits,
18759                    ..Default::default()
18760                })
18761            }
18762        });
18763
18764    async move {
18765        request.next().await;
18766    }
18767}
18768
18769pub(crate) fn update_test_language_settings(
18770    cx: &mut TestAppContext,
18771    f: impl Fn(&mut AllLanguageSettingsContent),
18772) {
18773    cx.update(|cx| {
18774        SettingsStore::update_global(cx, |store, cx| {
18775            store.update_user_settings::<AllLanguageSettings>(cx, f);
18776        });
18777    });
18778}
18779
18780pub(crate) fn update_test_project_settings(
18781    cx: &mut TestAppContext,
18782    f: impl Fn(&mut ProjectSettings),
18783) {
18784    cx.update(|cx| {
18785        SettingsStore::update_global(cx, |store, cx| {
18786            store.update_user_settings::<ProjectSettings>(cx, f);
18787        });
18788    });
18789}
18790
18791pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18792    cx.update(|cx| {
18793        assets::Assets.load_test_fonts(cx);
18794        let store = SettingsStore::test(cx);
18795        cx.set_global(store);
18796        theme::init(theme::LoadThemes::JustBase, cx);
18797        release_channel::init(SemanticVersion::default(), cx);
18798        client::init_settings(cx);
18799        language::init(cx);
18800        Project::init_settings(cx);
18801        workspace::init_settings(cx);
18802        crate::init(cx);
18803    });
18804
18805    update_test_language_settings(cx, f);
18806}
18807
18808#[track_caller]
18809fn assert_hunk_revert(
18810    not_reverted_text_with_selections: &str,
18811    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18812    expected_reverted_text_with_selections: &str,
18813    base_text: &str,
18814    cx: &mut EditorLspTestContext,
18815) {
18816    cx.set_state(not_reverted_text_with_selections);
18817    cx.set_head_text(base_text);
18818    cx.executor().run_until_parked();
18819
18820    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18821        let snapshot = editor.snapshot(window, cx);
18822        let reverted_hunk_statuses = snapshot
18823            .buffer_snapshot
18824            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18825            .map(|hunk| hunk.status().kind)
18826            .collect::<Vec<_>>();
18827
18828        editor.git_restore(&Default::default(), window, cx);
18829        reverted_hunk_statuses
18830    });
18831    cx.executor().run_until_parked();
18832    cx.assert_editor_state(expected_reverted_text_with_selections);
18833    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18834}