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::new(Point::new(0, 0)..Point::new(2, 0))],
 3169            cx,
 3170        );
 3171        multibuffer.push_excerpts(
 3172            rust_buffer.clone(),
 3173            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3174            cx,
 3175        );
 3176        multibuffer
 3177    });
 3178
 3179    cx.add_window(|window, cx| {
 3180        let mut editor = build_editor(multibuffer, window, cx);
 3181
 3182        assert_eq!(
 3183            editor.text(cx),
 3184            indoc! {"
 3185                a = 1
 3186                b = 2
 3187
 3188                const c: usize = 3;
 3189            "}
 3190        );
 3191
 3192        select_ranges(
 3193            &mut editor,
 3194            indoc! {"
 3195                «aˇ» = 1
 3196                b = 2
 3197
 3198                «const c:ˇ» usize = 3;
 3199            "},
 3200            window,
 3201            cx,
 3202        );
 3203
 3204        editor.tab(&Tab, window, cx);
 3205        assert_text_with_selections(
 3206            &mut editor,
 3207            indoc! {"
 3208                  «aˇ» = 1
 3209                b = 2
 3210
 3211                    «const c:ˇ» usize = 3;
 3212            "},
 3213            cx,
 3214        );
 3215        editor.backtab(&Backtab, window, cx);
 3216        assert_text_with_selections(
 3217            &mut editor,
 3218            indoc! {"
 3219                «aˇ» = 1
 3220                b = 2
 3221
 3222                «const c:ˇ» usize = 3;
 3223            "},
 3224            cx,
 3225        );
 3226
 3227        editor
 3228    });
 3229}
 3230
 3231#[gpui::test]
 3232async fn test_backspace(cx: &mut TestAppContext) {
 3233    init_test(cx, |_| {});
 3234
 3235    let mut cx = EditorTestContext::new(cx).await;
 3236
 3237    // Basic backspace
 3238    cx.set_state(indoc! {"
 3239        onˇe two three
 3240        fou«rˇ» five six
 3241        seven «ˇeight nine
 3242        »ten
 3243    "});
 3244    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3245    cx.assert_editor_state(indoc! {"
 3246        oˇe two three
 3247        fouˇ five six
 3248        seven ˇten
 3249    "});
 3250
 3251    // Test backspace inside and around indents
 3252    cx.set_state(indoc! {"
 3253        zero
 3254            ˇone
 3255                ˇtwo
 3256            ˇ ˇ ˇ  three
 3257        ˇ  ˇ  four
 3258    "});
 3259    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3260    cx.assert_editor_state(indoc! {"
 3261        zero
 3262        ˇone
 3263            ˇtwo
 3264        ˇ  threeˇ  four
 3265    "});
 3266}
 3267
 3268#[gpui::test]
 3269async fn test_delete(cx: &mut TestAppContext) {
 3270    init_test(cx, |_| {});
 3271
 3272    let mut cx = EditorTestContext::new(cx).await;
 3273    cx.set_state(indoc! {"
 3274        onˇe two three
 3275        fou«rˇ» five six
 3276        seven «ˇeight nine
 3277        »ten
 3278    "});
 3279    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3280    cx.assert_editor_state(indoc! {"
 3281        onˇ two three
 3282        fouˇ five six
 3283        seven ˇten
 3284    "});
 3285}
 3286
 3287#[gpui::test]
 3288fn test_delete_line(cx: &mut TestAppContext) {
 3289    init_test(cx, |_| {});
 3290
 3291    let editor = cx.add_window(|window, cx| {
 3292        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3293        build_editor(buffer, window, cx)
 3294    });
 3295    _ = editor.update(cx, |editor, window, cx| {
 3296        editor.change_selections(None, window, cx, |s| {
 3297            s.select_display_ranges([
 3298                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3299                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3300                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3301            ])
 3302        });
 3303        editor.delete_line(&DeleteLine, window, cx);
 3304        assert_eq!(editor.display_text(cx), "ghi");
 3305        assert_eq!(
 3306            editor.selections.display_ranges(cx),
 3307            vec![
 3308                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3309                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3310            ]
 3311        );
 3312    });
 3313
 3314    let editor = cx.add_window(|window, cx| {
 3315        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3316        build_editor(buffer, window, cx)
 3317    });
 3318    _ = editor.update(cx, |editor, window, cx| {
 3319        editor.change_selections(None, window, cx, |s| {
 3320            s.select_display_ranges([
 3321                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3322            ])
 3323        });
 3324        editor.delete_line(&DeleteLine, window, cx);
 3325        assert_eq!(editor.display_text(cx), "ghi\n");
 3326        assert_eq!(
 3327            editor.selections.display_ranges(cx),
 3328            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3329        );
 3330    });
 3331}
 3332
 3333#[gpui::test]
 3334fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3335    init_test(cx, |_| {});
 3336
 3337    cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3339        let mut editor = build_editor(buffer.clone(), window, cx);
 3340        let buffer = buffer.read(cx).as_singleton().unwrap();
 3341
 3342        assert_eq!(
 3343            editor.selections.ranges::<Point>(cx),
 3344            &[Point::new(0, 0)..Point::new(0, 0)]
 3345        );
 3346
 3347        // When on single line, replace newline at end by space
 3348        editor.join_lines(&JoinLines, window, cx);
 3349        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3350        assert_eq!(
 3351            editor.selections.ranges::<Point>(cx),
 3352            &[Point::new(0, 3)..Point::new(0, 3)]
 3353        );
 3354
 3355        // When multiple lines are selected, remove newlines that are spanned by the selection
 3356        editor.change_selections(None, window, cx, |s| {
 3357            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3358        });
 3359        editor.join_lines(&JoinLines, window, cx);
 3360        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3361        assert_eq!(
 3362            editor.selections.ranges::<Point>(cx),
 3363            &[Point::new(0, 11)..Point::new(0, 11)]
 3364        );
 3365
 3366        // Undo should be transactional
 3367        editor.undo(&Undo, window, cx);
 3368        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3369        assert_eq!(
 3370            editor.selections.ranges::<Point>(cx),
 3371            &[Point::new(0, 5)..Point::new(2, 2)]
 3372        );
 3373
 3374        // When joining an empty line don't insert a space
 3375        editor.change_selections(None, window, cx, |s| {
 3376            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3377        });
 3378        editor.join_lines(&JoinLines, window, cx);
 3379        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3380        assert_eq!(
 3381            editor.selections.ranges::<Point>(cx),
 3382            [Point::new(2, 3)..Point::new(2, 3)]
 3383        );
 3384
 3385        // We can remove trailing newlines
 3386        editor.join_lines(&JoinLines, window, cx);
 3387        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3388        assert_eq!(
 3389            editor.selections.ranges::<Point>(cx),
 3390            [Point::new(2, 3)..Point::new(2, 3)]
 3391        );
 3392
 3393        // We don't blow up on the last line
 3394        editor.join_lines(&JoinLines, window, cx);
 3395        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3396        assert_eq!(
 3397            editor.selections.ranges::<Point>(cx),
 3398            [Point::new(2, 3)..Point::new(2, 3)]
 3399        );
 3400
 3401        // reset to test indentation
 3402        editor.buffer.update(cx, |buffer, cx| {
 3403            buffer.edit(
 3404                [
 3405                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3406                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3407                ],
 3408                None,
 3409                cx,
 3410            )
 3411        });
 3412
 3413        // We remove any leading spaces
 3414        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3415        editor.change_selections(None, window, cx, |s| {
 3416            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3417        });
 3418        editor.join_lines(&JoinLines, window, cx);
 3419        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3420
 3421        // We don't insert a space for a line containing only spaces
 3422        editor.join_lines(&JoinLines, window, cx);
 3423        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3424
 3425        // We ignore any leading tabs
 3426        editor.join_lines(&JoinLines, window, cx);
 3427        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3428
 3429        editor
 3430    });
 3431}
 3432
 3433#[gpui::test]
 3434fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3435    init_test(cx, |_| {});
 3436
 3437    cx.add_window(|window, cx| {
 3438        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3439        let mut editor = build_editor(buffer.clone(), window, cx);
 3440        let buffer = buffer.read(cx).as_singleton().unwrap();
 3441
 3442        editor.change_selections(None, window, cx, |s| {
 3443            s.select_ranges([
 3444                Point::new(0, 2)..Point::new(1, 1),
 3445                Point::new(1, 2)..Point::new(1, 2),
 3446                Point::new(3, 1)..Point::new(3, 2),
 3447            ])
 3448        });
 3449
 3450        editor.join_lines(&JoinLines, window, cx);
 3451        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3452
 3453        assert_eq!(
 3454            editor.selections.ranges::<Point>(cx),
 3455            [
 3456                Point::new(0, 7)..Point::new(0, 7),
 3457                Point::new(1, 3)..Point::new(1, 3)
 3458            ]
 3459        );
 3460        editor
 3461    });
 3462}
 3463
 3464#[gpui::test]
 3465async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3466    init_test(cx, |_| {});
 3467
 3468    let mut cx = EditorTestContext::new(cx).await;
 3469
 3470    let diff_base = r#"
 3471        Line 0
 3472        Line 1
 3473        Line 2
 3474        Line 3
 3475        "#
 3476    .unindent();
 3477
 3478    cx.set_state(
 3479        &r#"
 3480        ˇLine 0
 3481        Line 1
 3482        Line 2
 3483        Line 3
 3484        "#
 3485        .unindent(),
 3486    );
 3487
 3488    cx.set_head_text(&diff_base);
 3489    executor.run_until_parked();
 3490
 3491    // Join lines
 3492    cx.update_editor(|editor, window, cx| {
 3493        editor.join_lines(&JoinLines, window, cx);
 3494    });
 3495    executor.run_until_parked();
 3496
 3497    cx.assert_editor_state(
 3498        &r#"
 3499        Line 0ˇ Line 1
 3500        Line 2
 3501        Line 3
 3502        "#
 3503        .unindent(),
 3504    );
 3505    // Join again
 3506    cx.update_editor(|editor, window, cx| {
 3507        editor.join_lines(&JoinLines, window, cx);
 3508    });
 3509    executor.run_until_parked();
 3510
 3511    cx.assert_editor_state(
 3512        &r#"
 3513        Line 0 Line 1ˇ Line 2
 3514        Line 3
 3515        "#
 3516        .unindent(),
 3517    );
 3518}
 3519
 3520#[gpui::test]
 3521async fn test_custom_newlines_cause_no_false_positive_diffs(
 3522    executor: BackgroundExecutor,
 3523    cx: &mut TestAppContext,
 3524) {
 3525    init_test(cx, |_| {});
 3526    let mut cx = EditorTestContext::new(cx).await;
 3527    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3528    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3529    executor.run_until_parked();
 3530
 3531    cx.update_editor(|editor, window, cx| {
 3532        let snapshot = editor.snapshot(window, cx);
 3533        assert_eq!(
 3534            snapshot
 3535                .buffer_snapshot
 3536                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3537                .collect::<Vec<_>>(),
 3538            Vec::new(),
 3539            "Should not have any diffs for files with custom newlines"
 3540        );
 3541    });
 3542}
 3543
 3544#[gpui::test]
 3545async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3546    init_test(cx, |_| {});
 3547
 3548    let mut cx = EditorTestContext::new(cx).await;
 3549
 3550    // Test sort_lines_case_insensitive()
 3551    cx.set_state(indoc! {"
 3552        «z
 3553        y
 3554        x
 3555        Z
 3556        Y
 3557        Xˇ»
 3558    "});
 3559    cx.update_editor(|e, window, cx| {
 3560        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3561    });
 3562    cx.assert_editor_state(indoc! {"
 3563        «x
 3564        X
 3565        y
 3566        Y
 3567        z
 3568        Zˇ»
 3569    "});
 3570
 3571    // Test reverse_lines()
 3572    cx.set_state(indoc! {"
 3573        «5
 3574        4
 3575        3
 3576        2
 3577        1ˇ»
 3578    "});
 3579    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3580    cx.assert_editor_state(indoc! {"
 3581        «1
 3582        2
 3583        3
 3584        4
 3585        5ˇ»
 3586    "});
 3587
 3588    // Skip testing shuffle_line()
 3589
 3590    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3591    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3592
 3593    // Don't manipulate when cursor is on single line, but expand the selection
 3594    cx.set_state(indoc! {"
 3595        ddˇdd
 3596        ccc
 3597        bb
 3598        a
 3599    "});
 3600    cx.update_editor(|e, window, cx| {
 3601        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3602    });
 3603    cx.assert_editor_state(indoc! {"
 3604        «ddddˇ»
 3605        ccc
 3606        bb
 3607        a
 3608    "});
 3609
 3610    // Basic manipulate case
 3611    // Start selection moves to column 0
 3612    // End of selection shrinks to fit shorter line
 3613    cx.set_state(indoc! {"
 3614        dd«d
 3615        ccc
 3616        bb
 3617        aaaaaˇ»
 3618    "});
 3619    cx.update_editor(|e, window, cx| {
 3620        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3621    });
 3622    cx.assert_editor_state(indoc! {"
 3623        «aaaaa
 3624        bb
 3625        ccc
 3626        dddˇ»
 3627    "});
 3628
 3629    // Manipulate case with newlines
 3630    cx.set_state(indoc! {"
 3631        dd«d
 3632        ccc
 3633
 3634        bb
 3635        aaaaa
 3636
 3637        ˇ»
 3638    "});
 3639    cx.update_editor(|e, window, cx| {
 3640        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3641    });
 3642    cx.assert_editor_state(indoc! {"
 3643        «
 3644
 3645        aaaaa
 3646        bb
 3647        ccc
 3648        dddˇ»
 3649
 3650    "});
 3651
 3652    // Adding new line
 3653    cx.set_state(indoc! {"
 3654        aa«a
 3655        bbˇ»b
 3656    "});
 3657    cx.update_editor(|e, window, cx| {
 3658        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3659    });
 3660    cx.assert_editor_state(indoc! {"
 3661        «aaa
 3662        bbb
 3663        added_lineˇ»
 3664    "});
 3665
 3666    // Removing line
 3667    cx.set_state(indoc! {"
 3668        aa«a
 3669        bbbˇ»
 3670    "});
 3671    cx.update_editor(|e, window, cx| {
 3672        e.manipulate_lines(window, cx, |lines| {
 3673            lines.pop();
 3674        })
 3675    });
 3676    cx.assert_editor_state(indoc! {"
 3677        «aaaˇ»
 3678    "});
 3679
 3680    // Removing all lines
 3681    cx.set_state(indoc! {"
 3682        aa«a
 3683        bbbˇ»
 3684    "});
 3685    cx.update_editor(|e, window, cx| {
 3686        e.manipulate_lines(window, cx, |lines| {
 3687            lines.drain(..);
 3688        })
 3689    });
 3690    cx.assert_editor_state(indoc! {"
 3691        ˇ
 3692    "});
 3693}
 3694
 3695#[gpui::test]
 3696async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3697    init_test(cx, |_| {});
 3698
 3699    let mut cx = EditorTestContext::new(cx).await;
 3700
 3701    // Consider continuous selection as single selection
 3702    cx.set_state(indoc! {"
 3703        Aaa«aa
 3704        cˇ»c«c
 3705        bb
 3706        aaaˇ»aa
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3710    });
 3711    cx.assert_editor_state(indoc! {"
 3712        «Aaaaa
 3713        ccc
 3714        bb
 3715        aaaaaˇ»
 3716    "});
 3717
 3718    cx.set_state(indoc! {"
 3719        Aaa«aa
 3720        cˇ»c«c
 3721        bb
 3722        aaaˇ»aa
 3723    "});
 3724    cx.update_editor(|e, window, cx| {
 3725        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3726    });
 3727    cx.assert_editor_state(indoc! {"
 3728        «Aaaaa
 3729        ccc
 3730        bbˇ»
 3731    "});
 3732
 3733    // Consider non continuous selection as distinct dedup operations
 3734    cx.set_state(indoc! {"
 3735        «aaaaa
 3736        bb
 3737        aaaaa
 3738        aaaaaˇ»
 3739
 3740        aaa«aaˇ»
 3741    "});
 3742    cx.update_editor(|e, window, cx| {
 3743        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3744    });
 3745    cx.assert_editor_state(indoc! {"
 3746        «aaaaa
 3747        bbˇ»
 3748
 3749        «aaaaaˇ»
 3750    "});
 3751}
 3752
 3753#[gpui::test]
 3754async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3755    init_test(cx, |_| {});
 3756
 3757    let mut cx = EditorTestContext::new(cx).await;
 3758
 3759    cx.set_state(indoc! {"
 3760        «Aaa
 3761        aAa
 3762        Aaaˇ»
 3763    "});
 3764    cx.update_editor(|e, window, cx| {
 3765        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3766    });
 3767    cx.assert_editor_state(indoc! {"
 3768        «Aaa
 3769        aAaˇ»
 3770    "});
 3771
 3772    cx.set_state(indoc! {"
 3773        «Aaa
 3774        aAa
 3775        aaAˇ»
 3776    "});
 3777    cx.update_editor(|e, window, cx| {
 3778        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3779    });
 3780    cx.assert_editor_state(indoc! {"
 3781        «Aaaˇ»
 3782    "});
 3783}
 3784
 3785#[gpui::test]
 3786async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3787    init_test(cx, |_| {});
 3788
 3789    let mut cx = EditorTestContext::new(cx).await;
 3790
 3791    // Manipulate with multiple selections on a single line
 3792    cx.set_state(indoc! {"
 3793        dd«dd
 3794        cˇ»c«c
 3795        bb
 3796        aaaˇ»aa
 3797    "});
 3798    cx.update_editor(|e, window, cx| {
 3799        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3800    });
 3801    cx.assert_editor_state(indoc! {"
 3802        «aaaaa
 3803        bb
 3804        ccc
 3805        ddddˇ»
 3806    "});
 3807
 3808    // Manipulate with multiple disjoin selections
 3809    cx.set_state(indoc! {"
 3810 3811        4
 3812        3
 3813        2
 3814        1ˇ»
 3815
 3816        dd«dd
 3817        ccc
 3818        bb
 3819        aaaˇ»aa
 3820    "});
 3821    cx.update_editor(|e, window, cx| {
 3822        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3823    });
 3824    cx.assert_editor_state(indoc! {"
 3825        «1
 3826        2
 3827        3
 3828        4
 3829        5ˇ»
 3830
 3831        «aaaaa
 3832        bb
 3833        ccc
 3834        ddddˇ»
 3835    "});
 3836
 3837    // Adding lines on each selection
 3838    cx.set_state(indoc! {"
 3839 3840        1ˇ»
 3841
 3842        bb«bb
 3843        aaaˇ»aa
 3844    "});
 3845    cx.update_editor(|e, window, cx| {
 3846        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3847    });
 3848    cx.assert_editor_state(indoc! {"
 3849        «2
 3850        1
 3851        added lineˇ»
 3852
 3853        «bbbb
 3854        aaaaa
 3855        added lineˇ»
 3856    "});
 3857
 3858    // Removing lines on each selection
 3859    cx.set_state(indoc! {"
 3860 3861        1ˇ»
 3862
 3863        bb«bb
 3864        aaaˇ»aa
 3865    "});
 3866    cx.update_editor(|e, window, cx| {
 3867        e.manipulate_lines(window, cx, |lines| {
 3868            lines.pop();
 3869        })
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2ˇ»
 3873
 3874        «bbbbˇ»
 3875    "});
 3876}
 3877
 3878#[gpui::test]
 3879async fn test_manipulate_text(cx: &mut TestAppContext) {
 3880    init_test(cx, |_| {});
 3881
 3882    let mut cx = EditorTestContext::new(cx).await;
 3883
 3884    // Test convert_to_upper_case()
 3885    cx.set_state(indoc! {"
 3886        «hello worldˇ»
 3887    "});
 3888    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3889    cx.assert_editor_state(indoc! {"
 3890        «HELLO WORLDˇ»
 3891    "});
 3892
 3893    // Test convert_to_lower_case()
 3894    cx.set_state(indoc! {"
 3895        «HELLO WORLDˇ»
 3896    "});
 3897    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3898    cx.assert_editor_state(indoc! {"
 3899        «hello worldˇ»
 3900    "});
 3901
 3902    // Test multiple line, single selection case
 3903    cx.set_state(indoc! {"
 3904        «The quick brown
 3905        fox jumps over
 3906        the lazy dogˇ»
 3907    "});
 3908    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3909    cx.assert_editor_state(indoc! {"
 3910        «The Quick Brown
 3911        Fox Jumps Over
 3912        The Lazy Dogˇ»
 3913    "});
 3914
 3915    // Test multiple line, single selection case
 3916    cx.set_state(indoc! {"
 3917        «The quick brown
 3918        fox jumps over
 3919        the lazy dogˇ»
 3920    "});
 3921    cx.update_editor(|e, window, cx| {
 3922        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3923    });
 3924    cx.assert_editor_state(indoc! {"
 3925        «TheQuickBrown
 3926        FoxJumpsOver
 3927        TheLazyDogˇ»
 3928    "});
 3929
 3930    // From here on out, test more complex cases of manipulate_text()
 3931
 3932    // Test no selection case - should affect words cursors are in
 3933    // Cursor at beginning, middle, and end of word
 3934    cx.set_state(indoc! {"
 3935        ˇhello big beauˇtiful worldˇ
 3936    "});
 3937    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3938    cx.assert_editor_state(indoc! {"
 3939        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3940    "});
 3941
 3942    // Test multiple selections on a single line and across multiple lines
 3943    cx.set_state(indoc! {"
 3944        «Theˇ» quick «brown
 3945        foxˇ» jumps «overˇ»
 3946        the «lazyˇ» dog
 3947    "});
 3948    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3949    cx.assert_editor_state(indoc! {"
 3950        «THEˇ» quick «BROWN
 3951        FOXˇ» jumps «OVERˇ»
 3952        the «LAZYˇ» dog
 3953    "});
 3954
 3955    // Test case where text length grows
 3956    cx.set_state(indoc! {"
 3957        «tschüߡ»
 3958    "});
 3959    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3960    cx.assert_editor_state(indoc! {"
 3961        «TSCHÜSSˇ»
 3962    "});
 3963
 3964    // Test to make sure we don't crash when text shrinks
 3965    cx.set_state(indoc! {"
 3966        aaa_bbbˇ
 3967    "});
 3968    cx.update_editor(|e, window, cx| {
 3969        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3970    });
 3971    cx.assert_editor_state(indoc! {"
 3972        «aaaBbbˇ»
 3973    "});
 3974
 3975    // Test to make sure we all aware of the fact that each word can grow and shrink
 3976    // Final selections should be aware of this fact
 3977    cx.set_state(indoc! {"
 3978        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 3979    "});
 3980    cx.update_editor(|e, window, cx| {
 3981        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 3982    });
 3983    cx.assert_editor_state(indoc! {"
 3984        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 3985    "});
 3986
 3987    cx.set_state(indoc! {"
 3988        «hElLo, WoRld!ˇ»
 3989    "});
 3990    cx.update_editor(|e, window, cx| {
 3991        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 3992    });
 3993    cx.assert_editor_state(indoc! {"
 3994        «HeLlO, wOrLD!ˇ»
 3995    "});
 3996}
 3997
 3998#[gpui::test]
 3999fn test_duplicate_line(cx: &mut TestAppContext) {
 4000    init_test(cx, |_| {});
 4001
 4002    let editor = cx.add_window(|window, cx| {
 4003        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4004        build_editor(buffer, window, cx)
 4005    });
 4006    _ = editor.update(cx, |editor, window, cx| {
 4007        editor.change_selections(None, window, cx, |s| {
 4008            s.select_display_ranges([
 4009                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4010                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4011                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4012                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4013            ])
 4014        });
 4015        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4016        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4017        assert_eq!(
 4018            editor.selections.display_ranges(cx),
 4019            vec![
 4020                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4021                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4022                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4023                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4024            ]
 4025        );
 4026    });
 4027
 4028    let editor = cx.add_window(|window, cx| {
 4029        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4030        build_editor(buffer, window, cx)
 4031    });
 4032    _ = editor.update(cx, |editor, window, cx| {
 4033        editor.change_selections(None, window, cx, |s| {
 4034            s.select_display_ranges([
 4035                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4036                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4037            ])
 4038        });
 4039        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4040        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4041        assert_eq!(
 4042            editor.selections.display_ranges(cx),
 4043            vec![
 4044                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4045                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4046            ]
 4047        );
 4048    });
 4049
 4050    // With `move_upwards` the selections stay in place, except for
 4051    // the lines inserted above them
 4052    let editor = cx.add_window(|window, cx| {
 4053        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4054        build_editor(buffer, window, cx)
 4055    });
 4056    _ = editor.update(cx, |editor, window, cx| {
 4057        editor.change_selections(None, window, cx, |s| {
 4058            s.select_display_ranges([
 4059                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4060                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4061                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4062                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4063            ])
 4064        });
 4065        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4066        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4067        assert_eq!(
 4068            editor.selections.display_ranges(cx),
 4069            vec![
 4070                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4071                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4072                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4073                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4074            ]
 4075        );
 4076    });
 4077
 4078    let editor = cx.add_window(|window, cx| {
 4079        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4080        build_editor(buffer, window, cx)
 4081    });
 4082    _ = editor.update(cx, |editor, window, cx| {
 4083        editor.change_selections(None, window, cx, |s| {
 4084            s.select_display_ranges([
 4085                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4086                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4087            ])
 4088        });
 4089        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4090        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4091        assert_eq!(
 4092            editor.selections.display_ranges(cx),
 4093            vec![
 4094                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4095                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4096            ]
 4097        );
 4098    });
 4099
 4100    let editor = cx.add_window(|window, cx| {
 4101        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4102        build_editor(buffer, window, cx)
 4103    });
 4104    _ = editor.update(cx, |editor, window, cx| {
 4105        editor.change_selections(None, window, cx, |s| {
 4106            s.select_display_ranges([
 4107                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4108                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4109            ])
 4110        });
 4111        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4112        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4113        assert_eq!(
 4114            editor.selections.display_ranges(cx),
 4115            vec![
 4116                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4117                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4118            ]
 4119        );
 4120    });
 4121}
 4122
 4123#[gpui::test]
 4124fn test_move_line_up_down(cx: &mut TestAppContext) {
 4125    init_test(cx, |_| {});
 4126
 4127    let editor = cx.add_window(|window, cx| {
 4128        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4129        build_editor(buffer, window, cx)
 4130    });
 4131    _ = editor.update(cx, |editor, window, cx| {
 4132        editor.fold_creases(
 4133            vec![
 4134                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4135                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4136                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4137            ],
 4138            true,
 4139            window,
 4140            cx,
 4141        );
 4142        editor.change_selections(None, window, cx, |s| {
 4143            s.select_display_ranges([
 4144                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4145                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4146                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4147                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4148            ])
 4149        });
 4150        assert_eq!(
 4151            editor.display_text(cx),
 4152            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4153        );
 4154
 4155        editor.move_line_up(&MoveLineUp, window, cx);
 4156        assert_eq!(
 4157            editor.display_text(cx),
 4158            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4159        );
 4160        assert_eq!(
 4161            editor.selections.display_ranges(cx),
 4162            vec![
 4163                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4164                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4165                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4166                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4167            ]
 4168        );
 4169    });
 4170
 4171    _ = editor.update(cx, |editor, window, cx| {
 4172        editor.move_line_down(&MoveLineDown, window, cx);
 4173        assert_eq!(
 4174            editor.display_text(cx),
 4175            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4176        );
 4177        assert_eq!(
 4178            editor.selections.display_ranges(cx),
 4179            vec![
 4180                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4181                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4182                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4183                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4184            ]
 4185        );
 4186    });
 4187
 4188    _ = editor.update(cx, |editor, window, cx| {
 4189        editor.move_line_down(&MoveLineDown, window, cx);
 4190        assert_eq!(
 4191            editor.display_text(cx),
 4192            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4193        );
 4194        assert_eq!(
 4195            editor.selections.display_ranges(cx),
 4196            vec![
 4197                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4198                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4199                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4200                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4201            ]
 4202        );
 4203    });
 4204
 4205    _ = editor.update(cx, |editor, window, cx| {
 4206        editor.move_line_up(&MoveLineUp, window, cx);
 4207        assert_eq!(
 4208            editor.display_text(cx),
 4209            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4210        );
 4211        assert_eq!(
 4212            editor.selections.display_ranges(cx),
 4213            vec![
 4214                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4215                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4216                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4217                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4218            ]
 4219        );
 4220    });
 4221}
 4222
 4223#[gpui::test]
 4224fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4225    init_test(cx, |_| {});
 4226
 4227    let editor = cx.add_window(|window, cx| {
 4228        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4229        build_editor(buffer, window, cx)
 4230    });
 4231    _ = editor.update(cx, |editor, window, cx| {
 4232        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4233        editor.insert_blocks(
 4234            [BlockProperties {
 4235                style: BlockStyle::Fixed,
 4236                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4237                height: 1,
 4238                render: Arc::new(|_| div().into_any()),
 4239                priority: 0,
 4240            }],
 4241            Some(Autoscroll::fit()),
 4242            cx,
 4243        );
 4244        editor.change_selections(None, window, cx, |s| {
 4245            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4246        });
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248    });
 4249}
 4250
 4251#[gpui::test]
 4252async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4253    init_test(cx, |_| {});
 4254
 4255    let mut cx = EditorTestContext::new(cx).await;
 4256    cx.set_state(
 4257        &"
 4258            ˇzero
 4259            one
 4260            two
 4261            three
 4262            four
 4263            five
 4264        "
 4265        .unindent(),
 4266    );
 4267
 4268    // Create a four-line block that replaces three lines of text.
 4269    cx.update_editor(|editor, window, cx| {
 4270        let snapshot = editor.snapshot(window, cx);
 4271        let snapshot = &snapshot.buffer_snapshot;
 4272        let placement = BlockPlacement::Replace(
 4273            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4274        );
 4275        editor.insert_blocks(
 4276            [BlockProperties {
 4277                placement,
 4278                height: 4,
 4279                style: BlockStyle::Sticky,
 4280                render: Arc::new(|_| gpui::div().into_any_element()),
 4281                priority: 0,
 4282            }],
 4283            None,
 4284            cx,
 4285        );
 4286    });
 4287
 4288    // Move down so that the cursor touches the block.
 4289    cx.update_editor(|editor, window, cx| {
 4290        editor.move_down(&Default::default(), window, cx);
 4291    });
 4292    cx.assert_editor_state(
 4293        &"
 4294            zero
 4295            «one
 4296            two
 4297            threeˇ»
 4298            four
 4299            five
 4300        "
 4301        .unindent(),
 4302    );
 4303
 4304    // Move down past the block.
 4305    cx.update_editor(|editor, window, cx| {
 4306        editor.move_down(&Default::default(), window, cx);
 4307    });
 4308    cx.assert_editor_state(
 4309        &"
 4310            zero
 4311            one
 4312            two
 4313            three
 4314            ˇfour
 4315            five
 4316        "
 4317        .unindent(),
 4318    );
 4319}
 4320
 4321#[gpui::test]
 4322fn test_transpose(cx: &mut TestAppContext) {
 4323    init_test(cx, |_| {});
 4324
 4325    _ = cx.add_window(|window, cx| {
 4326        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4327        editor.set_style(EditorStyle::default(), window, cx);
 4328        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4329        editor.transpose(&Default::default(), window, cx);
 4330        assert_eq!(editor.text(cx), "bac");
 4331        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4332
 4333        editor.transpose(&Default::default(), window, cx);
 4334        assert_eq!(editor.text(cx), "bca");
 4335        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4336
 4337        editor.transpose(&Default::default(), window, cx);
 4338        assert_eq!(editor.text(cx), "bac");
 4339        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4340
 4341        editor
 4342    });
 4343
 4344    _ = cx.add_window(|window, cx| {
 4345        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4346        editor.set_style(EditorStyle::default(), window, cx);
 4347        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4348        editor.transpose(&Default::default(), window, cx);
 4349        assert_eq!(editor.text(cx), "acb\nde");
 4350        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4351
 4352        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4353        editor.transpose(&Default::default(), window, cx);
 4354        assert_eq!(editor.text(cx), "acbd\ne");
 4355        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4356
 4357        editor.transpose(&Default::default(), window, cx);
 4358        assert_eq!(editor.text(cx), "acbde\n");
 4359        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4360
 4361        editor.transpose(&Default::default(), window, cx);
 4362        assert_eq!(editor.text(cx), "acbd\ne");
 4363        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4364
 4365        editor
 4366    });
 4367
 4368    _ = cx.add_window(|window, cx| {
 4369        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4370        editor.set_style(EditorStyle::default(), window, cx);
 4371        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4372        editor.transpose(&Default::default(), window, cx);
 4373        assert_eq!(editor.text(cx), "bacd\ne");
 4374        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4375
 4376        editor.transpose(&Default::default(), window, cx);
 4377        assert_eq!(editor.text(cx), "bcade\n");
 4378        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4379
 4380        editor.transpose(&Default::default(), window, cx);
 4381        assert_eq!(editor.text(cx), "bcda\ne");
 4382        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4383
 4384        editor.transpose(&Default::default(), window, cx);
 4385        assert_eq!(editor.text(cx), "bcade\n");
 4386        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4387
 4388        editor.transpose(&Default::default(), window, cx);
 4389        assert_eq!(editor.text(cx), "bcaed\n");
 4390        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4391
 4392        editor
 4393    });
 4394
 4395    _ = cx.add_window(|window, cx| {
 4396        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4397        editor.set_style(EditorStyle::default(), window, cx);
 4398        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4399        editor.transpose(&Default::default(), window, cx);
 4400        assert_eq!(editor.text(cx), "🏀🍐✋");
 4401        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4402
 4403        editor.transpose(&Default::default(), window, cx);
 4404        assert_eq!(editor.text(cx), "🏀✋🍐");
 4405        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4406
 4407        editor.transpose(&Default::default(), window, cx);
 4408        assert_eq!(editor.text(cx), "🏀🍐✋");
 4409        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4410
 4411        editor
 4412    });
 4413}
 4414
 4415#[gpui::test]
 4416async fn test_rewrap(cx: &mut TestAppContext) {
 4417    init_test(cx, |settings| {
 4418        settings.languages.extend([
 4419            (
 4420                "Markdown".into(),
 4421                LanguageSettingsContent {
 4422                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4423                    ..Default::default()
 4424                },
 4425            ),
 4426            (
 4427                "Plain Text".into(),
 4428                LanguageSettingsContent {
 4429                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4430                    ..Default::default()
 4431                },
 4432            ),
 4433        ])
 4434    });
 4435
 4436    let mut cx = EditorTestContext::new(cx).await;
 4437
 4438    let language_with_c_comments = Arc::new(Language::new(
 4439        LanguageConfig {
 4440            line_comments: vec!["// ".into()],
 4441            ..LanguageConfig::default()
 4442        },
 4443        None,
 4444    ));
 4445    let language_with_pound_comments = Arc::new(Language::new(
 4446        LanguageConfig {
 4447            line_comments: vec!["# ".into()],
 4448            ..LanguageConfig::default()
 4449        },
 4450        None,
 4451    ));
 4452    let markdown_language = Arc::new(Language::new(
 4453        LanguageConfig {
 4454            name: "Markdown".into(),
 4455            ..LanguageConfig::default()
 4456        },
 4457        None,
 4458    ));
 4459    let language_with_doc_comments = Arc::new(Language::new(
 4460        LanguageConfig {
 4461            line_comments: vec!["// ".into(), "/// ".into()],
 4462            ..LanguageConfig::default()
 4463        },
 4464        Some(tree_sitter_rust::LANGUAGE.into()),
 4465    ));
 4466
 4467    let plaintext_language = Arc::new(Language::new(
 4468        LanguageConfig {
 4469            name: "Plain Text".into(),
 4470            ..LanguageConfig::default()
 4471        },
 4472        None,
 4473    ));
 4474
 4475    assert_rewrap(
 4476        indoc! {"
 4477            // ˇ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.
 4478        "},
 4479        indoc! {"
 4480            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4481            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4482            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4483            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4484            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4485            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4486            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4487            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4488            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4489            // porttitor id. Aliquam id accumsan eros.
 4490        "},
 4491        language_with_c_comments.clone(),
 4492        &mut cx,
 4493    );
 4494
 4495    // Test that rewrapping works inside of a selection
 4496    assert_rewrap(
 4497        indoc! {"
 4498            «// 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.ˇ»
 4499        "},
 4500        indoc! {"
 4501            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4502            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4503            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4504            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4505            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4506            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4507            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4508            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4509            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4510            // porttitor id. Aliquam id accumsan eros.ˇ»
 4511        "},
 4512        language_with_c_comments.clone(),
 4513        &mut cx,
 4514    );
 4515
 4516    // Test that cursors that expand to the same region are collapsed.
 4517    assert_rewrap(
 4518        indoc! {"
 4519            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4520            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4521            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4522            // ˇ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.
 4523        "},
 4524        indoc! {"
 4525            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4526            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4527            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4528            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4529            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4530            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4531            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4532            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4533            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4534            // porttitor id. Aliquam id accumsan eros.
 4535        "},
 4536        language_with_c_comments.clone(),
 4537        &mut cx,
 4538    );
 4539
 4540    // Test that non-contiguous selections are treated separately.
 4541    assert_rewrap(
 4542        indoc! {"
 4543            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4544            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4545            //
 4546            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4547            // ˇ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.
 4548        "},
 4549        indoc! {"
 4550            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4551            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4552            // auctor, eu lacinia sapien scelerisque.
 4553            //
 4554            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4555            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4556            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4557            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4558            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4559            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4560            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4561        "},
 4562        language_with_c_comments.clone(),
 4563        &mut cx,
 4564    );
 4565
 4566    // Test that different comment prefixes are supported.
 4567    assert_rewrap(
 4568        indoc! {"
 4569            # ˇ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.
 4570        "},
 4571        indoc! {"
 4572            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4573            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4574            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4575            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4576            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4577            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4578            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4579            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4580            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4581            # accumsan eros.
 4582        "},
 4583        language_with_pound_comments.clone(),
 4584        &mut cx,
 4585    );
 4586
 4587    // Test that rewrapping is ignored outside of comments in most languages.
 4588    assert_rewrap(
 4589        indoc! {"
 4590            /// Adds two numbers.
 4591            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4592            fn add(a: u32, b: u32) -> u32 {
 4593                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ˇ
 4594            }
 4595        "},
 4596        indoc! {"
 4597            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4598            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4599            fn add(a: u32, b: u32) -> u32 {
 4600                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ˇ
 4601            }
 4602        "},
 4603        language_with_doc_comments.clone(),
 4604        &mut cx,
 4605    );
 4606
 4607    // Test that rewrapping works in Markdown and Plain Text languages.
 4608    assert_rewrap(
 4609        indoc! {"
 4610            # Hello
 4611
 4612            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.
 4613        "},
 4614        indoc! {"
 4615            # Hello
 4616
 4617            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4618            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4619            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4620            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4621            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4622            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4623            Integer sit amet scelerisque nisi.
 4624        "},
 4625        markdown_language,
 4626        &mut cx,
 4627    );
 4628
 4629    assert_rewrap(
 4630        indoc! {"
 4631            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.
 4632        "},
 4633        indoc! {"
 4634            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4635            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4636            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4637            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4638            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4639            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4640            Integer sit amet scelerisque nisi.
 4641        "},
 4642        plaintext_language,
 4643        &mut cx,
 4644    );
 4645
 4646    // Test rewrapping unaligned comments in a selection.
 4647    assert_rewrap(
 4648        indoc! {"
 4649            fn foo() {
 4650                if true {
 4651            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4652            // Praesent semper egestas tellus id dignissim.ˇ»
 4653                    do_something();
 4654                } else {
 4655                    //
 4656                }
 4657            }
 4658        "},
 4659        indoc! {"
 4660            fn foo() {
 4661                if true {
 4662            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4663                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4664                    // egestas tellus id dignissim.ˇ»
 4665                    do_something();
 4666                } else {
 4667                    //
 4668                }
 4669            }
 4670        "},
 4671        language_with_doc_comments.clone(),
 4672        &mut cx,
 4673    );
 4674
 4675    assert_rewrap(
 4676        indoc! {"
 4677            fn foo() {
 4678                if true {
 4679            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4680            // Praesent semper egestas tellus id dignissim.»
 4681                    do_something();
 4682                } else {
 4683                    //
 4684                }
 4685
 4686            }
 4687        "},
 4688        indoc! {"
 4689            fn foo() {
 4690                if true {
 4691            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4692                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4693                    // egestas tellus id dignissim.»
 4694                    do_something();
 4695                } else {
 4696                    //
 4697                }
 4698
 4699            }
 4700        "},
 4701        language_with_doc_comments.clone(),
 4702        &mut cx,
 4703    );
 4704
 4705    #[track_caller]
 4706    fn assert_rewrap(
 4707        unwrapped_text: &str,
 4708        wrapped_text: &str,
 4709        language: Arc<Language>,
 4710        cx: &mut EditorTestContext,
 4711    ) {
 4712        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4713        cx.set_state(unwrapped_text);
 4714        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4715        cx.assert_editor_state(wrapped_text);
 4716    }
 4717}
 4718
 4719#[gpui::test]
 4720async fn test_hard_wrap(cx: &mut TestAppContext) {
 4721    init_test(cx, |_| {});
 4722    let mut cx = EditorTestContext::new(cx).await;
 4723
 4724    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4725    cx.update_editor(|editor, _, cx| {
 4726        editor.set_hard_wrap(Some(14), cx);
 4727    });
 4728
 4729    cx.set_state(indoc!(
 4730        "
 4731        one two three ˇ
 4732        "
 4733    ));
 4734    cx.simulate_input("four");
 4735    cx.run_until_parked();
 4736
 4737    cx.assert_editor_state(indoc!(
 4738        "
 4739        one two three
 4740        fourˇ
 4741        "
 4742    ));
 4743
 4744    cx.update_editor(|editor, window, cx| {
 4745        editor.newline(&Default::default(), window, cx);
 4746    });
 4747    cx.run_until_parked();
 4748    cx.assert_editor_state(indoc!(
 4749        "
 4750        one two three
 4751        four
 4752        ˇ
 4753        "
 4754    ));
 4755
 4756    cx.simulate_input("five");
 4757    cx.run_until_parked();
 4758    cx.assert_editor_state(indoc!(
 4759        "
 4760        one two three
 4761        four
 4762        fiveˇ
 4763        "
 4764    ));
 4765
 4766    cx.update_editor(|editor, window, cx| {
 4767        editor.newline(&Default::default(), window, cx);
 4768    });
 4769    cx.run_until_parked();
 4770    cx.simulate_input("# ");
 4771    cx.run_until_parked();
 4772    cx.assert_editor_state(indoc!(
 4773        "
 4774        one two three
 4775        four
 4776        five
 4777        # ˇ
 4778        "
 4779    ));
 4780
 4781    cx.update_editor(|editor, window, cx| {
 4782        editor.newline(&Default::default(), window, cx);
 4783    });
 4784    cx.run_until_parked();
 4785    cx.assert_editor_state(indoc!(
 4786        "
 4787        one two three
 4788        four
 4789        five
 4790        #\x20
 4791 4792        "
 4793    ));
 4794
 4795    cx.simulate_input(" 6");
 4796    cx.run_until_parked();
 4797    cx.assert_editor_state(indoc!(
 4798        "
 4799        one two three
 4800        four
 4801        five
 4802        #
 4803        # 6ˇ
 4804        "
 4805    ));
 4806}
 4807
 4808#[gpui::test]
 4809async fn test_clipboard(cx: &mut TestAppContext) {
 4810    init_test(cx, |_| {});
 4811
 4812    let mut cx = EditorTestContext::new(cx).await;
 4813
 4814    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4815    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4816    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4817
 4818    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4819    cx.set_state("two ˇfour ˇsix ˇ");
 4820    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4821    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4822
 4823    // Paste again but with only two cursors. Since the number of cursors doesn't
 4824    // match the number of slices in the clipboard, the entire clipboard text
 4825    // is pasted at each cursor.
 4826    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4827    cx.update_editor(|e, window, cx| {
 4828        e.handle_input("( ", window, cx);
 4829        e.paste(&Paste, window, cx);
 4830        e.handle_input(") ", window, cx);
 4831    });
 4832    cx.assert_editor_state(
 4833        &([
 4834            "( one✅ ",
 4835            "three ",
 4836            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4837            "three ",
 4838            "five ) ˇ",
 4839        ]
 4840        .join("\n")),
 4841    );
 4842
 4843    // Cut with three selections, one of which is full-line.
 4844    cx.set_state(indoc! {"
 4845        1«2ˇ»3
 4846        4ˇ567
 4847        «8ˇ»9"});
 4848    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4849    cx.assert_editor_state(indoc! {"
 4850        1ˇ3
 4851        ˇ9"});
 4852
 4853    // Paste with three selections, noticing how the copied selection that was full-line
 4854    // gets inserted before the second cursor.
 4855    cx.set_state(indoc! {"
 4856        1ˇ3
 4857 4858        «oˇ»ne"});
 4859    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4860    cx.assert_editor_state(indoc! {"
 4861        12ˇ3
 4862        4567
 4863 4864        8ˇne"});
 4865
 4866    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4867    cx.set_state(indoc! {"
 4868        The quick brown
 4869        fox juˇmps over
 4870        the lazy dog"});
 4871    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4872    assert_eq!(
 4873        cx.read_from_clipboard()
 4874            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4875        Some("fox jumps over\n".to_string())
 4876    );
 4877
 4878    // Paste with three selections, noticing how the copied full-line selection is inserted
 4879    // before the empty selections but replaces the selection that is non-empty.
 4880    cx.set_state(indoc! {"
 4881        Tˇhe quick brown
 4882        «foˇ»x jumps over
 4883        tˇhe lazy dog"});
 4884    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4885    cx.assert_editor_state(indoc! {"
 4886        fox jumps over
 4887        Tˇhe quick brown
 4888        fox jumps over
 4889        ˇx jumps over
 4890        fox jumps over
 4891        tˇhe lazy dog"});
 4892}
 4893
 4894#[gpui::test]
 4895async fn test_copy_trim(cx: &mut TestAppContext) {
 4896    init_test(cx, |_| {});
 4897
 4898    let mut cx = EditorTestContext::new(cx).await;
 4899    cx.set_state(
 4900        r#"            «for selection in selections.iter() {
 4901            let mut start = selection.start;
 4902            let mut end = selection.end;
 4903            let is_entire_line = selection.is_empty();
 4904            if is_entire_line {
 4905                start = Point::new(start.row, 0);ˇ»
 4906                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4907            }
 4908        "#,
 4909    );
 4910    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4911    assert_eq!(
 4912        cx.read_from_clipboard()
 4913            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4914        Some(
 4915            "for selection in selections.iter() {
 4916            let mut start = selection.start;
 4917            let mut end = selection.end;
 4918            let is_entire_line = selection.is_empty();
 4919            if is_entire_line {
 4920                start = Point::new(start.row, 0);"
 4921                .to_string()
 4922        ),
 4923        "Regular copying preserves all indentation selected",
 4924    );
 4925    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4926    assert_eq!(
 4927        cx.read_from_clipboard()
 4928            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4929        Some(
 4930            "for selection in selections.iter() {
 4931let mut start = selection.start;
 4932let mut end = selection.end;
 4933let is_entire_line = selection.is_empty();
 4934if is_entire_line {
 4935    start = Point::new(start.row, 0);"
 4936                .to_string()
 4937        ),
 4938        "Copying with stripping should strip all leading whitespaces"
 4939    );
 4940
 4941    cx.set_state(
 4942        r#"       «     for selection in selections.iter() {
 4943            let mut start = selection.start;
 4944            let mut end = selection.end;
 4945            let is_entire_line = selection.is_empty();
 4946            if is_entire_line {
 4947                start = Point::new(start.row, 0);ˇ»
 4948                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4949            }
 4950        "#,
 4951    );
 4952    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4953    assert_eq!(
 4954        cx.read_from_clipboard()
 4955            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4956        Some(
 4957            "     for selection in selections.iter() {
 4958            let mut start = selection.start;
 4959            let mut end = selection.end;
 4960            let is_entire_line = selection.is_empty();
 4961            if is_entire_line {
 4962                start = Point::new(start.row, 0);"
 4963                .to_string()
 4964        ),
 4965        "Regular copying preserves all indentation selected",
 4966    );
 4967    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4968    assert_eq!(
 4969        cx.read_from_clipboard()
 4970            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4971        Some(
 4972            "for selection in selections.iter() {
 4973let mut start = selection.start;
 4974let mut end = selection.end;
 4975let is_entire_line = selection.is_empty();
 4976if is_entire_line {
 4977    start = Point::new(start.row, 0);"
 4978                .to_string()
 4979        ),
 4980        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 4981    );
 4982
 4983    cx.set_state(
 4984        r#"       «ˇ     for selection in selections.iter() {
 4985            let mut start = selection.start;
 4986            let mut end = selection.end;
 4987            let is_entire_line = selection.is_empty();
 4988            if is_entire_line {
 4989                start = Point::new(start.row, 0);»
 4990                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4991            }
 4992        "#,
 4993    );
 4994    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4995    assert_eq!(
 4996        cx.read_from_clipboard()
 4997            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4998        Some(
 4999            "     for selection in selections.iter() {
 5000            let mut start = selection.start;
 5001            let mut end = selection.end;
 5002            let is_entire_line = selection.is_empty();
 5003            if is_entire_line {
 5004                start = Point::new(start.row, 0);"
 5005                .to_string()
 5006        ),
 5007        "Regular copying for reverse selection works the same",
 5008    );
 5009    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5010    assert_eq!(
 5011        cx.read_from_clipboard()
 5012            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5013        Some(
 5014            "for selection in selections.iter() {
 5015let mut start = selection.start;
 5016let mut end = selection.end;
 5017let is_entire_line = selection.is_empty();
 5018if is_entire_line {
 5019    start = Point::new(start.row, 0);"
 5020                .to_string()
 5021        ),
 5022        "Copying with stripping for reverse selection works the same"
 5023    );
 5024
 5025    cx.set_state(
 5026        r#"            for selection «in selections.iter() {
 5027            let mut start = selection.start;
 5028            let mut end = selection.end;
 5029            let is_entire_line = selection.is_empty();
 5030            if is_entire_line {
 5031                start = Point::new(start.row, 0);ˇ»
 5032                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5033            }
 5034        "#,
 5035    );
 5036    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5037    assert_eq!(
 5038        cx.read_from_clipboard()
 5039            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5040        Some(
 5041            "in selections.iter() {
 5042            let mut start = selection.start;
 5043            let mut end = selection.end;
 5044            let is_entire_line = selection.is_empty();
 5045            if is_entire_line {
 5046                start = Point::new(start.row, 0);"
 5047                .to_string()
 5048        ),
 5049        "When selecting past the indent, the copying works as usual",
 5050    );
 5051    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5052    assert_eq!(
 5053        cx.read_from_clipboard()
 5054            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5055        Some(
 5056            "in selections.iter() {
 5057            let mut start = selection.start;
 5058            let mut end = selection.end;
 5059            let is_entire_line = selection.is_empty();
 5060            if is_entire_line {
 5061                start = Point::new(start.row, 0);"
 5062                .to_string()
 5063        ),
 5064        "When selecting past the indent, nothing is trimmed"
 5065    );
 5066}
 5067
 5068#[gpui::test]
 5069async fn test_paste_multiline(cx: &mut TestAppContext) {
 5070    init_test(cx, |_| {});
 5071
 5072    let mut cx = EditorTestContext::new(cx).await;
 5073    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5074
 5075    // Cut an indented block, without the leading whitespace.
 5076    cx.set_state(indoc! {"
 5077        const a: B = (
 5078            c(),
 5079            «d(
 5080                e,
 5081                f
 5082            )ˇ»
 5083        );
 5084    "});
 5085    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5086    cx.assert_editor_state(indoc! {"
 5087        const a: B = (
 5088            c(),
 5089            ˇ
 5090        );
 5091    "});
 5092
 5093    // Paste it at the same position.
 5094    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5095    cx.assert_editor_state(indoc! {"
 5096        const a: B = (
 5097            c(),
 5098            d(
 5099                e,
 5100                f
 5101 5102        );
 5103    "});
 5104
 5105    // Paste it at a line with a lower indent level.
 5106    cx.set_state(indoc! {"
 5107        ˇ
 5108        const a: B = (
 5109            c(),
 5110        );
 5111    "});
 5112    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5113    cx.assert_editor_state(indoc! {"
 5114        d(
 5115            e,
 5116            f
 5117 5118        const a: B = (
 5119            c(),
 5120        );
 5121    "});
 5122
 5123    // Cut an indented block, with the leading whitespace.
 5124    cx.set_state(indoc! {"
 5125        const a: B = (
 5126            c(),
 5127        «    d(
 5128                e,
 5129                f
 5130            )
 5131        ˇ»);
 5132    "});
 5133    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5134    cx.assert_editor_state(indoc! {"
 5135        const a: B = (
 5136            c(),
 5137        ˇ);
 5138    "});
 5139
 5140    // Paste it at the same position.
 5141    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5142    cx.assert_editor_state(indoc! {"
 5143        const a: B = (
 5144            c(),
 5145            d(
 5146                e,
 5147                f
 5148            )
 5149        ˇ);
 5150    "});
 5151
 5152    // Paste it at a line with a higher indent level.
 5153    cx.set_state(indoc! {"
 5154        const a: B = (
 5155            c(),
 5156            d(
 5157                e,
 5158 5159            )
 5160        );
 5161    "});
 5162    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5163    cx.assert_editor_state(indoc! {"
 5164        const a: B = (
 5165            c(),
 5166            d(
 5167                e,
 5168                f    d(
 5169                    e,
 5170                    f
 5171                )
 5172        ˇ
 5173            )
 5174        );
 5175    "});
 5176
 5177    // Copy an indented block, starting mid-line
 5178    cx.set_state(indoc! {"
 5179        const a: B = (
 5180            c(),
 5181            somethin«g(
 5182                e,
 5183                f
 5184            )ˇ»
 5185        );
 5186    "});
 5187    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5188
 5189    // Paste it on a line with a lower indent level
 5190    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5191    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5192    cx.assert_editor_state(indoc! {"
 5193        const a: B = (
 5194            c(),
 5195            something(
 5196                e,
 5197                f
 5198            )
 5199        );
 5200        g(
 5201            e,
 5202            f
 5203"});
 5204}
 5205
 5206#[gpui::test]
 5207async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5208    init_test(cx, |_| {});
 5209
 5210    cx.write_to_clipboard(ClipboardItem::new_string(
 5211        "    d(\n        e\n    );\n".into(),
 5212    ));
 5213
 5214    let mut cx = EditorTestContext::new(cx).await;
 5215    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5216
 5217    cx.set_state(indoc! {"
 5218        fn a() {
 5219            b();
 5220            if c() {
 5221                ˇ
 5222            }
 5223        }
 5224    "});
 5225
 5226    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5227    cx.assert_editor_state(indoc! {"
 5228        fn a() {
 5229            b();
 5230            if c() {
 5231                d(
 5232                    e
 5233                );
 5234        ˇ
 5235            }
 5236        }
 5237    "});
 5238
 5239    cx.set_state(indoc! {"
 5240        fn a() {
 5241            b();
 5242            ˇ
 5243        }
 5244    "});
 5245
 5246    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5247    cx.assert_editor_state(indoc! {"
 5248        fn a() {
 5249            b();
 5250            d(
 5251                e
 5252            );
 5253        ˇ
 5254        }
 5255    "});
 5256}
 5257
 5258#[gpui::test]
 5259fn test_select_all(cx: &mut TestAppContext) {
 5260    init_test(cx, |_| {});
 5261
 5262    let editor = cx.add_window(|window, cx| {
 5263        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5264        build_editor(buffer, window, cx)
 5265    });
 5266    _ = editor.update(cx, |editor, window, cx| {
 5267        editor.select_all(&SelectAll, window, cx);
 5268        assert_eq!(
 5269            editor.selections.display_ranges(cx),
 5270            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5271        );
 5272    });
 5273}
 5274
 5275#[gpui::test]
 5276fn test_select_line(cx: &mut TestAppContext) {
 5277    init_test(cx, |_| {});
 5278
 5279    let editor = cx.add_window(|window, cx| {
 5280        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5281        build_editor(buffer, window, cx)
 5282    });
 5283    _ = editor.update(cx, |editor, window, cx| {
 5284        editor.change_selections(None, window, cx, |s| {
 5285            s.select_display_ranges([
 5286                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5287                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5288                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5289                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5290            ])
 5291        });
 5292        editor.select_line(&SelectLine, window, cx);
 5293        assert_eq!(
 5294            editor.selections.display_ranges(cx),
 5295            vec![
 5296                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5297                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5298            ]
 5299        );
 5300    });
 5301
 5302    _ = editor.update(cx, |editor, window, cx| {
 5303        editor.select_line(&SelectLine, window, cx);
 5304        assert_eq!(
 5305            editor.selections.display_ranges(cx),
 5306            vec![
 5307                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5308                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5309            ]
 5310        );
 5311    });
 5312
 5313    _ = editor.update(cx, |editor, window, cx| {
 5314        editor.select_line(&SelectLine, window, cx);
 5315        assert_eq!(
 5316            editor.selections.display_ranges(cx),
 5317            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5318        );
 5319    });
 5320}
 5321
 5322#[gpui::test]
 5323async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5324    init_test(cx, |_| {});
 5325    let mut cx = EditorTestContext::new(cx).await;
 5326
 5327    #[track_caller]
 5328    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5329        cx.set_state(initial_state);
 5330        cx.update_editor(|e, window, cx| {
 5331            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5332        });
 5333        cx.assert_editor_state(expected_state);
 5334    }
 5335
 5336    // Selection starts and ends at the middle of lines, left-to-right
 5337    test(
 5338        &mut cx,
 5339        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5340        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5341    );
 5342    // Same thing, right-to-left
 5343    test(
 5344        &mut cx,
 5345        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5346        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5347    );
 5348
 5349    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5350    test(
 5351        &mut cx,
 5352        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5353        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5354    );
 5355    // Same thing, right-to-left
 5356    test(
 5357        &mut cx,
 5358        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5359        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5360    );
 5361
 5362    // Whole buffer, left-to-right, last line ends with newline
 5363    test(
 5364        &mut cx,
 5365        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5366        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5367    );
 5368    // Same thing, right-to-left
 5369    test(
 5370        &mut cx,
 5371        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5372        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5373    );
 5374
 5375    // Starts at the end of a line, ends at the start of another
 5376    test(
 5377        &mut cx,
 5378        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5379        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5380    );
 5381}
 5382
 5383#[gpui::test]
 5384async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5385    init_test(cx, |_| {});
 5386
 5387    let editor = cx.add_window(|window, cx| {
 5388        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5389        build_editor(buffer, window, cx)
 5390    });
 5391
 5392    // setup
 5393    _ = editor.update(cx, |editor, window, cx| {
 5394        editor.fold_creases(
 5395            vec![
 5396                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5397                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5398                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5399            ],
 5400            true,
 5401            window,
 5402            cx,
 5403        );
 5404        assert_eq!(
 5405            editor.display_text(cx),
 5406            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5407        );
 5408    });
 5409
 5410    _ = editor.update(cx, |editor, window, cx| {
 5411        editor.change_selections(None, window, cx, |s| {
 5412            s.select_display_ranges([
 5413                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5414                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5415                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5416                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5417            ])
 5418        });
 5419        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5420        assert_eq!(
 5421            editor.display_text(cx),
 5422            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5423        );
 5424    });
 5425    EditorTestContext::for_editor(editor, cx)
 5426        .await
 5427        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5428
 5429    _ = editor.update(cx, |editor, window, cx| {
 5430        editor.change_selections(None, window, cx, |s| {
 5431            s.select_display_ranges([
 5432                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5433            ])
 5434        });
 5435        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5436        assert_eq!(
 5437            editor.display_text(cx),
 5438            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5439        );
 5440        assert_eq!(
 5441            editor.selections.display_ranges(cx),
 5442            [
 5443                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5444                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5445                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5446                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5447                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5448                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5449                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5450            ]
 5451        );
 5452    });
 5453    EditorTestContext::for_editor(editor, cx)
 5454        .await
 5455        .assert_editor_state(
 5456            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5457        );
 5458}
 5459
 5460#[gpui::test]
 5461async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5462    init_test(cx, |_| {});
 5463
 5464    let mut cx = EditorTestContext::new(cx).await;
 5465
 5466    cx.set_state(indoc!(
 5467        r#"abc
 5468           defˇghi
 5469
 5470           jk
 5471           nlmo
 5472           "#
 5473    ));
 5474
 5475    cx.update_editor(|editor, window, cx| {
 5476        editor.add_selection_above(&Default::default(), window, cx);
 5477    });
 5478
 5479    cx.assert_editor_state(indoc!(
 5480        r#"abcˇ
 5481           defˇghi
 5482
 5483           jk
 5484           nlmo
 5485           "#
 5486    ));
 5487
 5488    cx.update_editor(|editor, window, cx| {
 5489        editor.add_selection_above(&Default::default(), window, cx);
 5490    });
 5491
 5492    cx.assert_editor_state(indoc!(
 5493        r#"abcˇ
 5494            defˇghi
 5495
 5496            jk
 5497            nlmo
 5498            "#
 5499    ));
 5500
 5501    cx.update_editor(|editor, window, cx| {
 5502        editor.add_selection_below(&Default::default(), window, cx);
 5503    });
 5504
 5505    cx.assert_editor_state(indoc!(
 5506        r#"abc
 5507           defˇghi
 5508
 5509           jk
 5510           nlmo
 5511           "#
 5512    ));
 5513
 5514    cx.update_editor(|editor, window, cx| {
 5515        editor.undo_selection(&Default::default(), window, cx);
 5516    });
 5517
 5518    cx.assert_editor_state(indoc!(
 5519        r#"abcˇ
 5520           defˇghi
 5521
 5522           jk
 5523           nlmo
 5524           "#
 5525    ));
 5526
 5527    cx.update_editor(|editor, window, cx| {
 5528        editor.redo_selection(&Default::default(), window, cx);
 5529    });
 5530
 5531    cx.assert_editor_state(indoc!(
 5532        r#"abc
 5533           defˇghi
 5534
 5535           jk
 5536           nlmo
 5537           "#
 5538    ));
 5539
 5540    cx.update_editor(|editor, window, cx| {
 5541        editor.add_selection_below(&Default::default(), window, cx);
 5542    });
 5543
 5544    cx.assert_editor_state(indoc!(
 5545        r#"abc
 5546           defˇghi
 5547
 5548           jk
 5549           nlmˇo
 5550           "#
 5551    ));
 5552
 5553    cx.update_editor(|editor, window, cx| {
 5554        editor.add_selection_below(&Default::default(), window, cx);
 5555    });
 5556
 5557    cx.assert_editor_state(indoc!(
 5558        r#"abc
 5559           defˇghi
 5560
 5561           jk
 5562           nlmˇo
 5563           "#
 5564    ));
 5565
 5566    // change selections
 5567    cx.set_state(indoc!(
 5568        r#"abc
 5569           def«ˇg»hi
 5570
 5571           jk
 5572           nlmo
 5573           "#
 5574    ));
 5575
 5576    cx.update_editor(|editor, window, cx| {
 5577        editor.add_selection_below(&Default::default(), window, cx);
 5578    });
 5579
 5580    cx.assert_editor_state(indoc!(
 5581        r#"abc
 5582           def«ˇg»hi
 5583
 5584           jk
 5585           nlm«ˇo»
 5586           "#
 5587    ));
 5588
 5589    cx.update_editor(|editor, window, cx| {
 5590        editor.add_selection_below(&Default::default(), window, cx);
 5591    });
 5592
 5593    cx.assert_editor_state(indoc!(
 5594        r#"abc
 5595           def«ˇg»hi
 5596
 5597           jk
 5598           nlm«ˇo»
 5599           "#
 5600    ));
 5601
 5602    cx.update_editor(|editor, window, cx| {
 5603        editor.add_selection_above(&Default::default(), window, cx);
 5604    });
 5605
 5606    cx.assert_editor_state(indoc!(
 5607        r#"abc
 5608           def«ˇg»hi
 5609
 5610           jk
 5611           nlmo
 5612           "#
 5613    ));
 5614
 5615    cx.update_editor(|editor, window, cx| {
 5616        editor.add_selection_above(&Default::default(), window, cx);
 5617    });
 5618
 5619    cx.assert_editor_state(indoc!(
 5620        r#"abc
 5621           def«ˇg»hi
 5622
 5623           jk
 5624           nlmo
 5625           "#
 5626    ));
 5627
 5628    // Change selections again
 5629    cx.set_state(indoc!(
 5630        r#"a«bc
 5631           defgˇ»hi
 5632
 5633           jk
 5634           nlmo
 5635           "#
 5636    ));
 5637
 5638    cx.update_editor(|editor, window, cx| {
 5639        editor.add_selection_below(&Default::default(), window, cx);
 5640    });
 5641
 5642    cx.assert_editor_state(indoc!(
 5643        r#"a«bcˇ»
 5644           d«efgˇ»hi
 5645
 5646           j«kˇ»
 5647           nlmo
 5648           "#
 5649    ));
 5650
 5651    cx.update_editor(|editor, window, cx| {
 5652        editor.add_selection_below(&Default::default(), window, cx);
 5653    });
 5654    cx.assert_editor_state(indoc!(
 5655        r#"a«bcˇ»
 5656           d«efgˇ»hi
 5657
 5658           j«kˇ»
 5659           n«lmoˇ»
 5660           "#
 5661    ));
 5662    cx.update_editor(|editor, window, cx| {
 5663        editor.add_selection_above(&Default::default(), window, cx);
 5664    });
 5665
 5666    cx.assert_editor_state(indoc!(
 5667        r#"a«bcˇ»
 5668           d«efgˇ»hi
 5669
 5670           j«kˇ»
 5671           nlmo
 5672           "#
 5673    ));
 5674
 5675    // Change selections again
 5676    cx.set_state(indoc!(
 5677        r#"abc
 5678           d«ˇefghi
 5679
 5680           jk
 5681           nlm»o
 5682           "#
 5683    ));
 5684
 5685    cx.update_editor(|editor, window, cx| {
 5686        editor.add_selection_above(&Default::default(), window, cx);
 5687    });
 5688
 5689    cx.assert_editor_state(indoc!(
 5690        r#"a«ˇbc»
 5691           d«ˇef»ghi
 5692
 5693           j«ˇk»
 5694           n«ˇlm»o
 5695           "#
 5696    ));
 5697
 5698    cx.update_editor(|editor, window, cx| {
 5699        editor.add_selection_below(&Default::default(), window, cx);
 5700    });
 5701
 5702    cx.assert_editor_state(indoc!(
 5703        r#"abc
 5704           d«ˇef»ghi
 5705
 5706           j«ˇk»
 5707           n«ˇlm»o
 5708           "#
 5709    ));
 5710}
 5711
 5712#[gpui::test]
 5713async fn test_select_next(cx: &mut TestAppContext) {
 5714    init_test(cx, |_| {});
 5715
 5716    let mut cx = EditorTestContext::new(cx).await;
 5717    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5718
 5719    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5720        .unwrap();
 5721    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5722
 5723    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5724        .unwrap();
 5725    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5726
 5727    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5728    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5729
 5730    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5731    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5732
 5733    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5734        .unwrap();
 5735    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5736
 5737    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5738        .unwrap();
 5739    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5740}
 5741
 5742#[gpui::test]
 5743async fn test_select_all_matches(cx: &mut TestAppContext) {
 5744    init_test(cx, |_| {});
 5745
 5746    let mut cx = EditorTestContext::new(cx).await;
 5747
 5748    // Test caret-only selections
 5749    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5750    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5751        .unwrap();
 5752    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5753
 5754    // Test left-to-right selections
 5755    cx.set_state("abc\n«abcˇ»\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ˇ»\n«abcˇ»");
 5759
 5760    // Test right-to-left 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 selecting whitespace with caret selection
 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\nabc");
 5771
 5772    // Test selecting whitespace with left-to-right 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 no matches with right-to-left 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
 5785#[gpui::test]
 5786async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5787    init_test(cx, |_| {});
 5788
 5789    let mut cx = EditorTestContext::new(cx).await;
 5790    cx.set_state(
 5791        r#"let foo = 2;
 5792lˇet foo = 2;
 5793let fooˇ = 2;
 5794let foo = 2;
 5795let foo = ˇ2;"#,
 5796    );
 5797
 5798    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5799        .unwrap();
 5800    cx.assert_editor_state(
 5801        r#"let foo = 2;
 5802«letˇ» foo = 2;
 5803let «fooˇ» = 2;
 5804let foo = 2;
 5805let foo = «2ˇ»;"#,
 5806    );
 5807
 5808    // noop for multiple selections with different contents
 5809    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5810        .unwrap();
 5811    cx.assert_editor_state(
 5812        r#"let foo = 2;
 5813«letˇ» foo = 2;
 5814let «fooˇ» = 2;
 5815let foo = 2;
 5816let foo = «2ˇ»;"#,
 5817    );
 5818}
 5819
 5820#[gpui::test]
 5821async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5822    init_test(cx, |_| {});
 5823
 5824    let mut cx =
 5825        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5826
 5827    cx.assert_editor_state(indoc! {"
 5828        ˇbbb
 5829        ccc
 5830
 5831        bbb
 5832        ccc
 5833        "});
 5834    cx.dispatch_action(SelectPrevious::default());
 5835    cx.assert_editor_state(indoc! {"
 5836                «bbbˇ»
 5837                ccc
 5838
 5839                bbb
 5840                ccc
 5841                "});
 5842    cx.dispatch_action(SelectPrevious::default());
 5843    cx.assert_editor_state(indoc! {"
 5844                «bbbˇ»
 5845                ccc
 5846
 5847                «bbbˇ»
 5848                ccc
 5849                "});
 5850}
 5851
 5852#[gpui::test]
 5853async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 5854    init_test(cx, |_| {});
 5855
 5856    let mut cx = EditorTestContext::new(cx).await;
 5857    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5858
 5859    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5860        .unwrap();
 5861    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5862
 5863    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5864        .unwrap();
 5865    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5866
 5867    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5868    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5869
 5870    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5871    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 5872
 5873    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5874        .unwrap();
 5875    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 5876
 5877    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5878        .unwrap();
 5879    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 5880
 5881    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5882        .unwrap();
 5883    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5884}
 5885
 5886#[gpui::test]
 5887async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 5888    init_test(cx, |_| {});
 5889
 5890    let mut cx = EditorTestContext::new(cx).await;
 5891    cx.set_state("");
 5892
 5893    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5894        .unwrap();
 5895    cx.assert_editor_state("«aˇ»");
 5896    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5897        .unwrap();
 5898    cx.assert_editor_state("«aˇ»");
 5899}
 5900
 5901#[gpui::test]
 5902async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 5903    init_test(cx, |_| {});
 5904
 5905    let mut cx = EditorTestContext::new(cx).await;
 5906    cx.set_state(
 5907        r#"let foo = 2;
 5908lˇet foo = 2;
 5909let fooˇ = 2;
 5910let foo = 2;
 5911let foo = ˇ2;"#,
 5912    );
 5913
 5914    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5915        .unwrap();
 5916    cx.assert_editor_state(
 5917        r#"let foo = 2;
 5918«letˇ» foo = 2;
 5919let «fooˇ» = 2;
 5920let foo = 2;
 5921let foo = «2ˇ»;"#,
 5922    );
 5923
 5924    // noop for multiple selections with different contents
 5925    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5926        .unwrap();
 5927    cx.assert_editor_state(
 5928        r#"let foo = 2;
 5929«letˇ» foo = 2;
 5930let «fooˇ» = 2;
 5931let foo = 2;
 5932let foo = «2ˇ»;"#,
 5933    );
 5934}
 5935
 5936#[gpui::test]
 5937async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 5938    init_test(cx, |_| {});
 5939
 5940    let mut cx = EditorTestContext::new(cx).await;
 5941    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 5942
 5943    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5944        .unwrap();
 5945    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5946
 5947    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5948        .unwrap();
 5949    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5950
 5951    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5952    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 5953
 5954    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5955    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 5956
 5957    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5958        .unwrap();
 5959    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 5960
 5961    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 5962        .unwrap();
 5963    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 5964}
 5965
 5966#[gpui::test]
 5967async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 5968    init_test(cx, |_| {});
 5969
 5970    let language = Arc::new(Language::new(
 5971        LanguageConfig::default(),
 5972        Some(tree_sitter_rust::LANGUAGE.into()),
 5973    ));
 5974
 5975    let text = r#"
 5976        use mod1::mod2::{mod3, mod4};
 5977
 5978        fn fn_1(param1: bool, param2: &str) {
 5979            let var1 = "text";
 5980        }
 5981    "#
 5982    .unindent();
 5983
 5984    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 5985    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 5986    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 5987
 5988    editor
 5989        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 5990        .await;
 5991
 5992    editor.update_in(cx, |editor, window, cx| {
 5993        editor.change_selections(None, window, cx, |s| {
 5994            s.select_display_ranges([
 5995                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 5996                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 5997                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 5998            ]);
 5999        });
 6000        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6001    });
 6002    editor.update(cx, |editor, cx| {
 6003        assert_text_with_selections(
 6004            editor,
 6005            indoc! {r#"
 6006                use mod1::mod2::{mod3, «mod4ˇ»};
 6007
 6008                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6009                    let var1 = "«ˇtext»";
 6010                }
 6011            "#},
 6012            cx,
 6013        );
 6014    });
 6015
 6016    editor.update_in(cx, |editor, window, cx| {
 6017        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6018    });
 6019    editor.update(cx, |editor, cx| {
 6020        assert_text_with_selections(
 6021            editor,
 6022            indoc! {r#"
 6023                use mod1::mod2::«{mod3, mod4}ˇ»;
 6024
 6025                «ˇfn fn_1(param1: bool, param2: &str) {
 6026                    let var1 = "text";
 6027 6028            "#},
 6029            cx,
 6030        );
 6031    });
 6032
 6033    editor.update_in(cx, |editor, window, cx| {
 6034        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6035    });
 6036    assert_eq!(
 6037        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6038        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6039    );
 6040
 6041    // Trying to expand the selected syntax node one more time has no effect.
 6042    editor.update_in(cx, |editor, window, cx| {
 6043        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6044    });
 6045    assert_eq!(
 6046        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6047        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6048    );
 6049
 6050    editor.update_in(cx, |editor, window, cx| {
 6051        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6052    });
 6053    editor.update(cx, |editor, cx| {
 6054        assert_text_with_selections(
 6055            editor,
 6056            indoc! {r#"
 6057                use mod1::mod2::«{mod3, mod4}ˇ»;
 6058
 6059                «ˇfn fn_1(param1: bool, param2: &str) {
 6060                    let var1 = "text";
 6061 6062            "#},
 6063            cx,
 6064        );
 6065    });
 6066
 6067    editor.update_in(cx, |editor, window, cx| {
 6068        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6069    });
 6070    editor.update(cx, |editor, cx| {
 6071        assert_text_with_selections(
 6072            editor,
 6073            indoc! {r#"
 6074                use mod1::mod2::{mod3, «mod4ˇ»};
 6075
 6076                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6077                    let var1 = "«ˇtext»";
 6078                }
 6079            "#},
 6080            cx,
 6081        );
 6082    });
 6083
 6084    editor.update_in(cx, |editor, window, cx| {
 6085        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6086    });
 6087    editor.update(cx, |editor, cx| {
 6088        assert_text_with_selections(
 6089            editor,
 6090            indoc! {r#"
 6091                use mod1::mod2::{mod3, mo«ˇ»d4};
 6092
 6093                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6094                    let var1 = "te«ˇ»xt";
 6095                }
 6096            "#},
 6097            cx,
 6098        );
 6099    });
 6100
 6101    // Trying to shrink the selected syntax node one more time has no effect.
 6102    editor.update_in(cx, |editor, window, cx| {
 6103        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6104    });
 6105    editor.update_in(cx, |editor, _, cx| {
 6106        assert_text_with_selections(
 6107            editor,
 6108            indoc! {r#"
 6109                use mod1::mod2::{mod3, mo«ˇ»d4};
 6110
 6111                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6112                    let var1 = "te«ˇ»xt";
 6113                }
 6114            "#},
 6115            cx,
 6116        );
 6117    });
 6118
 6119    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6120    // a fold.
 6121    editor.update_in(cx, |editor, window, cx| {
 6122        editor.fold_creases(
 6123            vec![
 6124                Crease::simple(
 6125                    Point::new(0, 21)..Point::new(0, 24),
 6126                    FoldPlaceholder::test(),
 6127                ),
 6128                Crease::simple(
 6129                    Point::new(3, 20)..Point::new(3, 22),
 6130                    FoldPlaceholder::test(),
 6131                ),
 6132            ],
 6133            true,
 6134            window,
 6135            cx,
 6136        );
 6137        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6138    });
 6139    editor.update(cx, |editor, cx| {
 6140        assert_text_with_selections(
 6141            editor,
 6142            indoc! {r#"
 6143                use mod1::mod2::«{mod3, mod4}ˇ»;
 6144
 6145                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6146                    «ˇlet var1 = "text";»
 6147                }
 6148            "#},
 6149            cx,
 6150        );
 6151    });
 6152}
 6153
 6154#[gpui::test]
 6155async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6156    init_test(cx, |_| {});
 6157
 6158    let base_text = r#"
 6159        impl A {
 6160            // this is an uncommitted comment
 6161
 6162            fn b() {
 6163                c();
 6164            }
 6165
 6166            // this is another uncommitted comment
 6167
 6168            fn d() {
 6169                // e
 6170                // f
 6171            }
 6172        }
 6173
 6174        fn g() {
 6175            // h
 6176        }
 6177    "#
 6178    .unindent();
 6179
 6180    let text = r#"
 6181        ˇimpl A {
 6182
 6183            fn b() {
 6184                c();
 6185            }
 6186
 6187            fn d() {
 6188                // e
 6189                // f
 6190            }
 6191        }
 6192
 6193        fn g() {
 6194            // h
 6195        }
 6196    "#
 6197    .unindent();
 6198
 6199    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6200    cx.set_state(&text);
 6201    cx.set_head_text(&base_text);
 6202    cx.update_editor(|editor, window, cx| {
 6203        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6204    });
 6205
 6206    cx.assert_state_with_diff(
 6207        "
 6208        ˇimpl A {
 6209      -     // this is an uncommitted comment
 6210
 6211            fn b() {
 6212                c();
 6213            }
 6214
 6215      -     // this is another uncommitted comment
 6216      -
 6217            fn d() {
 6218                // e
 6219                // f
 6220            }
 6221        }
 6222
 6223        fn g() {
 6224            // h
 6225        }
 6226    "
 6227        .unindent(),
 6228    );
 6229
 6230    let expected_display_text = "
 6231        impl A {
 6232            // this is an uncommitted comment
 6233
 6234            fn b() {
 6235 6236            }
 6237
 6238            // this is another uncommitted comment
 6239
 6240            fn d() {
 6241 6242            }
 6243        }
 6244
 6245        fn g() {
 6246 6247        }
 6248        "
 6249    .unindent();
 6250
 6251    cx.update_editor(|editor, window, cx| {
 6252        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6253        assert_eq!(editor.display_text(cx), expected_display_text);
 6254    });
 6255}
 6256
 6257#[gpui::test]
 6258async fn test_autoindent(cx: &mut TestAppContext) {
 6259    init_test(cx, |_| {});
 6260
 6261    let language = Arc::new(
 6262        Language::new(
 6263            LanguageConfig {
 6264                brackets: BracketPairConfig {
 6265                    pairs: vec![
 6266                        BracketPair {
 6267                            start: "{".to_string(),
 6268                            end: "}".to_string(),
 6269                            close: false,
 6270                            surround: false,
 6271                            newline: true,
 6272                        },
 6273                        BracketPair {
 6274                            start: "(".to_string(),
 6275                            end: ")".to_string(),
 6276                            close: false,
 6277                            surround: false,
 6278                            newline: true,
 6279                        },
 6280                    ],
 6281                    ..Default::default()
 6282                },
 6283                ..Default::default()
 6284            },
 6285            Some(tree_sitter_rust::LANGUAGE.into()),
 6286        )
 6287        .with_indents_query(
 6288            r#"
 6289                (_ "(" ")" @end) @indent
 6290                (_ "{" "}" @end) @indent
 6291            "#,
 6292        )
 6293        .unwrap(),
 6294    );
 6295
 6296    let text = "fn a() {}";
 6297
 6298    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6299    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6300    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6301    editor
 6302        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6303        .await;
 6304
 6305    editor.update_in(cx, |editor, window, cx| {
 6306        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6307        editor.newline(&Newline, window, cx);
 6308        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6309        assert_eq!(
 6310            editor.selections.ranges(cx),
 6311            &[
 6312                Point::new(1, 4)..Point::new(1, 4),
 6313                Point::new(3, 4)..Point::new(3, 4),
 6314                Point::new(5, 0)..Point::new(5, 0)
 6315            ]
 6316        );
 6317    });
 6318}
 6319
 6320#[gpui::test]
 6321async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6322    init_test(cx, |_| {});
 6323
 6324    {
 6325        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6326        cx.set_state(indoc! {"
 6327            impl A {
 6328
 6329                fn b() {}
 6330
 6331            «fn c() {
 6332
 6333            }ˇ»
 6334            }
 6335        "});
 6336
 6337        cx.update_editor(|editor, window, cx| {
 6338            editor.autoindent(&Default::default(), window, cx);
 6339        });
 6340
 6341        cx.assert_editor_state(indoc! {"
 6342            impl A {
 6343
 6344                fn b() {}
 6345
 6346                «fn c() {
 6347
 6348                }ˇ»
 6349            }
 6350        "});
 6351    }
 6352
 6353    {
 6354        let mut cx = EditorTestContext::new_multibuffer(
 6355            cx,
 6356            [indoc! { "
 6357                impl A {
 6358                «
 6359                // a
 6360                fn b(){}
 6361                »
 6362                «
 6363                    }
 6364                    fn c(){}
 6365                »
 6366            "}],
 6367        );
 6368
 6369        let buffer = cx.update_editor(|editor, _, cx| {
 6370            let buffer = editor.buffer().update(cx, |buffer, _| {
 6371                buffer.all_buffers().iter().next().unwrap().clone()
 6372            });
 6373            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6374            buffer
 6375        });
 6376
 6377        cx.run_until_parked();
 6378        cx.update_editor(|editor, window, cx| {
 6379            editor.select_all(&Default::default(), window, cx);
 6380            editor.autoindent(&Default::default(), window, cx)
 6381        });
 6382        cx.run_until_parked();
 6383
 6384        cx.update(|_, cx| {
 6385            pretty_assertions::assert_eq!(
 6386                buffer.read(cx).text(),
 6387                indoc! { "
 6388                    impl A {
 6389
 6390                        // a
 6391                        fn b(){}
 6392
 6393
 6394                    }
 6395                    fn c(){}
 6396
 6397                " }
 6398            )
 6399        });
 6400    }
 6401}
 6402
 6403#[gpui::test]
 6404async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6405    init_test(cx, |_| {});
 6406
 6407    let mut cx = EditorTestContext::new(cx).await;
 6408
 6409    let language = Arc::new(Language::new(
 6410        LanguageConfig {
 6411            brackets: BracketPairConfig {
 6412                pairs: vec![
 6413                    BracketPair {
 6414                        start: "{".to_string(),
 6415                        end: "}".to_string(),
 6416                        close: true,
 6417                        surround: true,
 6418                        newline: true,
 6419                    },
 6420                    BracketPair {
 6421                        start: "(".to_string(),
 6422                        end: ")".to_string(),
 6423                        close: true,
 6424                        surround: true,
 6425                        newline: true,
 6426                    },
 6427                    BracketPair {
 6428                        start: "/*".to_string(),
 6429                        end: " */".to_string(),
 6430                        close: true,
 6431                        surround: true,
 6432                        newline: true,
 6433                    },
 6434                    BracketPair {
 6435                        start: "[".to_string(),
 6436                        end: "]".to_string(),
 6437                        close: false,
 6438                        surround: false,
 6439                        newline: true,
 6440                    },
 6441                    BracketPair {
 6442                        start: "\"".to_string(),
 6443                        end: "\"".to_string(),
 6444                        close: true,
 6445                        surround: true,
 6446                        newline: false,
 6447                    },
 6448                    BracketPair {
 6449                        start: "<".to_string(),
 6450                        end: ">".to_string(),
 6451                        close: false,
 6452                        surround: true,
 6453                        newline: true,
 6454                    },
 6455                ],
 6456                ..Default::default()
 6457            },
 6458            autoclose_before: "})]".to_string(),
 6459            ..Default::default()
 6460        },
 6461        Some(tree_sitter_rust::LANGUAGE.into()),
 6462    ));
 6463
 6464    cx.language_registry().add(language.clone());
 6465    cx.update_buffer(|buffer, cx| {
 6466        buffer.set_language(Some(language), cx);
 6467    });
 6468
 6469    cx.set_state(
 6470        &r#"
 6471            🏀ˇ
 6472            εˇ
 6473            ❤️ˇ
 6474        "#
 6475        .unindent(),
 6476    );
 6477
 6478    // autoclose multiple nested brackets at multiple cursors
 6479    cx.update_editor(|editor, window, cx| {
 6480        editor.handle_input("{", window, cx);
 6481        editor.handle_input("{", window, cx);
 6482        editor.handle_input("{", window, cx);
 6483    });
 6484    cx.assert_editor_state(
 6485        &"
 6486            🏀{{{ˇ}}}
 6487            ε{{{ˇ}}}
 6488            ❤️{{{ˇ}}}
 6489        "
 6490        .unindent(),
 6491    );
 6492
 6493    // insert a different closing bracket
 6494    cx.update_editor(|editor, window, cx| {
 6495        editor.handle_input(")", window, cx);
 6496    });
 6497    cx.assert_editor_state(
 6498        &"
 6499            🏀{{{)ˇ}}}
 6500            ε{{{)ˇ}}}
 6501            ❤️{{{)ˇ}}}
 6502        "
 6503        .unindent(),
 6504    );
 6505
 6506    // skip over the auto-closed brackets when typing a closing bracket
 6507    cx.update_editor(|editor, window, cx| {
 6508        editor.move_right(&MoveRight, window, cx);
 6509        editor.handle_input("}", window, cx);
 6510        editor.handle_input("}", window, cx);
 6511        editor.handle_input("}", window, cx);
 6512    });
 6513    cx.assert_editor_state(
 6514        &"
 6515            🏀{{{)}}}}ˇ
 6516            ε{{{)}}}}ˇ
 6517            ❤️{{{)}}}}ˇ
 6518        "
 6519        .unindent(),
 6520    );
 6521
 6522    // autoclose multi-character pairs
 6523    cx.set_state(
 6524        &"
 6525            ˇ
 6526            ˇ
 6527        "
 6528        .unindent(),
 6529    );
 6530    cx.update_editor(|editor, window, cx| {
 6531        editor.handle_input("/", window, cx);
 6532        editor.handle_input("*", window, cx);
 6533    });
 6534    cx.assert_editor_state(
 6535        &"
 6536            /*ˇ */
 6537            /*ˇ */
 6538        "
 6539        .unindent(),
 6540    );
 6541
 6542    // one cursor autocloses a multi-character pair, one cursor
 6543    // does not autoclose.
 6544    cx.set_state(
 6545        &"
 6546 6547            ˇ
 6548        "
 6549        .unindent(),
 6550    );
 6551    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6552    cx.assert_editor_state(
 6553        &"
 6554            /*ˇ */
 6555 6556        "
 6557        .unindent(),
 6558    );
 6559
 6560    // Don't autoclose if the next character isn't whitespace and isn't
 6561    // listed in the language's "autoclose_before" section.
 6562    cx.set_state("ˇa b");
 6563    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6564    cx.assert_editor_state("{ˇa b");
 6565
 6566    // Don't autoclose if `close` is false for the bracket pair
 6567    cx.set_state("ˇ");
 6568    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6569    cx.assert_editor_state("");
 6570
 6571    // Surround with brackets if text is selected
 6572    cx.set_state("«aˇ» b");
 6573    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6574    cx.assert_editor_state("{«aˇ»} b");
 6575
 6576    // Autoclose when not immediately after a word character
 6577    cx.set_state("a ˇ");
 6578    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6579    cx.assert_editor_state("a \"ˇ\"");
 6580
 6581    // Autoclose pair where the start and end characters are the same
 6582    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6583    cx.assert_editor_state("a \"\"ˇ");
 6584
 6585    // Don't autoclose when immediately after a word character
 6586    cx.set_state("");
 6587    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6588    cx.assert_editor_state("a\"ˇ");
 6589
 6590    // Do autoclose when after a non-word character
 6591    cx.set_state("");
 6592    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6593    cx.assert_editor_state("{\"ˇ\"");
 6594
 6595    // Non identical pairs autoclose regardless of preceding character
 6596    cx.set_state("");
 6597    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6598    cx.assert_editor_state("a{ˇ}");
 6599
 6600    // Don't autoclose pair if autoclose is disabled
 6601    cx.set_state("ˇ");
 6602    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6603    cx.assert_editor_state("");
 6604
 6605    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6606    cx.set_state("«aˇ» b");
 6607    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6608    cx.assert_editor_state("<«aˇ»> b");
 6609}
 6610
 6611#[gpui::test]
 6612async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6613    init_test(cx, |settings| {
 6614        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6615    });
 6616
 6617    let mut cx = EditorTestContext::new(cx).await;
 6618
 6619    let language = Arc::new(Language::new(
 6620        LanguageConfig {
 6621            brackets: BracketPairConfig {
 6622                pairs: vec![
 6623                    BracketPair {
 6624                        start: "{".to_string(),
 6625                        end: "}".to_string(),
 6626                        close: true,
 6627                        surround: true,
 6628                        newline: true,
 6629                    },
 6630                    BracketPair {
 6631                        start: "(".to_string(),
 6632                        end: ")".to_string(),
 6633                        close: true,
 6634                        surround: true,
 6635                        newline: true,
 6636                    },
 6637                    BracketPair {
 6638                        start: "[".to_string(),
 6639                        end: "]".to_string(),
 6640                        close: false,
 6641                        surround: false,
 6642                        newline: true,
 6643                    },
 6644                ],
 6645                ..Default::default()
 6646            },
 6647            autoclose_before: "})]".to_string(),
 6648            ..Default::default()
 6649        },
 6650        Some(tree_sitter_rust::LANGUAGE.into()),
 6651    ));
 6652
 6653    cx.language_registry().add(language.clone());
 6654    cx.update_buffer(|buffer, cx| {
 6655        buffer.set_language(Some(language), cx);
 6656    });
 6657
 6658    cx.set_state(
 6659        &"
 6660            ˇ
 6661            ˇ
 6662            ˇ
 6663        "
 6664        .unindent(),
 6665    );
 6666
 6667    // ensure only matching closing brackets are skipped over
 6668    cx.update_editor(|editor, window, cx| {
 6669        editor.handle_input("}", window, cx);
 6670        editor.move_left(&MoveLeft, window, cx);
 6671        editor.handle_input(")", window, cx);
 6672        editor.move_left(&MoveLeft, window, cx);
 6673    });
 6674    cx.assert_editor_state(
 6675        &"
 6676            ˇ)}
 6677            ˇ)}
 6678            ˇ)}
 6679        "
 6680        .unindent(),
 6681    );
 6682
 6683    // skip-over closing brackets at multiple cursors
 6684    cx.update_editor(|editor, window, cx| {
 6685        editor.handle_input(")", window, cx);
 6686        editor.handle_input("}", window, cx);
 6687    });
 6688    cx.assert_editor_state(
 6689        &"
 6690            )}ˇ
 6691            )}ˇ
 6692            )}ˇ
 6693        "
 6694        .unindent(),
 6695    );
 6696
 6697    // ignore non-close brackets
 6698    cx.update_editor(|editor, window, cx| {
 6699        editor.handle_input("]", window, cx);
 6700        editor.move_left(&MoveLeft, window, cx);
 6701        editor.handle_input("]", window, cx);
 6702    });
 6703    cx.assert_editor_state(
 6704        &"
 6705            )}]ˇ]
 6706            )}]ˇ]
 6707            )}]ˇ]
 6708        "
 6709        .unindent(),
 6710    );
 6711}
 6712
 6713#[gpui::test]
 6714async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6715    init_test(cx, |_| {});
 6716
 6717    let mut cx = EditorTestContext::new(cx).await;
 6718
 6719    let html_language = Arc::new(
 6720        Language::new(
 6721            LanguageConfig {
 6722                name: "HTML".into(),
 6723                brackets: BracketPairConfig {
 6724                    pairs: vec![
 6725                        BracketPair {
 6726                            start: "<".into(),
 6727                            end: ">".into(),
 6728                            close: true,
 6729                            ..Default::default()
 6730                        },
 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                    ],
 6744                    ..Default::default()
 6745                },
 6746                autoclose_before: "})]>".into(),
 6747                ..Default::default()
 6748            },
 6749            Some(tree_sitter_html::LANGUAGE.into()),
 6750        )
 6751        .with_injection_query(
 6752            r#"
 6753            (script_element
 6754                (raw_text) @injection.content
 6755                (#set! injection.language "javascript"))
 6756            "#,
 6757        )
 6758        .unwrap(),
 6759    );
 6760
 6761    let javascript_language = Arc::new(Language::new(
 6762        LanguageConfig {
 6763            name: "JavaScript".into(),
 6764            brackets: BracketPairConfig {
 6765                pairs: vec![
 6766                    BracketPair {
 6767                        start: "/*".into(),
 6768                        end: " */".into(),
 6769                        close: true,
 6770                        ..Default::default()
 6771                    },
 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                ],
 6785                ..Default::default()
 6786            },
 6787            autoclose_before: "})]>".into(),
 6788            ..Default::default()
 6789        },
 6790        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6791    ));
 6792
 6793    cx.language_registry().add(html_language.clone());
 6794    cx.language_registry().add(javascript_language.clone());
 6795
 6796    cx.update_buffer(|buffer, cx| {
 6797        buffer.set_language(Some(html_language), cx);
 6798    });
 6799
 6800    cx.set_state(
 6801        &r#"
 6802            <body>ˇ
 6803                <script>
 6804                    var x = 1;ˇ
 6805                </script>
 6806            </body>ˇ
 6807        "#
 6808        .unindent(),
 6809    );
 6810
 6811    // Precondition: different languages are active at different locations.
 6812    cx.update_editor(|editor, window, cx| {
 6813        let snapshot = editor.snapshot(window, cx);
 6814        let cursors = editor.selections.ranges::<usize>(cx);
 6815        let languages = cursors
 6816            .iter()
 6817            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6818            .collect::<Vec<_>>();
 6819        assert_eq!(
 6820            languages,
 6821            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6822        );
 6823    });
 6824
 6825    // Angle brackets autoclose in HTML, but not JavaScript.
 6826    cx.update_editor(|editor, window, cx| {
 6827        editor.handle_input("<", window, cx);
 6828        editor.handle_input("a", window, cx);
 6829    });
 6830    cx.assert_editor_state(
 6831        &r#"
 6832            <body><aˇ>
 6833                <script>
 6834                    var x = 1;<aˇ
 6835                </script>
 6836            </body><aˇ>
 6837        "#
 6838        .unindent(),
 6839    );
 6840
 6841    // Curly braces and parens autoclose in both HTML and JavaScript.
 6842    cx.update_editor(|editor, window, cx| {
 6843        editor.handle_input(" b=", window, cx);
 6844        editor.handle_input("{", window, cx);
 6845        editor.handle_input("c", window, cx);
 6846        editor.handle_input("(", window, cx);
 6847    });
 6848    cx.assert_editor_state(
 6849        &r#"
 6850            <body><a b={c(ˇ)}>
 6851                <script>
 6852                    var x = 1;<a b={c(ˇ)}
 6853                </script>
 6854            </body><a b={c(ˇ)}>
 6855        "#
 6856        .unindent(),
 6857    );
 6858
 6859    // Brackets that were already autoclosed are skipped.
 6860    cx.update_editor(|editor, window, cx| {
 6861        editor.handle_input(")", window, cx);
 6862        editor.handle_input("d", window, cx);
 6863        editor.handle_input("}", window, cx);
 6864    });
 6865    cx.assert_editor_state(
 6866        &r#"
 6867            <body><a b={c()d}ˇ>
 6868                <script>
 6869                    var x = 1;<a b={c()d}ˇ
 6870                </script>
 6871            </body><a b={c()d}ˇ>
 6872        "#
 6873        .unindent(),
 6874    );
 6875    cx.update_editor(|editor, window, cx| {
 6876        editor.handle_input(">", window, cx);
 6877    });
 6878    cx.assert_editor_state(
 6879        &r#"
 6880            <body><a b={c()d}>ˇ
 6881                <script>
 6882                    var x = 1;<a b={c()d}>ˇ
 6883                </script>
 6884            </body><a b={c()d}>ˇ
 6885        "#
 6886        .unindent(),
 6887    );
 6888
 6889    // Reset
 6890    cx.set_state(
 6891        &r#"
 6892            <body>ˇ
 6893                <script>
 6894                    var x = 1;ˇ
 6895                </script>
 6896            </body>ˇ
 6897        "#
 6898        .unindent(),
 6899    );
 6900
 6901    cx.update_editor(|editor, window, cx| {
 6902        editor.handle_input("<", window, cx);
 6903    });
 6904    cx.assert_editor_state(
 6905        &r#"
 6906            <body><ˇ>
 6907                <script>
 6908                    var x = 1;<ˇ
 6909                </script>
 6910            </body><ˇ>
 6911        "#
 6912        .unindent(),
 6913    );
 6914
 6915    // When backspacing, the closing angle brackets are removed.
 6916    cx.update_editor(|editor, window, cx| {
 6917        editor.backspace(&Backspace, window, cx);
 6918    });
 6919    cx.assert_editor_state(
 6920        &r#"
 6921            <body>ˇ
 6922                <script>
 6923                    var x = 1;ˇ
 6924                </script>
 6925            </body>ˇ
 6926        "#
 6927        .unindent(),
 6928    );
 6929
 6930    // Block comments autoclose in JavaScript, but not HTML.
 6931    cx.update_editor(|editor, window, cx| {
 6932        editor.handle_input("/", window, cx);
 6933        editor.handle_input("*", window, cx);
 6934    });
 6935    cx.assert_editor_state(
 6936        &r#"
 6937            <body>/*ˇ
 6938                <script>
 6939                    var x = 1;/*ˇ */
 6940                </script>
 6941            </body>/*ˇ
 6942        "#
 6943        .unindent(),
 6944    );
 6945}
 6946
 6947#[gpui::test]
 6948async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 6949    init_test(cx, |_| {});
 6950
 6951    let mut cx = EditorTestContext::new(cx).await;
 6952
 6953    let rust_language = Arc::new(
 6954        Language::new(
 6955            LanguageConfig {
 6956                name: "Rust".into(),
 6957                brackets: serde_json::from_value(json!([
 6958                    { "start": "{", "end": "}", "close": true, "newline": true },
 6959                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 6960                ]))
 6961                .unwrap(),
 6962                autoclose_before: "})]>".into(),
 6963                ..Default::default()
 6964            },
 6965            Some(tree_sitter_rust::LANGUAGE.into()),
 6966        )
 6967        .with_override_query("(string_literal) @string")
 6968        .unwrap(),
 6969    );
 6970
 6971    cx.language_registry().add(rust_language.clone());
 6972    cx.update_buffer(|buffer, cx| {
 6973        buffer.set_language(Some(rust_language), cx);
 6974    });
 6975
 6976    cx.set_state(
 6977        &r#"
 6978            let x = ˇ
 6979        "#
 6980        .unindent(),
 6981    );
 6982
 6983    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 6984    cx.update_editor(|editor, window, cx| {
 6985        editor.handle_input("\"", window, cx);
 6986    });
 6987    cx.assert_editor_state(
 6988        &r#"
 6989            let x = "ˇ"
 6990        "#
 6991        .unindent(),
 6992    );
 6993
 6994    // Inserting another quotation mark. The cursor moves across the existing
 6995    // automatically-inserted quotation mark.
 6996    cx.update_editor(|editor, window, cx| {
 6997        editor.handle_input("\"", window, cx);
 6998    });
 6999    cx.assert_editor_state(
 7000        &r#"
 7001            let x = ""ˇ
 7002        "#
 7003        .unindent(),
 7004    );
 7005
 7006    // Reset
 7007    cx.set_state(
 7008        &r#"
 7009            let x = ˇ
 7010        "#
 7011        .unindent(),
 7012    );
 7013
 7014    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7015    cx.update_editor(|editor, window, cx| {
 7016        editor.handle_input("\"", window, cx);
 7017        editor.handle_input(" ", window, cx);
 7018        editor.move_left(&Default::default(), window, cx);
 7019        editor.handle_input("\\", window, cx);
 7020        editor.handle_input("\"", window, cx);
 7021    });
 7022    cx.assert_editor_state(
 7023        &r#"
 7024            let x = "\"ˇ "
 7025        "#
 7026        .unindent(),
 7027    );
 7028
 7029    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7030    // mark. Nothing is inserted.
 7031    cx.update_editor(|editor, window, cx| {
 7032        editor.move_right(&Default::default(), window, cx);
 7033        editor.handle_input("\"", window, cx);
 7034    });
 7035    cx.assert_editor_state(
 7036        &r#"
 7037            let x = "\" "ˇ
 7038        "#
 7039        .unindent(),
 7040    );
 7041}
 7042
 7043#[gpui::test]
 7044async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7045    init_test(cx, |_| {});
 7046
 7047    let language = Arc::new(Language::new(
 7048        LanguageConfig {
 7049            brackets: BracketPairConfig {
 7050                pairs: vec![
 7051                    BracketPair {
 7052                        start: "{".to_string(),
 7053                        end: "}".to_string(),
 7054                        close: true,
 7055                        surround: true,
 7056                        newline: true,
 7057                    },
 7058                    BracketPair {
 7059                        start: "/* ".to_string(),
 7060                        end: "*/".to_string(),
 7061                        close: true,
 7062                        surround: true,
 7063                        ..Default::default()
 7064                    },
 7065                ],
 7066                ..Default::default()
 7067            },
 7068            ..Default::default()
 7069        },
 7070        Some(tree_sitter_rust::LANGUAGE.into()),
 7071    ));
 7072
 7073    let text = r#"
 7074        a
 7075        b
 7076        c
 7077    "#
 7078    .unindent();
 7079
 7080    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7081    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7082    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7083    editor
 7084        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7085        .await;
 7086
 7087    editor.update_in(cx, |editor, window, cx| {
 7088        editor.change_selections(None, window, cx, |s| {
 7089            s.select_display_ranges([
 7090                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7091                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7092                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7093            ])
 7094        });
 7095
 7096        editor.handle_input("{", window, cx);
 7097        editor.handle_input("{", window, cx);
 7098        editor.handle_input("{", window, cx);
 7099        assert_eq!(
 7100            editor.text(cx),
 7101            "
 7102                {{{a}}}
 7103                {{{b}}}
 7104                {{{c}}}
 7105            "
 7106            .unindent()
 7107        );
 7108        assert_eq!(
 7109            editor.selections.display_ranges(cx),
 7110            [
 7111                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7112                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7113                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7114            ]
 7115        );
 7116
 7117        editor.undo(&Undo, window, cx);
 7118        editor.undo(&Undo, window, cx);
 7119        editor.undo(&Undo, window, cx);
 7120        assert_eq!(
 7121            editor.text(cx),
 7122            "
 7123                a
 7124                b
 7125                c
 7126            "
 7127            .unindent()
 7128        );
 7129        assert_eq!(
 7130            editor.selections.display_ranges(cx),
 7131            [
 7132                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7133                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7134                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7135            ]
 7136        );
 7137
 7138        // Ensure inserting the first character of a multi-byte bracket pair
 7139        // doesn't surround the selections with the bracket.
 7140        editor.handle_input("/", window, cx);
 7141        assert_eq!(
 7142            editor.text(cx),
 7143            "
 7144                /
 7145                /
 7146                /
 7147            "
 7148            .unindent()
 7149        );
 7150        assert_eq!(
 7151            editor.selections.display_ranges(cx),
 7152            [
 7153                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7154                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7155                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7156            ]
 7157        );
 7158
 7159        editor.undo(&Undo, window, cx);
 7160        assert_eq!(
 7161            editor.text(cx),
 7162            "
 7163                a
 7164                b
 7165                c
 7166            "
 7167            .unindent()
 7168        );
 7169        assert_eq!(
 7170            editor.selections.display_ranges(cx),
 7171            [
 7172                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7173                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7174                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7175            ]
 7176        );
 7177
 7178        // Ensure inserting the last character of a multi-byte bracket pair
 7179        // doesn't surround the selections with the bracket.
 7180        editor.handle_input("*", window, cx);
 7181        assert_eq!(
 7182            editor.text(cx),
 7183            "
 7184                *
 7185                *
 7186                *
 7187            "
 7188            .unindent()
 7189        );
 7190        assert_eq!(
 7191            editor.selections.display_ranges(cx),
 7192            [
 7193                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7194                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7195                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7196            ]
 7197        );
 7198    });
 7199}
 7200
 7201#[gpui::test]
 7202async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7203    init_test(cx, |_| {});
 7204
 7205    let language = Arc::new(Language::new(
 7206        LanguageConfig {
 7207            brackets: BracketPairConfig {
 7208                pairs: vec![BracketPair {
 7209                    start: "{".to_string(),
 7210                    end: "}".to_string(),
 7211                    close: true,
 7212                    surround: true,
 7213                    newline: true,
 7214                }],
 7215                ..Default::default()
 7216            },
 7217            autoclose_before: "}".to_string(),
 7218            ..Default::default()
 7219        },
 7220        Some(tree_sitter_rust::LANGUAGE.into()),
 7221    ));
 7222
 7223    let text = r#"
 7224        a
 7225        b
 7226        c
 7227    "#
 7228    .unindent();
 7229
 7230    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7231    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7232    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7233    editor
 7234        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7235        .await;
 7236
 7237    editor.update_in(cx, |editor, window, cx| {
 7238        editor.change_selections(None, window, cx, |s| {
 7239            s.select_ranges([
 7240                Point::new(0, 1)..Point::new(0, 1),
 7241                Point::new(1, 1)..Point::new(1, 1),
 7242                Point::new(2, 1)..Point::new(2, 1),
 7243            ])
 7244        });
 7245
 7246        editor.handle_input("{", window, cx);
 7247        editor.handle_input("{", window, cx);
 7248        editor.handle_input("_", window, cx);
 7249        assert_eq!(
 7250            editor.text(cx),
 7251            "
 7252                a{{_}}
 7253                b{{_}}
 7254                c{{_}}
 7255            "
 7256            .unindent()
 7257        );
 7258        assert_eq!(
 7259            editor.selections.ranges::<Point>(cx),
 7260            [
 7261                Point::new(0, 4)..Point::new(0, 4),
 7262                Point::new(1, 4)..Point::new(1, 4),
 7263                Point::new(2, 4)..Point::new(2, 4)
 7264            ]
 7265        );
 7266
 7267        editor.backspace(&Default::default(), window, cx);
 7268        editor.backspace(&Default::default(), window, cx);
 7269        assert_eq!(
 7270            editor.text(cx),
 7271            "
 7272                a{}
 7273                b{}
 7274                c{}
 7275            "
 7276            .unindent()
 7277        );
 7278        assert_eq!(
 7279            editor.selections.ranges::<Point>(cx),
 7280            [
 7281                Point::new(0, 2)..Point::new(0, 2),
 7282                Point::new(1, 2)..Point::new(1, 2),
 7283                Point::new(2, 2)..Point::new(2, 2)
 7284            ]
 7285        );
 7286
 7287        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7288        assert_eq!(
 7289            editor.text(cx),
 7290            "
 7291                a
 7292                b
 7293                c
 7294            "
 7295            .unindent()
 7296        );
 7297        assert_eq!(
 7298            editor.selections.ranges::<Point>(cx),
 7299            [
 7300                Point::new(0, 1)..Point::new(0, 1),
 7301                Point::new(1, 1)..Point::new(1, 1),
 7302                Point::new(2, 1)..Point::new(2, 1)
 7303            ]
 7304        );
 7305    });
 7306}
 7307
 7308#[gpui::test]
 7309async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7310    init_test(cx, |settings| {
 7311        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7312    });
 7313
 7314    let mut cx = EditorTestContext::new(cx).await;
 7315
 7316    let language = Arc::new(Language::new(
 7317        LanguageConfig {
 7318            brackets: BracketPairConfig {
 7319                pairs: vec![
 7320                    BracketPair {
 7321                        start: "{".to_string(),
 7322                        end: "}".to_string(),
 7323                        close: true,
 7324                        surround: true,
 7325                        newline: true,
 7326                    },
 7327                    BracketPair {
 7328                        start: "(".to_string(),
 7329                        end: ")".to_string(),
 7330                        close: true,
 7331                        surround: true,
 7332                        newline: true,
 7333                    },
 7334                    BracketPair {
 7335                        start: "[".to_string(),
 7336                        end: "]".to_string(),
 7337                        close: false,
 7338                        surround: true,
 7339                        newline: true,
 7340                    },
 7341                ],
 7342                ..Default::default()
 7343            },
 7344            autoclose_before: "})]".to_string(),
 7345            ..Default::default()
 7346        },
 7347        Some(tree_sitter_rust::LANGUAGE.into()),
 7348    ));
 7349
 7350    cx.language_registry().add(language.clone());
 7351    cx.update_buffer(|buffer, cx| {
 7352        buffer.set_language(Some(language), cx);
 7353    });
 7354
 7355    cx.set_state(
 7356        &"
 7357            {(ˇ)}
 7358            [[ˇ]]
 7359            {(ˇ)}
 7360        "
 7361        .unindent(),
 7362    );
 7363
 7364    cx.update_editor(|editor, window, cx| {
 7365        editor.backspace(&Default::default(), window, cx);
 7366        editor.backspace(&Default::default(), window, cx);
 7367    });
 7368
 7369    cx.assert_editor_state(
 7370        &"
 7371            ˇ
 7372            ˇ]]
 7373            ˇ
 7374        "
 7375        .unindent(),
 7376    );
 7377
 7378    cx.update_editor(|editor, window, cx| {
 7379        editor.handle_input("{", window, cx);
 7380        editor.handle_input("{", window, cx);
 7381        editor.move_right(&MoveRight, window, cx);
 7382        editor.move_right(&MoveRight, window, cx);
 7383        editor.move_left(&MoveLeft, window, cx);
 7384        editor.move_left(&MoveLeft, window, cx);
 7385        editor.backspace(&Default::default(), window, cx);
 7386    });
 7387
 7388    cx.assert_editor_state(
 7389        &"
 7390            {ˇ}
 7391            {ˇ}]]
 7392            {ˇ}
 7393        "
 7394        .unindent(),
 7395    );
 7396
 7397    cx.update_editor(|editor, window, cx| {
 7398        editor.backspace(&Default::default(), window, cx);
 7399    });
 7400
 7401    cx.assert_editor_state(
 7402        &"
 7403            ˇ
 7404            ˇ]]
 7405            ˇ
 7406        "
 7407        .unindent(),
 7408    );
 7409}
 7410
 7411#[gpui::test]
 7412async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7413    init_test(cx, |_| {});
 7414
 7415    let language = Arc::new(Language::new(
 7416        LanguageConfig::default(),
 7417        Some(tree_sitter_rust::LANGUAGE.into()),
 7418    ));
 7419
 7420    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7421    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7422    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7423    editor
 7424        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7425        .await;
 7426
 7427    editor.update_in(cx, |editor, window, cx| {
 7428        editor.set_auto_replace_emoji_shortcode(true);
 7429
 7430        editor.handle_input("Hello ", window, cx);
 7431        editor.handle_input(":wave", window, cx);
 7432        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7433
 7434        editor.handle_input(":", window, cx);
 7435        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7436
 7437        editor.handle_input(" :smile", window, cx);
 7438        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7439
 7440        editor.handle_input(":", window, cx);
 7441        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7442
 7443        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7444        editor.handle_input(":wave", window, cx);
 7445        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7446
 7447        editor.handle_input(":", window, cx);
 7448        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7449
 7450        editor.handle_input(":1", window, cx);
 7451        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7452
 7453        editor.handle_input(":", window, cx);
 7454        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7455
 7456        // Ensure shortcode does not get replaced when it is part of a word
 7457        editor.handle_input(" Test:wave", window, cx);
 7458        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7459
 7460        editor.handle_input(":", window, cx);
 7461        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7462
 7463        editor.set_auto_replace_emoji_shortcode(false);
 7464
 7465        // Ensure shortcode does not get replaced when auto replace is off
 7466        editor.handle_input(" :wave", window, cx);
 7467        assert_eq!(
 7468            editor.text(cx),
 7469            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7470        );
 7471
 7472        editor.handle_input(":", window, cx);
 7473        assert_eq!(
 7474            editor.text(cx),
 7475            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7476        );
 7477    });
 7478}
 7479
 7480#[gpui::test]
 7481async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7482    init_test(cx, |_| {});
 7483
 7484    let (text, insertion_ranges) = marked_text_ranges(
 7485        indoc! {"
 7486            ˇ
 7487        "},
 7488        false,
 7489    );
 7490
 7491    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7492    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7493
 7494    _ = editor.update_in(cx, |editor, window, cx| {
 7495        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7496
 7497        editor
 7498            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7499            .unwrap();
 7500
 7501        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7502            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7503            assert_eq!(editor.text(cx), expected_text);
 7504            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7505        }
 7506
 7507        assert(
 7508            editor,
 7509            cx,
 7510            indoc! {"
 7511            type «» =•
 7512            "},
 7513        );
 7514
 7515        assert!(editor.context_menu_visible(), "There should be a matches");
 7516    });
 7517}
 7518
 7519#[gpui::test]
 7520async fn test_snippets(cx: &mut TestAppContext) {
 7521    init_test(cx, |_| {});
 7522
 7523    let (text, insertion_ranges) = marked_text_ranges(
 7524        indoc! {"
 7525            a.ˇ b
 7526            a.ˇ b
 7527            a.ˇ b
 7528        "},
 7529        false,
 7530    );
 7531
 7532    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7533    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7534
 7535    editor.update_in(cx, |editor, window, cx| {
 7536        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7537
 7538        editor
 7539            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7540            .unwrap();
 7541
 7542        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7543            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7544            assert_eq!(editor.text(cx), expected_text);
 7545            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7546        }
 7547
 7548        assert(
 7549            editor,
 7550            cx,
 7551            indoc! {"
 7552                a.f(«one», two, «three») b
 7553                a.f(«one», two, «three») b
 7554                a.f(«one», two, «three») b
 7555            "},
 7556        );
 7557
 7558        // Can't move earlier than the first tab stop
 7559        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7560        assert(
 7561            editor,
 7562            cx,
 7563            indoc! {"
 7564                a.f(«one», two, «three») b
 7565                a.f(«one», two, «three») b
 7566                a.f(«one», two, «three») b
 7567            "},
 7568        );
 7569
 7570        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7571        assert(
 7572            editor,
 7573            cx,
 7574            indoc! {"
 7575                a.f(one, «two», three) b
 7576                a.f(one, «two», three) b
 7577                a.f(one, «two», three) b
 7578            "},
 7579        );
 7580
 7581        editor.move_to_prev_snippet_tabstop(window, cx);
 7582        assert(
 7583            editor,
 7584            cx,
 7585            indoc! {"
 7586                a.f(«one», two, «three») b
 7587                a.f(«one», two, «three») b
 7588                a.f(«one», two, «three») b
 7589            "},
 7590        );
 7591
 7592        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7593        assert(
 7594            editor,
 7595            cx,
 7596            indoc! {"
 7597                a.f(one, «two», three) b
 7598                a.f(one, «two», three) b
 7599                a.f(one, «two», three) b
 7600            "},
 7601        );
 7602        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7603        assert(
 7604            editor,
 7605            cx,
 7606            indoc! {"
 7607                a.f(one, two, three)ˇ b
 7608                a.f(one, two, three)ˇ b
 7609                a.f(one, two, three)ˇ b
 7610            "},
 7611        );
 7612
 7613        // As soon as the last tab stop is reached, snippet state is gone
 7614        editor.move_to_prev_snippet_tabstop(window, cx);
 7615        assert(
 7616            editor,
 7617            cx,
 7618            indoc! {"
 7619                a.f(one, two, three)ˇ b
 7620                a.f(one, two, three)ˇ b
 7621                a.f(one, two, three)ˇ b
 7622            "},
 7623        );
 7624    });
 7625}
 7626
 7627#[gpui::test]
 7628async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7629    init_test(cx, |_| {});
 7630
 7631    let fs = FakeFs::new(cx.executor());
 7632    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7633
 7634    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7635
 7636    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7637    language_registry.add(rust_lang());
 7638    let mut fake_servers = language_registry.register_fake_lsp(
 7639        "Rust",
 7640        FakeLspAdapter {
 7641            capabilities: lsp::ServerCapabilities {
 7642                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7643                ..Default::default()
 7644            },
 7645            ..Default::default()
 7646        },
 7647    );
 7648
 7649    let buffer = project
 7650        .update(cx, |project, cx| {
 7651            project.open_local_buffer(path!("/file.rs"), cx)
 7652        })
 7653        .await
 7654        .unwrap();
 7655
 7656    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7657    let (editor, cx) = cx.add_window_view(|window, cx| {
 7658        build_editor_with_project(project.clone(), buffer, window, cx)
 7659    });
 7660    editor.update_in(cx, |editor, window, cx| {
 7661        editor.set_text("one\ntwo\nthree\n", window, cx)
 7662    });
 7663    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7664
 7665    cx.executor().start_waiting();
 7666    let fake_server = fake_servers.next().await.unwrap();
 7667
 7668    let save = editor
 7669        .update_in(cx, |editor, window, cx| {
 7670            editor.save(true, project.clone(), window, cx)
 7671        })
 7672        .unwrap();
 7673    fake_server
 7674        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7675            assert_eq!(
 7676                params.text_document.uri,
 7677                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7678            );
 7679            assert_eq!(params.options.tab_size, 4);
 7680            Ok(Some(vec![lsp::TextEdit::new(
 7681                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7682                ", ".to_string(),
 7683            )]))
 7684        })
 7685        .next()
 7686        .await;
 7687    cx.executor().start_waiting();
 7688    save.await;
 7689
 7690    assert_eq!(
 7691        editor.update(cx, |editor, cx| editor.text(cx)),
 7692        "one, two\nthree\n"
 7693    );
 7694    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7695
 7696    editor.update_in(cx, |editor, window, cx| {
 7697        editor.set_text("one\ntwo\nthree\n", window, cx)
 7698    });
 7699    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7700
 7701    // Ensure we can still save even if formatting hangs.
 7702    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7703        move |params, _| async move {
 7704            assert_eq!(
 7705                params.text_document.uri,
 7706                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7707            );
 7708            futures::future::pending::<()>().await;
 7709            unreachable!()
 7710        },
 7711    );
 7712    let save = editor
 7713        .update_in(cx, |editor, window, cx| {
 7714            editor.save(true, project.clone(), window, cx)
 7715        })
 7716        .unwrap();
 7717    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7718    cx.executor().start_waiting();
 7719    save.await;
 7720    assert_eq!(
 7721        editor.update(cx, |editor, cx| editor.text(cx)),
 7722        "one\ntwo\nthree\n"
 7723    );
 7724    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7725
 7726    // For non-dirty buffer, no formatting request should be sent
 7727    let save = editor
 7728        .update_in(cx, |editor, window, cx| {
 7729            editor.save(true, project.clone(), window, cx)
 7730        })
 7731        .unwrap();
 7732    let _pending_format_request = fake_server
 7733        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 7734            panic!("Should not be invoked on non-dirty buffer");
 7735        })
 7736        .next();
 7737    cx.executor().start_waiting();
 7738    save.await;
 7739
 7740    // Set rust language override and assert overridden tabsize is sent to language server
 7741    update_test_language_settings(cx, |settings| {
 7742        settings.languages.insert(
 7743            "Rust".into(),
 7744            LanguageSettingsContent {
 7745                tab_size: NonZeroU32::new(8),
 7746                ..Default::default()
 7747            },
 7748        );
 7749    });
 7750
 7751    editor.update_in(cx, |editor, window, cx| {
 7752        editor.set_text("somehting_new\n", window, cx)
 7753    });
 7754    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7755    let save = editor
 7756        .update_in(cx, |editor, window, cx| {
 7757            editor.save(true, project.clone(), window, cx)
 7758        })
 7759        .unwrap();
 7760    fake_server
 7761        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7762            assert_eq!(
 7763                params.text_document.uri,
 7764                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7765            );
 7766            assert_eq!(params.options.tab_size, 8);
 7767            Ok(Some(vec![]))
 7768        })
 7769        .next()
 7770        .await;
 7771    cx.executor().start_waiting();
 7772    save.await;
 7773}
 7774
 7775#[gpui::test]
 7776async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7777    init_test(cx, |_| {});
 7778
 7779    let cols = 4;
 7780    let rows = 10;
 7781    let sample_text_1 = sample_text(rows, cols, 'a');
 7782    assert_eq!(
 7783        sample_text_1,
 7784        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7785    );
 7786    let sample_text_2 = sample_text(rows, cols, 'l');
 7787    assert_eq!(
 7788        sample_text_2,
 7789        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7790    );
 7791    let sample_text_3 = sample_text(rows, cols, 'v');
 7792    assert_eq!(
 7793        sample_text_3,
 7794        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7795    );
 7796
 7797    let fs = FakeFs::new(cx.executor());
 7798    fs.insert_tree(
 7799        path!("/a"),
 7800        json!({
 7801            "main.rs": sample_text_1,
 7802            "other.rs": sample_text_2,
 7803            "lib.rs": sample_text_3,
 7804        }),
 7805    )
 7806    .await;
 7807
 7808    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7809    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7810    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7811
 7812    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7813    language_registry.add(rust_lang());
 7814    let mut fake_servers = language_registry.register_fake_lsp(
 7815        "Rust",
 7816        FakeLspAdapter {
 7817            capabilities: lsp::ServerCapabilities {
 7818                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7819                ..Default::default()
 7820            },
 7821            ..Default::default()
 7822        },
 7823    );
 7824
 7825    let worktree = project.update(cx, |project, cx| {
 7826        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7827        assert_eq!(worktrees.len(), 1);
 7828        worktrees.pop().unwrap()
 7829    });
 7830    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 7831
 7832    let buffer_1 = project
 7833        .update(cx, |project, cx| {
 7834            project.open_buffer((worktree_id, "main.rs"), cx)
 7835        })
 7836        .await
 7837        .unwrap();
 7838    let buffer_2 = project
 7839        .update(cx, |project, cx| {
 7840            project.open_buffer((worktree_id, "other.rs"), cx)
 7841        })
 7842        .await
 7843        .unwrap();
 7844    let buffer_3 = project
 7845        .update(cx, |project, cx| {
 7846            project.open_buffer((worktree_id, "lib.rs"), cx)
 7847        })
 7848        .await
 7849        .unwrap();
 7850
 7851    let multi_buffer = cx.new(|cx| {
 7852        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 7853        multi_buffer.push_excerpts(
 7854            buffer_1.clone(),
 7855            [
 7856                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7857                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7858                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7859            ],
 7860            cx,
 7861        );
 7862        multi_buffer.push_excerpts(
 7863            buffer_2.clone(),
 7864            [
 7865                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7866                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7867                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7868            ],
 7869            cx,
 7870        );
 7871        multi_buffer.push_excerpts(
 7872            buffer_3.clone(),
 7873            [
 7874                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 7875                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 7876                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 7877            ],
 7878            cx,
 7879        );
 7880        multi_buffer
 7881    });
 7882    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 7883        Editor::new(
 7884            EditorMode::Full,
 7885            multi_buffer,
 7886            Some(project.clone()),
 7887            window,
 7888            cx,
 7889        )
 7890    });
 7891
 7892    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7893        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7894            s.select_ranges(Some(1..2))
 7895        });
 7896        editor.insert("|one|two|three|", window, cx);
 7897    });
 7898    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7899    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 7900        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 7901            s.select_ranges(Some(60..70))
 7902        });
 7903        editor.insert("|four|five|six|", window, cx);
 7904    });
 7905    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 7906
 7907    // First two buffers should be edited, but not the third one.
 7908    assert_eq!(
 7909        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7910        "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}",
 7911    );
 7912    buffer_1.update(cx, |buffer, _| {
 7913        assert!(buffer.is_dirty());
 7914        assert_eq!(
 7915            buffer.text(),
 7916            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 7917        )
 7918    });
 7919    buffer_2.update(cx, |buffer, _| {
 7920        assert!(buffer.is_dirty());
 7921        assert_eq!(
 7922            buffer.text(),
 7923            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 7924        )
 7925    });
 7926    buffer_3.update(cx, |buffer, _| {
 7927        assert!(!buffer.is_dirty());
 7928        assert_eq!(buffer.text(), sample_text_3,)
 7929    });
 7930    cx.executor().run_until_parked();
 7931
 7932    cx.executor().start_waiting();
 7933    let save = multi_buffer_editor
 7934        .update_in(cx, |editor, window, cx| {
 7935            editor.save(true, project.clone(), window, cx)
 7936        })
 7937        .unwrap();
 7938
 7939    let fake_server = fake_servers.next().await.unwrap();
 7940    fake_server
 7941        .server
 7942        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7943            Ok(Some(vec![lsp::TextEdit::new(
 7944                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7945                format!("[{} formatted]", params.text_document.uri),
 7946            )]))
 7947        })
 7948        .detach();
 7949    save.await;
 7950
 7951    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 7952    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 7953    assert_eq!(
 7954        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 7955        uri!(
 7956            "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}"
 7957        ),
 7958    );
 7959    buffer_1.update(cx, |buffer, _| {
 7960        assert!(!buffer.is_dirty());
 7961        assert_eq!(
 7962            buffer.text(),
 7963            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 7964        )
 7965    });
 7966    buffer_2.update(cx, |buffer, _| {
 7967        assert!(!buffer.is_dirty());
 7968        assert_eq!(
 7969            buffer.text(),
 7970            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 7971        )
 7972    });
 7973    buffer_3.update(cx, |buffer, _| {
 7974        assert!(!buffer.is_dirty());
 7975        assert_eq!(buffer.text(), sample_text_3,)
 7976    });
 7977}
 7978
 7979#[gpui::test]
 7980async fn test_range_format_during_save(cx: &mut TestAppContext) {
 7981    init_test(cx, |_| {});
 7982
 7983    let fs = FakeFs::new(cx.executor());
 7984    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7985
 7986    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 7987
 7988    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7989    language_registry.add(rust_lang());
 7990    let mut fake_servers = language_registry.register_fake_lsp(
 7991        "Rust",
 7992        FakeLspAdapter {
 7993            capabilities: lsp::ServerCapabilities {
 7994                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 7995                ..Default::default()
 7996            },
 7997            ..Default::default()
 7998        },
 7999    );
 8000
 8001    let buffer = project
 8002        .update(cx, |project, cx| {
 8003            project.open_local_buffer(path!("/file.rs"), cx)
 8004        })
 8005        .await
 8006        .unwrap();
 8007
 8008    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8009    let (editor, cx) = cx.add_window_view(|window, cx| {
 8010        build_editor_with_project(project.clone(), buffer, window, cx)
 8011    });
 8012    editor.update_in(cx, |editor, window, cx| {
 8013        editor.set_text("one\ntwo\nthree\n", window, cx)
 8014    });
 8015    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8016
 8017    cx.executor().start_waiting();
 8018    let fake_server = fake_servers.next().await.unwrap();
 8019
 8020    let save = editor
 8021        .update_in(cx, |editor, window, cx| {
 8022            editor.save(true, project.clone(), window, cx)
 8023        })
 8024        .unwrap();
 8025    fake_server
 8026        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8027            assert_eq!(
 8028                params.text_document.uri,
 8029                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8030            );
 8031            assert_eq!(params.options.tab_size, 4);
 8032            Ok(Some(vec![lsp::TextEdit::new(
 8033                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8034                ", ".to_string(),
 8035            )]))
 8036        })
 8037        .next()
 8038        .await;
 8039    cx.executor().start_waiting();
 8040    save.await;
 8041    assert_eq!(
 8042        editor.update(cx, |editor, cx| editor.text(cx)),
 8043        "one, two\nthree\n"
 8044    );
 8045    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8046
 8047    editor.update_in(cx, |editor, window, cx| {
 8048        editor.set_text("one\ntwo\nthree\n", window, cx)
 8049    });
 8050    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8051
 8052    // Ensure we can still save even if formatting hangs.
 8053    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8054        move |params, _| async move {
 8055            assert_eq!(
 8056                params.text_document.uri,
 8057                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8058            );
 8059            futures::future::pending::<()>().await;
 8060            unreachable!()
 8061        },
 8062    );
 8063    let save = editor
 8064        .update_in(cx, |editor, window, cx| {
 8065            editor.save(true, project.clone(), window, cx)
 8066        })
 8067        .unwrap();
 8068    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8069    cx.executor().start_waiting();
 8070    save.await;
 8071    assert_eq!(
 8072        editor.update(cx, |editor, cx| editor.text(cx)),
 8073        "one\ntwo\nthree\n"
 8074    );
 8075    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8076
 8077    // For non-dirty buffer, no formatting request should be sent
 8078    let save = editor
 8079        .update_in(cx, |editor, window, cx| {
 8080            editor.save(true, project.clone(), window, cx)
 8081        })
 8082        .unwrap();
 8083    let _pending_format_request = fake_server
 8084        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8085            panic!("Should not be invoked on non-dirty buffer");
 8086        })
 8087        .next();
 8088    cx.executor().start_waiting();
 8089    save.await;
 8090
 8091    // Set Rust language override and assert overridden tabsize is sent to language server
 8092    update_test_language_settings(cx, |settings| {
 8093        settings.languages.insert(
 8094            "Rust".into(),
 8095            LanguageSettingsContent {
 8096                tab_size: NonZeroU32::new(8),
 8097                ..Default::default()
 8098            },
 8099        );
 8100    });
 8101
 8102    editor.update_in(cx, |editor, window, cx| {
 8103        editor.set_text("somehting_new\n", window, cx)
 8104    });
 8105    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8106    let save = editor
 8107        .update_in(cx, |editor, window, cx| {
 8108            editor.save(true, project.clone(), window, cx)
 8109        })
 8110        .unwrap();
 8111    fake_server
 8112        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8113            assert_eq!(
 8114                params.text_document.uri,
 8115                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8116            );
 8117            assert_eq!(params.options.tab_size, 8);
 8118            Ok(Some(vec![]))
 8119        })
 8120        .next()
 8121        .await;
 8122    cx.executor().start_waiting();
 8123    save.await;
 8124}
 8125
 8126#[gpui::test]
 8127async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8128    init_test(cx, |settings| {
 8129        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8130            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8131        ))
 8132    });
 8133
 8134    let fs = FakeFs::new(cx.executor());
 8135    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8136
 8137    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8138
 8139    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8140    language_registry.add(Arc::new(Language::new(
 8141        LanguageConfig {
 8142            name: "Rust".into(),
 8143            matcher: LanguageMatcher {
 8144                path_suffixes: vec!["rs".to_string()],
 8145                ..Default::default()
 8146            },
 8147            ..LanguageConfig::default()
 8148        },
 8149        Some(tree_sitter_rust::LANGUAGE.into()),
 8150    )));
 8151    update_test_language_settings(cx, |settings| {
 8152        // Enable Prettier formatting for the same buffer, and ensure
 8153        // LSP is called instead of Prettier.
 8154        settings.defaults.prettier = Some(PrettierSettings {
 8155            allowed: true,
 8156            ..PrettierSettings::default()
 8157        });
 8158    });
 8159    let mut fake_servers = language_registry.register_fake_lsp(
 8160        "Rust",
 8161        FakeLspAdapter {
 8162            capabilities: lsp::ServerCapabilities {
 8163                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8164                ..Default::default()
 8165            },
 8166            ..Default::default()
 8167        },
 8168    );
 8169
 8170    let buffer = project
 8171        .update(cx, |project, cx| {
 8172            project.open_local_buffer(path!("/file.rs"), cx)
 8173        })
 8174        .await
 8175        .unwrap();
 8176
 8177    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8178    let (editor, cx) = cx.add_window_view(|window, cx| {
 8179        build_editor_with_project(project.clone(), buffer, window, cx)
 8180    });
 8181    editor.update_in(cx, |editor, window, cx| {
 8182        editor.set_text("one\ntwo\nthree\n", window, cx)
 8183    });
 8184
 8185    cx.executor().start_waiting();
 8186    let fake_server = fake_servers.next().await.unwrap();
 8187
 8188    let format = editor
 8189        .update_in(cx, |editor, window, cx| {
 8190            editor.perform_format(
 8191                project.clone(),
 8192                FormatTrigger::Manual,
 8193                FormatTarget::Buffers,
 8194                window,
 8195                cx,
 8196            )
 8197        })
 8198        .unwrap();
 8199    fake_server
 8200        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8201            assert_eq!(
 8202                params.text_document.uri,
 8203                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8204            );
 8205            assert_eq!(params.options.tab_size, 4);
 8206            Ok(Some(vec![lsp::TextEdit::new(
 8207                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8208                ", ".to_string(),
 8209            )]))
 8210        })
 8211        .next()
 8212        .await;
 8213    cx.executor().start_waiting();
 8214    format.await;
 8215    assert_eq!(
 8216        editor.update(cx, |editor, cx| editor.text(cx)),
 8217        "one, two\nthree\n"
 8218    );
 8219
 8220    editor.update_in(cx, |editor, window, cx| {
 8221        editor.set_text("one\ntwo\nthree\n", window, cx)
 8222    });
 8223    // Ensure we don't lock if formatting hangs.
 8224    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8225        move |params, _| async move {
 8226            assert_eq!(
 8227                params.text_document.uri,
 8228                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8229            );
 8230            futures::future::pending::<()>().await;
 8231            unreachable!()
 8232        },
 8233    );
 8234    let format = editor
 8235        .update_in(cx, |editor, window, cx| {
 8236            editor.perform_format(
 8237                project,
 8238                FormatTrigger::Manual,
 8239                FormatTarget::Buffers,
 8240                window,
 8241                cx,
 8242            )
 8243        })
 8244        .unwrap();
 8245    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8246    cx.executor().start_waiting();
 8247    format.await;
 8248    assert_eq!(
 8249        editor.update(cx, |editor, cx| editor.text(cx)),
 8250        "one\ntwo\nthree\n"
 8251    );
 8252}
 8253
 8254#[gpui::test]
 8255async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8256    init_test(cx, |settings| {
 8257        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8258            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8259        ))
 8260    });
 8261
 8262    let fs = FakeFs::new(cx.executor());
 8263    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8264
 8265    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8266
 8267    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8268    language_registry.add(Arc::new(Language::new(
 8269        LanguageConfig {
 8270            name: "TypeScript".into(),
 8271            matcher: LanguageMatcher {
 8272                path_suffixes: vec!["ts".to_string()],
 8273                ..Default::default()
 8274            },
 8275            ..LanguageConfig::default()
 8276        },
 8277        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8278    )));
 8279    update_test_language_settings(cx, |settings| {
 8280        settings.defaults.prettier = Some(PrettierSettings {
 8281            allowed: true,
 8282            ..PrettierSettings::default()
 8283        });
 8284    });
 8285    let mut fake_servers = language_registry.register_fake_lsp(
 8286        "TypeScript",
 8287        FakeLspAdapter {
 8288            capabilities: lsp::ServerCapabilities {
 8289                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8290                ..Default::default()
 8291            },
 8292            ..Default::default()
 8293        },
 8294    );
 8295
 8296    let buffer = project
 8297        .update(cx, |project, cx| {
 8298            project.open_local_buffer(path!("/file.ts"), cx)
 8299        })
 8300        .await
 8301        .unwrap();
 8302
 8303    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8304    let (editor, cx) = cx.add_window_view(|window, cx| {
 8305        build_editor_with_project(project.clone(), buffer, window, cx)
 8306    });
 8307    editor.update_in(cx, |editor, window, cx| {
 8308        editor.set_text(
 8309            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8310            window,
 8311            cx,
 8312        )
 8313    });
 8314
 8315    cx.executor().start_waiting();
 8316    let fake_server = fake_servers.next().await.unwrap();
 8317
 8318    let format = editor
 8319        .update_in(cx, |editor, window, cx| {
 8320            editor.perform_code_action_kind(
 8321                project.clone(),
 8322                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8323                window,
 8324                cx,
 8325            )
 8326        })
 8327        .unwrap();
 8328    fake_server
 8329        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8330            assert_eq!(
 8331                params.text_document.uri,
 8332                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8333            );
 8334            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8335                lsp::CodeAction {
 8336                    title: "Organize Imports".to_string(),
 8337                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8338                    edit: Some(lsp::WorkspaceEdit {
 8339                        changes: Some(
 8340                            [(
 8341                                params.text_document.uri.clone(),
 8342                                vec![lsp::TextEdit::new(
 8343                                    lsp::Range::new(
 8344                                        lsp::Position::new(1, 0),
 8345                                        lsp::Position::new(2, 0),
 8346                                    ),
 8347                                    "".to_string(),
 8348                                )],
 8349                            )]
 8350                            .into_iter()
 8351                            .collect(),
 8352                        ),
 8353                        ..Default::default()
 8354                    }),
 8355                    ..Default::default()
 8356                },
 8357            )]))
 8358        })
 8359        .next()
 8360        .await;
 8361    cx.executor().start_waiting();
 8362    format.await;
 8363    assert_eq!(
 8364        editor.update(cx, |editor, cx| editor.text(cx)),
 8365        "import { a } from 'module';\n\nconst x = a;\n"
 8366    );
 8367
 8368    editor.update_in(cx, |editor, window, cx| {
 8369        editor.set_text(
 8370            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8371            window,
 8372            cx,
 8373        )
 8374    });
 8375    // Ensure we don't lock if code action hangs.
 8376    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8377        move |params, _| async move {
 8378            assert_eq!(
 8379                params.text_document.uri,
 8380                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8381            );
 8382            futures::future::pending::<()>().await;
 8383            unreachable!()
 8384        },
 8385    );
 8386    let format = editor
 8387        .update_in(cx, |editor, window, cx| {
 8388            editor.perform_code_action_kind(
 8389                project,
 8390                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8391                window,
 8392                cx,
 8393            )
 8394        })
 8395        .unwrap();
 8396    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8397    cx.executor().start_waiting();
 8398    format.await;
 8399    assert_eq!(
 8400        editor.update(cx, |editor, cx| editor.text(cx)),
 8401        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8402    );
 8403}
 8404
 8405#[gpui::test]
 8406async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8407    init_test(cx, |_| {});
 8408
 8409    let mut cx = EditorLspTestContext::new_rust(
 8410        lsp::ServerCapabilities {
 8411            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8412            ..Default::default()
 8413        },
 8414        cx,
 8415    )
 8416    .await;
 8417
 8418    cx.set_state(indoc! {"
 8419        one.twoˇ
 8420    "});
 8421
 8422    // The format request takes a long time. When it completes, it inserts
 8423    // a newline and an indent before the `.`
 8424    cx.lsp
 8425        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8426            let executor = cx.background_executor().clone();
 8427            async move {
 8428                executor.timer(Duration::from_millis(100)).await;
 8429                Ok(Some(vec![lsp::TextEdit {
 8430                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8431                    new_text: "\n    ".into(),
 8432                }]))
 8433            }
 8434        });
 8435
 8436    // Submit a format request.
 8437    let format_1 = cx
 8438        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8439        .unwrap();
 8440    cx.executor().run_until_parked();
 8441
 8442    // Submit a second format request.
 8443    let format_2 = cx
 8444        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8445        .unwrap();
 8446    cx.executor().run_until_parked();
 8447
 8448    // Wait for both format requests to complete
 8449    cx.executor().advance_clock(Duration::from_millis(200));
 8450    cx.executor().start_waiting();
 8451    format_1.await.unwrap();
 8452    cx.executor().start_waiting();
 8453    format_2.await.unwrap();
 8454
 8455    // The formatting edits only happens once.
 8456    cx.assert_editor_state(indoc! {"
 8457        one
 8458            .twoˇ
 8459    "});
 8460}
 8461
 8462#[gpui::test]
 8463async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8464    init_test(cx, |settings| {
 8465        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8466    });
 8467
 8468    let mut cx = EditorLspTestContext::new_rust(
 8469        lsp::ServerCapabilities {
 8470            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8471            ..Default::default()
 8472        },
 8473        cx,
 8474    )
 8475    .await;
 8476
 8477    // Set up a buffer white some trailing whitespace and no trailing newline.
 8478    cx.set_state(
 8479        &[
 8480            "one ",   //
 8481            "twoˇ",   //
 8482            "three ", //
 8483            "four",   //
 8484        ]
 8485        .join("\n"),
 8486    );
 8487
 8488    // Submit a format request.
 8489    let format = cx
 8490        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8491        .unwrap();
 8492
 8493    // Record which buffer changes have been sent to the language server
 8494    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8495    cx.lsp
 8496        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8497            let buffer_changes = buffer_changes.clone();
 8498            move |params, _| {
 8499                buffer_changes.lock().extend(
 8500                    params
 8501                        .content_changes
 8502                        .into_iter()
 8503                        .map(|e| (e.range.unwrap(), e.text)),
 8504                );
 8505            }
 8506        });
 8507
 8508    // Handle formatting requests to the language server.
 8509    cx.lsp
 8510        .set_request_handler::<lsp::request::Formatting, _, _>({
 8511            let buffer_changes = buffer_changes.clone();
 8512            move |_, _| {
 8513                // When formatting is requested, trailing whitespace has already been stripped,
 8514                // and the trailing newline has already been added.
 8515                assert_eq!(
 8516                    &buffer_changes.lock()[1..],
 8517                    &[
 8518                        (
 8519                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8520                            "".into()
 8521                        ),
 8522                        (
 8523                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8524                            "".into()
 8525                        ),
 8526                        (
 8527                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8528                            "\n".into()
 8529                        ),
 8530                    ]
 8531                );
 8532
 8533                // Insert blank lines between each line of the buffer.
 8534                async move {
 8535                    Ok(Some(vec![
 8536                        lsp::TextEdit {
 8537                            range: lsp::Range::new(
 8538                                lsp::Position::new(1, 0),
 8539                                lsp::Position::new(1, 0),
 8540                            ),
 8541                            new_text: "\n".into(),
 8542                        },
 8543                        lsp::TextEdit {
 8544                            range: lsp::Range::new(
 8545                                lsp::Position::new(2, 0),
 8546                                lsp::Position::new(2, 0),
 8547                            ),
 8548                            new_text: "\n".into(),
 8549                        },
 8550                    ]))
 8551                }
 8552            }
 8553        });
 8554
 8555    // After formatting the buffer, the trailing whitespace is stripped,
 8556    // a newline is appended, and the edits provided by the language server
 8557    // have been applied.
 8558    format.await.unwrap();
 8559    cx.assert_editor_state(
 8560        &[
 8561            "one",   //
 8562            "",      //
 8563            "twoˇ",  //
 8564            "",      //
 8565            "three", //
 8566            "four",  //
 8567            "",      //
 8568        ]
 8569        .join("\n"),
 8570    );
 8571
 8572    // Undoing the formatting undoes the trailing whitespace removal, the
 8573    // trailing newline, and the LSP edits.
 8574    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 8575    cx.assert_editor_state(
 8576        &[
 8577            "one ",   //
 8578            "twoˇ",   //
 8579            "three ", //
 8580            "four",   //
 8581        ]
 8582        .join("\n"),
 8583    );
 8584}
 8585
 8586#[gpui::test]
 8587async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 8588    cx: &mut TestAppContext,
 8589) {
 8590    init_test(cx, |_| {});
 8591
 8592    cx.update(|cx| {
 8593        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8594            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8595                settings.auto_signature_help = Some(true);
 8596            });
 8597        });
 8598    });
 8599
 8600    let mut cx = EditorLspTestContext::new_rust(
 8601        lsp::ServerCapabilities {
 8602            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8603                ..Default::default()
 8604            }),
 8605            ..Default::default()
 8606        },
 8607        cx,
 8608    )
 8609    .await;
 8610
 8611    let language = Language::new(
 8612        LanguageConfig {
 8613            name: "Rust".into(),
 8614            brackets: BracketPairConfig {
 8615                pairs: vec![
 8616                    BracketPair {
 8617                        start: "{".to_string(),
 8618                        end: "}".to_string(),
 8619                        close: true,
 8620                        surround: true,
 8621                        newline: true,
 8622                    },
 8623                    BracketPair {
 8624                        start: "(".to_string(),
 8625                        end: ")".to_string(),
 8626                        close: true,
 8627                        surround: true,
 8628                        newline: true,
 8629                    },
 8630                    BracketPair {
 8631                        start: "/*".to_string(),
 8632                        end: " */".to_string(),
 8633                        close: true,
 8634                        surround: true,
 8635                        newline: true,
 8636                    },
 8637                    BracketPair {
 8638                        start: "[".to_string(),
 8639                        end: "]".to_string(),
 8640                        close: false,
 8641                        surround: false,
 8642                        newline: true,
 8643                    },
 8644                    BracketPair {
 8645                        start: "\"".to_string(),
 8646                        end: "\"".to_string(),
 8647                        close: true,
 8648                        surround: true,
 8649                        newline: false,
 8650                    },
 8651                    BracketPair {
 8652                        start: "<".to_string(),
 8653                        end: ">".to_string(),
 8654                        close: false,
 8655                        surround: true,
 8656                        newline: true,
 8657                    },
 8658                ],
 8659                ..Default::default()
 8660            },
 8661            autoclose_before: "})]".to_string(),
 8662            ..Default::default()
 8663        },
 8664        Some(tree_sitter_rust::LANGUAGE.into()),
 8665    );
 8666    let language = Arc::new(language);
 8667
 8668    cx.language_registry().add(language.clone());
 8669    cx.update_buffer(|buffer, cx| {
 8670        buffer.set_language(Some(language), cx);
 8671    });
 8672
 8673    cx.set_state(
 8674        &r#"
 8675            fn main() {
 8676                sampleˇ
 8677            }
 8678        "#
 8679        .unindent(),
 8680    );
 8681
 8682    cx.update_editor(|editor, window, cx| {
 8683        editor.handle_input("(", window, cx);
 8684    });
 8685    cx.assert_editor_state(
 8686        &"
 8687            fn main() {
 8688                sample(ˇ)
 8689            }
 8690        "
 8691        .unindent(),
 8692    );
 8693
 8694    let mocked_response = lsp::SignatureHelp {
 8695        signatures: vec![lsp::SignatureInformation {
 8696            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8697            documentation: None,
 8698            parameters: Some(vec![
 8699                lsp::ParameterInformation {
 8700                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8701                    documentation: None,
 8702                },
 8703                lsp::ParameterInformation {
 8704                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8705                    documentation: None,
 8706                },
 8707            ]),
 8708            active_parameter: None,
 8709        }],
 8710        active_signature: Some(0),
 8711        active_parameter: Some(0),
 8712    };
 8713    handle_signature_help_request(&mut cx, mocked_response).await;
 8714
 8715    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8716        .await;
 8717
 8718    cx.editor(|editor, _, _| {
 8719        let signature_help_state = editor.signature_help_state.popover().cloned();
 8720        assert_eq!(
 8721            signature_help_state.unwrap().label,
 8722            "param1: u8, param2: u8"
 8723        );
 8724    });
 8725}
 8726
 8727#[gpui::test]
 8728async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 8729    init_test(cx, |_| {});
 8730
 8731    cx.update(|cx| {
 8732        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8733            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8734                settings.auto_signature_help = Some(false);
 8735                settings.show_signature_help_after_edits = Some(false);
 8736            });
 8737        });
 8738    });
 8739
 8740    let mut cx = EditorLspTestContext::new_rust(
 8741        lsp::ServerCapabilities {
 8742            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8743                ..Default::default()
 8744            }),
 8745            ..Default::default()
 8746        },
 8747        cx,
 8748    )
 8749    .await;
 8750
 8751    let language = Language::new(
 8752        LanguageConfig {
 8753            name: "Rust".into(),
 8754            brackets: BracketPairConfig {
 8755                pairs: vec![
 8756                    BracketPair {
 8757                        start: "{".to_string(),
 8758                        end: "}".to_string(),
 8759                        close: true,
 8760                        surround: true,
 8761                        newline: true,
 8762                    },
 8763                    BracketPair {
 8764                        start: "(".to_string(),
 8765                        end: ")".to_string(),
 8766                        close: true,
 8767                        surround: true,
 8768                        newline: true,
 8769                    },
 8770                    BracketPair {
 8771                        start: "/*".to_string(),
 8772                        end: " */".to_string(),
 8773                        close: true,
 8774                        surround: true,
 8775                        newline: true,
 8776                    },
 8777                    BracketPair {
 8778                        start: "[".to_string(),
 8779                        end: "]".to_string(),
 8780                        close: false,
 8781                        surround: false,
 8782                        newline: true,
 8783                    },
 8784                    BracketPair {
 8785                        start: "\"".to_string(),
 8786                        end: "\"".to_string(),
 8787                        close: true,
 8788                        surround: true,
 8789                        newline: false,
 8790                    },
 8791                    BracketPair {
 8792                        start: "<".to_string(),
 8793                        end: ">".to_string(),
 8794                        close: false,
 8795                        surround: true,
 8796                        newline: true,
 8797                    },
 8798                ],
 8799                ..Default::default()
 8800            },
 8801            autoclose_before: "})]".to_string(),
 8802            ..Default::default()
 8803        },
 8804        Some(tree_sitter_rust::LANGUAGE.into()),
 8805    );
 8806    let language = Arc::new(language);
 8807
 8808    cx.language_registry().add(language.clone());
 8809    cx.update_buffer(|buffer, cx| {
 8810        buffer.set_language(Some(language), cx);
 8811    });
 8812
 8813    // Ensure that signature_help is not called when no signature help is enabled.
 8814    cx.set_state(
 8815        &r#"
 8816            fn main() {
 8817                sampleˇ
 8818            }
 8819        "#
 8820        .unindent(),
 8821    );
 8822    cx.update_editor(|editor, window, cx| {
 8823        editor.handle_input("(", window, cx);
 8824    });
 8825    cx.assert_editor_state(
 8826        &"
 8827            fn main() {
 8828                sample(ˇ)
 8829            }
 8830        "
 8831        .unindent(),
 8832    );
 8833    cx.editor(|editor, _, _| {
 8834        assert!(editor.signature_help_state.task().is_none());
 8835    });
 8836
 8837    let mocked_response = lsp::SignatureHelp {
 8838        signatures: vec![lsp::SignatureInformation {
 8839            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8840            documentation: None,
 8841            parameters: Some(vec![
 8842                lsp::ParameterInformation {
 8843                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8844                    documentation: None,
 8845                },
 8846                lsp::ParameterInformation {
 8847                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8848                    documentation: None,
 8849                },
 8850            ]),
 8851            active_parameter: None,
 8852        }],
 8853        active_signature: Some(0),
 8854        active_parameter: Some(0),
 8855    };
 8856
 8857    // Ensure that signature_help is called when enabled afte edits
 8858    cx.update(|_, cx| {
 8859        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8860            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8861                settings.auto_signature_help = Some(false);
 8862                settings.show_signature_help_after_edits = Some(true);
 8863            });
 8864        });
 8865    });
 8866    cx.set_state(
 8867        &r#"
 8868            fn main() {
 8869                sampleˇ
 8870            }
 8871        "#
 8872        .unindent(),
 8873    );
 8874    cx.update_editor(|editor, window, cx| {
 8875        editor.handle_input("(", window, cx);
 8876    });
 8877    cx.assert_editor_state(
 8878        &"
 8879            fn main() {
 8880                sample(ˇ)
 8881            }
 8882        "
 8883        .unindent(),
 8884    );
 8885    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 8886    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8887        .await;
 8888    cx.update_editor(|editor, _, _| {
 8889        let signature_help_state = editor.signature_help_state.popover().cloned();
 8890        assert!(signature_help_state.is_some());
 8891        assert_eq!(
 8892            signature_help_state.unwrap().label,
 8893            "param1: u8, param2: u8"
 8894        );
 8895        editor.signature_help_state = SignatureHelpState::default();
 8896    });
 8897
 8898    // Ensure that signature_help is called when auto signature help override is enabled
 8899    cx.update(|_, cx| {
 8900        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8901            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8902                settings.auto_signature_help = Some(true);
 8903                settings.show_signature_help_after_edits = Some(false);
 8904            });
 8905        });
 8906    });
 8907    cx.set_state(
 8908        &r#"
 8909            fn main() {
 8910                sampleˇ
 8911            }
 8912        "#
 8913        .unindent(),
 8914    );
 8915    cx.update_editor(|editor, window, cx| {
 8916        editor.handle_input("(", window, cx);
 8917    });
 8918    cx.assert_editor_state(
 8919        &"
 8920            fn main() {
 8921                sample(ˇ)
 8922            }
 8923        "
 8924        .unindent(),
 8925    );
 8926    handle_signature_help_request(&mut cx, mocked_response).await;
 8927    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8928        .await;
 8929    cx.editor(|editor, _, _| {
 8930        let signature_help_state = editor.signature_help_state.popover().cloned();
 8931        assert!(signature_help_state.is_some());
 8932        assert_eq!(
 8933            signature_help_state.unwrap().label,
 8934            "param1: u8, param2: u8"
 8935        );
 8936    });
 8937}
 8938
 8939#[gpui::test]
 8940async fn test_signature_help(cx: &mut TestAppContext) {
 8941    init_test(cx, |_| {});
 8942    cx.update(|cx| {
 8943        cx.update_global::<SettingsStore, _>(|settings, cx| {
 8944            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 8945                settings.auto_signature_help = Some(true);
 8946            });
 8947        });
 8948    });
 8949
 8950    let mut cx = EditorLspTestContext::new_rust(
 8951        lsp::ServerCapabilities {
 8952            signature_help_provider: Some(lsp::SignatureHelpOptions {
 8953                ..Default::default()
 8954            }),
 8955            ..Default::default()
 8956        },
 8957        cx,
 8958    )
 8959    .await;
 8960
 8961    // A test that directly calls `show_signature_help`
 8962    cx.update_editor(|editor, window, cx| {
 8963        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 8964    });
 8965
 8966    let mocked_response = lsp::SignatureHelp {
 8967        signatures: vec![lsp::SignatureInformation {
 8968            label: "fn sample(param1: u8, param2: u8)".to_string(),
 8969            documentation: None,
 8970            parameters: Some(vec![
 8971                lsp::ParameterInformation {
 8972                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 8973                    documentation: None,
 8974                },
 8975                lsp::ParameterInformation {
 8976                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 8977                    documentation: None,
 8978                },
 8979            ]),
 8980            active_parameter: None,
 8981        }],
 8982        active_signature: Some(0),
 8983        active_parameter: Some(0),
 8984    };
 8985    handle_signature_help_request(&mut cx, mocked_response).await;
 8986
 8987    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 8988        .await;
 8989
 8990    cx.editor(|editor, _, _| {
 8991        let signature_help_state = editor.signature_help_state.popover().cloned();
 8992        assert!(signature_help_state.is_some());
 8993        assert_eq!(
 8994            signature_help_state.unwrap().label,
 8995            "param1: u8, param2: u8"
 8996        );
 8997    });
 8998
 8999    // When exiting outside from inside the brackets, `signature_help` is closed.
 9000    cx.set_state(indoc! {"
 9001        fn main() {
 9002            sample(ˇ);
 9003        }
 9004
 9005        fn sample(param1: u8, param2: u8) {}
 9006    "});
 9007
 9008    cx.update_editor(|editor, window, cx| {
 9009        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9010    });
 9011
 9012    let mocked_response = lsp::SignatureHelp {
 9013        signatures: Vec::new(),
 9014        active_signature: None,
 9015        active_parameter: None,
 9016    };
 9017    handle_signature_help_request(&mut cx, mocked_response).await;
 9018
 9019    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9020        .await;
 9021
 9022    cx.editor(|editor, _, _| {
 9023        assert!(!editor.signature_help_state.is_shown());
 9024    });
 9025
 9026    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9027    cx.set_state(indoc! {"
 9028        fn main() {
 9029            sample(ˇ);
 9030        }
 9031
 9032        fn sample(param1: u8, param2: u8) {}
 9033    "});
 9034
 9035    let mocked_response = lsp::SignatureHelp {
 9036        signatures: vec![lsp::SignatureInformation {
 9037            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9038            documentation: None,
 9039            parameters: Some(vec![
 9040                lsp::ParameterInformation {
 9041                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9042                    documentation: None,
 9043                },
 9044                lsp::ParameterInformation {
 9045                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9046                    documentation: None,
 9047                },
 9048            ]),
 9049            active_parameter: None,
 9050        }],
 9051        active_signature: Some(0),
 9052        active_parameter: Some(0),
 9053    };
 9054    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9055    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9056        .await;
 9057    cx.editor(|editor, _, _| {
 9058        assert!(editor.signature_help_state.is_shown());
 9059    });
 9060
 9061    // Restore the popover with more parameter input
 9062    cx.set_state(indoc! {"
 9063        fn main() {
 9064            sample(param1, param2ˇ);
 9065        }
 9066
 9067        fn sample(param1: u8, param2: u8) {}
 9068    "});
 9069
 9070    let mocked_response = lsp::SignatureHelp {
 9071        signatures: vec![lsp::SignatureInformation {
 9072            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9073            documentation: None,
 9074            parameters: Some(vec![
 9075                lsp::ParameterInformation {
 9076                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9077                    documentation: None,
 9078                },
 9079                lsp::ParameterInformation {
 9080                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9081                    documentation: None,
 9082                },
 9083            ]),
 9084            active_parameter: None,
 9085        }],
 9086        active_signature: Some(0),
 9087        active_parameter: Some(1),
 9088    };
 9089    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9090    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9091        .await;
 9092
 9093    // When selecting a range, the popover is gone.
 9094    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9095    cx.update_editor(|editor, window, cx| {
 9096        editor.change_selections(None, window, cx, |s| {
 9097            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9098        })
 9099    });
 9100    cx.assert_editor_state(indoc! {"
 9101        fn main() {
 9102            sample(param1, «ˇparam2»);
 9103        }
 9104
 9105        fn sample(param1: u8, param2: u8) {}
 9106    "});
 9107    cx.editor(|editor, _, _| {
 9108        assert!(!editor.signature_help_state.is_shown());
 9109    });
 9110
 9111    // When unselecting again, the popover is back if within the brackets.
 9112    cx.update_editor(|editor, window, cx| {
 9113        editor.change_selections(None, window, cx, |s| {
 9114            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9115        })
 9116    });
 9117    cx.assert_editor_state(indoc! {"
 9118        fn main() {
 9119            sample(param1, ˇparam2);
 9120        }
 9121
 9122        fn sample(param1: u8, param2: u8) {}
 9123    "});
 9124    handle_signature_help_request(&mut cx, mocked_response).await;
 9125    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9126        .await;
 9127    cx.editor(|editor, _, _| {
 9128        assert!(editor.signature_help_state.is_shown());
 9129    });
 9130
 9131    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9132    cx.update_editor(|editor, window, cx| {
 9133        editor.change_selections(None, window, cx, |s| {
 9134            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9135            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9136        })
 9137    });
 9138    cx.assert_editor_state(indoc! {"
 9139        fn main() {
 9140            sample(param1, ˇparam2);
 9141        }
 9142
 9143        fn sample(param1: u8, param2: u8) {}
 9144    "});
 9145
 9146    let mocked_response = lsp::SignatureHelp {
 9147        signatures: vec![lsp::SignatureInformation {
 9148            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9149            documentation: None,
 9150            parameters: Some(vec![
 9151                lsp::ParameterInformation {
 9152                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9153                    documentation: None,
 9154                },
 9155                lsp::ParameterInformation {
 9156                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9157                    documentation: None,
 9158                },
 9159            ]),
 9160            active_parameter: None,
 9161        }],
 9162        active_signature: Some(0),
 9163        active_parameter: Some(1),
 9164    };
 9165    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9166    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9167        .await;
 9168    cx.update_editor(|editor, _, cx| {
 9169        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9170    });
 9171    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9172        .await;
 9173    cx.update_editor(|editor, window, cx| {
 9174        editor.change_selections(None, window, cx, |s| {
 9175            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9176        })
 9177    });
 9178    cx.assert_editor_state(indoc! {"
 9179        fn main() {
 9180            sample(param1, «ˇparam2»);
 9181        }
 9182
 9183        fn sample(param1: u8, param2: u8) {}
 9184    "});
 9185    cx.update_editor(|editor, window, cx| {
 9186        editor.change_selections(None, window, cx, |s| {
 9187            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9188        })
 9189    });
 9190    cx.assert_editor_state(indoc! {"
 9191        fn main() {
 9192            sample(param1, ˇparam2);
 9193        }
 9194
 9195        fn sample(param1: u8, param2: u8) {}
 9196    "});
 9197    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9198        .await;
 9199}
 9200
 9201#[gpui::test]
 9202async fn test_completion(cx: &mut TestAppContext) {
 9203    init_test(cx, |_| {});
 9204
 9205    let mut cx = EditorLspTestContext::new_rust(
 9206        lsp::ServerCapabilities {
 9207            completion_provider: Some(lsp::CompletionOptions {
 9208                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9209                resolve_provider: Some(true),
 9210                ..Default::default()
 9211            }),
 9212            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9213            ..Default::default()
 9214        },
 9215        cx,
 9216    )
 9217    .await;
 9218    let counter = Arc::new(AtomicUsize::new(0));
 9219
 9220    cx.set_state(indoc! {"
 9221        oneˇ
 9222        two
 9223        three
 9224    "});
 9225    cx.simulate_keystroke(".");
 9226    handle_completion_request(
 9227        &mut cx,
 9228        indoc! {"
 9229            one.|<>
 9230            two
 9231            three
 9232        "},
 9233        vec!["first_completion", "second_completion"],
 9234        counter.clone(),
 9235    )
 9236    .await;
 9237    cx.condition(|editor, _| editor.context_menu_visible())
 9238        .await;
 9239    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9240
 9241    let _handler = handle_signature_help_request(
 9242        &mut cx,
 9243        lsp::SignatureHelp {
 9244            signatures: vec![lsp::SignatureInformation {
 9245                label: "test signature".to_string(),
 9246                documentation: None,
 9247                parameters: Some(vec![lsp::ParameterInformation {
 9248                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9249                    documentation: None,
 9250                }]),
 9251                active_parameter: None,
 9252            }],
 9253            active_signature: None,
 9254            active_parameter: None,
 9255        },
 9256    );
 9257    cx.update_editor(|editor, window, cx| {
 9258        assert!(
 9259            !editor.signature_help_state.is_shown(),
 9260            "No signature help was called for"
 9261        );
 9262        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9263    });
 9264    cx.run_until_parked();
 9265    cx.update_editor(|editor, _, _| {
 9266        assert!(
 9267            !editor.signature_help_state.is_shown(),
 9268            "No signature help should be shown when completions menu is open"
 9269        );
 9270    });
 9271
 9272    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9273        editor.context_menu_next(&Default::default(), window, cx);
 9274        editor
 9275            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9276            .unwrap()
 9277    });
 9278    cx.assert_editor_state(indoc! {"
 9279        one.second_completionˇ
 9280        two
 9281        three
 9282    "});
 9283
 9284    handle_resolve_completion_request(
 9285        &mut cx,
 9286        Some(vec![
 9287            (
 9288                //This overlaps with the primary completion edit which is
 9289                //misbehavior from the LSP spec, test that we filter it out
 9290                indoc! {"
 9291                    one.second_ˇcompletion
 9292                    two
 9293                    threeˇ
 9294                "},
 9295                "overlapping additional edit",
 9296            ),
 9297            (
 9298                indoc! {"
 9299                    one.second_completion
 9300                    two
 9301                    threeˇ
 9302                "},
 9303                "\nadditional edit",
 9304            ),
 9305        ]),
 9306    )
 9307    .await;
 9308    apply_additional_edits.await.unwrap();
 9309    cx.assert_editor_state(indoc! {"
 9310        one.second_completionˇ
 9311        two
 9312        three
 9313        additional edit
 9314    "});
 9315
 9316    cx.set_state(indoc! {"
 9317        one.second_completion
 9318        twoˇ
 9319        threeˇ
 9320        additional edit
 9321    "});
 9322    cx.simulate_keystroke(" ");
 9323    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9324    cx.simulate_keystroke("s");
 9325    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9326
 9327    cx.assert_editor_state(indoc! {"
 9328        one.second_completion
 9329        two sˇ
 9330        three sˇ
 9331        additional edit
 9332    "});
 9333    handle_completion_request(
 9334        &mut cx,
 9335        indoc! {"
 9336            one.second_completion
 9337            two s
 9338            three <s|>
 9339            additional edit
 9340        "},
 9341        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9342        counter.clone(),
 9343    )
 9344    .await;
 9345    cx.condition(|editor, _| editor.context_menu_visible())
 9346        .await;
 9347    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9348
 9349    cx.simulate_keystroke("i");
 9350
 9351    handle_completion_request(
 9352        &mut cx,
 9353        indoc! {"
 9354            one.second_completion
 9355            two si
 9356            three <si|>
 9357            additional edit
 9358        "},
 9359        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9360        counter.clone(),
 9361    )
 9362    .await;
 9363    cx.condition(|editor, _| editor.context_menu_visible())
 9364        .await;
 9365    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9366
 9367    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9368        editor
 9369            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9370            .unwrap()
 9371    });
 9372    cx.assert_editor_state(indoc! {"
 9373        one.second_completion
 9374        two sixth_completionˇ
 9375        three sixth_completionˇ
 9376        additional edit
 9377    "});
 9378
 9379    apply_additional_edits.await.unwrap();
 9380
 9381    update_test_language_settings(&mut cx, |settings| {
 9382        settings.defaults.show_completions_on_input = Some(false);
 9383    });
 9384    cx.set_state("editorˇ");
 9385    cx.simulate_keystroke(".");
 9386    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9387    cx.simulate_keystrokes("c l o");
 9388    cx.assert_editor_state("editor.cloˇ");
 9389    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9390    cx.update_editor(|editor, window, cx| {
 9391        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9392    });
 9393    handle_completion_request(
 9394        &mut cx,
 9395        "editor.<clo|>",
 9396        vec!["close", "clobber"],
 9397        counter.clone(),
 9398    )
 9399    .await;
 9400    cx.condition(|editor, _| editor.context_menu_visible())
 9401        .await;
 9402    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9403
 9404    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9405        editor
 9406            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9407            .unwrap()
 9408    });
 9409    cx.assert_editor_state("editor.closeˇ");
 9410    handle_resolve_completion_request(&mut cx, None).await;
 9411    apply_additional_edits.await.unwrap();
 9412}
 9413
 9414#[gpui::test]
 9415async fn test_word_completion(cx: &mut TestAppContext) {
 9416    let lsp_fetch_timeout_ms = 10;
 9417    init_test(cx, |language_settings| {
 9418        language_settings.defaults.completions = Some(CompletionSettings {
 9419            words: WordsCompletionMode::Fallback,
 9420            lsp: true,
 9421            lsp_fetch_timeout_ms: 10,
 9422        });
 9423    });
 9424
 9425    let mut cx = EditorLspTestContext::new_rust(
 9426        lsp::ServerCapabilities {
 9427            completion_provider: Some(lsp::CompletionOptions {
 9428                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9429                ..lsp::CompletionOptions::default()
 9430            }),
 9431            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9432            ..lsp::ServerCapabilities::default()
 9433        },
 9434        cx,
 9435    )
 9436    .await;
 9437
 9438    let throttle_completions = Arc::new(AtomicBool::new(false));
 9439
 9440    let lsp_throttle_completions = throttle_completions.clone();
 9441    let _completion_requests_handler =
 9442        cx.lsp
 9443            .server
 9444            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9445                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9446                let cx = cx.clone();
 9447                async move {
 9448                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9449                        cx.background_executor()
 9450                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9451                            .await;
 9452                    }
 9453                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9454                        lsp::CompletionItem {
 9455                            label: "first".into(),
 9456                            ..lsp::CompletionItem::default()
 9457                        },
 9458                        lsp::CompletionItem {
 9459                            label: "last".into(),
 9460                            ..lsp::CompletionItem::default()
 9461                        },
 9462                    ])))
 9463                }
 9464            });
 9465
 9466    cx.set_state(indoc! {"
 9467        oneˇ
 9468        two
 9469        three
 9470    "});
 9471    cx.simulate_keystroke(".");
 9472    cx.executor().run_until_parked();
 9473    cx.condition(|editor, _| editor.context_menu_visible())
 9474        .await;
 9475    cx.update_editor(|editor, window, cx| {
 9476        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9477        {
 9478            assert_eq!(
 9479                completion_menu_entries(&menu),
 9480                &["first", "last"],
 9481                "When LSP server is fast to reply, no fallback word completions are used"
 9482            );
 9483        } else {
 9484            panic!("expected completion menu to be open");
 9485        }
 9486        editor.cancel(&Cancel, window, cx);
 9487    });
 9488    cx.executor().run_until_parked();
 9489    cx.condition(|editor, _| !editor.context_menu_visible())
 9490        .await;
 9491
 9492    throttle_completions.store(true, atomic::Ordering::Release);
 9493    cx.simulate_keystroke(".");
 9494    cx.executor()
 9495        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9496    cx.executor().run_until_parked();
 9497    cx.condition(|editor, _| editor.context_menu_visible())
 9498        .await;
 9499    cx.update_editor(|editor, _, _| {
 9500        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9501        {
 9502            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9503                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9504        } else {
 9505            panic!("expected completion menu to be open");
 9506        }
 9507    });
 9508}
 9509
 9510#[gpui::test]
 9511async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9512    init_test(cx, |language_settings| {
 9513        language_settings.defaults.completions = Some(CompletionSettings {
 9514            words: WordsCompletionMode::Enabled,
 9515            lsp: true,
 9516            lsp_fetch_timeout_ms: 0,
 9517        });
 9518    });
 9519
 9520    let mut cx = EditorLspTestContext::new_rust(
 9521        lsp::ServerCapabilities {
 9522            completion_provider: Some(lsp::CompletionOptions {
 9523                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9524                ..lsp::CompletionOptions::default()
 9525            }),
 9526            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9527            ..lsp::ServerCapabilities::default()
 9528        },
 9529        cx,
 9530    )
 9531    .await;
 9532
 9533    let _completion_requests_handler =
 9534        cx.lsp
 9535            .server
 9536            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9537                Ok(Some(lsp::CompletionResponse::Array(vec![
 9538                    lsp::CompletionItem {
 9539                        label: "first".into(),
 9540                        ..lsp::CompletionItem::default()
 9541                    },
 9542                    lsp::CompletionItem {
 9543                        label: "last".into(),
 9544                        ..lsp::CompletionItem::default()
 9545                    },
 9546                ])))
 9547            });
 9548
 9549    cx.set_state(indoc! {"ˇ
 9550        first
 9551        last
 9552        second
 9553    "});
 9554    cx.simulate_keystroke(".");
 9555    cx.executor().run_until_parked();
 9556    cx.condition(|editor, _| editor.context_menu_visible())
 9557        .await;
 9558    cx.update_editor(|editor, _, _| {
 9559        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9560        {
 9561            assert_eq!(
 9562                completion_menu_entries(&menu),
 9563                &["first", "last", "second"],
 9564                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9565            );
 9566        } else {
 9567            panic!("expected completion menu to be open");
 9568        }
 9569    });
 9570}
 9571
 9572#[gpui::test]
 9573async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9574    init_test(cx, |language_settings| {
 9575        language_settings.defaults.completions = Some(CompletionSettings {
 9576            words: WordsCompletionMode::Disabled,
 9577            lsp: true,
 9578            lsp_fetch_timeout_ms: 0,
 9579        });
 9580    });
 9581
 9582    let mut cx = EditorLspTestContext::new_rust(
 9583        lsp::ServerCapabilities {
 9584            completion_provider: Some(lsp::CompletionOptions {
 9585                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9586                ..lsp::CompletionOptions::default()
 9587            }),
 9588            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9589            ..lsp::ServerCapabilities::default()
 9590        },
 9591        cx,
 9592    )
 9593    .await;
 9594
 9595    let _completion_requests_handler =
 9596        cx.lsp
 9597            .server
 9598            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9599                panic!("LSP completions should not be queried when dealing with word completions")
 9600            });
 9601
 9602    cx.set_state(indoc! {"ˇ
 9603        first
 9604        last
 9605        second
 9606    "});
 9607    cx.update_editor(|editor, window, cx| {
 9608        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9609    });
 9610    cx.executor().run_until_parked();
 9611    cx.condition(|editor, _| editor.context_menu_visible())
 9612        .await;
 9613    cx.update_editor(|editor, _, _| {
 9614        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9615        {
 9616            assert_eq!(
 9617                completion_menu_entries(&menu),
 9618                &["first", "last", "second"],
 9619                "`ShowWordCompletions` action should show word completions"
 9620            );
 9621        } else {
 9622            panic!("expected completion menu to be open");
 9623        }
 9624    });
 9625
 9626    cx.simulate_keystroke("l");
 9627    cx.executor().run_until_parked();
 9628    cx.condition(|editor, _| editor.context_menu_visible())
 9629        .await;
 9630    cx.update_editor(|editor, _, _| {
 9631        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9632        {
 9633            assert_eq!(
 9634                completion_menu_entries(&menu),
 9635                &["last"],
 9636                "After showing word completions, further editing should filter them and not query the LSP"
 9637            );
 9638        } else {
 9639            panic!("expected completion menu to be open");
 9640        }
 9641    });
 9642}
 9643
 9644#[gpui::test]
 9645async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9646    init_test(cx, |language_settings| {
 9647        language_settings.defaults.completions = Some(CompletionSettings {
 9648            words: WordsCompletionMode::Fallback,
 9649            lsp: false,
 9650            lsp_fetch_timeout_ms: 0,
 9651        });
 9652    });
 9653
 9654    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9655
 9656    cx.set_state(indoc! {"ˇ
 9657        0_usize
 9658        let
 9659        33
 9660        4.5f32
 9661    "});
 9662    cx.update_editor(|editor, window, cx| {
 9663        editor.show_completions(&ShowCompletions::default(), window, cx);
 9664    });
 9665    cx.executor().run_until_parked();
 9666    cx.condition(|editor, _| editor.context_menu_visible())
 9667        .await;
 9668    cx.update_editor(|editor, window, cx| {
 9669        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9670        {
 9671            assert_eq!(
 9672                completion_menu_entries(&menu),
 9673                &["let"],
 9674                "With no digits in the completion query, no digits should be in the word completions"
 9675            );
 9676        } else {
 9677            panic!("expected completion menu to be open");
 9678        }
 9679        editor.cancel(&Cancel, window, cx);
 9680    });
 9681
 9682    cx.set_state(indoc! {" 9683        0_usize
 9684        let
 9685        3
 9686        33.35f32
 9687    "});
 9688    cx.update_editor(|editor, window, cx| {
 9689        editor.show_completions(&ShowCompletions::default(), window, cx);
 9690    });
 9691    cx.executor().run_until_parked();
 9692    cx.condition(|editor, _| editor.context_menu_visible())
 9693        .await;
 9694    cx.update_editor(|editor, _, _| {
 9695        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9696        {
 9697            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9698                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9699        } else {
 9700            panic!("expected completion menu to be open");
 9701        }
 9702    });
 9703}
 9704
 9705fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9706    let position = || lsp::Position {
 9707        line: params.text_document_position.position.line,
 9708        character: params.text_document_position.position.character,
 9709    };
 9710    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
 9711        range: lsp::Range {
 9712            start: position(),
 9713            end: position(),
 9714        },
 9715        new_text: text.to_string(),
 9716    }))
 9717}
 9718
 9719#[gpui::test]
 9720async fn test_multiline_completion(cx: &mut TestAppContext) {
 9721    init_test(cx, |_| {});
 9722
 9723    let fs = FakeFs::new(cx.executor());
 9724    fs.insert_tree(
 9725        path!("/a"),
 9726        json!({
 9727            "main.ts": "a",
 9728        }),
 9729    )
 9730    .await;
 9731
 9732    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 9733    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 9734    let typescript_language = Arc::new(Language::new(
 9735        LanguageConfig {
 9736            name: "TypeScript".into(),
 9737            matcher: LanguageMatcher {
 9738                path_suffixes: vec!["ts".to_string()],
 9739                ..LanguageMatcher::default()
 9740            },
 9741            line_comments: vec!["// ".into()],
 9742            ..LanguageConfig::default()
 9743        },
 9744        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 9745    ));
 9746    language_registry.add(typescript_language.clone());
 9747    let mut fake_servers = language_registry.register_fake_lsp(
 9748        "TypeScript",
 9749        FakeLspAdapter {
 9750            capabilities: lsp::ServerCapabilities {
 9751                completion_provider: Some(lsp::CompletionOptions {
 9752                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9753                    ..lsp::CompletionOptions::default()
 9754                }),
 9755                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9756                ..lsp::ServerCapabilities::default()
 9757            },
 9758            // Emulate vtsls label generation
 9759            label_for_completion: Some(Box::new(|item, _| {
 9760                let text = if let Some(description) = item
 9761                    .label_details
 9762                    .as_ref()
 9763                    .and_then(|label_details| label_details.description.as_ref())
 9764                {
 9765                    format!("{} {}", item.label, description)
 9766                } else if let Some(detail) = &item.detail {
 9767                    format!("{} {}", item.label, detail)
 9768                } else {
 9769                    item.label.clone()
 9770                };
 9771                let len = text.len();
 9772                Some(language::CodeLabel {
 9773                    text,
 9774                    runs: Vec::new(),
 9775                    filter_range: 0..len,
 9776                })
 9777            })),
 9778            ..FakeLspAdapter::default()
 9779        },
 9780    );
 9781    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 9782    let cx = &mut VisualTestContext::from_window(*workspace, cx);
 9783    let worktree_id = workspace
 9784        .update(cx, |workspace, _window, cx| {
 9785            workspace.project().update(cx, |project, cx| {
 9786                project.worktrees(cx).next().unwrap().read(cx).id()
 9787            })
 9788        })
 9789        .unwrap();
 9790    let _buffer = project
 9791        .update(cx, |project, cx| {
 9792            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
 9793        })
 9794        .await
 9795        .unwrap();
 9796    let editor = workspace
 9797        .update(cx, |workspace, window, cx| {
 9798            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
 9799        })
 9800        .unwrap()
 9801        .await
 9802        .unwrap()
 9803        .downcast::<Editor>()
 9804        .unwrap();
 9805    let fake_server = fake_servers.next().await.unwrap();
 9806
 9807    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
 9808    let multiline_label_2 = "a\nb\nc\n";
 9809    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
 9810    let multiline_description = "d\ne\nf\n";
 9811    let multiline_detail_2 = "g\nh\ni\n";
 9812
 9813    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
 9814        move |params, _| async move {
 9815            Ok(Some(lsp::CompletionResponse::Array(vec![
 9816                lsp::CompletionItem {
 9817                    label: multiline_label.to_string(),
 9818                    text_edit: gen_text_edit(&params, "new_text_1"),
 9819                    ..lsp::CompletionItem::default()
 9820                },
 9821                lsp::CompletionItem {
 9822                    label: "single line label 1".to_string(),
 9823                    detail: Some(multiline_detail.to_string()),
 9824                    text_edit: gen_text_edit(&params, "new_text_2"),
 9825                    ..lsp::CompletionItem::default()
 9826                },
 9827                lsp::CompletionItem {
 9828                    label: "single line label 2".to_string(),
 9829                    label_details: Some(lsp::CompletionItemLabelDetails {
 9830                        description: Some(multiline_description.to_string()),
 9831                        detail: None,
 9832                    }),
 9833                    text_edit: gen_text_edit(&params, "new_text_2"),
 9834                    ..lsp::CompletionItem::default()
 9835                },
 9836                lsp::CompletionItem {
 9837                    label: multiline_label_2.to_string(),
 9838                    detail: Some(multiline_detail_2.to_string()),
 9839                    text_edit: gen_text_edit(&params, "new_text_3"),
 9840                    ..lsp::CompletionItem::default()
 9841                },
 9842                lsp::CompletionItem {
 9843                    label: "Label with many     spaces and \t but without newlines".to_string(),
 9844                    detail: Some(
 9845                        "Details with many     spaces and \t but without newlines".to_string(),
 9846                    ),
 9847                    text_edit: gen_text_edit(&params, "new_text_4"),
 9848                    ..lsp::CompletionItem::default()
 9849                },
 9850            ])))
 9851        },
 9852    );
 9853
 9854    editor.update_in(cx, |editor, window, cx| {
 9855        cx.focus_self(window);
 9856        editor.move_to_end(&MoveToEnd, window, cx);
 9857        editor.handle_input(".", window, cx);
 9858    });
 9859    cx.run_until_parked();
 9860    completion_handle.next().await.unwrap();
 9861
 9862    editor.update(cx, |editor, _| {
 9863        assert!(editor.context_menu_visible());
 9864        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9865        {
 9866            let completion_labels = menu
 9867                .completions
 9868                .borrow()
 9869                .iter()
 9870                .map(|c| c.label.text.clone())
 9871                .collect::<Vec<_>>();
 9872            assert_eq!(
 9873                completion_labels,
 9874                &[
 9875                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
 9876                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
 9877                    "single line label 2 d e f ",
 9878                    "a b c g h i ",
 9879                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
 9880                ],
 9881                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
 9882            );
 9883
 9884            for completion in menu
 9885                .completions
 9886                .borrow()
 9887                .iter() {
 9888                    assert_eq!(
 9889                        completion.label.filter_range,
 9890                        0..completion.label.text.len(),
 9891                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
 9892                    );
 9893                }
 9894        } else {
 9895            panic!("expected completion menu to be open");
 9896        }
 9897    });
 9898}
 9899
 9900#[gpui::test]
 9901async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
 9902    init_test(cx, |_| {});
 9903    let mut cx = EditorLspTestContext::new_rust(
 9904        lsp::ServerCapabilities {
 9905            completion_provider: Some(lsp::CompletionOptions {
 9906                trigger_characters: Some(vec![".".to_string()]),
 9907                ..Default::default()
 9908            }),
 9909            ..Default::default()
 9910        },
 9911        cx,
 9912    )
 9913    .await;
 9914    cx.lsp
 9915        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 9916            Ok(Some(lsp::CompletionResponse::Array(vec![
 9917                lsp::CompletionItem {
 9918                    label: "first".into(),
 9919                    ..Default::default()
 9920                },
 9921                lsp::CompletionItem {
 9922                    label: "last".into(),
 9923                    ..Default::default()
 9924                },
 9925            ])))
 9926        });
 9927    cx.set_state("variableˇ");
 9928    cx.simulate_keystroke(".");
 9929    cx.executor().run_until_parked();
 9930
 9931    cx.update_editor(|editor, _, _| {
 9932        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9933        {
 9934            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
 9935        } else {
 9936            panic!("expected completion menu to be open");
 9937        }
 9938    });
 9939
 9940    cx.update_editor(|editor, window, cx| {
 9941        editor.move_page_down(&MovePageDown::default(), window, cx);
 9942        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9943        {
 9944            assert!(
 9945                menu.selected_item == 1,
 9946                "expected PageDown to select the last item from the context menu"
 9947            );
 9948        } else {
 9949            panic!("expected completion menu to stay open after PageDown");
 9950        }
 9951    });
 9952
 9953    cx.update_editor(|editor, window, cx| {
 9954        editor.move_page_up(&MovePageUp::default(), window, cx);
 9955        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9956        {
 9957            assert!(
 9958                menu.selected_item == 0,
 9959                "expected PageUp to select the first item from the context menu"
 9960            );
 9961        } else {
 9962            panic!("expected completion menu to stay open after PageUp");
 9963        }
 9964    });
 9965}
 9966
 9967#[gpui::test]
 9968async fn test_completion_sort(cx: &mut TestAppContext) {
 9969    init_test(cx, |_| {});
 9970    let mut cx = EditorLspTestContext::new_rust(
 9971        lsp::ServerCapabilities {
 9972            completion_provider: Some(lsp::CompletionOptions {
 9973                trigger_characters: Some(vec![".".to_string()]),
 9974                ..Default::default()
 9975            }),
 9976            ..Default::default()
 9977        },
 9978        cx,
 9979    )
 9980    .await;
 9981    cx.lsp
 9982        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
 9983            Ok(Some(lsp::CompletionResponse::Array(vec![
 9984                lsp::CompletionItem {
 9985                    label: "Range".into(),
 9986                    sort_text: Some("a".into()),
 9987                    ..Default::default()
 9988                },
 9989                lsp::CompletionItem {
 9990                    label: "r".into(),
 9991                    sort_text: Some("b".into()),
 9992                    ..Default::default()
 9993                },
 9994                lsp::CompletionItem {
 9995                    label: "ret".into(),
 9996                    sort_text: Some("c".into()),
 9997                    ..Default::default()
 9998                },
 9999                lsp::CompletionItem {
10000                    label: "return".into(),
10001                    sort_text: Some("d".into()),
10002                    ..Default::default()
10003                },
10004                lsp::CompletionItem {
10005                    label: "slice".into(),
10006                    sort_text: Some("d".into()),
10007                    ..Default::default()
10008                },
10009            ])))
10010        });
10011    cx.set_state("");
10012    cx.executor().run_until_parked();
10013    cx.update_editor(|editor, window, cx| {
10014        editor.show_completions(
10015            &ShowCompletions {
10016                trigger: Some("r".into()),
10017            },
10018            window,
10019            cx,
10020        );
10021    });
10022    cx.executor().run_until_parked();
10023
10024    cx.update_editor(|editor, _, _| {
10025        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10026        {
10027            assert_eq!(
10028                completion_menu_entries(&menu),
10029                &["r", "ret", "Range", "return"]
10030            );
10031        } else {
10032            panic!("expected completion menu to be open");
10033        }
10034    });
10035}
10036
10037#[gpui::test]
10038async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10039    init_test(cx, |_| {});
10040
10041    let mut cx = EditorLspTestContext::new_rust(
10042        lsp::ServerCapabilities {
10043            completion_provider: Some(lsp::CompletionOptions {
10044                trigger_characters: Some(vec![".".to_string()]),
10045                resolve_provider: Some(true),
10046                ..Default::default()
10047            }),
10048            ..Default::default()
10049        },
10050        cx,
10051    )
10052    .await;
10053
10054    cx.set_state("fn main() { let a = 2ˇ; }");
10055    cx.simulate_keystroke(".");
10056    let completion_item = lsp::CompletionItem {
10057        label: "Some".into(),
10058        kind: Some(lsp::CompletionItemKind::SNIPPET),
10059        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10060        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10061            kind: lsp::MarkupKind::Markdown,
10062            value: "```rust\nSome(2)\n```".to_string(),
10063        })),
10064        deprecated: Some(false),
10065        sort_text: Some("Some".to_string()),
10066        filter_text: Some("Some".to_string()),
10067        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10068        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10069            range: lsp::Range {
10070                start: lsp::Position {
10071                    line: 0,
10072                    character: 22,
10073                },
10074                end: lsp::Position {
10075                    line: 0,
10076                    character: 22,
10077                },
10078            },
10079            new_text: "Some(2)".to_string(),
10080        })),
10081        additional_text_edits: Some(vec![lsp::TextEdit {
10082            range: lsp::Range {
10083                start: lsp::Position {
10084                    line: 0,
10085                    character: 20,
10086                },
10087                end: lsp::Position {
10088                    line: 0,
10089                    character: 22,
10090                },
10091            },
10092            new_text: "".to_string(),
10093        }]),
10094        ..Default::default()
10095    };
10096
10097    let closure_completion_item = completion_item.clone();
10098    let counter = Arc::new(AtomicUsize::new(0));
10099    let counter_clone = counter.clone();
10100    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10101        let task_completion_item = closure_completion_item.clone();
10102        counter_clone.fetch_add(1, atomic::Ordering::Release);
10103        async move {
10104            Ok(Some(lsp::CompletionResponse::Array(vec![
10105                task_completion_item,
10106            ])))
10107        }
10108    });
10109
10110    cx.condition(|editor, _| editor.context_menu_visible())
10111        .await;
10112    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10113    assert!(request.next().await.is_some());
10114    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10115
10116    cx.simulate_keystrokes("S o m");
10117    cx.condition(|editor, _| editor.context_menu_visible())
10118        .await;
10119    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10120    assert!(request.next().await.is_some());
10121    assert!(request.next().await.is_some());
10122    assert!(request.next().await.is_some());
10123    request.close();
10124    assert!(request.next().await.is_none());
10125    assert_eq!(
10126        counter.load(atomic::Ordering::Acquire),
10127        4,
10128        "With the completions menu open, only one LSP request should happen per input"
10129    );
10130}
10131
10132#[gpui::test]
10133async fn test_toggle_comment(cx: &mut TestAppContext) {
10134    init_test(cx, |_| {});
10135    let mut cx = EditorTestContext::new(cx).await;
10136    let language = Arc::new(Language::new(
10137        LanguageConfig {
10138            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10139            ..Default::default()
10140        },
10141        Some(tree_sitter_rust::LANGUAGE.into()),
10142    ));
10143    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10144
10145    // If multiple selections intersect a line, the line is only toggled once.
10146    cx.set_state(indoc! {"
10147        fn a() {
10148            «//b();
10149            ˇ»// «c();
10150            //ˇ»  d();
10151        }
10152    "});
10153
10154    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10155
10156    cx.assert_editor_state(indoc! {"
10157        fn a() {
10158            «b();
10159            c();
10160            ˇ» d();
10161        }
10162    "});
10163
10164    // The comment prefix is inserted at the same column for every line in a
10165    // selection.
10166    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10167
10168    cx.assert_editor_state(indoc! {"
10169        fn a() {
10170            // «b();
10171            // c();
10172            ˇ»//  d();
10173        }
10174    "});
10175
10176    // If a selection ends at the beginning of a line, that line is not toggled.
10177    cx.set_selections_state(indoc! {"
10178        fn a() {
10179            // b();
10180            «// c();
10181        ˇ»    //  d();
10182        }
10183    "});
10184
10185    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10186
10187    cx.assert_editor_state(indoc! {"
10188        fn a() {
10189            // b();
10190            «c();
10191        ˇ»    //  d();
10192        }
10193    "});
10194
10195    // If a selection span a single line and is empty, the line is toggled.
10196    cx.set_state(indoc! {"
10197        fn a() {
10198            a();
10199            b();
10200        ˇ
10201        }
10202    "});
10203
10204    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10205
10206    cx.assert_editor_state(indoc! {"
10207        fn a() {
10208            a();
10209            b();
10210        //•ˇ
10211        }
10212    "});
10213
10214    // If a selection span multiple lines, empty lines are not toggled.
10215    cx.set_state(indoc! {"
10216        fn a() {
10217            «a();
10218
10219            c();ˇ»
10220        }
10221    "});
10222
10223    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10224
10225    cx.assert_editor_state(indoc! {"
10226        fn a() {
10227            // «a();
10228
10229            // c();ˇ»
10230        }
10231    "});
10232
10233    // If a selection includes multiple comment prefixes, all lines are uncommented.
10234    cx.set_state(indoc! {"
10235        fn a() {
10236            «// a();
10237            /// b();
10238            //! c();ˇ»
10239        }
10240    "});
10241
10242    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10243
10244    cx.assert_editor_state(indoc! {"
10245        fn a() {
10246            «a();
10247            b();
10248            c();ˇ»
10249        }
10250    "});
10251}
10252
10253#[gpui::test]
10254async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10255    init_test(cx, |_| {});
10256    let mut cx = EditorTestContext::new(cx).await;
10257    let language = Arc::new(Language::new(
10258        LanguageConfig {
10259            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10260            ..Default::default()
10261        },
10262        Some(tree_sitter_rust::LANGUAGE.into()),
10263    ));
10264    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10265
10266    let toggle_comments = &ToggleComments {
10267        advance_downwards: false,
10268        ignore_indent: true,
10269    };
10270
10271    // If multiple selections intersect a line, the line is only toggled once.
10272    cx.set_state(indoc! {"
10273        fn a() {
10274        //    «b();
10275        //    c();
10276        //    ˇ» d();
10277        }
10278    "});
10279
10280    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10281
10282    cx.assert_editor_state(indoc! {"
10283        fn a() {
10284            «b();
10285            c();
10286            ˇ» d();
10287        }
10288    "});
10289
10290    // The comment prefix is inserted at the beginning of each line
10291    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10292
10293    cx.assert_editor_state(indoc! {"
10294        fn a() {
10295        //    «b();
10296        //    c();
10297        //    ˇ» d();
10298        }
10299    "});
10300
10301    // If a selection ends at the beginning of a line, that line is not toggled.
10302    cx.set_selections_state(indoc! {"
10303        fn a() {
10304        //    b();
10305        //    «c();
10306        ˇ»//     d();
10307        }
10308    "});
10309
10310    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10311
10312    cx.assert_editor_state(indoc! {"
10313        fn a() {
10314        //    b();
10315            «c();
10316        ˇ»//     d();
10317        }
10318    "});
10319
10320    // If a selection span a single line and is empty, the line is toggled.
10321    cx.set_state(indoc! {"
10322        fn a() {
10323            a();
10324            b();
10325        ˇ
10326        }
10327    "});
10328
10329    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10330
10331    cx.assert_editor_state(indoc! {"
10332        fn a() {
10333            a();
10334            b();
10335        //ˇ
10336        }
10337    "});
10338
10339    // If a selection span multiple lines, empty lines are not toggled.
10340    cx.set_state(indoc! {"
10341        fn a() {
10342            «a();
10343
10344            c();ˇ»
10345        }
10346    "});
10347
10348    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10349
10350    cx.assert_editor_state(indoc! {"
10351        fn a() {
10352        //    «a();
10353
10354        //    c();ˇ»
10355        }
10356    "});
10357
10358    // If a selection includes multiple comment prefixes, all lines are uncommented.
10359    cx.set_state(indoc! {"
10360        fn a() {
10361        //    «a();
10362        ///    b();
10363        //!    c();ˇ»
10364        }
10365    "});
10366
10367    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10368
10369    cx.assert_editor_state(indoc! {"
10370        fn a() {
10371            «a();
10372            b();
10373            c();ˇ»
10374        }
10375    "});
10376}
10377
10378#[gpui::test]
10379async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10380    init_test(cx, |_| {});
10381
10382    let language = Arc::new(Language::new(
10383        LanguageConfig {
10384            line_comments: vec!["// ".into()],
10385            ..Default::default()
10386        },
10387        Some(tree_sitter_rust::LANGUAGE.into()),
10388    ));
10389
10390    let mut cx = EditorTestContext::new(cx).await;
10391
10392    cx.language_registry().add(language.clone());
10393    cx.update_buffer(|buffer, cx| {
10394        buffer.set_language(Some(language), cx);
10395    });
10396
10397    let toggle_comments = &ToggleComments {
10398        advance_downwards: true,
10399        ignore_indent: false,
10400    };
10401
10402    // Single cursor on one line -> advance
10403    // Cursor moves horizontally 3 characters as well on non-blank line
10404    cx.set_state(indoc!(
10405        "fn a() {
10406             ˇdog();
10407             cat();
10408        }"
10409    ));
10410    cx.update_editor(|editor, window, cx| {
10411        editor.toggle_comments(toggle_comments, window, cx);
10412    });
10413    cx.assert_editor_state(indoc!(
10414        "fn a() {
10415             // dog();
10416             catˇ();
10417        }"
10418    ));
10419
10420    // Single selection on one line -> don't advance
10421    cx.set_state(indoc!(
10422        "fn a() {
10423             «dog()ˇ»;
10424             cat();
10425        }"
10426    ));
10427    cx.update_editor(|editor, window, cx| {
10428        editor.toggle_comments(toggle_comments, window, cx);
10429    });
10430    cx.assert_editor_state(indoc!(
10431        "fn a() {
10432             // «dog()ˇ»;
10433             cat();
10434        }"
10435    ));
10436
10437    // Multiple cursors on one line -> advance
10438    cx.set_state(indoc!(
10439        "fn a() {
10440             ˇdˇog();
10441             cat();
10442        }"
10443    ));
10444    cx.update_editor(|editor, window, cx| {
10445        editor.toggle_comments(toggle_comments, window, cx);
10446    });
10447    cx.assert_editor_state(indoc!(
10448        "fn a() {
10449             // dog();
10450             catˇ(ˇ);
10451        }"
10452    ));
10453
10454    // Multiple cursors on one line, with selection -> don't advance
10455    cx.set_state(indoc!(
10456        "fn a() {
10457             ˇdˇog«()ˇ»;
10458             cat();
10459        }"
10460    ));
10461    cx.update_editor(|editor, window, cx| {
10462        editor.toggle_comments(toggle_comments, window, cx);
10463    });
10464    cx.assert_editor_state(indoc!(
10465        "fn a() {
10466             // ˇdˇog«()ˇ»;
10467             cat();
10468        }"
10469    ));
10470
10471    // Single cursor on one line -> advance
10472    // Cursor moves to column 0 on blank line
10473    cx.set_state(indoc!(
10474        "fn a() {
10475             ˇdog();
10476
10477             cat();
10478        }"
10479    ));
10480    cx.update_editor(|editor, window, cx| {
10481        editor.toggle_comments(toggle_comments, window, cx);
10482    });
10483    cx.assert_editor_state(indoc!(
10484        "fn a() {
10485             // dog();
10486        ˇ
10487             cat();
10488        }"
10489    ));
10490
10491    // Single cursor on one line -> advance
10492    // Cursor starts and ends at column 0
10493    cx.set_state(indoc!(
10494        "fn a() {
10495         ˇ    dog();
10496             cat();
10497        }"
10498    ));
10499    cx.update_editor(|editor, window, cx| {
10500        editor.toggle_comments(toggle_comments, window, cx);
10501    });
10502    cx.assert_editor_state(indoc!(
10503        "fn a() {
10504             // dog();
10505         ˇ    cat();
10506        }"
10507    ));
10508}
10509
10510#[gpui::test]
10511async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10512    init_test(cx, |_| {});
10513
10514    let mut cx = EditorTestContext::new(cx).await;
10515
10516    let html_language = Arc::new(
10517        Language::new(
10518            LanguageConfig {
10519                name: "HTML".into(),
10520                block_comment: Some(("<!-- ".into(), " -->".into())),
10521                ..Default::default()
10522            },
10523            Some(tree_sitter_html::LANGUAGE.into()),
10524        )
10525        .with_injection_query(
10526            r#"
10527            (script_element
10528                (raw_text) @injection.content
10529                (#set! injection.language "javascript"))
10530            "#,
10531        )
10532        .unwrap(),
10533    );
10534
10535    let javascript_language = Arc::new(Language::new(
10536        LanguageConfig {
10537            name: "JavaScript".into(),
10538            line_comments: vec!["// ".into()],
10539            ..Default::default()
10540        },
10541        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10542    ));
10543
10544    cx.language_registry().add(html_language.clone());
10545    cx.language_registry().add(javascript_language.clone());
10546    cx.update_buffer(|buffer, cx| {
10547        buffer.set_language(Some(html_language), cx);
10548    });
10549
10550    // Toggle comments for empty selections
10551    cx.set_state(
10552        &r#"
10553            <p>A</p>ˇ
10554            <p>B</p>ˇ
10555            <p>C</p>ˇ
10556        "#
10557        .unindent(),
10558    );
10559    cx.update_editor(|editor, window, cx| {
10560        editor.toggle_comments(&ToggleComments::default(), window, cx)
10561    });
10562    cx.assert_editor_state(
10563        &r#"
10564            <!-- <p>A</p>ˇ -->
10565            <!-- <p>B</p>ˇ -->
10566            <!-- <p>C</p>ˇ -->
10567        "#
10568        .unindent(),
10569    );
10570    cx.update_editor(|editor, window, cx| {
10571        editor.toggle_comments(&ToggleComments::default(), window, cx)
10572    });
10573    cx.assert_editor_state(
10574        &r#"
10575            <p>A</p>ˇ
10576            <p>B</p>ˇ
10577            <p>C</p>ˇ
10578        "#
10579        .unindent(),
10580    );
10581
10582    // Toggle comments for mixture of empty and non-empty selections, where
10583    // multiple selections occupy a given line.
10584    cx.set_state(
10585        &r#"
10586            <p>A«</p>
10587            <p>ˇ»B</p>ˇ
10588            <p>C«</p>
10589            <p>ˇ»D</p>ˇ
10590        "#
10591        .unindent(),
10592    );
10593
10594    cx.update_editor(|editor, window, cx| {
10595        editor.toggle_comments(&ToggleComments::default(), window, cx)
10596    });
10597    cx.assert_editor_state(
10598        &r#"
10599            <!-- <p>A«</p>
10600            <p>ˇ»B</p>ˇ -->
10601            <!-- <p>C«</p>
10602            <p>ˇ»D</p>ˇ -->
10603        "#
10604        .unindent(),
10605    );
10606    cx.update_editor(|editor, window, cx| {
10607        editor.toggle_comments(&ToggleComments::default(), window, cx)
10608    });
10609    cx.assert_editor_state(
10610        &r#"
10611            <p>A«</p>
10612            <p>ˇ»B</p>ˇ
10613            <p>C«</p>
10614            <p>ˇ»D</p>ˇ
10615        "#
10616        .unindent(),
10617    );
10618
10619    // Toggle comments when different languages are active for different
10620    // selections.
10621    cx.set_state(
10622        &r#"
10623            ˇ<script>
10624                ˇvar x = new Y();
10625            ˇ</script>
10626        "#
10627        .unindent(),
10628    );
10629    cx.executor().run_until_parked();
10630    cx.update_editor(|editor, window, cx| {
10631        editor.toggle_comments(&ToggleComments::default(), window, cx)
10632    });
10633    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10634    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10635    cx.assert_editor_state(
10636        &r#"
10637            <!-- ˇ<script> -->
10638                // ˇvar x = new Y();
10639            <!-- ˇ</script> -->
10640        "#
10641        .unindent(),
10642    );
10643}
10644
10645#[gpui::test]
10646fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10647    init_test(cx, |_| {});
10648
10649    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10650    let multibuffer = cx.new(|cx| {
10651        let mut multibuffer = MultiBuffer::new(ReadWrite);
10652        multibuffer.push_excerpts(
10653            buffer.clone(),
10654            [
10655                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
10656                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
10657            ],
10658            cx,
10659        );
10660        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
10661        multibuffer
10662    });
10663
10664    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10665    editor.update_in(cx, |editor, window, cx| {
10666        assert_eq!(editor.text(cx), "aaaa\nbbbb");
10667        editor.change_selections(None, window, cx, |s| {
10668            s.select_ranges([
10669                Point::new(0, 0)..Point::new(0, 0),
10670                Point::new(1, 0)..Point::new(1, 0),
10671            ])
10672        });
10673
10674        editor.handle_input("X", window, cx);
10675        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
10676        assert_eq!(
10677            editor.selections.ranges(cx),
10678            [
10679                Point::new(0, 1)..Point::new(0, 1),
10680                Point::new(1, 1)..Point::new(1, 1),
10681            ]
10682        );
10683
10684        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
10685        editor.change_selections(None, window, cx, |s| {
10686            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
10687        });
10688        editor.backspace(&Default::default(), window, cx);
10689        assert_eq!(editor.text(cx), "Xa\nbbb");
10690        assert_eq!(
10691            editor.selections.ranges(cx),
10692            [Point::new(1, 0)..Point::new(1, 0)]
10693        );
10694
10695        editor.change_selections(None, window, cx, |s| {
10696            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
10697        });
10698        editor.backspace(&Default::default(), window, cx);
10699        assert_eq!(editor.text(cx), "X\nbb");
10700        assert_eq!(
10701            editor.selections.ranges(cx),
10702            [Point::new(0, 1)..Point::new(0, 1)]
10703        );
10704    });
10705}
10706
10707#[gpui::test]
10708fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
10709    init_test(cx, |_| {});
10710
10711    let markers = vec![('[', ']').into(), ('(', ')').into()];
10712    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
10713        indoc! {"
10714            [aaaa
10715            (bbbb]
10716            cccc)",
10717        },
10718        markers.clone(),
10719    );
10720    let excerpt_ranges = markers.into_iter().map(|marker| {
10721        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
10722        ExcerptRange::new(context.clone())
10723    });
10724    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
10725    let multibuffer = cx.new(|cx| {
10726        let mut multibuffer = MultiBuffer::new(ReadWrite);
10727        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
10728        multibuffer
10729    });
10730
10731    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
10732    editor.update_in(cx, |editor, window, cx| {
10733        let (expected_text, selection_ranges) = marked_text_ranges(
10734            indoc! {"
10735                aaaa
10736                bˇbbb
10737                bˇbbˇb
10738                cccc"
10739            },
10740            true,
10741        );
10742        assert_eq!(editor.text(cx), expected_text);
10743        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
10744
10745        editor.handle_input("X", window, cx);
10746
10747        let (expected_text, expected_selections) = marked_text_ranges(
10748            indoc! {"
10749                aaaa
10750                bXˇbbXb
10751                bXˇbbXˇb
10752                cccc"
10753            },
10754            false,
10755        );
10756        assert_eq!(editor.text(cx), expected_text);
10757        assert_eq!(editor.selections.ranges(cx), expected_selections);
10758
10759        editor.newline(&Newline, window, cx);
10760        let (expected_text, expected_selections) = marked_text_ranges(
10761            indoc! {"
10762                aaaa
10763                bX
10764                ˇbbX
10765                b
10766                bX
10767                ˇbbX
10768                ˇb
10769                cccc"
10770            },
10771            false,
10772        );
10773        assert_eq!(editor.text(cx), expected_text);
10774        assert_eq!(editor.selections.ranges(cx), expected_selections);
10775    });
10776}
10777
10778#[gpui::test]
10779fn test_refresh_selections(cx: &mut TestAppContext) {
10780    init_test(cx, |_| {});
10781
10782    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10783    let mut excerpt1_id = None;
10784    let multibuffer = cx.new(|cx| {
10785        let mut multibuffer = MultiBuffer::new(ReadWrite);
10786        excerpt1_id = multibuffer
10787            .push_excerpts(
10788                buffer.clone(),
10789                [
10790                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
10791                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
10792                ],
10793                cx,
10794            )
10795            .into_iter()
10796            .next();
10797        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10798        multibuffer
10799    });
10800
10801    let editor = cx.add_window(|window, cx| {
10802        let mut editor = build_editor(multibuffer.clone(), window, cx);
10803        let snapshot = editor.snapshot(window, cx);
10804        editor.change_selections(None, window, cx, |s| {
10805            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
10806        });
10807        editor.begin_selection(
10808            Point::new(2, 1).to_display_point(&snapshot),
10809            true,
10810            1,
10811            window,
10812            cx,
10813        );
10814        assert_eq!(
10815            editor.selections.ranges(cx),
10816            [
10817                Point::new(1, 3)..Point::new(1, 3),
10818                Point::new(2, 1)..Point::new(2, 1),
10819            ]
10820        );
10821        editor
10822    });
10823
10824    // Refreshing selections is a no-op when excerpts haven't changed.
10825    _ = editor.update(cx, |editor, window, cx| {
10826        editor.change_selections(None, window, cx, |s| s.refresh());
10827        assert_eq!(
10828            editor.selections.ranges(cx),
10829            [
10830                Point::new(1, 3)..Point::new(1, 3),
10831                Point::new(2, 1)..Point::new(2, 1),
10832            ]
10833        );
10834    });
10835
10836    multibuffer.update(cx, |multibuffer, cx| {
10837        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10838    });
10839    _ = editor.update(cx, |editor, window, cx| {
10840        // Removing an excerpt causes the first selection to become degenerate.
10841        assert_eq!(
10842            editor.selections.ranges(cx),
10843            [
10844                Point::new(0, 0)..Point::new(0, 0),
10845                Point::new(0, 1)..Point::new(0, 1)
10846            ]
10847        );
10848
10849        // Refreshing selections will relocate the first selection to the original buffer
10850        // location.
10851        editor.change_selections(None, window, cx, |s| s.refresh());
10852        assert_eq!(
10853            editor.selections.ranges(cx),
10854            [
10855                Point::new(0, 1)..Point::new(0, 1),
10856                Point::new(0, 3)..Point::new(0, 3)
10857            ]
10858        );
10859        assert!(editor.selections.pending_anchor().is_some());
10860    });
10861}
10862
10863#[gpui::test]
10864fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
10865    init_test(cx, |_| {});
10866
10867    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10868    let mut excerpt1_id = None;
10869    let multibuffer = cx.new(|cx| {
10870        let mut multibuffer = MultiBuffer::new(ReadWrite);
10871        excerpt1_id = multibuffer
10872            .push_excerpts(
10873                buffer.clone(),
10874                [
10875                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
10876                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
10877                ],
10878                cx,
10879            )
10880            .into_iter()
10881            .next();
10882        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
10883        multibuffer
10884    });
10885
10886    let editor = cx.add_window(|window, cx| {
10887        let mut editor = build_editor(multibuffer.clone(), window, cx);
10888        let snapshot = editor.snapshot(window, cx);
10889        editor.begin_selection(
10890            Point::new(1, 3).to_display_point(&snapshot),
10891            false,
10892            1,
10893            window,
10894            cx,
10895        );
10896        assert_eq!(
10897            editor.selections.ranges(cx),
10898            [Point::new(1, 3)..Point::new(1, 3)]
10899        );
10900        editor
10901    });
10902
10903    multibuffer.update(cx, |multibuffer, cx| {
10904        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
10905    });
10906    _ = editor.update(cx, |editor, window, cx| {
10907        assert_eq!(
10908            editor.selections.ranges(cx),
10909            [Point::new(0, 0)..Point::new(0, 0)]
10910        );
10911
10912        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10913        editor.change_selections(None, window, cx, |s| s.refresh());
10914        assert_eq!(
10915            editor.selections.ranges(cx),
10916            [Point::new(0, 3)..Point::new(0, 3)]
10917        );
10918        assert!(editor.selections.pending_anchor().is_some());
10919    });
10920}
10921
10922#[gpui::test]
10923async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
10924    init_test(cx, |_| {});
10925
10926    let language = Arc::new(
10927        Language::new(
10928            LanguageConfig {
10929                brackets: BracketPairConfig {
10930                    pairs: vec![
10931                        BracketPair {
10932                            start: "{".to_string(),
10933                            end: "}".to_string(),
10934                            close: true,
10935                            surround: true,
10936                            newline: true,
10937                        },
10938                        BracketPair {
10939                            start: "/* ".to_string(),
10940                            end: " */".to_string(),
10941                            close: true,
10942                            surround: true,
10943                            newline: true,
10944                        },
10945                    ],
10946                    ..Default::default()
10947                },
10948                ..Default::default()
10949            },
10950            Some(tree_sitter_rust::LANGUAGE.into()),
10951        )
10952        .with_indents_query("")
10953        .unwrap(),
10954    );
10955
10956    let text = concat!(
10957        "{   }\n",     //
10958        "  x\n",       //
10959        "  /*   */\n", //
10960        "x\n",         //
10961        "{{} }\n",     //
10962    );
10963
10964    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
10965    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
10966    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
10967    editor
10968        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
10969        .await;
10970
10971    editor.update_in(cx, |editor, window, cx| {
10972        editor.change_selections(None, window, cx, |s| {
10973            s.select_display_ranges([
10974                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
10975                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
10976                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
10977            ])
10978        });
10979        editor.newline(&Newline, window, cx);
10980
10981        assert_eq!(
10982            editor.buffer().read(cx).read(cx).text(),
10983            concat!(
10984                "{ \n",    // Suppress rustfmt
10985                "\n",      //
10986                "}\n",     //
10987                "  x\n",   //
10988                "  /* \n", //
10989                "  \n",    //
10990                "  */\n",  //
10991                "x\n",     //
10992                "{{} \n",  //
10993                "}\n",     //
10994            )
10995        );
10996    });
10997}
10998
10999#[gpui::test]
11000fn test_highlighted_ranges(cx: &mut TestAppContext) {
11001    init_test(cx, |_| {});
11002
11003    let editor = cx.add_window(|window, cx| {
11004        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11005        build_editor(buffer.clone(), window, cx)
11006    });
11007
11008    _ = editor.update(cx, |editor, window, cx| {
11009        struct Type1;
11010        struct Type2;
11011
11012        let buffer = editor.buffer.read(cx).snapshot(cx);
11013
11014        let anchor_range =
11015            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11016
11017        editor.highlight_background::<Type1>(
11018            &[
11019                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11020                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11021                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11022                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11023            ],
11024            |_| Hsla::red(),
11025            cx,
11026        );
11027        editor.highlight_background::<Type2>(
11028            &[
11029                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11030                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11031                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11032                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11033            ],
11034            |_| Hsla::green(),
11035            cx,
11036        );
11037
11038        let snapshot = editor.snapshot(window, cx);
11039        let mut highlighted_ranges = editor.background_highlights_in_range(
11040            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11041            &snapshot,
11042            cx.theme().colors(),
11043        );
11044        // Enforce a consistent ordering based on color without relying on the ordering of the
11045        // highlight's `TypeId` which is non-executor.
11046        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11047        assert_eq!(
11048            highlighted_ranges,
11049            &[
11050                (
11051                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11052                    Hsla::red(),
11053                ),
11054                (
11055                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11056                    Hsla::red(),
11057                ),
11058                (
11059                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11060                    Hsla::green(),
11061                ),
11062                (
11063                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11064                    Hsla::green(),
11065                ),
11066            ]
11067        );
11068        assert_eq!(
11069            editor.background_highlights_in_range(
11070                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11071                &snapshot,
11072                cx.theme().colors(),
11073            ),
11074            &[(
11075                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11076                Hsla::red(),
11077            )]
11078        );
11079    });
11080}
11081
11082#[gpui::test]
11083async fn test_following(cx: &mut TestAppContext) {
11084    init_test(cx, |_| {});
11085
11086    let fs = FakeFs::new(cx.executor());
11087    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11088
11089    let buffer = project.update(cx, |project, cx| {
11090        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11091        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11092    });
11093    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11094    let follower = cx.update(|cx| {
11095        cx.open_window(
11096            WindowOptions {
11097                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11098                    gpui::Point::new(px(0.), px(0.)),
11099                    gpui::Point::new(px(10.), px(80.)),
11100                ))),
11101                ..Default::default()
11102            },
11103            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11104        )
11105        .unwrap()
11106    });
11107
11108    let is_still_following = Rc::new(RefCell::new(true));
11109    let follower_edit_event_count = Rc::new(RefCell::new(0));
11110    let pending_update = Rc::new(RefCell::new(None));
11111    let leader_entity = leader.root(cx).unwrap();
11112    let follower_entity = follower.root(cx).unwrap();
11113    _ = follower.update(cx, {
11114        let update = pending_update.clone();
11115        let is_still_following = is_still_following.clone();
11116        let follower_edit_event_count = follower_edit_event_count.clone();
11117        |_, window, cx| {
11118            cx.subscribe_in(
11119                &leader_entity,
11120                window,
11121                move |_, leader, event, window, cx| {
11122                    leader.read(cx).add_event_to_update_proto(
11123                        event,
11124                        &mut update.borrow_mut(),
11125                        window,
11126                        cx,
11127                    );
11128                },
11129            )
11130            .detach();
11131
11132            cx.subscribe_in(
11133                &follower_entity,
11134                window,
11135                move |_, _, event: &EditorEvent, _window, _cx| {
11136                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11137                        *is_still_following.borrow_mut() = false;
11138                    }
11139
11140                    if let EditorEvent::BufferEdited = event {
11141                        *follower_edit_event_count.borrow_mut() += 1;
11142                    }
11143                },
11144            )
11145            .detach();
11146        }
11147    });
11148
11149    // Update the selections only
11150    _ = leader.update(cx, |leader, window, cx| {
11151        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11152    });
11153    follower
11154        .update(cx, |follower, window, cx| {
11155            follower.apply_update_proto(
11156                &project,
11157                pending_update.borrow_mut().take().unwrap(),
11158                window,
11159                cx,
11160            )
11161        })
11162        .unwrap()
11163        .await
11164        .unwrap();
11165    _ = follower.update(cx, |follower, _, cx| {
11166        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11167    });
11168    assert!(*is_still_following.borrow());
11169    assert_eq!(*follower_edit_event_count.borrow(), 0);
11170
11171    // Update the scroll position only
11172    _ = leader.update(cx, |leader, window, cx| {
11173        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11174    });
11175    follower
11176        .update(cx, |follower, window, cx| {
11177            follower.apply_update_proto(
11178                &project,
11179                pending_update.borrow_mut().take().unwrap(),
11180                window,
11181                cx,
11182            )
11183        })
11184        .unwrap()
11185        .await
11186        .unwrap();
11187    assert_eq!(
11188        follower
11189            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11190            .unwrap(),
11191        gpui::Point::new(1.5, 3.5)
11192    );
11193    assert!(*is_still_following.borrow());
11194    assert_eq!(*follower_edit_event_count.borrow(), 0);
11195
11196    // Update the selections and scroll position. The follower's scroll position is updated
11197    // via autoscroll, not via the leader's exact scroll position.
11198    _ = leader.update(cx, |leader, window, cx| {
11199        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11200        leader.request_autoscroll(Autoscroll::newest(), cx);
11201        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11202    });
11203    follower
11204        .update(cx, |follower, window, cx| {
11205            follower.apply_update_proto(
11206                &project,
11207                pending_update.borrow_mut().take().unwrap(),
11208                window,
11209                cx,
11210            )
11211        })
11212        .unwrap()
11213        .await
11214        .unwrap();
11215    _ = follower.update(cx, |follower, _, cx| {
11216        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11217        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11218    });
11219    assert!(*is_still_following.borrow());
11220
11221    // Creating a pending selection that precedes another selection
11222    _ = leader.update(cx, |leader, window, cx| {
11223        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11224        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11225    });
11226    follower
11227        .update(cx, |follower, window, cx| {
11228            follower.apply_update_proto(
11229                &project,
11230                pending_update.borrow_mut().take().unwrap(),
11231                window,
11232                cx,
11233            )
11234        })
11235        .unwrap()
11236        .await
11237        .unwrap();
11238    _ = follower.update(cx, |follower, _, cx| {
11239        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11240    });
11241    assert!(*is_still_following.borrow());
11242
11243    // Extend the pending selection so that it surrounds another selection
11244    _ = leader.update(cx, |leader, window, cx| {
11245        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11246    });
11247    follower
11248        .update(cx, |follower, window, cx| {
11249            follower.apply_update_proto(
11250                &project,
11251                pending_update.borrow_mut().take().unwrap(),
11252                window,
11253                cx,
11254            )
11255        })
11256        .unwrap()
11257        .await
11258        .unwrap();
11259    _ = follower.update(cx, |follower, _, cx| {
11260        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11261    });
11262
11263    // Scrolling locally breaks the follow
11264    _ = follower.update(cx, |follower, window, cx| {
11265        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11266        follower.set_scroll_anchor(
11267            ScrollAnchor {
11268                anchor: top_anchor,
11269                offset: gpui::Point::new(0.0, 0.5),
11270            },
11271            window,
11272            cx,
11273        );
11274    });
11275    assert!(!(*is_still_following.borrow()));
11276}
11277
11278#[gpui::test]
11279async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11280    init_test(cx, |_| {});
11281
11282    let fs = FakeFs::new(cx.executor());
11283    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11284    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11285    let pane = workspace
11286        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11287        .unwrap();
11288
11289    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11290
11291    let leader = pane.update_in(cx, |_, window, cx| {
11292        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11293        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11294    });
11295
11296    // Start following the editor when it has no excerpts.
11297    let mut state_message =
11298        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11299    let workspace_entity = workspace.root(cx).unwrap();
11300    let follower_1 = cx
11301        .update_window(*workspace.deref(), |_, window, cx| {
11302            Editor::from_state_proto(
11303                workspace_entity,
11304                ViewId {
11305                    creator: Default::default(),
11306                    id: 0,
11307                },
11308                &mut state_message,
11309                window,
11310                cx,
11311            )
11312        })
11313        .unwrap()
11314        .unwrap()
11315        .await
11316        .unwrap();
11317
11318    let update_message = Rc::new(RefCell::new(None));
11319    follower_1.update_in(cx, {
11320        let update = update_message.clone();
11321        |_, window, cx| {
11322            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11323                leader.read(cx).add_event_to_update_proto(
11324                    event,
11325                    &mut update.borrow_mut(),
11326                    window,
11327                    cx,
11328                );
11329            })
11330            .detach();
11331        }
11332    });
11333
11334    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11335        (
11336            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11337            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11338        )
11339    });
11340
11341    // Insert some excerpts.
11342    leader.update(cx, |leader, cx| {
11343        leader.buffer.update(cx, |multibuffer, cx| {
11344            let excerpt_ids = multibuffer.push_excerpts(
11345                buffer_1.clone(),
11346                [
11347                    ExcerptRange::new(1..6),
11348                    ExcerptRange::new(12..15),
11349                    ExcerptRange::new(0..3),
11350                ],
11351                cx,
11352            );
11353            multibuffer.insert_excerpts_after(
11354                excerpt_ids[0],
11355                buffer_2.clone(),
11356                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11357                cx,
11358            );
11359        });
11360    });
11361
11362    // Apply the update of adding the excerpts.
11363    follower_1
11364        .update_in(cx, |follower, window, cx| {
11365            follower.apply_update_proto(
11366                &project,
11367                update_message.borrow().clone().unwrap(),
11368                window,
11369                cx,
11370            )
11371        })
11372        .await
11373        .unwrap();
11374    assert_eq!(
11375        follower_1.update(cx, |editor, cx| editor.text(cx)),
11376        leader.update(cx, |editor, cx| editor.text(cx))
11377    );
11378    update_message.borrow_mut().take();
11379
11380    // Start following separately after it already has excerpts.
11381    let mut state_message =
11382        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11383    let workspace_entity = workspace.root(cx).unwrap();
11384    let follower_2 = cx
11385        .update_window(*workspace.deref(), |_, window, cx| {
11386            Editor::from_state_proto(
11387                workspace_entity,
11388                ViewId {
11389                    creator: Default::default(),
11390                    id: 0,
11391                },
11392                &mut state_message,
11393                window,
11394                cx,
11395            )
11396        })
11397        .unwrap()
11398        .unwrap()
11399        .await
11400        .unwrap();
11401    assert_eq!(
11402        follower_2.update(cx, |editor, cx| editor.text(cx)),
11403        leader.update(cx, |editor, cx| editor.text(cx))
11404    );
11405
11406    // Remove some excerpts.
11407    leader.update(cx, |leader, cx| {
11408        leader.buffer.update(cx, |multibuffer, cx| {
11409            let excerpt_ids = multibuffer.excerpt_ids();
11410            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11411            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11412        });
11413    });
11414
11415    // Apply the update of removing the excerpts.
11416    follower_1
11417        .update_in(cx, |follower, window, cx| {
11418            follower.apply_update_proto(
11419                &project,
11420                update_message.borrow().clone().unwrap(),
11421                window,
11422                cx,
11423            )
11424        })
11425        .await
11426        .unwrap();
11427    follower_2
11428        .update_in(cx, |follower, window, cx| {
11429            follower.apply_update_proto(
11430                &project,
11431                update_message.borrow().clone().unwrap(),
11432                window,
11433                cx,
11434            )
11435        })
11436        .await
11437        .unwrap();
11438    update_message.borrow_mut().take();
11439    assert_eq!(
11440        follower_1.update(cx, |editor, cx| editor.text(cx)),
11441        leader.update(cx, |editor, cx| editor.text(cx))
11442    );
11443}
11444
11445#[gpui::test]
11446async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11447    init_test(cx, |_| {});
11448
11449    let mut cx = EditorTestContext::new(cx).await;
11450    let lsp_store =
11451        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11452
11453    cx.set_state(indoc! {"
11454        ˇfn func(abc def: i32) -> u32 {
11455        }
11456    "});
11457
11458    cx.update(|_, cx| {
11459        lsp_store.update(cx, |lsp_store, cx| {
11460            lsp_store
11461                .update_diagnostics(
11462                    LanguageServerId(0),
11463                    lsp::PublishDiagnosticsParams {
11464                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11465                        version: None,
11466                        diagnostics: vec![
11467                            lsp::Diagnostic {
11468                                range: lsp::Range::new(
11469                                    lsp::Position::new(0, 11),
11470                                    lsp::Position::new(0, 12),
11471                                ),
11472                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11473                                ..Default::default()
11474                            },
11475                            lsp::Diagnostic {
11476                                range: lsp::Range::new(
11477                                    lsp::Position::new(0, 12),
11478                                    lsp::Position::new(0, 15),
11479                                ),
11480                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11481                                ..Default::default()
11482                            },
11483                            lsp::Diagnostic {
11484                                range: lsp::Range::new(
11485                                    lsp::Position::new(0, 25),
11486                                    lsp::Position::new(0, 28),
11487                                ),
11488                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11489                                ..Default::default()
11490                            },
11491                        ],
11492                    },
11493                    &[],
11494                    cx,
11495                )
11496                .unwrap()
11497        });
11498    });
11499
11500    executor.run_until_parked();
11501
11502    cx.update_editor(|editor, window, cx| {
11503        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11504    });
11505
11506    cx.assert_editor_state(indoc! {"
11507        fn func(abc def: i32) -> ˇu32 {
11508        }
11509    "});
11510
11511    cx.update_editor(|editor, window, cx| {
11512        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11513    });
11514
11515    cx.assert_editor_state(indoc! {"
11516        fn func(abc ˇdef: i32) -> u32 {
11517        }
11518    "});
11519
11520    cx.update_editor(|editor, window, cx| {
11521        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11522    });
11523
11524    cx.assert_editor_state(indoc! {"
11525        fn func(abcˇ def: i32) -> u32 {
11526        }
11527    "});
11528
11529    cx.update_editor(|editor, window, cx| {
11530        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11531    });
11532
11533    cx.assert_editor_state(indoc! {"
11534        fn func(abc def: i32) -> ˇu32 {
11535        }
11536    "});
11537}
11538
11539#[gpui::test]
11540async fn cycle_through_same_place_diagnostics(
11541    executor: BackgroundExecutor,
11542    cx: &mut TestAppContext,
11543) {
11544    init_test(cx, |_| {});
11545
11546    let mut cx = EditorTestContext::new(cx).await;
11547    let lsp_store =
11548        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11549
11550    cx.set_state(indoc! {"
11551        ˇfn func(abc def: i32) -> u32 {
11552        }
11553    "});
11554
11555    cx.update(|_, cx| {
11556        lsp_store.update(cx, |lsp_store, cx| {
11557            lsp_store
11558                .update_diagnostics(
11559                    LanguageServerId(0),
11560                    lsp::PublishDiagnosticsParams {
11561                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11562                        version: None,
11563                        diagnostics: vec![
11564                            lsp::Diagnostic {
11565                                range: lsp::Range::new(
11566                                    lsp::Position::new(0, 11),
11567                                    lsp::Position::new(0, 12),
11568                                ),
11569                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11570                                ..Default::default()
11571                            },
11572                            lsp::Diagnostic {
11573                                range: lsp::Range::new(
11574                                    lsp::Position::new(0, 12),
11575                                    lsp::Position::new(0, 15),
11576                                ),
11577                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11578                                ..Default::default()
11579                            },
11580                            lsp::Diagnostic {
11581                                range: lsp::Range::new(
11582                                    lsp::Position::new(0, 12),
11583                                    lsp::Position::new(0, 15),
11584                                ),
11585                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11586                                ..Default::default()
11587                            },
11588                            lsp::Diagnostic {
11589                                range: lsp::Range::new(
11590                                    lsp::Position::new(0, 25),
11591                                    lsp::Position::new(0, 28),
11592                                ),
11593                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11594                                ..Default::default()
11595                            },
11596                        ],
11597                    },
11598                    &[],
11599                    cx,
11600                )
11601                .unwrap()
11602        });
11603    });
11604    executor.run_until_parked();
11605
11606    //// Backward
11607
11608    // Fourth diagnostic
11609    cx.update_editor(|editor, window, cx| {
11610        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11611    });
11612    cx.assert_editor_state(indoc! {"
11613        fn func(abc def: i32) -> ˇu32 {
11614        }
11615    "});
11616
11617    // Third diagnostic
11618    cx.update_editor(|editor, window, cx| {
11619        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11620    });
11621    cx.assert_editor_state(indoc! {"
11622        fn func(abc ˇdef: i32) -> u32 {
11623        }
11624    "});
11625
11626    // Second diagnostic, same place
11627    cx.update_editor(|editor, window, cx| {
11628        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11629    });
11630    cx.assert_editor_state(indoc! {"
11631        fn func(abc ˇdef: i32) -> u32 {
11632        }
11633    "});
11634
11635    // First diagnostic
11636    cx.update_editor(|editor, window, cx| {
11637        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11638    });
11639    cx.assert_editor_state(indoc! {"
11640        fn func(abcˇ def: i32) -> u32 {
11641        }
11642    "});
11643
11644    // Wrapped over, fourth diagnostic
11645    cx.update_editor(|editor, window, cx| {
11646        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11647    });
11648    cx.assert_editor_state(indoc! {"
11649        fn func(abc def: i32) -> ˇu32 {
11650        }
11651    "});
11652
11653    cx.update_editor(|editor, window, cx| {
11654        editor.move_to_beginning(&MoveToBeginning, window, cx);
11655    });
11656    cx.assert_editor_state(indoc! {"
11657        ˇfn func(abc def: i32) -> u32 {
11658        }
11659    "});
11660
11661    //// Forward
11662
11663    // First diagnostic
11664    cx.update_editor(|editor, window, cx| {
11665        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11666    });
11667    cx.assert_editor_state(indoc! {"
11668        fn func(abcˇ def: i32) -> u32 {
11669        }
11670    "});
11671
11672    // Second diagnostic
11673    cx.update_editor(|editor, window, cx| {
11674        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11675    });
11676    cx.assert_editor_state(indoc! {"
11677        fn func(abc ˇdef: i32) -> u32 {
11678        }
11679    "});
11680
11681    // Third diagnostic, same place
11682    cx.update_editor(|editor, window, cx| {
11683        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11684    });
11685    cx.assert_editor_state(indoc! {"
11686        fn func(abc ˇdef: i32) -> u32 {
11687        }
11688    "});
11689
11690    // Fourth diagnostic
11691    cx.update_editor(|editor, window, cx| {
11692        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11693    });
11694    cx.assert_editor_state(indoc! {"
11695        fn func(abc def: i32) -> ˇu32 {
11696        }
11697    "});
11698
11699    // Wrapped around, first diagnostic
11700    cx.update_editor(|editor, window, cx| {
11701        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11702    });
11703    cx.assert_editor_state(indoc! {"
11704        fn func(abcˇ def: i32) -> u32 {
11705        }
11706    "});
11707}
11708
11709#[gpui::test]
11710async fn active_diagnostics_dismiss_after_invalidation(
11711    executor: BackgroundExecutor,
11712    cx: &mut TestAppContext,
11713) {
11714    init_test(cx, |_| {});
11715
11716    let mut cx = EditorTestContext::new(cx).await;
11717    let lsp_store =
11718        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11719
11720    cx.set_state(indoc! {"
11721        ˇfn func(abc def: i32) -> u32 {
11722        }
11723    "});
11724
11725    let message = "Something's wrong!";
11726    cx.update(|_, cx| {
11727        lsp_store.update(cx, |lsp_store, cx| {
11728            lsp_store
11729                .update_diagnostics(
11730                    LanguageServerId(0),
11731                    lsp::PublishDiagnosticsParams {
11732                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11733                        version: None,
11734                        diagnostics: vec![lsp::Diagnostic {
11735                            range: lsp::Range::new(
11736                                lsp::Position::new(0, 11),
11737                                lsp::Position::new(0, 12),
11738                            ),
11739                            severity: Some(lsp::DiagnosticSeverity::ERROR),
11740                            message: message.to_string(),
11741                            ..Default::default()
11742                        }],
11743                    },
11744                    &[],
11745                    cx,
11746                )
11747                .unwrap()
11748        });
11749    });
11750    executor.run_until_parked();
11751
11752    cx.update_editor(|editor, window, cx| {
11753        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11754        assert_eq!(
11755            editor
11756                .active_diagnostics
11757                .as_ref()
11758                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
11759            Some(message),
11760            "Should have a diagnostics group activated"
11761        );
11762    });
11763    cx.assert_editor_state(indoc! {"
11764        fn func(abcˇ def: i32) -> u32 {
11765        }
11766    "});
11767
11768    cx.update(|_, cx| {
11769        lsp_store.update(cx, |lsp_store, cx| {
11770            lsp_store
11771                .update_diagnostics(
11772                    LanguageServerId(0),
11773                    lsp::PublishDiagnosticsParams {
11774                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11775                        version: None,
11776                        diagnostics: Vec::new(),
11777                    },
11778                    &[],
11779                    cx,
11780                )
11781                .unwrap()
11782        });
11783    });
11784    executor.run_until_parked();
11785    cx.update_editor(|editor, _, _| {
11786        assert_eq!(
11787            editor.active_diagnostics, None,
11788            "After no diagnostics set to the editor, no diagnostics should be active"
11789        );
11790    });
11791    cx.assert_editor_state(indoc! {"
11792        fn func(abcˇ def: i32) -> u32 {
11793        }
11794    "});
11795
11796    cx.update_editor(|editor, window, cx| {
11797        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
11798        assert_eq!(
11799            editor.active_diagnostics, None,
11800            "Should be no diagnostics to go to and activate"
11801        );
11802    });
11803    cx.assert_editor_state(indoc! {"
11804        fn func(abcˇ def: i32) -> u32 {
11805        }
11806    "});
11807}
11808
11809#[gpui::test]
11810async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
11811    init_test(cx, |_| {});
11812
11813    let mut cx = EditorTestContext::new(cx).await;
11814
11815    cx.set_state(indoc! {"
11816        fn func(abˇc def: i32) -> u32 {
11817        }
11818    "});
11819    let lsp_store =
11820        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11821
11822    cx.update(|_, cx| {
11823        lsp_store.update(cx, |lsp_store, cx| {
11824            lsp_store.update_diagnostics(
11825                LanguageServerId(0),
11826                lsp::PublishDiagnosticsParams {
11827                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11828                    version: None,
11829                    diagnostics: vec![lsp::Diagnostic {
11830                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
11831                        severity: Some(lsp::DiagnosticSeverity::ERROR),
11832                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
11833                        ..Default::default()
11834                    }],
11835                },
11836                &[],
11837                cx,
11838            )
11839        })
11840    }).unwrap();
11841    cx.run_until_parked();
11842    cx.update_editor(|editor, window, cx| {
11843        hover_popover::hover(editor, &Default::default(), window, cx)
11844    });
11845    cx.run_until_parked();
11846    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
11847}
11848
11849#[gpui::test]
11850async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11851    init_test(cx, |_| {});
11852
11853    let mut cx = EditorTestContext::new(cx).await;
11854
11855    let diff_base = r#"
11856        use some::mod;
11857
11858        const A: u32 = 42;
11859
11860        fn main() {
11861            println!("hello");
11862
11863            println!("world");
11864        }
11865        "#
11866    .unindent();
11867
11868    // Edits are modified, removed, modified, added
11869    cx.set_state(
11870        &r#"
11871        use some::modified;
11872
11873        ˇ
11874        fn main() {
11875            println!("hello there");
11876
11877            println!("around the");
11878            println!("world");
11879        }
11880        "#
11881        .unindent(),
11882    );
11883
11884    cx.set_head_text(&diff_base);
11885    executor.run_until_parked();
11886
11887    cx.update_editor(|editor, window, cx| {
11888        //Wrap around the bottom of the buffer
11889        for _ in 0..3 {
11890            editor.go_to_next_hunk(&GoToHunk, window, cx);
11891        }
11892    });
11893
11894    cx.assert_editor_state(
11895        &r#"
11896        ˇuse some::modified;
11897
11898
11899        fn main() {
11900            println!("hello there");
11901
11902            println!("around the");
11903            println!("world");
11904        }
11905        "#
11906        .unindent(),
11907    );
11908
11909    cx.update_editor(|editor, window, cx| {
11910        //Wrap around the top of the buffer
11911        for _ in 0..2 {
11912            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11913        }
11914    });
11915
11916    cx.assert_editor_state(
11917        &r#"
11918        use some::modified;
11919
11920
11921        fn main() {
11922        ˇ    println!("hello there");
11923
11924            println!("around the");
11925            println!("world");
11926        }
11927        "#
11928        .unindent(),
11929    );
11930
11931    cx.update_editor(|editor, window, cx| {
11932        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11933    });
11934
11935    cx.assert_editor_state(
11936        &r#"
11937        use some::modified;
11938
11939        ˇ
11940        fn main() {
11941            println!("hello there");
11942
11943            println!("around the");
11944            println!("world");
11945        }
11946        "#
11947        .unindent(),
11948    );
11949
11950    cx.update_editor(|editor, window, cx| {
11951        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11952    });
11953
11954    cx.assert_editor_state(
11955        &r#"
11956        ˇuse some::modified;
11957
11958
11959        fn main() {
11960            println!("hello there");
11961
11962            println!("around the");
11963            println!("world");
11964        }
11965        "#
11966        .unindent(),
11967    );
11968
11969    cx.update_editor(|editor, window, cx| {
11970        for _ in 0..2 {
11971            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
11972        }
11973    });
11974
11975    cx.assert_editor_state(
11976        &r#"
11977        use some::modified;
11978
11979
11980        fn main() {
11981        ˇ    println!("hello there");
11982
11983            println!("around the");
11984            println!("world");
11985        }
11986        "#
11987        .unindent(),
11988    );
11989
11990    cx.update_editor(|editor, window, cx| {
11991        editor.fold(&Fold, window, cx);
11992    });
11993
11994    cx.update_editor(|editor, window, cx| {
11995        editor.go_to_next_hunk(&GoToHunk, window, cx);
11996    });
11997
11998    cx.assert_editor_state(
11999        &r#"
12000        ˇuse some::modified;
12001
12002
12003        fn main() {
12004            println!("hello there");
12005
12006            println!("around the");
12007            println!("world");
12008        }
12009        "#
12010        .unindent(),
12011    );
12012}
12013
12014#[test]
12015fn test_split_words() {
12016    fn split(text: &str) -> Vec<&str> {
12017        split_words(text).collect()
12018    }
12019
12020    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12021    assert_eq!(split("hello_world"), &["hello_", "world"]);
12022    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12023    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12024    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12025    assert_eq!(split("helloworld"), &["helloworld"]);
12026
12027    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12028}
12029
12030#[gpui::test]
12031async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12032    init_test(cx, |_| {});
12033
12034    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12035    let mut assert = |before, after| {
12036        let _state_context = cx.set_state(before);
12037        cx.run_until_parked();
12038        cx.update_editor(|editor, window, cx| {
12039            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12040        });
12041        cx.run_until_parked();
12042        cx.assert_editor_state(after);
12043    };
12044
12045    // Outside bracket jumps to outside of matching bracket
12046    assert("console.logˇ(var);", "console.log(var)ˇ;");
12047    assert("console.log(var)ˇ;", "console.logˇ(var);");
12048
12049    // Inside bracket jumps to inside of matching bracket
12050    assert("console.log(ˇvar);", "console.log(varˇ);");
12051    assert("console.log(varˇ);", "console.log(ˇvar);");
12052
12053    // When outside a bracket and inside, favor jumping to the inside bracket
12054    assert(
12055        "console.log('foo', [1, 2, 3]ˇ);",
12056        "console.log(ˇ'foo', [1, 2, 3]);",
12057    );
12058    assert(
12059        "console.log(ˇ'foo', [1, 2, 3]);",
12060        "console.log('foo', [1, 2, 3]ˇ);",
12061    );
12062
12063    // Bias forward if two options are equally likely
12064    assert(
12065        "let result = curried_fun()ˇ();",
12066        "let result = curried_fun()()ˇ;",
12067    );
12068
12069    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12070    assert(
12071        indoc! {"
12072            function test() {
12073                console.log('test')ˇ
12074            }"},
12075        indoc! {"
12076            function test() {
12077                console.logˇ('test')
12078            }"},
12079    );
12080}
12081
12082#[gpui::test]
12083async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12084    init_test(cx, |_| {});
12085
12086    let fs = FakeFs::new(cx.executor());
12087    fs.insert_tree(
12088        path!("/a"),
12089        json!({
12090            "main.rs": "fn main() { let a = 5; }",
12091            "other.rs": "// Test file",
12092        }),
12093    )
12094    .await;
12095    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12096
12097    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12098    language_registry.add(Arc::new(Language::new(
12099        LanguageConfig {
12100            name: "Rust".into(),
12101            matcher: LanguageMatcher {
12102                path_suffixes: vec!["rs".to_string()],
12103                ..Default::default()
12104            },
12105            brackets: BracketPairConfig {
12106                pairs: vec![BracketPair {
12107                    start: "{".to_string(),
12108                    end: "}".to_string(),
12109                    close: true,
12110                    surround: true,
12111                    newline: true,
12112                }],
12113                disabled_scopes_by_bracket_ix: Vec::new(),
12114            },
12115            ..Default::default()
12116        },
12117        Some(tree_sitter_rust::LANGUAGE.into()),
12118    )));
12119    let mut fake_servers = language_registry.register_fake_lsp(
12120        "Rust",
12121        FakeLspAdapter {
12122            capabilities: lsp::ServerCapabilities {
12123                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12124                    first_trigger_character: "{".to_string(),
12125                    more_trigger_character: None,
12126                }),
12127                ..Default::default()
12128            },
12129            ..Default::default()
12130        },
12131    );
12132
12133    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12134
12135    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12136
12137    let worktree_id = workspace
12138        .update(cx, |workspace, _, cx| {
12139            workspace.project().update(cx, |project, cx| {
12140                project.worktrees(cx).next().unwrap().read(cx).id()
12141            })
12142        })
12143        .unwrap();
12144
12145    let buffer = project
12146        .update(cx, |project, cx| {
12147            project.open_local_buffer(path!("/a/main.rs"), cx)
12148        })
12149        .await
12150        .unwrap();
12151    let editor_handle = workspace
12152        .update(cx, |workspace, window, cx| {
12153            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12154        })
12155        .unwrap()
12156        .await
12157        .unwrap()
12158        .downcast::<Editor>()
12159        .unwrap();
12160
12161    cx.executor().start_waiting();
12162    let fake_server = fake_servers.next().await.unwrap();
12163
12164    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12165        |params, _| async move {
12166            assert_eq!(
12167                params.text_document_position.text_document.uri,
12168                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12169            );
12170            assert_eq!(
12171                params.text_document_position.position,
12172                lsp::Position::new(0, 21),
12173            );
12174
12175            Ok(Some(vec![lsp::TextEdit {
12176                new_text: "]".to_string(),
12177                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12178            }]))
12179        },
12180    );
12181
12182    editor_handle.update_in(cx, |editor, window, cx| {
12183        window.focus(&editor.focus_handle(cx));
12184        editor.change_selections(None, window, cx, |s| {
12185            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12186        });
12187        editor.handle_input("{", window, cx);
12188    });
12189
12190    cx.executor().run_until_parked();
12191
12192    buffer.update(cx, |buffer, _| {
12193        assert_eq!(
12194            buffer.text(),
12195            "fn main() { let a = {5}; }",
12196            "No extra braces from on type formatting should appear in the buffer"
12197        )
12198    });
12199}
12200
12201#[gpui::test]
12202async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12203    init_test(cx, |_| {});
12204
12205    let fs = FakeFs::new(cx.executor());
12206    fs.insert_tree(
12207        path!("/a"),
12208        json!({
12209            "main.rs": "fn main() { let a = 5; }",
12210            "other.rs": "// Test file",
12211        }),
12212    )
12213    .await;
12214
12215    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12216
12217    let server_restarts = Arc::new(AtomicUsize::new(0));
12218    let closure_restarts = Arc::clone(&server_restarts);
12219    let language_server_name = "test language server";
12220    let language_name: LanguageName = "Rust".into();
12221
12222    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12223    language_registry.add(Arc::new(Language::new(
12224        LanguageConfig {
12225            name: language_name.clone(),
12226            matcher: LanguageMatcher {
12227                path_suffixes: vec!["rs".to_string()],
12228                ..Default::default()
12229            },
12230            ..Default::default()
12231        },
12232        Some(tree_sitter_rust::LANGUAGE.into()),
12233    )));
12234    let mut fake_servers = language_registry.register_fake_lsp(
12235        "Rust",
12236        FakeLspAdapter {
12237            name: language_server_name,
12238            initialization_options: Some(json!({
12239                "testOptionValue": true
12240            })),
12241            initializer: Some(Box::new(move |fake_server| {
12242                let task_restarts = Arc::clone(&closure_restarts);
12243                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12244                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12245                    futures::future::ready(Ok(()))
12246                });
12247            })),
12248            ..Default::default()
12249        },
12250    );
12251
12252    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12253    let _buffer = project
12254        .update(cx, |project, cx| {
12255            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12256        })
12257        .await
12258        .unwrap();
12259    let _fake_server = fake_servers.next().await.unwrap();
12260    update_test_language_settings(cx, |language_settings| {
12261        language_settings.languages.insert(
12262            language_name.clone(),
12263            LanguageSettingsContent {
12264                tab_size: NonZeroU32::new(8),
12265                ..Default::default()
12266            },
12267        );
12268    });
12269    cx.executor().run_until_parked();
12270    assert_eq!(
12271        server_restarts.load(atomic::Ordering::Acquire),
12272        0,
12273        "Should not restart LSP server on an unrelated change"
12274    );
12275
12276    update_test_project_settings(cx, |project_settings| {
12277        project_settings.lsp.insert(
12278            "Some other server name".into(),
12279            LspSettings {
12280                binary: None,
12281                settings: None,
12282                initialization_options: Some(json!({
12283                    "some other init value": false
12284                })),
12285            },
12286        );
12287    });
12288    cx.executor().run_until_parked();
12289    assert_eq!(
12290        server_restarts.load(atomic::Ordering::Acquire),
12291        0,
12292        "Should not restart LSP server on an unrelated LSP settings change"
12293    );
12294
12295    update_test_project_settings(cx, |project_settings| {
12296        project_settings.lsp.insert(
12297            language_server_name.into(),
12298            LspSettings {
12299                binary: None,
12300                settings: None,
12301                initialization_options: Some(json!({
12302                    "anotherInitValue": false
12303                })),
12304            },
12305        );
12306    });
12307    cx.executor().run_until_parked();
12308    assert_eq!(
12309        server_restarts.load(atomic::Ordering::Acquire),
12310        1,
12311        "Should restart LSP server on a related LSP settings change"
12312    );
12313
12314    update_test_project_settings(cx, |project_settings| {
12315        project_settings.lsp.insert(
12316            language_server_name.into(),
12317            LspSettings {
12318                binary: None,
12319                settings: None,
12320                initialization_options: Some(json!({
12321                    "anotherInitValue": false
12322                })),
12323            },
12324        );
12325    });
12326    cx.executor().run_until_parked();
12327    assert_eq!(
12328        server_restarts.load(atomic::Ordering::Acquire),
12329        1,
12330        "Should not restart LSP server on a related LSP settings change that is the same"
12331    );
12332
12333    update_test_project_settings(cx, |project_settings| {
12334        project_settings.lsp.insert(
12335            language_server_name.into(),
12336            LspSettings {
12337                binary: None,
12338                settings: None,
12339                initialization_options: None,
12340            },
12341        );
12342    });
12343    cx.executor().run_until_parked();
12344    assert_eq!(
12345        server_restarts.load(atomic::Ordering::Acquire),
12346        2,
12347        "Should restart LSP server on another related LSP settings change"
12348    );
12349}
12350
12351#[gpui::test]
12352async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12353    init_test(cx, |_| {});
12354
12355    let mut cx = EditorLspTestContext::new_rust(
12356        lsp::ServerCapabilities {
12357            completion_provider: Some(lsp::CompletionOptions {
12358                trigger_characters: Some(vec![".".to_string()]),
12359                resolve_provider: Some(true),
12360                ..Default::default()
12361            }),
12362            ..Default::default()
12363        },
12364        cx,
12365    )
12366    .await;
12367
12368    cx.set_state("fn main() { let a = 2ˇ; }");
12369    cx.simulate_keystroke(".");
12370    let completion_item = lsp::CompletionItem {
12371        label: "some".into(),
12372        kind: Some(lsp::CompletionItemKind::SNIPPET),
12373        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12374        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12375            kind: lsp::MarkupKind::Markdown,
12376            value: "```rust\nSome(2)\n```".to_string(),
12377        })),
12378        deprecated: Some(false),
12379        sort_text: Some("fffffff2".to_string()),
12380        filter_text: Some("some".to_string()),
12381        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12382        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12383            range: lsp::Range {
12384                start: lsp::Position {
12385                    line: 0,
12386                    character: 22,
12387                },
12388                end: lsp::Position {
12389                    line: 0,
12390                    character: 22,
12391                },
12392            },
12393            new_text: "Some(2)".to_string(),
12394        })),
12395        additional_text_edits: Some(vec![lsp::TextEdit {
12396            range: lsp::Range {
12397                start: lsp::Position {
12398                    line: 0,
12399                    character: 20,
12400                },
12401                end: lsp::Position {
12402                    line: 0,
12403                    character: 22,
12404                },
12405            },
12406            new_text: "".to_string(),
12407        }]),
12408        ..Default::default()
12409    };
12410
12411    let closure_completion_item = completion_item.clone();
12412    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12413        let task_completion_item = closure_completion_item.clone();
12414        async move {
12415            Ok(Some(lsp::CompletionResponse::Array(vec![
12416                task_completion_item,
12417            ])))
12418        }
12419    });
12420
12421    request.next().await;
12422
12423    cx.condition(|editor, _| editor.context_menu_visible())
12424        .await;
12425    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12426        editor
12427            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12428            .unwrap()
12429    });
12430    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12431
12432    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12433        let task_completion_item = completion_item.clone();
12434        async move { Ok(task_completion_item) }
12435    })
12436    .next()
12437    .await
12438    .unwrap();
12439    apply_additional_edits.await.unwrap();
12440    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12441}
12442
12443#[gpui::test]
12444async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12445    init_test(cx, |_| {});
12446
12447    let mut cx = EditorLspTestContext::new_rust(
12448        lsp::ServerCapabilities {
12449            completion_provider: Some(lsp::CompletionOptions {
12450                trigger_characters: Some(vec![".".to_string()]),
12451                resolve_provider: Some(true),
12452                ..Default::default()
12453            }),
12454            ..Default::default()
12455        },
12456        cx,
12457    )
12458    .await;
12459
12460    cx.set_state("fn main() { let a = 2ˇ; }");
12461    cx.simulate_keystroke(".");
12462
12463    let item1 = lsp::CompletionItem {
12464        label: "method id()".to_string(),
12465        filter_text: Some("id".to_string()),
12466        detail: None,
12467        documentation: None,
12468        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12469            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12470            new_text: ".id".to_string(),
12471        })),
12472        ..lsp::CompletionItem::default()
12473    };
12474
12475    let item2 = lsp::CompletionItem {
12476        label: "other".to_string(),
12477        filter_text: Some("other".to_string()),
12478        detail: None,
12479        documentation: None,
12480        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12481            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12482            new_text: ".other".to_string(),
12483        })),
12484        ..lsp::CompletionItem::default()
12485    };
12486
12487    let item1 = item1.clone();
12488    cx.set_request_handler::<lsp::request::Completion, _, _>({
12489        let item1 = item1.clone();
12490        move |_, _, _| {
12491            let item1 = item1.clone();
12492            let item2 = item2.clone();
12493            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12494        }
12495    })
12496    .next()
12497    .await;
12498
12499    cx.condition(|editor, _| editor.context_menu_visible())
12500        .await;
12501    cx.update_editor(|editor, _, _| {
12502        let context_menu = editor.context_menu.borrow_mut();
12503        let context_menu = context_menu
12504            .as_ref()
12505            .expect("Should have the context menu deployed");
12506        match context_menu {
12507            CodeContextMenu::Completions(completions_menu) => {
12508                let completions = completions_menu.completions.borrow_mut();
12509                assert_eq!(
12510                    completions
12511                        .iter()
12512                        .map(|completion| &completion.label.text)
12513                        .collect::<Vec<_>>(),
12514                    vec!["method id()", "other"]
12515                )
12516            }
12517            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12518        }
12519    });
12520
12521    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12522        let item1 = item1.clone();
12523        move |_, item_to_resolve, _| {
12524            let item1 = item1.clone();
12525            async move {
12526                if item1 == item_to_resolve {
12527                    Ok(lsp::CompletionItem {
12528                        label: "method id()".to_string(),
12529                        filter_text: Some("id".to_string()),
12530                        detail: Some("Now resolved!".to_string()),
12531                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12532                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12533                            range: lsp::Range::new(
12534                                lsp::Position::new(0, 22),
12535                                lsp::Position::new(0, 22),
12536                            ),
12537                            new_text: ".id".to_string(),
12538                        })),
12539                        ..lsp::CompletionItem::default()
12540                    })
12541                } else {
12542                    Ok(item_to_resolve)
12543                }
12544            }
12545        }
12546    })
12547    .next()
12548    .await
12549    .unwrap();
12550    cx.run_until_parked();
12551
12552    cx.update_editor(|editor, window, cx| {
12553        editor.context_menu_next(&Default::default(), window, cx);
12554    });
12555
12556    cx.update_editor(|editor, _, _| {
12557        let context_menu = editor.context_menu.borrow_mut();
12558        let context_menu = context_menu
12559            .as_ref()
12560            .expect("Should have the context menu deployed");
12561        match context_menu {
12562            CodeContextMenu::Completions(completions_menu) => {
12563                let completions = completions_menu.completions.borrow_mut();
12564                assert_eq!(
12565                    completions
12566                        .iter()
12567                        .map(|completion| &completion.label.text)
12568                        .collect::<Vec<_>>(),
12569                    vec!["method id() Now resolved!", "other"],
12570                    "Should update first completion label, but not second as the filter text did not match."
12571                );
12572            }
12573            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12574        }
12575    });
12576}
12577
12578#[gpui::test]
12579async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12580    init_test(cx, |_| {});
12581
12582    let mut cx = EditorLspTestContext::new_rust(
12583        lsp::ServerCapabilities {
12584            completion_provider: Some(lsp::CompletionOptions {
12585                trigger_characters: Some(vec![".".to_string()]),
12586                resolve_provider: Some(true),
12587                ..Default::default()
12588            }),
12589            ..Default::default()
12590        },
12591        cx,
12592    )
12593    .await;
12594
12595    cx.set_state("fn main() { let a = 2ˇ; }");
12596    cx.simulate_keystroke(".");
12597
12598    let unresolved_item_1 = lsp::CompletionItem {
12599        label: "id".to_string(),
12600        filter_text: Some("id".to_string()),
12601        detail: None,
12602        documentation: None,
12603        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12604            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12605            new_text: ".id".to_string(),
12606        })),
12607        ..lsp::CompletionItem::default()
12608    };
12609    let resolved_item_1 = lsp::CompletionItem {
12610        additional_text_edits: Some(vec![lsp::TextEdit {
12611            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12612            new_text: "!!".to_string(),
12613        }]),
12614        ..unresolved_item_1.clone()
12615    };
12616    let unresolved_item_2 = lsp::CompletionItem {
12617        label: "other".to_string(),
12618        filter_text: Some("other".to_string()),
12619        detail: None,
12620        documentation: None,
12621        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12622            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12623            new_text: ".other".to_string(),
12624        })),
12625        ..lsp::CompletionItem::default()
12626    };
12627    let resolved_item_2 = lsp::CompletionItem {
12628        additional_text_edits: Some(vec![lsp::TextEdit {
12629            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12630            new_text: "??".to_string(),
12631        }]),
12632        ..unresolved_item_2.clone()
12633    };
12634
12635    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12636    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12637    cx.lsp
12638        .server
12639        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12640            let unresolved_item_1 = unresolved_item_1.clone();
12641            let resolved_item_1 = resolved_item_1.clone();
12642            let unresolved_item_2 = unresolved_item_2.clone();
12643            let resolved_item_2 = resolved_item_2.clone();
12644            let resolve_requests_1 = resolve_requests_1.clone();
12645            let resolve_requests_2 = resolve_requests_2.clone();
12646            move |unresolved_request, _| {
12647                let unresolved_item_1 = unresolved_item_1.clone();
12648                let resolved_item_1 = resolved_item_1.clone();
12649                let unresolved_item_2 = unresolved_item_2.clone();
12650                let resolved_item_2 = resolved_item_2.clone();
12651                let resolve_requests_1 = resolve_requests_1.clone();
12652                let resolve_requests_2 = resolve_requests_2.clone();
12653                async move {
12654                    if unresolved_request == unresolved_item_1 {
12655                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
12656                        Ok(resolved_item_1.clone())
12657                    } else if unresolved_request == unresolved_item_2 {
12658                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
12659                        Ok(resolved_item_2.clone())
12660                    } else {
12661                        panic!("Unexpected completion item {unresolved_request:?}")
12662                    }
12663                }
12664            }
12665        })
12666        .detach();
12667
12668    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12669        let unresolved_item_1 = unresolved_item_1.clone();
12670        let unresolved_item_2 = unresolved_item_2.clone();
12671        async move {
12672            Ok(Some(lsp::CompletionResponse::Array(vec![
12673                unresolved_item_1,
12674                unresolved_item_2,
12675            ])))
12676        }
12677    })
12678    .next()
12679    .await;
12680
12681    cx.condition(|editor, _| editor.context_menu_visible())
12682        .await;
12683    cx.update_editor(|editor, _, _| {
12684        let context_menu = editor.context_menu.borrow_mut();
12685        let context_menu = context_menu
12686            .as_ref()
12687            .expect("Should have the context menu deployed");
12688        match context_menu {
12689            CodeContextMenu::Completions(completions_menu) => {
12690                let completions = completions_menu.completions.borrow_mut();
12691                assert_eq!(
12692                    completions
12693                        .iter()
12694                        .map(|completion| &completion.label.text)
12695                        .collect::<Vec<_>>(),
12696                    vec!["id", "other"]
12697                )
12698            }
12699            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12700        }
12701    });
12702    cx.run_until_parked();
12703
12704    cx.update_editor(|editor, window, cx| {
12705        editor.context_menu_next(&ContextMenuNext, window, cx);
12706    });
12707    cx.run_until_parked();
12708    cx.update_editor(|editor, window, cx| {
12709        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12710    });
12711    cx.run_until_parked();
12712    cx.update_editor(|editor, window, cx| {
12713        editor.context_menu_next(&ContextMenuNext, window, cx);
12714    });
12715    cx.run_until_parked();
12716    cx.update_editor(|editor, window, cx| {
12717        editor
12718            .compose_completion(&ComposeCompletion::default(), window, cx)
12719            .expect("No task returned")
12720    })
12721    .await
12722    .expect("Completion failed");
12723    cx.run_until_parked();
12724
12725    cx.update_editor(|editor, _, cx| {
12726        assert_eq!(
12727            resolve_requests_1.load(atomic::Ordering::Acquire),
12728            1,
12729            "Should always resolve once despite multiple selections"
12730        );
12731        assert_eq!(
12732            resolve_requests_2.load(atomic::Ordering::Acquire),
12733            1,
12734            "Should always resolve once after multiple selections and applying the completion"
12735        );
12736        assert_eq!(
12737            editor.text(cx),
12738            "fn main() { let a = ??.other; }",
12739            "Should use resolved data when applying the completion"
12740        );
12741    });
12742}
12743
12744#[gpui::test]
12745async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
12746    init_test(cx, |_| {});
12747
12748    let item_0 = lsp::CompletionItem {
12749        label: "abs".into(),
12750        insert_text: Some("abs".into()),
12751        data: Some(json!({ "very": "special"})),
12752        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
12753        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12754            lsp::InsertReplaceEdit {
12755                new_text: "abs".to_string(),
12756                insert: lsp::Range::default(),
12757                replace: lsp::Range::default(),
12758            },
12759        )),
12760        ..lsp::CompletionItem::default()
12761    };
12762    let items = iter::once(item_0.clone())
12763        .chain((11..51).map(|i| lsp::CompletionItem {
12764            label: format!("item_{}", i),
12765            insert_text: Some(format!("item_{}", i)),
12766            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
12767            ..lsp::CompletionItem::default()
12768        }))
12769        .collect::<Vec<_>>();
12770
12771    let default_commit_characters = vec!["?".to_string()];
12772    let default_data = json!({ "default": "data"});
12773    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
12774    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
12775    let default_edit_range = lsp::Range {
12776        start: lsp::Position {
12777            line: 0,
12778            character: 5,
12779        },
12780        end: lsp::Position {
12781            line: 0,
12782            character: 5,
12783        },
12784    };
12785
12786    let mut cx = EditorLspTestContext::new_rust(
12787        lsp::ServerCapabilities {
12788            completion_provider: Some(lsp::CompletionOptions {
12789                trigger_characters: Some(vec![".".to_string()]),
12790                resolve_provider: Some(true),
12791                ..Default::default()
12792            }),
12793            ..Default::default()
12794        },
12795        cx,
12796    )
12797    .await;
12798
12799    cx.set_state("fn main() { let a = 2ˇ; }");
12800    cx.simulate_keystroke(".");
12801
12802    let completion_data = default_data.clone();
12803    let completion_characters = default_commit_characters.clone();
12804    let completion_items = items.clone();
12805    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12806        let default_data = completion_data.clone();
12807        let default_commit_characters = completion_characters.clone();
12808        let items = completion_items.clone();
12809        async move {
12810            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
12811                items,
12812                item_defaults: Some(lsp::CompletionListItemDefaults {
12813                    data: Some(default_data.clone()),
12814                    commit_characters: Some(default_commit_characters.clone()),
12815                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
12816                        default_edit_range,
12817                    )),
12818                    insert_text_format: Some(default_insert_text_format),
12819                    insert_text_mode: Some(default_insert_text_mode),
12820                }),
12821                ..lsp::CompletionList::default()
12822            })))
12823        }
12824    })
12825    .next()
12826    .await;
12827
12828    let resolved_items = Arc::new(Mutex::new(Vec::new()));
12829    cx.lsp
12830        .server
12831        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12832            let closure_resolved_items = resolved_items.clone();
12833            move |item_to_resolve, _| {
12834                let closure_resolved_items = closure_resolved_items.clone();
12835                async move {
12836                    closure_resolved_items.lock().push(item_to_resolve.clone());
12837                    Ok(item_to_resolve)
12838                }
12839            }
12840        })
12841        .detach();
12842
12843    cx.condition(|editor, _| editor.context_menu_visible())
12844        .await;
12845    cx.run_until_parked();
12846    cx.update_editor(|editor, _, _| {
12847        let menu = editor.context_menu.borrow_mut();
12848        match menu.as_ref().expect("should have the completions menu") {
12849            CodeContextMenu::Completions(completions_menu) => {
12850                assert_eq!(
12851                    completions_menu
12852                        .entries
12853                        .borrow()
12854                        .iter()
12855                        .map(|mat| mat.string.clone())
12856                        .collect::<Vec<String>>(),
12857                    items
12858                        .iter()
12859                        .map(|completion| completion.label.clone())
12860                        .collect::<Vec<String>>()
12861                );
12862            }
12863            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
12864        }
12865    });
12866    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
12867    // with 4 from the end.
12868    assert_eq!(
12869        *resolved_items.lock(),
12870        [&items[0..16], &items[items.len() - 4..items.len()]]
12871            .concat()
12872            .iter()
12873            .cloned()
12874            .map(|mut item| {
12875                if item.data.is_none() {
12876                    item.data = Some(default_data.clone());
12877                }
12878                item
12879            })
12880            .collect::<Vec<lsp::CompletionItem>>(),
12881        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
12882    );
12883    resolved_items.lock().clear();
12884
12885    cx.update_editor(|editor, window, cx| {
12886        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
12887    });
12888    cx.run_until_parked();
12889    // Completions that have already been resolved are skipped.
12890    assert_eq!(
12891        *resolved_items.lock(),
12892        items[items.len() - 16..items.len() - 4]
12893            .iter()
12894            .cloned()
12895            .map(|mut item| {
12896                if item.data.is_none() {
12897                    item.data = Some(default_data.clone());
12898                }
12899                item
12900            })
12901            .collect::<Vec<lsp::CompletionItem>>()
12902    );
12903    resolved_items.lock().clear();
12904}
12905
12906#[gpui::test]
12907async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
12908    init_test(cx, |_| {});
12909
12910    let mut cx = EditorLspTestContext::new(
12911        Language::new(
12912            LanguageConfig {
12913                matcher: LanguageMatcher {
12914                    path_suffixes: vec!["jsx".into()],
12915                    ..Default::default()
12916                },
12917                overrides: [(
12918                    "element".into(),
12919                    LanguageConfigOverride {
12920                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
12921                        ..Default::default()
12922                    },
12923                )]
12924                .into_iter()
12925                .collect(),
12926                ..Default::default()
12927            },
12928            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
12929        )
12930        .with_override_query("(jsx_self_closing_element) @element")
12931        .unwrap(),
12932        lsp::ServerCapabilities {
12933            completion_provider: Some(lsp::CompletionOptions {
12934                trigger_characters: Some(vec![":".to_string()]),
12935                ..Default::default()
12936            }),
12937            ..Default::default()
12938        },
12939        cx,
12940    )
12941    .await;
12942
12943    cx.lsp
12944        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
12945            Ok(Some(lsp::CompletionResponse::Array(vec![
12946                lsp::CompletionItem {
12947                    label: "bg-blue".into(),
12948                    ..Default::default()
12949                },
12950                lsp::CompletionItem {
12951                    label: "bg-red".into(),
12952                    ..Default::default()
12953                },
12954                lsp::CompletionItem {
12955                    label: "bg-yellow".into(),
12956                    ..Default::default()
12957                },
12958            ])))
12959        });
12960
12961    cx.set_state(r#"<p class="bgˇ" />"#);
12962
12963    // Trigger completion when typing a dash, because the dash is an extra
12964    // word character in the 'element' scope, which contains the cursor.
12965    cx.simulate_keystroke("-");
12966    cx.executor().run_until_parked();
12967    cx.update_editor(|editor, _, _| {
12968        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12969        {
12970            assert_eq!(
12971                completion_menu_entries(&menu),
12972                &["bg-red", "bg-blue", "bg-yellow"]
12973            );
12974        } else {
12975            panic!("expected completion menu to be open");
12976        }
12977    });
12978
12979    cx.simulate_keystroke("l");
12980    cx.executor().run_until_parked();
12981    cx.update_editor(|editor, _, _| {
12982        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12983        {
12984            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
12985        } else {
12986            panic!("expected completion menu to be open");
12987        }
12988    });
12989
12990    // When filtering completions, consider the character after the '-' to
12991    // be the start of a subword.
12992    cx.set_state(r#"<p class="yelˇ" />"#);
12993    cx.simulate_keystroke("l");
12994    cx.executor().run_until_parked();
12995    cx.update_editor(|editor, _, _| {
12996        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
12997        {
12998            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
12999        } else {
13000            panic!("expected completion menu to be open");
13001        }
13002    });
13003}
13004
13005fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13006    let entries = menu.entries.borrow();
13007    entries.iter().map(|mat| mat.string.clone()).collect()
13008}
13009
13010#[gpui::test]
13011async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13012    init_test(cx, |settings| {
13013        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13014            FormatterList(vec![Formatter::Prettier].into()),
13015        ))
13016    });
13017
13018    let fs = FakeFs::new(cx.executor());
13019    fs.insert_file(path!("/file.ts"), Default::default()).await;
13020
13021    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13022    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13023
13024    language_registry.add(Arc::new(Language::new(
13025        LanguageConfig {
13026            name: "TypeScript".into(),
13027            matcher: LanguageMatcher {
13028                path_suffixes: vec!["ts".to_string()],
13029                ..Default::default()
13030            },
13031            ..Default::default()
13032        },
13033        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13034    )));
13035    update_test_language_settings(cx, |settings| {
13036        settings.defaults.prettier = Some(PrettierSettings {
13037            allowed: true,
13038            ..PrettierSettings::default()
13039        });
13040    });
13041
13042    let test_plugin = "test_plugin";
13043    let _ = language_registry.register_fake_lsp(
13044        "TypeScript",
13045        FakeLspAdapter {
13046            prettier_plugins: vec![test_plugin],
13047            ..Default::default()
13048        },
13049    );
13050
13051    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13052    let buffer = project
13053        .update(cx, |project, cx| {
13054            project.open_local_buffer(path!("/file.ts"), cx)
13055        })
13056        .await
13057        .unwrap();
13058
13059    let buffer_text = "one\ntwo\nthree\n";
13060    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13061    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13062    editor.update_in(cx, |editor, window, cx| {
13063        editor.set_text(buffer_text, window, cx)
13064    });
13065
13066    editor
13067        .update_in(cx, |editor, window, cx| {
13068            editor.perform_format(
13069                project.clone(),
13070                FormatTrigger::Manual,
13071                FormatTarget::Buffers,
13072                window,
13073                cx,
13074            )
13075        })
13076        .unwrap()
13077        .await;
13078    assert_eq!(
13079        editor.update(cx, |editor, cx| editor.text(cx)),
13080        buffer_text.to_string() + prettier_format_suffix,
13081        "Test prettier formatting was not applied to the original buffer text",
13082    );
13083
13084    update_test_language_settings(cx, |settings| {
13085        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13086    });
13087    let format = editor.update_in(cx, |editor, window, cx| {
13088        editor.perform_format(
13089            project.clone(),
13090            FormatTrigger::Manual,
13091            FormatTarget::Buffers,
13092            window,
13093            cx,
13094        )
13095    });
13096    format.await.unwrap();
13097    assert_eq!(
13098        editor.update(cx, |editor, cx| editor.text(cx)),
13099        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13100        "Autoformatting (via test prettier) was not applied to the original buffer text",
13101    );
13102}
13103
13104#[gpui::test]
13105async fn test_addition_reverts(cx: &mut TestAppContext) {
13106    init_test(cx, |_| {});
13107    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13108    let base_text = indoc! {r#"
13109        struct Row;
13110        struct Row1;
13111        struct Row2;
13112
13113        struct Row4;
13114        struct Row5;
13115        struct Row6;
13116
13117        struct Row8;
13118        struct Row9;
13119        struct Row10;"#};
13120
13121    // When addition hunks are not adjacent to carets, no hunk revert is performed
13122    assert_hunk_revert(
13123        indoc! {r#"struct Row;
13124                   struct Row1;
13125                   struct Row1.1;
13126                   struct Row1.2;
13127                   struct Row2;ˇ
13128
13129                   struct Row4;
13130                   struct Row5;
13131                   struct Row6;
13132
13133                   struct Row8;
13134                   ˇstruct Row9;
13135                   struct Row9.1;
13136                   struct Row9.2;
13137                   struct Row9.3;
13138                   struct Row10;"#},
13139        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13140        indoc! {r#"struct Row;
13141                   struct Row1;
13142                   struct Row1.1;
13143                   struct Row1.2;
13144                   struct Row2;ˇ
13145
13146                   struct Row4;
13147                   struct Row5;
13148                   struct Row6;
13149
13150                   struct Row8;
13151                   ˇstruct Row9;
13152                   struct Row9.1;
13153                   struct Row9.2;
13154                   struct Row9.3;
13155                   struct Row10;"#},
13156        base_text,
13157        &mut cx,
13158    );
13159    // Same for selections
13160    assert_hunk_revert(
13161        indoc! {r#"struct Row;
13162                   struct Row1;
13163                   struct Row2;
13164                   struct Row2.1;
13165                   struct Row2.2;
13166                   «ˇ
13167                   struct Row4;
13168                   struct» Row5;
13169                   «struct Row6;
13170                   ˇ»
13171                   struct Row9.1;
13172                   struct Row9.2;
13173                   struct Row9.3;
13174                   struct Row8;
13175                   struct Row9;
13176                   struct Row10;"#},
13177        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13178        indoc! {r#"struct Row;
13179                   struct Row1;
13180                   struct Row2;
13181                   struct Row2.1;
13182                   struct Row2.2;
13183                   «ˇ
13184                   struct Row4;
13185                   struct» Row5;
13186                   «struct Row6;
13187                   ˇ»
13188                   struct Row9.1;
13189                   struct Row9.2;
13190                   struct Row9.3;
13191                   struct Row8;
13192                   struct Row9;
13193                   struct Row10;"#},
13194        base_text,
13195        &mut cx,
13196    );
13197
13198    // When carets and selections intersect the addition hunks, those are reverted.
13199    // Adjacent carets got merged.
13200    assert_hunk_revert(
13201        indoc! {r#"struct Row;
13202                   ˇ// something on the top
13203                   struct Row1;
13204                   struct Row2;
13205                   struct Roˇw3.1;
13206                   struct Row2.2;
13207                   struct Row2.3;ˇ
13208
13209                   struct Row4;
13210                   struct ˇRow5.1;
13211                   struct Row5.2;
13212                   struct «Rowˇ»5.3;
13213                   struct Row5;
13214                   struct Row6;
13215                   ˇ
13216                   struct Row9.1;
13217                   struct «Rowˇ»9.2;
13218                   struct «ˇRow»9.3;
13219                   struct Row8;
13220                   struct Row9;
13221                   «ˇ// something on bottom»
13222                   struct Row10;"#},
13223        vec![
13224            DiffHunkStatusKind::Added,
13225            DiffHunkStatusKind::Added,
13226            DiffHunkStatusKind::Added,
13227            DiffHunkStatusKind::Added,
13228            DiffHunkStatusKind::Added,
13229        ],
13230        indoc! {r#"struct Row;
13231                   ˇstruct Row1;
13232                   struct Row2;
13233                   ˇ
13234                   struct Row4;
13235                   ˇstruct Row5;
13236                   struct Row6;
13237                   ˇ
13238                   ˇstruct Row8;
13239                   struct Row9;
13240                   ˇstruct Row10;"#},
13241        base_text,
13242        &mut cx,
13243    );
13244}
13245
13246#[gpui::test]
13247async fn test_modification_reverts(cx: &mut TestAppContext) {
13248    init_test(cx, |_| {});
13249    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13250    let base_text = indoc! {r#"
13251        struct Row;
13252        struct Row1;
13253        struct Row2;
13254
13255        struct Row4;
13256        struct Row5;
13257        struct Row6;
13258
13259        struct Row8;
13260        struct Row9;
13261        struct Row10;"#};
13262
13263    // Modification hunks behave the same as the addition ones.
13264    assert_hunk_revert(
13265        indoc! {r#"struct Row;
13266                   struct Row1;
13267                   struct Row33;
13268                   ˇ
13269                   struct Row4;
13270                   struct Row5;
13271                   struct Row6;
13272                   ˇ
13273                   struct Row99;
13274                   struct Row9;
13275                   struct Row10;"#},
13276        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13277        indoc! {r#"struct Row;
13278                   struct Row1;
13279                   struct Row33;
13280                   ˇ
13281                   struct Row4;
13282                   struct Row5;
13283                   struct Row6;
13284                   ˇ
13285                   struct Row99;
13286                   struct Row9;
13287                   struct Row10;"#},
13288        base_text,
13289        &mut cx,
13290    );
13291    assert_hunk_revert(
13292        indoc! {r#"struct Row;
13293                   struct Row1;
13294                   struct Row33;
13295                   «ˇ
13296                   struct Row4;
13297                   struct» Row5;
13298                   «struct Row6;
13299                   ˇ»
13300                   struct Row99;
13301                   struct Row9;
13302                   struct Row10;"#},
13303        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13304        indoc! {r#"struct Row;
13305                   struct Row1;
13306                   struct Row33;
13307                   «ˇ
13308                   struct Row4;
13309                   struct» Row5;
13310                   «struct Row6;
13311                   ˇ»
13312                   struct Row99;
13313                   struct Row9;
13314                   struct Row10;"#},
13315        base_text,
13316        &mut cx,
13317    );
13318
13319    assert_hunk_revert(
13320        indoc! {r#"ˇstruct Row1.1;
13321                   struct Row1;
13322                   «ˇstr»uct Row22;
13323
13324                   struct ˇRow44;
13325                   struct Row5;
13326                   struct «Rˇ»ow66;ˇ
13327
13328                   «struˇ»ct Row88;
13329                   struct Row9;
13330                   struct Row1011;ˇ"#},
13331        vec![
13332            DiffHunkStatusKind::Modified,
13333            DiffHunkStatusKind::Modified,
13334            DiffHunkStatusKind::Modified,
13335            DiffHunkStatusKind::Modified,
13336            DiffHunkStatusKind::Modified,
13337            DiffHunkStatusKind::Modified,
13338        ],
13339        indoc! {r#"struct Row;
13340                   ˇstruct Row1;
13341                   struct Row2;
13342                   ˇ
13343                   struct Row4;
13344                   ˇstruct Row5;
13345                   struct Row6;
13346                   ˇ
13347                   struct Row8;
13348                   ˇstruct Row9;
13349                   struct Row10;ˇ"#},
13350        base_text,
13351        &mut cx,
13352    );
13353}
13354
13355#[gpui::test]
13356async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13357    init_test(cx, |_| {});
13358    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13359    let base_text = indoc! {r#"
13360        one
13361
13362        two
13363        three
13364        "#};
13365
13366    cx.set_head_text(base_text);
13367    cx.set_state("\nˇ\n");
13368    cx.executor().run_until_parked();
13369    cx.update_editor(|editor, _window, cx| {
13370        editor.expand_selected_diff_hunks(cx);
13371    });
13372    cx.executor().run_until_parked();
13373    cx.update_editor(|editor, window, cx| {
13374        editor.backspace(&Default::default(), window, cx);
13375    });
13376    cx.run_until_parked();
13377    cx.assert_state_with_diff(
13378        indoc! {r#"
13379
13380        - two
13381        - threeˇ
13382        +
13383        "#}
13384        .to_string(),
13385    );
13386}
13387
13388#[gpui::test]
13389async fn test_deletion_reverts(cx: &mut TestAppContext) {
13390    init_test(cx, |_| {});
13391    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13392    let base_text = indoc! {r#"struct Row;
13393struct Row1;
13394struct Row2;
13395
13396struct Row4;
13397struct Row5;
13398struct Row6;
13399
13400struct Row8;
13401struct Row9;
13402struct Row10;"#};
13403
13404    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13405    assert_hunk_revert(
13406        indoc! {r#"struct Row;
13407                   struct Row2;
13408
13409                   ˇstruct Row4;
13410                   struct Row5;
13411                   struct Row6;
13412                   ˇ
13413                   struct Row8;
13414                   struct Row10;"#},
13415        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13416        indoc! {r#"struct Row;
13417                   struct Row2;
13418
13419                   ˇstruct Row4;
13420                   struct Row5;
13421                   struct Row6;
13422                   ˇ
13423                   struct Row8;
13424                   struct Row10;"#},
13425        base_text,
13426        &mut cx,
13427    );
13428    assert_hunk_revert(
13429        indoc! {r#"struct Row;
13430                   struct Row2;
13431
13432                   «ˇstruct Row4;
13433                   struct» Row5;
13434                   «struct Row6;
13435                   ˇ»
13436                   struct Row8;
13437                   struct Row10;"#},
13438        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13439        indoc! {r#"struct Row;
13440                   struct Row2;
13441
13442                   «ˇstruct Row4;
13443                   struct» Row5;
13444                   «struct Row6;
13445                   ˇ»
13446                   struct Row8;
13447                   struct Row10;"#},
13448        base_text,
13449        &mut cx,
13450    );
13451
13452    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13453    assert_hunk_revert(
13454        indoc! {r#"struct Row;
13455                   ˇstruct Row2;
13456
13457                   struct Row4;
13458                   struct Row5;
13459                   struct Row6;
13460
13461                   struct Row8;ˇ
13462                   struct Row10;"#},
13463        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13464        indoc! {r#"struct Row;
13465                   struct Row1;
13466                   ˇstruct Row2;
13467
13468                   struct Row4;
13469                   struct Row5;
13470                   struct Row6;
13471
13472                   struct Row8;ˇ
13473                   struct Row9;
13474                   struct Row10;"#},
13475        base_text,
13476        &mut cx,
13477    );
13478    assert_hunk_revert(
13479        indoc! {r#"struct Row;
13480                   struct Row2«ˇ;
13481                   struct Row4;
13482                   struct» Row5;
13483                   «struct Row6;
13484
13485                   struct Row8;ˇ»
13486                   struct Row10;"#},
13487        vec![
13488            DiffHunkStatusKind::Deleted,
13489            DiffHunkStatusKind::Deleted,
13490            DiffHunkStatusKind::Deleted,
13491        ],
13492        indoc! {r#"struct Row;
13493                   struct Row1;
13494                   struct Row2«ˇ;
13495
13496                   struct Row4;
13497                   struct» Row5;
13498                   «struct Row6;
13499
13500                   struct Row8;ˇ»
13501                   struct Row9;
13502                   struct Row10;"#},
13503        base_text,
13504        &mut cx,
13505    );
13506}
13507
13508#[gpui::test]
13509async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13510    init_test(cx, |_| {});
13511
13512    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13513    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13514    let base_text_3 =
13515        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13516
13517    let text_1 = edit_first_char_of_every_line(base_text_1);
13518    let text_2 = edit_first_char_of_every_line(base_text_2);
13519    let text_3 = edit_first_char_of_every_line(base_text_3);
13520
13521    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13522    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13523    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13524
13525    let multibuffer = cx.new(|cx| {
13526        let mut multibuffer = MultiBuffer::new(ReadWrite);
13527        multibuffer.push_excerpts(
13528            buffer_1.clone(),
13529            [
13530                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13531                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13532                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13533            ],
13534            cx,
13535        );
13536        multibuffer.push_excerpts(
13537            buffer_2.clone(),
13538            [
13539                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13540                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13541                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13542            ],
13543            cx,
13544        );
13545        multibuffer.push_excerpts(
13546            buffer_3.clone(),
13547            [
13548                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13549                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13550                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13551            ],
13552            cx,
13553        );
13554        multibuffer
13555    });
13556
13557    let fs = FakeFs::new(cx.executor());
13558    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13559    let (editor, cx) = cx
13560        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13561    editor.update_in(cx, |editor, _window, cx| {
13562        for (buffer, diff_base) in [
13563            (buffer_1.clone(), base_text_1),
13564            (buffer_2.clone(), base_text_2),
13565            (buffer_3.clone(), base_text_3),
13566        ] {
13567            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13568            editor
13569                .buffer
13570                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13571        }
13572    });
13573    cx.executor().run_until_parked();
13574
13575    editor.update_in(cx, |editor, window, cx| {
13576        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}");
13577        editor.select_all(&SelectAll, window, cx);
13578        editor.git_restore(&Default::default(), window, cx);
13579    });
13580    cx.executor().run_until_parked();
13581
13582    // When all ranges are selected, all buffer hunks are reverted.
13583    editor.update(cx, |editor, cx| {
13584        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");
13585    });
13586    buffer_1.update(cx, |buffer, _| {
13587        assert_eq!(buffer.text(), base_text_1);
13588    });
13589    buffer_2.update(cx, |buffer, _| {
13590        assert_eq!(buffer.text(), base_text_2);
13591    });
13592    buffer_3.update(cx, |buffer, _| {
13593        assert_eq!(buffer.text(), base_text_3);
13594    });
13595
13596    editor.update_in(cx, |editor, window, cx| {
13597        editor.undo(&Default::default(), window, cx);
13598    });
13599
13600    editor.update_in(cx, |editor, window, cx| {
13601        editor.change_selections(None, window, cx, |s| {
13602            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13603        });
13604        editor.git_restore(&Default::default(), window, cx);
13605    });
13606
13607    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13608    // but not affect buffer_2 and its related excerpts.
13609    editor.update(cx, |editor, cx| {
13610        assert_eq!(
13611            editor.text(cx),
13612            "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}"
13613        );
13614    });
13615    buffer_1.update(cx, |buffer, _| {
13616        assert_eq!(buffer.text(), base_text_1);
13617    });
13618    buffer_2.update(cx, |buffer, _| {
13619        assert_eq!(
13620            buffer.text(),
13621            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13622        );
13623    });
13624    buffer_3.update(cx, |buffer, _| {
13625        assert_eq!(
13626            buffer.text(),
13627            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13628        );
13629    });
13630
13631    fn edit_first_char_of_every_line(text: &str) -> String {
13632        text.split('\n')
13633            .map(|line| format!("X{}", &line[1..]))
13634            .collect::<Vec<_>>()
13635            .join("\n")
13636    }
13637}
13638
13639#[gpui::test]
13640async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13641    init_test(cx, |_| {});
13642
13643    let cols = 4;
13644    let rows = 10;
13645    let sample_text_1 = sample_text(rows, cols, 'a');
13646    assert_eq!(
13647        sample_text_1,
13648        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
13649    );
13650    let sample_text_2 = sample_text(rows, cols, 'l');
13651    assert_eq!(
13652        sample_text_2,
13653        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
13654    );
13655    let sample_text_3 = sample_text(rows, cols, 'v');
13656    assert_eq!(
13657        sample_text_3,
13658        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
13659    );
13660
13661    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
13662    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
13663    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
13664
13665    let multi_buffer = cx.new(|cx| {
13666        let mut multibuffer = MultiBuffer::new(ReadWrite);
13667        multibuffer.push_excerpts(
13668            buffer_1.clone(),
13669            [
13670                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13671                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13672                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13673            ],
13674            cx,
13675        );
13676        multibuffer.push_excerpts(
13677            buffer_2.clone(),
13678            [
13679                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13680                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13681                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13682            ],
13683            cx,
13684        );
13685        multibuffer.push_excerpts(
13686            buffer_3.clone(),
13687            [
13688                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13689                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13690                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13691            ],
13692            cx,
13693        );
13694        multibuffer
13695    });
13696
13697    let fs = FakeFs::new(cx.executor());
13698    fs.insert_tree(
13699        "/a",
13700        json!({
13701            "main.rs": sample_text_1,
13702            "other.rs": sample_text_2,
13703            "lib.rs": sample_text_3,
13704        }),
13705    )
13706    .await;
13707    let project = Project::test(fs, ["/a".as_ref()], cx).await;
13708    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13709    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
13710    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
13711        Editor::new(
13712            EditorMode::Full,
13713            multi_buffer,
13714            Some(project.clone()),
13715            window,
13716            cx,
13717        )
13718    });
13719    let multibuffer_item_id = workspace
13720        .update(cx, |workspace, window, cx| {
13721            assert!(
13722                workspace.active_item(cx).is_none(),
13723                "active item should be None before the first item is added"
13724            );
13725            workspace.add_item_to_active_pane(
13726                Box::new(multi_buffer_editor.clone()),
13727                None,
13728                true,
13729                window,
13730                cx,
13731            );
13732            let active_item = workspace
13733                .active_item(cx)
13734                .expect("should have an active item after adding the multi buffer");
13735            assert!(
13736                !active_item.is_singleton(cx),
13737                "A multi buffer was expected to active after adding"
13738            );
13739            active_item.item_id()
13740        })
13741        .unwrap();
13742    cx.executor().run_until_parked();
13743
13744    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13745        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13746            s.select_ranges(Some(1..2))
13747        });
13748        editor.open_excerpts(&OpenExcerpts, window, cx);
13749    });
13750    cx.executor().run_until_parked();
13751    let first_item_id = workspace
13752        .update(cx, |workspace, window, cx| {
13753            let active_item = workspace
13754                .active_item(cx)
13755                .expect("should have an active item after navigating into the 1st buffer");
13756            let first_item_id = active_item.item_id();
13757            assert_ne!(
13758                first_item_id, multibuffer_item_id,
13759                "Should navigate into the 1st buffer and activate it"
13760            );
13761            assert!(
13762                active_item.is_singleton(cx),
13763                "New active item should be a singleton buffer"
13764            );
13765            assert_eq!(
13766                active_item
13767                    .act_as::<Editor>(cx)
13768                    .expect("should have navigated into an editor for the 1st buffer")
13769                    .read(cx)
13770                    .text(cx),
13771                sample_text_1
13772            );
13773
13774            workspace
13775                .go_back(workspace.active_pane().downgrade(), window, cx)
13776                .detach_and_log_err(cx);
13777
13778            first_item_id
13779        })
13780        .unwrap();
13781    cx.executor().run_until_parked();
13782    workspace
13783        .update(cx, |workspace, _, cx| {
13784            let active_item = workspace
13785                .active_item(cx)
13786                .expect("should have an active item after navigating back");
13787            assert_eq!(
13788                active_item.item_id(),
13789                multibuffer_item_id,
13790                "Should navigate back to the multi buffer"
13791            );
13792            assert!(!active_item.is_singleton(cx));
13793        })
13794        .unwrap();
13795
13796    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13797        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13798            s.select_ranges(Some(39..40))
13799        });
13800        editor.open_excerpts(&OpenExcerpts, window, cx);
13801    });
13802    cx.executor().run_until_parked();
13803    let second_item_id = workspace
13804        .update(cx, |workspace, window, cx| {
13805            let active_item = workspace
13806                .active_item(cx)
13807                .expect("should have an active item after navigating into the 2nd buffer");
13808            let second_item_id = active_item.item_id();
13809            assert_ne!(
13810                second_item_id, multibuffer_item_id,
13811                "Should navigate away from the multibuffer"
13812            );
13813            assert_ne!(
13814                second_item_id, first_item_id,
13815                "Should navigate into the 2nd buffer and activate it"
13816            );
13817            assert!(
13818                active_item.is_singleton(cx),
13819                "New active item should be a singleton buffer"
13820            );
13821            assert_eq!(
13822                active_item
13823                    .act_as::<Editor>(cx)
13824                    .expect("should have navigated into an editor")
13825                    .read(cx)
13826                    .text(cx),
13827                sample_text_2
13828            );
13829
13830            workspace
13831                .go_back(workspace.active_pane().downgrade(), window, cx)
13832                .detach_and_log_err(cx);
13833
13834            second_item_id
13835        })
13836        .unwrap();
13837    cx.executor().run_until_parked();
13838    workspace
13839        .update(cx, |workspace, _, cx| {
13840            let active_item = workspace
13841                .active_item(cx)
13842                .expect("should have an active item after navigating back from the 2nd buffer");
13843            assert_eq!(
13844                active_item.item_id(),
13845                multibuffer_item_id,
13846                "Should navigate back from the 2nd buffer to the multi buffer"
13847            );
13848            assert!(!active_item.is_singleton(cx));
13849        })
13850        .unwrap();
13851
13852    multi_buffer_editor.update_in(cx, |editor, window, cx| {
13853        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
13854            s.select_ranges(Some(70..70))
13855        });
13856        editor.open_excerpts(&OpenExcerpts, window, cx);
13857    });
13858    cx.executor().run_until_parked();
13859    workspace
13860        .update(cx, |workspace, window, cx| {
13861            let active_item = workspace
13862                .active_item(cx)
13863                .expect("should have an active item after navigating into the 3rd buffer");
13864            let third_item_id = active_item.item_id();
13865            assert_ne!(
13866                third_item_id, multibuffer_item_id,
13867                "Should navigate into the 3rd buffer and activate it"
13868            );
13869            assert_ne!(third_item_id, first_item_id);
13870            assert_ne!(third_item_id, second_item_id);
13871            assert!(
13872                active_item.is_singleton(cx),
13873                "New active item should be a singleton buffer"
13874            );
13875            assert_eq!(
13876                active_item
13877                    .act_as::<Editor>(cx)
13878                    .expect("should have navigated into an editor")
13879                    .read(cx)
13880                    .text(cx),
13881                sample_text_3
13882            );
13883
13884            workspace
13885                .go_back(workspace.active_pane().downgrade(), window, cx)
13886                .detach_and_log_err(cx);
13887        })
13888        .unwrap();
13889    cx.executor().run_until_parked();
13890    workspace
13891        .update(cx, |workspace, _, cx| {
13892            let active_item = workspace
13893                .active_item(cx)
13894                .expect("should have an active item after navigating back from the 3rd buffer");
13895            assert_eq!(
13896                active_item.item_id(),
13897                multibuffer_item_id,
13898                "Should navigate back from the 3rd buffer to the multi buffer"
13899            );
13900            assert!(!active_item.is_singleton(cx));
13901        })
13902        .unwrap();
13903}
13904
13905#[gpui::test]
13906async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
13907    init_test(cx, |_| {});
13908
13909    let mut cx = EditorTestContext::new(cx).await;
13910
13911    let diff_base = r#"
13912        use some::mod;
13913
13914        const A: u32 = 42;
13915
13916        fn main() {
13917            println!("hello");
13918
13919            println!("world");
13920        }
13921        "#
13922    .unindent();
13923
13924    cx.set_state(
13925        &r#"
13926        use some::modified;
13927
13928        ˇ
13929        fn main() {
13930            println!("hello there");
13931
13932            println!("around the");
13933            println!("world");
13934        }
13935        "#
13936        .unindent(),
13937    );
13938
13939    cx.set_head_text(&diff_base);
13940    executor.run_until_parked();
13941
13942    cx.update_editor(|editor, window, cx| {
13943        editor.go_to_next_hunk(&GoToHunk, window, cx);
13944        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13945    });
13946    executor.run_until_parked();
13947    cx.assert_state_with_diff(
13948        r#"
13949          use some::modified;
13950
13951
13952          fn main() {
13953        -     println!("hello");
13954        + ˇ    println!("hello there");
13955
13956              println!("around the");
13957              println!("world");
13958          }
13959        "#
13960        .unindent(),
13961    );
13962
13963    cx.update_editor(|editor, window, cx| {
13964        for _ in 0..2 {
13965            editor.go_to_next_hunk(&GoToHunk, window, cx);
13966            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13967        }
13968    });
13969    executor.run_until_parked();
13970    cx.assert_state_with_diff(
13971        r#"
13972        - use some::mod;
13973        + ˇuse some::modified;
13974
13975
13976          fn main() {
13977        -     println!("hello");
13978        +     println!("hello there");
13979
13980        +     println!("around the");
13981              println!("world");
13982          }
13983        "#
13984        .unindent(),
13985    );
13986
13987    cx.update_editor(|editor, window, cx| {
13988        editor.go_to_next_hunk(&GoToHunk, window, cx);
13989        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
13990    });
13991    executor.run_until_parked();
13992    cx.assert_state_with_diff(
13993        r#"
13994        - use some::mod;
13995        + use some::modified;
13996
13997        - const A: u32 = 42;
13998          ˇ
13999          fn main() {
14000        -     println!("hello");
14001        +     println!("hello there");
14002
14003        +     println!("around the");
14004              println!("world");
14005          }
14006        "#
14007        .unindent(),
14008    );
14009
14010    cx.update_editor(|editor, window, cx| {
14011        editor.cancel(&Cancel, window, cx);
14012    });
14013
14014    cx.assert_state_with_diff(
14015        r#"
14016          use some::modified;
14017
14018          ˇ
14019          fn main() {
14020              println!("hello there");
14021
14022              println!("around the");
14023              println!("world");
14024          }
14025        "#
14026        .unindent(),
14027    );
14028}
14029
14030#[gpui::test]
14031async fn test_diff_base_change_with_expanded_diff_hunks(
14032    executor: BackgroundExecutor,
14033    cx: &mut TestAppContext,
14034) {
14035    init_test(cx, |_| {});
14036
14037    let mut cx = EditorTestContext::new(cx).await;
14038
14039    let diff_base = r#"
14040        use some::mod1;
14041        use some::mod2;
14042
14043        const A: u32 = 42;
14044        const B: u32 = 42;
14045        const C: u32 = 42;
14046
14047        fn main() {
14048            println!("hello");
14049
14050            println!("world");
14051        }
14052        "#
14053    .unindent();
14054
14055    cx.set_state(
14056        &r#"
14057        use some::mod2;
14058
14059        const A: u32 = 42;
14060        const C: u32 = 42;
14061
14062        fn main(ˇ) {
14063            //println!("hello");
14064
14065            println!("world");
14066            //
14067            //
14068        }
14069        "#
14070        .unindent(),
14071    );
14072
14073    cx.set_head_text(&diff_base);
14074    executor.run_until_parked();
14075
14076    cx.update_editor(|editor, window, cx| {
14077        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14078    });
14079    executor.run_until_parked();
14080    cx.assert_state_with_diff(
14081        r#"
14082        - use some::mod1;
14083          use some::mod2;
14084
14085          const A: u32 = 42;
14086        - const B: u32 = 42;
14087          const C: u32 = 42;
14088
14089          fn main(ˇ) {
14090        -     println!("hello");
14091        +     //println!("hello");
14092
14093              println!("world");
14094        +     //
14095        +     //
14096          }
14097        "#
14098        .unindent(),
14099    );
14100
14101    cx.set_head_text("new diff base!");
14102    executor.run_until_parked();
14103    cx.assert_state_with_diff(
14104        r#"
14105        - new diff base!
14106        + use some::mod2;
14107        +
14108        + const A: u32 = 42;
14109        + const C: u32 = 42;
14110        +
14111        + fn main(ˇ) {
14112        +     //println!("hello");
14113        +
14114        +     println!("world");
14115        +     //
14116        +     //
14117        + }
14118        "#
14119        .unindent(),
14120    );
14121}
14122
14123#[gpui::test]
14124async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14125    init_test(cx, |_| {});
14126
14127    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14128    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14129    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14130    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14131    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14132    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14133
14134    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14135    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14136    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14137
14138    let multi_buffer = cx.new(|cx| {
14139        let mut multibuffer = MultiBuffer::new(ReadWrite);
14140        multibuffer.push_excerpts(
14141            buffer_1.clone(),
14142            [
14143                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14144                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14145                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14146            ],
14147            cx,
14148        );
14149        multibuffer.push_excerpts(
14150            buffer_2.clone(),
14151            [
14152                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14153                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14154                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14155            ],
14156            cx,
14157        );
14158        multibuffer.push_excerpts(
14159            buffer_3.clone(),
14160            [
14161                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14162                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14163                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14164            ],
14165            cx,
14166        );
14167        multibuffer
14168    });
14169
14170    let editor =
14171        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14172    editor
14173        .update(cx, |editor, _window, cx| {
14174            for (buffer, diff_base) in [
14175                (buffer_1.clone(), file_1_old),
14176                (buffer_2.clone(), file_2_old),
14177                (buffer_3.clone(), file_3_old),
14178            ] {
14179                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14180                editor
14181                    .buffer
14182                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14183            }
14184        })
14185        .unwrap();
14186
14187    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14188    cx.run_until_parked();
14189
14190    cx.assert_editor_state(
14191        &"
14192            ˇaaa
14193            ccc
14194            ddd
14195
14196            ggg
14197            hhh
14198
14199
14200            lll
14201            mmm
14202            NNN
14203
14204            qqq
14205            rrr
14206
14207            uuu
14208            111
14209            222
14210            333
14211
14212            666
14213            777
14214
14215            000
14216            !!!"
14217        .unindent(),
14218    );
14219
14220    cx.update_editor(|editor, window, cx| {
14221        editor.select_all(&SelectAll, window, cx);
14222        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14223    });
14224    cx.executor().run_until_parked();
14225
14226    cx.assert_state_with_diff(
14227        "
14228            «aaa
14229          - bbb
14230            ccc
14231            ddd
14232
14233            ggg
14234            hhh
14235
14236
14237            lll
14238            mmm
14239          - nnn
14240          + NNN
14241
14242            qqq
14243            rrr
14244
14245            uuu
14246            111
14247            222
14248            333
14249
14250          + 666
14251            777
14252
14253            000
14254            !!!ˇ»"
14255            .unindent(),
14256    );
14257}
14258
14259#[gpui::test]
14260async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14261    init_test(cx, |_| {});
14262
14263    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14264    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14265
14266    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14267    let multi_buffer = cx.new(|cx| {
14268        let mut multibuffer = MultiBuffer::new(ReadWrite);
14269        multibuffer.push_excerpts(
14270            buffer.clone(),
14271            [
14272                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14273                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14274                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14275            ],
14276            cx,
14277        );
14278        multibuffer
14279    });
14280
14281    let editor =
14282        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14283    editor
14284        .update(cx, |editor, _window, cx| {
14285            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14286            editor
14287                .buffer
14288                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14289        })
14290        .unwrap();
14291
14292    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14293    cx.run_until_parked();
14294
14295    cx.update_editor(|editor, window, cx| {
14296        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14297    });
14298    cx.executor().run_until_parked();
14299
14300    // When the start of a hunk coincides with the start of its excerpt,
14301    // the hunk is expanded. When the start of a a hunk is earlier than
14302    // the start of its excerpt, the hunk is not expanded.
14303    cx.assert_state_with_diff(
14304        "
14305            ˇaaa
14306          - bbb
14307          + BBB
14308
14309          - ddd
14310          - eee
14311          + DDD
14312          + EEE
14313            fff
14314
14315            iii
14316        "
14317        .unindent(),
14318    );
14319}
14320
14321#[gpui::test]
14322async fn test_edits_around_expanded_insertion_hunks(
14323    executor: BackgroundExecutor,
14324    cx: &mut TestAppContext,
14325) {
14326    init_test(cx, |_| {});
14327
14328    let mut cx = EditorTestContext::new(cx).await;
14329
14330    let diff_base = r#"
14331        use some::mod1;
14332        use some::mod2;
14333
14334        const A: u32 = 42;
14335
14336        fn main() {
14337            println!("hello");
14338
14339            println!("world");
14340        }
14341        "#
14342    .unindent();
14343    executor.run_until_parked();
14344    cx.set_state(
14345        &r#"
14346        use some::mod1;
14347        use some::mod2;
14348
14349        const A: u32 = 42;
14350        const B: u32 = 42;
14351        const C: u32 = 42;
14352        ˇ
14353
14354        fn main() {
14355            println!("hello");
14356
14357            println!("world");
14358        }
14359        "#
14360        .unindent(),
14361    );
14362
14363    cx.set_head_text(&diff_base);
14364    executor.run_until_parked();
14365
14366    cx.update_editor(|editor, window, cx| {
14367        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14368    });
14369    executor.run_until_parked();
14370
14371    cx.assert_state_with_diff(
14372        r#"
14373        use some::mod1;
14374        use some::mod2;
14375
14376        const A: u32 = 42;
14377      + const B: u32 = 42;
14378      + const C: u32 = 42;
14379      + ˇ
14380
14381        fn main() {
14382            println!("hello");
14383
14384            println!("world");
14385        }
14386      "#
14387        .unindent(),
14388    );
14389
14390    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14391    executor.run_until_parked();
14392
14393    cx.assert_state_with_diff(
14394        r#"
14395        use some::mod1;
14396        use some::mod2;
14397
14398        const A: u32 = 42;
14399      + const B: u32 = 42;
14400      + const C: u32 = 42;
14401      + const D: u32 = 42;
14402      + ˇ
14403
14404        fn main() {
14405            println!("hello");
14406
14407            println!("world");
14408        }
14409      "#
14410        .unindent(),
14411    );
14412
14413    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14414    executor.run_until_parked();
14415
14416    cx.assert_state_with_diff(
14417        r#"
14418        use some::mod1;
14419        use some::mod2;
14420
14421        const A: u32 = 42;
14422      + const B: u32 = 42;
14423      + const C: u32 = 42;
14424      + const D: u32 = 42;
14425      + const E: u32 = 42;
14426      + ˇ
14427
14428        fn main() {
14429            println!("hello");
14430
14431            println!("world");
14432        }
14433      "#
14434        .unindent(),
14435    );
14436
14437    cx.update_editor(|editor, window, cx| {
14438        editor.delete_line(&DeleteLine, window, cx);
14439    });
14440    executor.run_until_parked();
14441
14442    cx.assert_state_with_diff(
14443        r#"
14444        use some::mod1;
14445        use some::mod2;
14446
14447        const A: u32 = 42;
14448      + const B: u32 = 42;
14449      + const C: u32 = 42;
14450      + const D: u32 = 42;
14451      + const E: u32 = 42;
14452        ˇ
14453        fn main() {
14454            println!("hello");
14455
14456            println!("world");
14457        }
14458      "#
14459        .unindent(),
14460    );
14461
14462    cx.update_editor(|editor, window, cx| {
14463        editor.move_up(&MoveUp, window, cx);
14464        editor.delete_line(&DeleteLine, window, cx);
14465        editor.move_up(&MoveUp, window, cx);
14466        editor.delete_line(&DeleteLine, window, cx);
14467        editor.move_up(&MoveUp, window, cx);
14468        editor.delete_line(&DeleteLine, window, cx);
14469    });
14470    executor.run_until_parked();
14471    cx.assert_state_with_diff(
14472        r#"
14473        use some::mod1;
14474        use some::mod2;
14475
14476        const A: u32 = 42;
14477      + const B: u32 = 42;
14478        ˇ
14479        fn main() {
14480            println!("hello");
14481
14482            println!("world");
14483        }
14484      "#
14485        .unindent(),
14486    );
14487
14488    cx.update_editor(|editor, window, cx| {
14489        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14490        editor.delete_line(&DeleteLine, window, cx);
14491    });
14492    executor.run_until_parked();
14493    cx.assert_state_with_diff(
14494        r#"
14495        ˇ
14496        fn main() {
14497            println!("hello");
14498
14499            println!("world");
14500        }
14501      "#
14502        .unindent(),
14503    );
14504}
14505
14506#[gpui::test]
14507async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14508    init_test(cx, |_| {});
14509
14510    let mut cx = EditorTestContext::new(cx).await;
14511    cx.set_head_text(indoc! { "
14512        one
14513        two
14514        three
14515        four
14516        five
14517        "
14518    });
14519    cx.set_state(indoc! { "
14520        one
14521        ˇthree
14522        five
14523    "});
14524    cx.run_until_parked();
14525    cx.update_editor(|editor, window, cx| {
14526        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14527    });
14528    cx.assert_state_with_diff(
14529        indoc! { "
14530        one
14531      - two
14532        ˇthree
14533      - four
14534        five
14535    "}
14536        .to_string(),
14537    );
14538    cx.update_editor(|editor, window, cx| {
14539        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14540    });
14541
14542    cx.assert_state_with_diff(
14543        indoc! { "
14544        one
14545        ˇthree
14546        five
14547    "}
14548        .to_string(),
14549    );
14550
14551    cx.set_state(indoc! { "
14552        one
14553        ˇTWO
14554        three
14555        four
14556        five
14557    "});
14558    cx.run_until_parked();
14559    cx.update_editor(|editor, window, cx| {
14560        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14561    });
14562
14563    cx.assert_state_with_diff(
14564        indoc! { "
14565            one
14566          - two
14567          + ˇTWO
14568            three
14569            four
14570            five
14571        "}
14572        .to_string(),
14573    );
14574    cx.update_editor(|editor, window, cx| {
14575        editor.move_up(&Default::default(), window, cx);
14576        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14577    });
14578    cx.assert_state_with_diff(
14579        indoc! { "
14580            one
14581            ˇTWO
14582            three
14583            four
14584            five
14585        "}
14586        .to_string(),
14587    );
14588}
14589
14590#[gpui::test]
14591async fn test_edits_around_expanded_deletion_hunks(
14592    executor: BackgroundExecutor,
14593    cx: &mut TestAppContext,
14594) {
14595    init_test(cx, |_| {});
14596
14597    let mut cx = EditorTestContext::new(cx).await;
14598
14599    let diff_base = r#"
14600        use some::mod1;
14601        use some::mod2;
14602
14603        const A: u32 = 42;
14604        const B: u32 = 42;
14605        const C: u32 = 42;
14606
14607
14608        fn main() {
14609            println!("hello");
14610
14611            println!("world");
14612        }
14613    "#
14614    .unindent();
14615    executor.run_until_parked();
14616    cx.set_state(
14617        &r#"
14618        use some::mod1;
14619        use some::mod2;
14620
14621        ˇconst B: u32 = 42;
14622        const C: u32 = 42;
14623
14624
14625        fn main() {
14626            println!("hello");
14627
14628            println!("world");
14629        }
14630        "#
14631        .unindent(),
14632    );
14633
14634    cx.set_head_text(&diff_base);
14635    executor.run_until_parked();
14636
14637    cx.update_editor(|editor, window, cx| {
14638        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14639    });
14640    executor.run_until_parked();
14641
14642    cx.assert_state_with_diff(
14643        r#"
14644        use some::mod1;
14645        use some::mod2;
14646
14647      - const A: u32 = 42;
14648        ˇconst B: u32 = 42;
14649        const C: u32 = 42;
14650
14651
14652        fn main() {
14653            println!("hello");
14654
14655            println!("world");
14656        }
14657      "#
14658        .unindent(),
14659    );
14660
14661    cx.update_editor(|editor, window, cx| {
14662        editor.delete_line(&DeleteLine, window, cx);
14663    });
14664    executor.run_until_parked();
14665    cx.assert_state_with_diff(
14666        r#"
14667        use some::mod1;
14668        use some::mod2;
14669
14670      - const A: u32 = 42;
14671      - const B: u32 = 42;
14672        ˇconst C: u32 = 42;
14673
14674
14675        fn main() {
14676            println!("hello");
14677
14678            println!("world");
14679        }
14680      "#
14681        .unindent(),
14682    );
14683
14684    cx.update_editor(|editor, window, cx| {
14685        editor.delete_line(&DeleteLine, window, cx);
14686    });
14687    executor.run_until_parked();
14688    cx.assert_state_with_diff(
14689        r#"
14690        use some::mod1;
14691        use some::mod2;
14692
14693      - const A: u32 = 42;
14694      - const B: u32 = 42;
14695      - const C: u32 = 42;
14696        ˇ
14697
14698        fn main() {
14699            println!("hello");
14700
14701            println!("world");
14702        }
14703      "#
14704        .unindent(),
14705    );
14706
14707    cx.update_editor(|editor, window, cx| {
14708        editor.handle_input("replacement", window, cx);
14709    });
14710    executor.run_until_parked();
14711    cx.assert_state_with_diff(
14712        r#"
14713        use some::mod1;
14714        use some::mod2;
14715
14716      - const A: u32 = 42;
14717      - const B: u32 = 42;
14718      - const C: u32 = 42;
14719      -
14720      + replacementˇ
14721
14722        fn main() {
14723            println!("hello");
14724
14725            println!("world");
14726        }
14727      "#
14728        .unindent(),
14729    );
14730}
14731
14732#[gpui::test]
14733async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14734    init_test(cx, |_| {});
14735
14736    let mut cx = EditorTestContext::new(cx).await;
14737
14738    let base_text = r#"
14739        one
14740        two
14741        three
14742        four
14743        five
14744    "#
14745    .unindent();
14746    executor.run_until_parked();
14747    cx.set_state(
14748        &r#"
14749        one
14750        two
14751        fˇour
14752        five
14753        "#
14754        .unindent(),
14755    );
14756
14757    cx.set_head_text(&base_text);
14758    executor.run_until_parked();
14759
14760    cx.update_editor(|editor, window, cx| {
14761        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14762    });
14763    executor.run_until_parked();
14764
14765    cx.assert_state_with_diff(
14766        r#"
14767          one
14768          two
14769        - three
14770          fˇour
14771          five
14772        "#
14773        .unindent(),
14774    );
14775
14776    cx.update_editor(|editor, window, cx| {
14777        editor.backspace(&Backspace, window, cx);
14778        editor.backspace(&Backspace, window, cx);
14779    });
14780    executor.run_until_parked();
14781    cx.assert_state_with_diff(
14782        r#"
14783          one
14784          two
14785        - threeˇ
14786        - four
14787        + our
14788          five
14789        "#
14790        .unindent(),
14791    );
14792}
14793
14794#[gpui::test]
14795async fn test_edit_after_expanded_modification_hunk(
14796    executor: BackgroundExecutor,
14797    cx: &mut TestAppContext,
14798) {
14799    init_test(cx, |_| {});
14800
14801    let mut cx = EditorTestContext::new(cx).await;
14802
14803    let diff_base = r#"
14804        use some::mod1;
14805        use some::mod2;
14806
14807        const A: u32 = 42;
14808        const B: u32 = 42;
14809        const C: u32 = 42;
14810        const D: u32 = 42;
14811
14812
14813        fn main() {
14814            println!("hello");
14815
14816            println!("world");
14817        }"#
14818    .unindent();
14819
14820    cx.set_state(
14821        &r#"
14822        use some::mod1;
14823        use some::mod2;
14824
14825        const A: u32 = 42;
14826        const B: u32 = 42;
14827        const C: u32 = 43ˇ
14828        const D: u32 = 42;
14829
14830
14831        fn main() {
14832            println!("hello");
14833
14834            println!("world");
14835        }"#
14836        .unindent(),
14837    );
14838
14839    cx.set_head_text(&diff_base);
14840    executor.run_until_parked();
14841    cx.update_editor(|editor, window, cx| {
14842        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14843    });
14844    executor.run_until_parked();
14845
14846    cx.assert_state_with_diff(
14847        r#"
14848        use some::mod1;
14849        use some::mod2;
14850
14851        const A: u32 = 42;
14852        const B: u32 = 42;
14853      - const C: u32 = 42;
14854      + const C: u32 = 43ˇ
14855        const D: u32 = 42;
14856
14857
14858        fn main() {
14859            println!("hello");
14860
14861            println!("world");
14862        }"#
14863        .unindent(),
14864    );
14865
14866    cx.update_editor(|editor, window, cx| {
14867        editor.handle_input("\nnew_line\n", window, cx);
14868    });
14869    executor.run_until_parked();
14870
14871    cx.assert_state_with_diff(
14872        r#"
14873        use some::mod1;
14874        use some::mod2;
14875
14876        const A: u32 = 42;
14877        const B: u32 = 42;
14878      - const C: u32 = 42;
14879      + const C: u32 = 43
14880      + new_line
14881      + ˇ
14882        const D: u32 = 42;
14883
14884
14885        fn main() {
14886            println!("hello");
14887
14888            println!("world");
14889        }"#
14890        .unindent(),
14891    );
14892}
14893
14894#[gpui::test]
14895async fn test_stage_and_unstage_added_file_hunk(
14896    executor: BackgroundExecutor,
14897    cx: &mut TestAppContext,
14898) {
14899    init_test(cx, |_| {});
14900
14901    let mut cx = EditorTestContext::new(cx).await;
14902    cx.update_editor(|editor, _, cx| {
14903        editor.set_expand_all_diff_hunks(cx);
14904    });
14905
14906    let working_copy = r#"
14907            ˇfn main() {
14908                println!("hello, world!");
14909            }
14910        "#
14911    .unindent();
14912
14913    cx.set_state(&working_copy);
14914    executor.run_until_parked();
14915
14916    cx.assert_state_with_diff(
14917        r#"
14918            + ˇfn main() {
14919            +     println!("hello, world!");
14920            + }
14921        "#
14922        .unindent(),
14923    );
14924    cx.assert_index_text(None);
14925
14926    cx.update_editor(|editor, window, cx| {
14927        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14928    });
14929    executor.run_until_parked();
14930    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
14931    cx.assert_state_with_diff(
14932        r#"
14933            + ˇfn main() {
14934            +     println!("hello, world!");
14935            + }
14936        "#
14937        .unindent(),
14938    );
14939
14940    cx.update_editor(|editor, window, cx| {
14941        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
14942    });
14943    executor.run_until_parked();
14944    cx.assert_index_text(None);
14945}
14946
14947async fn setup_indent_guides_editor(
14948    text: &str,
14949    cx: &mut TestAppContext,
14950) -> (BufferId, EditorTestContext) {
14951    init_test(cx, |_| {});
14952
14953    let mut cx = EditorTestContext::new(cx).await;
14954
14955    let buffer_id = cx.update_editor(|editor, window, cx| {
14956        editor.set_text(text, window, cx);
14957        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
14958
14959        buffer_ids[0]
14960    });
14961
14962    (buffer_id, cx)
14963}
14964
14965fn assert_indent_guides(
14966    range: Range<u32>,
14967    expected: Vec<IndentGuide>,
14968    active_indices: Option<Vec<usize>>,
14969    cx: &mut EditorTestContext,
14970) {
14971    let indent_guides = cx.update_editor(|editor, window, cx| {
14972        let snapshot = editor.snapshot(window, cx).display_snapshot;
14973        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
14974            editor,
14975            MultiBufferRow(range.start)..MultiBufferRow(range.end),
14976            true,
14977            &snapshot,
14978            cx,
14979        );
14980
14981        indent_guides.sort_by(|a, b| {
14982            a.depth.cmp(&b.depth).then(
14983                a.start_row
14984                    .cmp(&b.start_row)
14985                    .then(a.end_row.cmp(&b.end_row)),
14986            )
14987        });
14988        indent_guides
14989    });
14990
14991    if let Some(expected) = active_indices {
14992        let active_indices = cx.update_editor(|editor, window, cx| {
14993            let snapshot = editor.snapshot(window, cx).display_snapshot;
14994            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
14995        });
14996
14997        assert_eq!(
14998            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
14999            expected,
15000            "Active indent guide indices do not match"
15001        );
15002    }
15003
15004    assert_eq!(indent_guides, expected, "Indent guides do not match");
15005}
15006
15007fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15008    IndentGuide {
15009        buffer_id,
15010        start_row: MultiBufferRow(start_row),
15011        end_row: MultiBufferRow(end_row),
15012        depth,
15013        tab_size: 4,
15014        settings: IndentGuideSettings {
15015            enabled: true,
15016            line_width: 1,
15017            active_line_width: 1,
15018            ..Default::default()
15019        },
15020    }
15021}
15022
15023#[gpui::test]
15024async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15025    let (buffer_id, mut cx) = setup_indent_guides_editor(
15026        &"
15027    fn main() {
15028        let a = 1;
15029    }"
15030        .unindent(),
15031        cx,
15032    )
15033    .await;
15034
15035    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15036}
15037
15038#[gpui::test]
15039async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15040    let (buffer_id, mut cx) = setup_indent_guides_editor(
15041        &"
15042    fn main() {
15043        let a = 1;
15044        let b = 2;
15045    }"
15046        .unindent(),
15047        cx,
15048    )
15049    .await;
15050
15051    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15052}
15053
15054#[gpui::test]
15055async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15056    let (buffer_id, mut cx) = setup_indent_guides_editor(
15057        &"
15058    fn main() {
15059        let a = 1;
15060        if a == 3 {
15061            let b = 2;
15062        } else {
15063            let c = 3;
15064        }
15065    }"
15066        .unindent(),
15067        cx,
15068    )
15069    .await;
15070
15071    assert_indent_guides(
15072        0..8,
15073        vec![
15074            indent_guide(buffer_id, 1, 6, 0),
15075            indent_guide(buffer_id, 3, 3, 1),
15076            indent_guide(buffer_id, 5, 5, 1),
15077        ],
15078        None,
15079        &mut cx,
15080    );
15081}
15082
15083#[gpui::test]
15084async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15085    let (buffer_id, mut cx) = setup_indent_guides_editor(
15086        &"
15087    fn main() {
15088        let a = 1;
15089            let b = 2;
15090        let c = 3;
15091    }"
15092        .unindent(),
15093        cx,
15094    )
15095    .await;
15096
15097    assert_indent_guides(
15098        0..5,
15099        vec![
15100            indent_guide(buffer_id, 1, 3, 0),
15101            indent_guide(buffer_id, 2, 2, 1),
15102        ],
15103        None,
15104        &mut cx,
15105    );
15106}
15107
15108#[gpui::test]
15109async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15110    let (buffer_id, mut cx) = setup_indent_guides_editor(
15111        &"
15112        fn main() {
15113            let a = 1;
15114
15115            let c = 3;
15116        }"
15117        .unindent(),
15118        cx,
15119    )
15120    .await;
15121
15122    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15123}
15124
15125#[gpui::test]
15126async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15127    let (buffer_id, mut cx) = setup_indent_guides_editor(
15128        &"
15129        fn main() {
15130            let a = 1;
15131
15132            let c = 3;
15133
15134            if a == 3 {
15135                let b = 2;
15136            } else {
15137                let c = 3;
15138            }
15139        }"
15140        .unindent(),
15141        cx,
15142    )
15143    .await;
15144
15145    assert_indent_guides(
15146        0..11,
15147        vec![
15148            indent_guide(buffer_id, 1, 9, 0),
15149            indent_guide(buffer_id, 6, 6, 1),
15150            indent_guide(buffer_id, 8, 8, 1),
15151        ],
15152        None,
15153        &mut cx,
15154    );
15155}
15156
15157#[gpui::test]
15158async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15159    let (buffer_id, mut cx) = setup_indent_guides_editor(
15160        &"
15161        fn main() {
15162            let a = 1;
15163
15164            let c = 3;
15165
15166            if a == 3 {
15167                let b = 2;
15168            } else {
15169                let c = 3;
15170            }
15171        }"
15172        .unindent(),
15173        cx,
15174    )
15175    .await;
15176
15177    assert_indent_guides(
15178        1..11,
15179        vec![
15180            indent_guide(buffer_id, 1, 9, 0),
15181            indent_guide(buffer_id, 6, 6, 1),
15182            indent_guide(buffer_id, 8, 8, 1),
15183        ],
15184        None,
15185        &mut cx,
15186    );
15187}
15188
15189#[gpui::test]
15190async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15191    let (buffer_id, mut cx) = setup_indent_guides_editor(
15192        &"
15193        fn main() {
15194            let a = 1;
15195
15196            let c = 3;
15197
15198            if a == 3 {
15199                let b = 2;
15200            } else {
15201                let c = 3;
15202            }
15203        }"
15204        .unindent(),
15205        cx,
15206    )
15207    .await;
15208
15209    assert_indent_guides(
15210        1..10,
15211        vec![
15212            indent_guide(buffer_id, 1, 9, 0),
15213            indent_guide(buffer_id, 6, 6, 1),
15214            indent_guide(buffer_id, 8, 8, 1),
15215        ],
15216        None,
15217        &mut cx,
15218    );
15219}
15220
15221#[gpui::test]
15222async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15223    let (buffer_id, mut cx) = setup_indent_guides_editor(
15224        &"
15225        block1
15226            block2
15227                block3
15228                    block4
15229            block2
15230        block1
15231        block1"
15232            .unindent(),
15233        cx,
15234    )
15235    .await;
15236
15237    assert_indent_guides(
15238        1..10,
15239        vec![
15240            indent_guide(buffer_id, 1, 4, 0),
15241            indent_guide(buffer_id, 2, 3, 1),
15242            indent_guide(buffer_id, 3, 3, 2),
15243        ],
15244        None,
15245        &mut cx,
15246    );
15247}
15248
15249#[gpui::test]
15250async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15251    let (buffer_id, mut cx) = setup_indent_guides_editor(
15252        &"
15253        block1
15254            block2
15255                block3
15256
15257        block1
15258        block1"
15259            .unindent(),
15260        cx,
15261    )
15262    .await;
15263
15264    assert_indent_guides(
15265        0..6,
15266        vec![
15267            indent_guide(buffer_id, 1, 2, 0),
15268            indent_guide(buffer_id, 2, 2, 1),
15269        ],
15270        None,
15271        &mut cx,
15272    );
15273}
15274
15275#[gpui::test]
15276async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15277    let (buffer_id, mut cx) = setup_indent_guides_editor(
15278        &"
15279        block1
15280
15281
15282
15283            block2
15284        "
15285        .unindent(),
15286        cx,
15287    )
15288    .await;
15289
15290    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15291}
15292
15293#[gpui::test]
15294async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15295    let (buffer_id, mut cx) = setup_indent_guides_editor(
15296        &"
15297        def a:
15298        \tb = 3
15299        \tif True:
15300        \t\tc = 4
15301        \t\td = 5
15302        \tprint(b)
15303        "
15304        .unindent(),
15305        cx,
15306    )
15307    .await;
15308
15309    assert_indent_guides(
15310        0..6,
15311        vec![
15312            indent_guide(buffer_id, 1, 6, 0),
15313            indent_guide(buffer_id, 3, 4, 1),
15314        ],
15315        None,
15316        &mut cx,
15317    );
15318}
15319
15320#[gpui::test]
15321async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15322    let (buffer_id, mut cx) = setup_indent_guides_editor(
15323        &"
15324    fn main() {
15325        let a = 1;
15326    }"
15327        .unindent(),
15328        cx,
15329    )
15330    .await;
15331
15332    cx.update_editor(|editor, window, cx| {
15333        editor.change_selections(None, window, cx, |s| {
15334            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15335        });
15336    });
15337
15338    assert_indent_guides(
15339        0..3,
15340        vec![indent_guide(buffer_id, 1, 1, 0)],
15341        Some(vec![0]),
15342        &mut cx,
15343    );
15344}
15345
15346#[gpui::test]
15347async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15348    let (buffer_id, mut cx) = setup_indent_guides_editor(
15349        &"
15350    fn main() {
15351        if 1 == 2 {
15352            let a = 1;
15353        }
15354    }"
15355        .unindent(),
15356        cx,
15357    )
15358    .await;
15359
15360    cx.update_editor(|editor, window, cx| {
15361        editor.change_selections(None, window, cx, |s| {
15362            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15363        });
15364    });
15365
15366    assert_indent_guides(
15367        0..4,
15368        vec![
15369            indent_guide(buffer_id, 1, 3, 0),
15370            indent_guide(buffer_id, 2, 2, 1),
15371        ],
15372        Some(vec![1]),
15373        &mut cx,
15374    );
15375
15376    cx.update_editor(|editor, window, cx| {
15377        editor.change_selections(None, window, cx, |s| {
15378            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15379        });
15380    });
15381
15382    assert_indent_guides(
15383        0..4,
15384        vec![
15385            indent_guide(buffer_id, 1, 3, 0),
15386            indent_guide(buffer_id, 2, 2, 1),
15387        ],
15388        Some(vec![1]),
15389        &mut cx,
15390    );
15391
15392    cx.update_editor(|editor, window, cx| {
15393        editor.change_selections(None, window, cx, |s| {
15394            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15395        });
15396    });
15397
15398    assert_indent_guides(
15399        0..4,
15400        vec![
15401            indent_guide(buffer_id, 1, 3, 0),
15402            indent_guide(buffer_id, 2, 2, 1),
15403        ],
15404        Some(vec![0]),
15405        &mut cx,
15406    );
15407}
15408
15409#[gpui::test]
15410async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15411    let (buffer_id, mut cx) = setup_indent_guides_editor(
15412        &"
15413    fn main() {
15414        let a = 1;
15415
15416        let b = 2;
15417    }"
15418        .unindent(),
15419        cx,
15420    )
15421    .await;
15422
15423    cx.update_editor(|editor, window, cx| {
15424        editor.change_selections(None, window, cx, |s| {
15425            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15426        });
15427    });
15428
15429    assert_indent_guides(
15430        0..5,
15431        vec![indent_guide(buffer_id, 1, 3, 0)],
15432        Some(vec![0]),
15433        &mut cx,
15434    );
15435}
15436
15437#[gpui::test]
15438async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15439    let (buffer_id, mut cx) = setup_indent_guides_editor(
15440        &"
15441    def m:
15442        a = 1
15443        pass"
15444            .unindent(),
15445        cx,
15446    )
15447    .await;
15448
15449    cx.update_editor(|editor, window, cx| {
15450        editor.change_selections(None, window, cx, |s| {
15451            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15452        });
15453    });
15454
15455    assert_indent_guides(
15456        0..3,
15457        vec![indent_guide(buffer_id, 1, 2, 0)],
15458        Some(vec![0]),
15459        &mut cx,
15460    );
15461}
15462
15463#[gpui::test]
15464async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15465    init_test(cx, |_| {});
15466    let mut cx = EditorTestContext::new(cx).await;
15467    let text = indoc! {
15468        "
15469        impl A {
15470            fn b() {
15471                0;
15472                3;
15473                5;
15474                6;
15475                7;
15476            }
15477        }
15478        "
15479    };
15480    let base_text = indoc! {
15481        "
15482        impl A {
15483            fn b() {
15484                0;
15485                1;
15486                2;
15487                3;
15488                4;
15489            }
15490            fn c() {
15491                5;
15492                6;
15493                7;
15494            }
15495        }
15496        "
15497    };
15498
15499    cx.update_editor(|editor, window, cx| {
15500        editor.set_text(text, window, cx);
15501
15502        editor.buffer().update(cx, |multibuffer, cx| {
15503            let buffer = multibuffer.as_singleton().unwrap();
15504            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15505
15506            multibuffer.set_all_diff_hunks_expanded(cx);
15507            multibuffer.add_diff(diff, cx);
15508
15509            buffer.read(cx).remote_id()
15510        })
15511    });
15512    cx.run_until_parked();
15513
15514    cx.assert_state_with_diff(
15515        indoc! { "
15516          impl A {
15517              fn b() {
15518                  0;
15519        -         1;
15520        -         2;
15521                  3;
15522        -         4;
15523        -     }
15524        -     fn c() {
15525                  5;
15526                  6;
15527                  7;
15528              }
15529          }
15530          ˇ"
15531        }
15532        .to_string(),
15533    );
15534
15535    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15536        editor
15537            .snapshot(window, cx)
15538            .buffer_snapshot
15539            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15540            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15541            .collect::<Vec<_>>()
15542    });
15543    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15544    assert_eq!(
15545        actual_guides,
15546        vec![
15547            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15548            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15549            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15550        ]
15551    );
15552}
15553
15554#[gpui::test]
15555async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15556    init_test(cx, |_| {});
15557    let mut cx = EditorTestContext::new(cx).await;
15558
15559    let diff_base = r#"
15560        a
15561        b
15562        c
15563        "#
15564    .unindent();
15565
15566    cx.set_state(
15567        &r#"
15568        ˇA
15569        b
15570        C
15571        "#
15572        .unindent(),
15573    );
15574    cx.set_head_text(&diff_base);
15575    cx.update_editor(|editor, window, cx| {
15576        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15577    });
15578    executor.run_until_parked();
15579
15580    let both_hunks_expanded = r#"
15581        - a
15582        + ˇA
15583          b
15584        - c
15585        + C
15586        "#
15587    .unindent();
15588
15589    cx.assert_state_with_diff(both_hunks_expanded.clone());
15590
15591    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15592        let snapshot = editor.snapshot(window, cx);
15593        let hunks = editor
15594            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15595            .collect::<Vec<_>>();
15596        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15597        let buffer_id = hunks[0].buffer_id;
15598        hunks
15599            .into_iter()
15600            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15601            .collect::<Vec<_>>()
15602    });
15603    assert_eq!(hunk_ranges.len(), 2);
15604
15605    cx.update_editor(|editor, _, cx| {
15606        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15607    });
15608    executor.run_until_parked();
15609
15610    let second_hunk_expanded = r#"
15611          ˇA
15612          b
15613        - c
15614        + C
15615        "#
15616    .unindent();
15617
15618    cx.assert_state_with_diff(second_hunk_expanded);
15619
15620    cx.update_editor(|editor, _, cx| {
15621        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15622    });
15623    executor.run_until_parked();
15624
15625    cx.assert_state_with_diff(both_hunks_expanded.clone());
15626
15627    cx.update_editor(|editor, _, cx| {
15628        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15629    });
15630    executor.run_until_parked();
15631
15632    let first_hunk_expanded = r#"
15633        - a
15634        + ˇA
15635          b
15636          C
15637        "#
15638    .unindent();
15639
15640    cx.assert_state_with_diff(first_hunk_expanded);
15641
15642    cx.update_editor(|editor, _, cx| {
15643        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15644    });
15645    executor.run_until_parked();
15646
15647    cx.assert_state_with_diff(both_hunks_expanded);
15648
15649    cx.set_state(
15650        &r#"
15651        ˇA
15652        b
15653        "#
15654        .unindent(),
15655    );
15656    cx.run_until_parked();
15657
15658    // TODO this cursor position seems bad
15659    cx.assert_state_with_diff(
15660        r#"
15661        - ˇa
15662        + A
15663          b
15664        "#
15665        .unindent(),
15666    );
15667
15668    cx.update_editor(|editor, window, cx| {
15669        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15670    });
15671
15672    cx.assert_state_with_diff(
15673        r#"
15674            - ˇa
15675            + A
15676              b
15677            - c
15678            "#
15679        .unindent(),
15680    );
15681
15682    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15683        let snapshot = editor.snapshot(window, cx);
15684        let hunks = editor
15685            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15686            .collect::<Vec<_>>();
15687        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15688        let buffer_id = hunks[0].buffer_id;
15689        hunks
15690            .into_iter()
15691            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15692            .collect::<Vec<_>>()
15693    });
15694    assert_eq!(hunk_ranges.len(), 2);
15695
15696    cx.update_editor(|editor, _, cx| {
15697        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15698    });
15699    executor.run_until_parked();
15700
15701    cx.assert_state_with_diff(
15702        r#"
15703        - ˇa
15704        + A
15705          b
15706        "#
15707        .unindent(),
15708    );
15709}
15710
15711#[gpui::test]
15712async fn test_toggle_deletion_hunk_at_start_of_file(
15713    executor: BackgroundExecutor,
15714    cx: &mut TestAppContext,
15715) {
15716    init_test(cx, |_| {});
15717    let mut cx = EditorTestContext::new(cx).await;
15718
15719    let diff_base = r#"
15720        a
15721        b
15722        c
15723        "#
15724    .unindent();
15725
15726    cx.set_state(
15727        &r#"
15728        ˇb
15729        c
15730        "#
15731        .unindent(),
15732    );
15733    cx.set_head_text(&diff_base);
15734    cx.update_editor(|editor, window, cx| {
15735        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15736    });
15737    executor.run_until_parked();
15738
15739    let hunk_expanded = r#"
15740        - a
15741          ˇb
15742          c
15743        "#
15744    .unindent();
15745
15746    cx.assert_state_with_diff(hunk_expanded.clone());
15747
15748    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15749        let snapshot = editor.snapshot(window, cx);
15750        let hunks = editor
15751            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15752            .collect::<Vec<_>>();
15753        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15754        let buffer_id = hunks[0].buffer_id;
15755        hunks
15756            .into_iter()
15757            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15758            .collect::<Vec<_>>()
15759    });
15760    assert_eq!(hunk_ranges.len(), 1);
15761
15762    cx.update_editor(|editor, _, cx| {
15763        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15764    });
15765    executor.run_until_parked();
15766
15767    let hunk_collapsed = r#"
15768          ˇb
15769          c
15770        "#
15771    .unindent();
15772
15773    cx.assert_state_with_diff(hunk_collapsed);
15774
15775    cx.update_editor(|editor, _, cx| {
15776        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15777    });
15778    executor.run_until_parked();
15779
15780    cx.assert_state_with_diff(hunk_expanded.clone());
15781}
15782
15783#[gpui::test]
15784async fn test_display_diff_hunks(cx: &mut TestAppContext) {
15785    init_test(cx, |_| {});
15786
15787    let fs = FakeFs::new(cx.executor());
15788    fs.insert_tree(
15789        path!("/test"),
15790        json!({
15791            ".git": {},
15792            "file-1": "ONE\n",
15793            "file-2": "TWO\n",
15794            "file-3": "THREE\n",
15795        }),
15796    )
15797    .await;
15798
15799    fs.set_head_for_repo(
15800        path!("/test/.git").as_ref(),
15801        &[
15802            ("file-1".into(), "one\n".into()),
15803            ("file-2".into(), "two\n".into()),
15804            ("file-3".into(), "three\n".into()),
15805        ],
15806    );
15807
15808    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
15809    let mut buffers = vec![];
15810    for i in 1..=3 {
15811        let buffer = project
15812            .update(cx, |project, cx| {
15813                let path = format!(path!("/test/file-{}"), i);
15814                project.open_local_buffer(path, cx)
15815            })
15816            .await
15817            .unwrap();
15818        buffers.push(buffer);
15819    }
15820
15821    let multibuffer = cx.new(|cx| {
15822        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
15823        multibuffer.set_all_diff_hunks_expanded(cx);
15824        for buffer in &buffers {
15825            let snapshot = buffer.read(cx).snapshot();
15826            multibuffer.set_excerpts_for_path(
15827                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
15828                buffer.clone(),
15829                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
15830                DEFAULT_MULTIBUFFER_CONTEXT,
15831                cx,
15832            );
15833        }
15834        multibuffer
15835    });
15836
15837    let editor = cx.add_window(|window, cx| {
15838        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
15839    });
15840    cx.run_until_parked();
15841
15842    let snapshot = editor
15843        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
15844        .unwrap();
15845    let hunks = snapshot
15846        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
15847        .map(|hunk| match hunk {
15848            DisplayDiffHunk::Unfolded {
15849                display_row_range, ..
15850            } => display_row_range,
15851            DisplayDiffHunk::Folded { .. } => unreachable!(),
15852        })
15853        .collect::<Vec<_>>();
15854    assert_eq!(
15855        hunks,
15856        [
15857            DisplayRow(2)..DisplayRow(4),
15858            DisplayRow(7)..DisplayRow(9),
15859            DisplayRow(12)..DisplayRow(14),
15860        ]
15861    );
15862}
15863
15864#[gpui::test]
15865async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
15866    init_test(cx, |_| {});
15867
15868    let mut cx = EditorTestContext::new(cx).await;
15869    cx.set_head_text(indoc! { "
15870        one
15871        two
15872        three
15873        four
15874        five
15875        "
15876    });
15877    cx.set_index_text(indoc! { "
15878        one
15879        two
15880        three
15881        four
15882        five
15883        "
15884    });
15885    cx.set_state(indoc! {"
15886        one
15887        TWO
15888        ˇTHREE
15889        FOUR
15890        five
15891    "});
15892    cx.run_until_parked();
15893    cx.update_editor(|editor, window, cx| {
15894        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15895    });
15896    cx.run_until_parked();
15897    cx.assert_index_text(Some(indoc! {"
15898        one
15899        TWO
15900        THREE
15901        FOUR
15902        five
15903    "}));
15904    cx.set_state(indoc! { "
15905        one
15906        TWO
15907        ˇTHREE-HUNDRED
15908        FOUR
15909        five
15910    "});
15911    cx.run_until_parked();
15912    cx.update_editor(|editor, window, cx| {
15913        let snapshot = editor.snapshot(window, cx);
15914        let hunks = editor
15915            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15916            .collect::<Vec<_>>();
15917        assert_eq!(hunks.len(), 1);
15918        assert_eq!(
15919            hunks[0].status(),
15920            DiffHunkStatus {
15921                kind: DiffHunkStatusKind::Modified,
15922                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
15923            }
15924        );
15925
15926        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15927    });
15928    cx.run_until_parked();
15929    cx.assert_index_text(Some(indoc! {"
15930        one
15931        TWO
15932        THREE-HUNDRED
15933        FOUR
15934        five
15935    "}));
15936}
15937
15938#[gpui::test]
15939fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
15940    init_test(cx, |_| {});
15941
15942    let editor = cx.add_window(|window, cx| {
15943        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
15944        build_editor(buffer, window, cx)
15945    });
15946
15947    let render_args = Arc::new(Mutex::new(None));
15948    let snapshot = editor
15949        .update(cx, |editor, window, cx| {
15950            let snapshot = editor.buffer().read(cx).snapshot(cx);
15951            let range =
15952                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
15953
15954            struct RenderArgs {
15955                row: MultiBufferRow,
15956                folded: bool,
15957                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
15958            }
15959
15960            let crease = Crease::inline(
15961                range,
15962                FoldPlaceholder::test(),
15963                {
15964                    let toggle_callback = render_args.clone();
15965                    move |row, folded, callback, _window, _cx| {
15966                        *toggle_callback.lock() = Some(RenderArgs {
15967                            row,
15968                            folded,
15969                            callback,
15970                        });
15971                        div()
15972                    }
15973                },
15974                |_row, _folded, _window, _cx| div(),
15975            );
15976
15977            editor.insert_creases(Some(crease), cx);
15978            let snapshot = editor.snapshot(window, cx);
15979            let _div = snapshot.render_crease_toggle(
15980                MultiBufferRow(1),
15981                false,
15982                cx.entity().clone(),
15983                window,
15984                cx,
15985            );
15986            snapshot
15987        })
15988        .unwrap();
15989
15990    let render_args = render_args.lock().take().unwrap();
15991    assert_eq!(render_args.row, MultiBufferRow(1));
15992    assert!(!render_args.folded);
15993    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
15994
15995    cx.update_window(*editor, |_, window, cx| {
15996        (render_args.callback)(true, window, cx)
15997    })
15998    .unwrap();
15999    let snapshot = editor
16000        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16001        .unwrap();
16002    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16003
16004    cx.update_window(*editor, |_, window, cx| {
16005        (render_args.callback)(false, window, cx)
16006    })
16007    .unwrap();
16008    let snapshot = editor
16009        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16010        .unwrap();
16011    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16012}
16013
16014#[gpui::test]
16015async fn test_input_text(cx: &mut TestAppContext) {
16016    init_test(cx, |_| {});
16017    let mut cx = EditorTestContext::new(cx).await;
16018
16019    cx.set_state(
16020        &r#"ˇone
16021        two
16022
16023        three
16024        fourˇ
16025        five
16026
16027        siˇx"#
16028            .unindent(),
16029    );
16030
16031    cx.dispatch_action(HandleInput(String::new()));
16032    cx.assert_editor_state(
16033        &r#"ˇone
16034        two
16035
16036        three
16037        fourˇ
16038        five
16039
16040        siˇx"#
16041            .unindent(),
16042    );
16043
16044    cx.dispatch_action(HandleInput("AAAA".to_string()));
16045    cx.assert_editor_state(
16046        &r#"AAAAˇone
16047        two
16048
16049        three
16050        fourAAAAˇ
16051        five
16052
16053        siAAAAˇx"#
16054            .unindent(),
16055    );
16056}
16057
16058#[gpui::test]
16059async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16060    init_test(cx, |_| {});
16061
16062    let mut cx = EditorTestContext::new(cx).await;
16063    cx.set_state(
16064        r#"let foo = 1;
16065let foo = 2;
16066let foo = 3;
16067let fooˇ = 4;
16068let foo = 5;
16069let foo = 6;
16070let foo = 7;
16071let foo = 8;
16072let foo = 9;
16073let foo = 10;
16074let foo = 11;
16075let foo = 12;
16076let foo = 13;
16077let foo = 14;
16078let foo = 15;"#,
16079    );
16080
16081    cx.update_editor(|e, window, cx| {
16082        assert_eq!(
16083            e.next_scroll_position,
16084            NextScrollCursorCenterTopBottom::Center,
16085            "Default next scroll direction is center",
16086        );
16087
16088        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16089        assert_eq!(
16090            e.next_scroll_position,
16091            NextScrollCursorCenterTopBottom::Top,
16092            "After center, next scroll direction should be top",
16093        );
16094
16095        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16096        assert_eq!(
16097            e.next_scroll_position,
16098            NextScrollCursorCenterTopBottom::Bottom,
16099            "After top, next scroll direction should be bottom",
16100        );
16101
16102        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16103        assert_eq!(
16104            e.next_scroll_position,
16105            NextScrollCursorCenterTopBottom::Center,
16106            "After bottom, scrolling should start over",
16107        );
16108
16109        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16110        assert_eq!(
16111            e.next_scroll_position,
16112            NextScrollCursorCenterTopBottom::Top,
16113            "Scrolling continues if retriggered fast enough"
16114        );
16115    });
16116
16117    cx.executor()
16118        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16119    cx.executor().run_until_parked();
16120    cx.update_editor(|e, _, _| {
16121        assert_eq!(
16122            e.next_scroll_position,
16123            NextScrollCursorCenterTopBottom::Center,
16124            "If scrolling is not triggered fast enough, it should reset"
16125        );
16126    });
16127}
16128
16129#[gpui::test]
16130async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16131    init_test(cx, |_| {});
16132    let mut cx = EditorLspTestContext::new_rust(
16133        lsp::ServerCapabilities {
16134            definition_provider: Some(lsp::OneOf::Left(true)),
16135            references_provider: Some(lsp::OneOf::Left(true)),
16136            ..lsp::ServerCapabilities::default()
16137        },
16138        cx,
16139    )
16140    .await;
16141
16142    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16143        let go_to_definition = cx
16144            .lsp
16145            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16146                move |params, _| async move {
16147                    if empty_go_to_definition {
16148                        Ok(None)
16149                    } else {
16150                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16151                            uri: params.text_document_position_params.text_document.uri,
16152                            range: lsp::Range::new(
16153                                lsp::Position::new(4, 3),
16154                                lsp::Position::new(4, 6),
16155                            ),
16156                        })))
16157                    }
16158                },
16159            );
16160        let references = cx
16161            .lsp
16162            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16163                Ok(Some(vec![lsp::Location {
16164                    uri: params.text_document_position.text_document.uri,
16165                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16166                }]))
16167            });
16168        (go_to_definition, references)
16169    };
16170
16171    cx.set_state(
16172        &r#"fn one() {
16173            let mut a = ˇtwo();
16174        }
16175
16176        fn two() {}"#
16177            .unindent(),
16178    );
16179    set_up_lsp_handlers(false, &mut cx);
16180    let navigated = cx
16181        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16182        .await
16183        .expect("Failed to navigate to definition");
16184    assert_eq!(
16185        navigated,
16186        Navigated::Yes,
16187        "Should have navigated to definition from the GetDefinition response"
16188    );
16189    cx.assert_editor_state(
16190        &r#"fn one() {
16191            let mut a = two();
16192        }
16193
16194        fn «twoˇ»() {}"#
16195            .unindent(),
16196    );
16197
16198    let editors = cx.update_workspace(|workspace, _, cx| {
16199        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16200    });
16201    cx.update_editor(|_, _, test_editor_cx| {
16202        assert_eq!(
16203            editors.len(),
16204            1,
16205            "Initially, only one, test, editor should be open in the workspace"
16206        );
16207        assert_eq!(
16208            test_editor_cx.entity(),
16209            editors.last().expect("Asserted len is 1").clone()
16210        );
16211    });
16212
16213    set_up_lsp_handlers(true, &mut cx);
16214    let navigated = cx
16215        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16216        .await
16217        .expect("Failed to navigate to lookup references");
16218    assert_eq!(
16219        navigated,
16220        Navigated::Yes,
16221        "Should have navigated to references as a fallback after empty GoToDefinition response"
16222    );
16223    // We should not change the selections in the existing file,
16224    // if opening another milti buffer with the references
16225    cx.assert_editor_state(
16226        &r#"fn one() {
16227            let mut a = two();
16228        }
16229
16230        fn «twoˇ»() {}"#
16231            .unindent(),
16232    );
16233    let editors = cx.update_workspace(|workspace, _, cx| {
16234        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16235    });
16236    cx.update_editor(|_, _, test_editor_cx| {
16237        assert_eq!(
16238            editors.len(),
16239            2,
16240            "After falling back to references search, we open a new editor with the results"
16241        );
16242        let references_fallback_text = editors
16243            .into_iter()
16244            .find(|new_editor| *new_editor != test_editor_cx.entity())
16245            .expect("Should have one non-test editor now")
16246            .read(test_editor_cx)
16247            .text(test_editor_cx);
16248        assert_eq!(
16249            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16250            "Should use the range from the references response and not the GoToDefinition one"
16251        );
16252    });
16253}
16254
16255#[gpui::test]
16256async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16257    init_test(cx, |_| {});
16258    cx.update(|cx| {
16259        let mut editor_settings = EditorSettings::get_global(cx).clone();
16260        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16261        EditorSettings::override_global(editor_settings, cx);
16262    });
16263    let mut cx = EditorLspTestContext::new_rust(
16264        lsp::ServerCapabilities {
16265            definition_provider: Some(lsp::OneOf::Left(true)),
16266            references_provider: Some(lsp::OneOf::Left(true)),
16267            ..lsp::ServerCapabilities::default()
16268        },
16269        cx,
16270    )
16271    .await;
16272    let original_state = r#"fn one() {
16273        let mut a = ˇtwo();
16274    }
16275
16276    fn two() {}"#
16277        .unindent();
16278    cx.set_state(&original_state);
16279
16280    let mut go_to_definition = cx
16281        .lsp
16282        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16283            move |_, _| async move { Ok(None) },
16284        );
16285    let _references = cx
16286        .lsp
16287        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16288            panic!("Should not call for references with no go to definition fallback")
16289        });
16290
16291    let navigated = cx
16292        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16293        .await
16294        .expect("Failed to navigate to lookup references");
16295    go_to_definition
16296        .next()
16297        .await
16298        .expect("Should have called the go_to_definition handler");
16299
16300    assert_eq!(
16301        navigated,
16302        Navigated::No,
16303        "Should have navigated to references as a fallback after empty GoToDefinition response"
16304    );
16305    cx.assert_editor_state(&original_state);
16306    let editors = cx.update_workspace(|workspace, _, cx| {
16307        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16308    });
16309    cx.update_editor(|_, _, _| {
16310        assert_eq!(
16311            editors.len(),
16312            1,
16313            "After unsuccessful fallback, no other editor should have been opened"
16314        );
16315    });
16316}
16317
16318#[gpui::test]
16319async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16320    init_test(cx, |_| {});
16321
16322    let language = Arc::new(Language::new(
16323        LanguageConfig::default(),
16324        Some(tree_sitter_rust::LANGUAGE.into()),
16325    ));
16326
16327    let text = r#"
16328        #[cfg(test)]
16329        mod tests() {
16330            #[test]
16331            fn runnable_1() {
16332                let a = 1;
16333            }
16334
16335            #[test]
16336            fn runnable_2() {
16337                let a = 1;
16338                let b = 2;
16339            }
16340        }
16341    "#
16342    .unindent();
16343
16344    let fs = FakeFs::new(cx.executor());
16345    fs.insert_file("/file.rs", Default::default()).await;
16346
16347    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16348    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16349    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16350    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16351    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16352
16353    let editor = cx.new_window_entity(|window, cx| {
16354        Editor::new(
16355            EditorMode::Full,
16356            multi_buffer,
16357            Some(project.clone()),
16358            window,
16359            cx,
16360        )
16361    });
16362
16363    editor.update_in(cx, |editor, window, cx| {
16364        let snapshot = editor.buffer().read(cx).snapshot(cx);
16365        editor.tasks.insert(
16366            (buffer.read(cx).remote_id(), 3),
16367            RunnableTasks {
16368                templates: vec![],
16369                offset: snapshot.anchor_before(43),
16370                column: 0,
16371                extra_variables: HashMap::default(),
16372                context_range: BufferOffset(43)..BufferOffset(85),
16373            },
16374        );
16375        editor.tasks.insert(
16376            (buffer.read(cx).remote_id(), 8),
16377            RunnableTasks {
16378                templates: vec![],
16379                offset: snapshot.anchor_before(86),
16380                column: 0,
16381                extra_variables: HashMap::default(),
16382                context_range: BufferOffset(86)..BufferOffset(191),
16383            },
16384        );
16385
16386        // Test finding task when cursor is inside function body
16387        editor.change_selections(None, window, cx, |s| {
16388            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16389        });
16390        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16391        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16392
16393        // Test finding task when cursor is on function name
16394        editor.change_selections(None, window, cx, |s| {
16395            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16396        });
16397        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16398        assert_eq!(row, 8, "Should find task when cursor is on function name");
16399    });
16400}
16401
16402#[gpui::test]
16403async fn test_folding_buffers(cx: &mut TestAppContext) {
16404    init_test(cx, |_| {});
16405
16406    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16407    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16408    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16409
16410    let fs = FakeFs::new(cx.executor());
16411    fs.insert_tree(
16412        path!("/a"),
16413        json!({
16414            "first.rs": sample_text_1,
16415            "second.rs": sample_text_2,
16416            "third.rs": sample_text_3,
16417        }),
16418    )
16419    .await;
16420    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16421    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16422    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16423    let worktree = project.update(cx, |project, cx| {
16424        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16425        assert_eq!(worktrees.len(), 1);
16426        worktrees.pop().unwrap()
16427    });
16428    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16429
16430    let buffer_1 = project
16431        .update(cx, |project, cx| {
16432            project.open_buffer((worktree_id, "first.rs"), cx)
16433        })
16434        .await
16435        .unwrap();
16436    let buffer_2 = project
16437        .update(cx, |project, cx| {
16438            project.open_buffer((worktree_id, "second.rs"), cx)
16439        })
16440        .await
16441        .unwrap();
16442    let buffer_3 = project
16443        .update(cx, |project, cx| {
16444            project.open_buffer((worktree_id, "third.rs"), cx)
16445        })
16446        .await
16447        .unwrap();
16448
16449    let multi_buffer = cx.new(|cx| {
16450        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16451        multi_buffer.push_excerpts(
16452            buffer_1.clone(),
16453            [
16454                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16455                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16456                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16457            ],
16458            cx,
16459        );
16460        multi_buffer.push_excerpts(
16461            buffer_2.clone(),
16462            [
16463                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16464                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16465                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16466            ],
16467            cx,
16468        );
16469        multi_buffer.push_excerpts(
16470            buffer_3.clone(),
16471            [
16472                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16473                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16474                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16475            ],
16476            cx,
16477        );
16478        multi_buffer
16479    });
16480    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16481        Editor::new(
16482            EditorMode::Full,
16483            multi_buffer.clone(),
16484            Some(project.clone()),
16485            window,
16486            cx,
16487        )
16488    });
16489
16490    assert_eq!(
16491        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16492        "\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",
16493    );
16494
16495    multi_buffer_editor.update(cx, |editor, cx| {
16496        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16497    });
16498    assert_eq!(
16499        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16500        "\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",
16501        "After folding the first buffer, its text should not be displayed"
16502    );
16503
16504    multi_buffer_editor.update(cx, |editor, cx| {
16505        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16506    });
16507    assert_eq!(
16508        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16509        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16510        "After folding the second buffer, its text should not be displayed"
16511    );
16512
16513    multi_buffer_editor.update(cx, |editor, cx| {
16514        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16515    });
16516    assert_eq!(
16517        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16518        "\n\n\n\n\n",
16519        "After folding the third buffer, its text should not be displayed"
16520    );
16521
16522    // Emulate selection inside the fold logic, that should work
16523    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16524        editor
16525            .snapshot(window, cx)
16526            .next_line_boundary(Point::new(0, 4));
16527    });
16528
16529    multi_buffer_editor.update(cx, |editor, cx| {
16530        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16531    });
16532    assert_eq!(
16533        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16534        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16535        "After unfolding the second buffer, its text should be displayed"
16536    );
16537
16538    // Typing inside of buffer 1 causes that buffer to be unfolded.
16539    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16540        assert_eq!(
16541            multi_buffer
16542                .read(cx)
16543                .snapshot(cx)
16544                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16545                .collect::<String>(),
16546            "bbbb"
16547        );
16548        editor.change_selections(None, window, cx, |selections| {
16549            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16550        });
16551        editor.handle_input("B", window, cx);
16552    });
16553
16554    assert_eq!(
16555        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16556        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16557        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16558    );
16559
16560    multi_buffer_editor.update(cx, |editor, cx| {
16561        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16562    });
16563    assert_eq!(
16564        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16565        "\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",
16566        "After unfolding the all buffers, all original text should be displayed"
16567    );
16568}
16569
16570#[gpui::test]
16571async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16572    init_test(cx, |_| {});
16573
16574    let sample_text_1 = "1111\n2222\n3333".to_string();
16575    let sample_text_2 = "4444\n5555\n6666".to_string();
16576    let sample_text_3 = "7777\n8888\n9999".to_string();
16577
16578    let fs = FakeFs::new(cx.executor());
16579    fs.insert_tree(
16580        path!("/a"),
16581        json!({
16582            "first.rs": sample_text_1,
16583            "second.rs": sample_text_2,
16584            "third.rs": sample_text_3,
16585        }),
16586    )
16587    .await;
16588    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16589    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16590    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16591    let worktree = project.update(cx, |project, cx| {
16592        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16593        assert_eq!(worktrees.len(), 1);
16594        worktrees.pop().unwrap()
16595    });
16596    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16597
16598    let buffer_1 = project
16599        .update(cx, |project, cx| {
16600            project.open_buffer((worktree_id, "first.rs"), cx)
16601        })
16602        .await
16603        .unwrap();
16604    let buffer_2 = project
16605        .update(cx, |project, cx| {
16606            project.open_buffer((worktree_id, "second.rs"), cx)
16607        })
16608        .await
16609        .unwrap();
16610    let buffer_3 = project
16611        .update(cx, |project, cx| {
16612            project.open_buffer((worktree_id, "third.rs"), cx)
16613        })
16614        .await
16615        .unwrap();
16616
16617    let multi_buffer = cx.new(|cx| {
16618        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16619        multi_buffer.push_excerpts(
16620            buffer_1.clone(),
16621            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16622            cx,
16623        );
16624        multi_buffer.push_excerpts(
16625            buffer_2.clone(),
16626            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16627            cx,
16628        );
16629        multi_buffer.push_excerpts(
16630            buffer_3.clone(),
16631            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16632            cx,
16633        );
16634        multi_buffer
16635    });
16636
16637    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16638        Editor::new(
16639            EditorMode::Full,
16640            multi_buffer,
16641            Some(project.clone()),
16642            window,
16643            cx,
16644        )
16645    });
16646
16647    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
16648    assert_eq!(
16649        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16650        full_text,
16651    );
16652
16653    multi_buffer_editor.update(cx, |editor, cx| {
16654        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16655    });
16656    assert_eq!(
16657        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16658        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
16659        "After folding the first buffer, its text should not be displayed"
16660    );
16661
16662    multi_buffer_editor.update(cx, |editor, cx| {
16663        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16664    });
16665
16666    assert_eq!(
16667        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16668        "\n\n\n\n\n\n7777\n8888\n9999",
16669        "After folding the second buffer, its text should not be displayed"
16670    );
16671
16672    multi_buffer_editor.update(cx, |editor, cx| {
16673        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16674    });
16675    assert_eq!(
16676        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16677        "\n\n\n\n\n",
16678        "After folding the third buffer, its text should not be displayed"
16679    );
16680
16681    multi_buffer_editor.update(cx, |editor, cx| {
16682        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16683    });
16684    assert_eq!(
16685        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16686        "\n\n\n\n4444\n5555\n6666\n\n",
16687        "After unfolding the second buffer, its text should be displayed"
16688    );
16689
16690    multi_buffer_editor.update(cx, |editor, cx| {
16691        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
16692    });
16693    assert_eq!(
16694        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16695        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
16696        "After unfolding the first buffer, its text should be displayed"
16697    );
16698
16699    multi_buffer_editor.update(cx, |editor, cx| {
16700        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16701    });
16702    assert_eq!(
16703        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16704        full_text,
16705        "After unfolding all buffers, all original text should be displayed"
16706    );
16707}
16708
16709#[gpui::test]
16710async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
16711    init_test(cx, |_| {});
16712
16713    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16714
16715    let fs = FakeFs::new(cx.executor());
16716    fs.insert_tree(
16717        path!("/a"),
16718        json!({
16719            "main.rs": sample_text,
16720        }),
16721    )
16722    .await;
16723    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16724    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16725    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16726    let worktree = project.update(cx, |project, cx| {
16727        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16728        assert_eq!(worktrees.len(), 1);
16729        worktrees.pop().unwrap()
16730    });
16731    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16732
16733    let buffer_1 = project
16734        .update(cx, |project, cx| {
16735            project.open_buffer((worktree_id, "main.rs"), cx)
16736        })
16737        .await
16738        .unwrap();
16739
16740    let multi_buffer = cx.new(|cx| {
16741        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16742        multi_buffer.push_excerpts(
16743            buffer_1.clone(),
16744            [ExcerptRange::new(
16745                Point::new(0, 0)
16746                    ..Point::new(
16747                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
16748                        0,
16749                    ),
16750            )],
16751            cx,
16752        );
16753        multi_buffer
16754    });
16755    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16756        Editor::new(
16757            EditorMode::Full,
16758            multi_buffer,
16759            Some(project.clone()),
16760            window,
16761            cx,
16762        )
16763    });
16764
16765    let selection_range = Point::new(1, 0)..Point::new(2, 0);
16766    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16767        enum TestHighlight {}
16768        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
16769        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
16770        editor.highlight_text::<TestHighlight>(
16771            vec![highlight_range.clone()],
16772            HighlightStyle::color(Hsla::green()),
16773            cx,
16774        );
16775        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
16776    });
16777
16778    let full_text = format!("\n\n{sample_text}");
16779    assert_eq!(
16780        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16781        full_text,
16782    );
16783}
16784
16785#[gpui::test]
16786async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
16787    init_test(cx, |_| {});
16788    cx.update(|cx| {
16789        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
16790            "keymaps/default-linux.json",
16791            cx,
16792        )
16793        .unwrap();
16794        cx.bind_keys(default_key_bindings);
16795    });
16796
16797    let (editor, cx) = cx.add_window_view(|window, cx| {
16798        let multi_buffer = MultiBuffer::build_multi(
16799            [
16800                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
16801                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
16802                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
16803                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
16804            ],
16805            cx,
16806        );
16807        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
16808
16809        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
16810        // fold all but the second buffer, so that we test navigating between two
16811        // adjacent folded buffers, as well as folded buffers at the start and
16812        // end the multibuffer
16813        editor.fold_buffer(buffer_ids[0], cx);
16814        editor.fold_buffer(buffer_ids[2], cx);
16815        editor.fold_buffer(buffer_ids[3], cx);
16816
16817        editor
16818    });
16819    cx.simulate_resize(size(px(1000.), px(1000.)));
16820
16821    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
16822    cx.assert_excerpts_with_selections(indoc! {"
16823        [EXCERPT]
16824        ˇ[FOLDED]
16825        [EXCERPT]
16826        a1
16827        b1
16828        [EXCERPT]
16829        [FOLDED]
16830        [EXCERPT]
16831        [FOLDED]
16832        "
16833    });
16834    cx.simulate_keystroke("down");
16835    cx.assert_excerpts_with_selections(indoc! {"
16836        [EXCERPT]
16837        [FOLDED]
16838        [EXCERPT]
16839        ˇa1
16840        b1
16841        [EXCERPT]
16842        [FOLDED]
16843        [EXCERPT]
16844        [FOLDED]
16845        "
16846    });
16847    cx.simulate_keystroke("down");
16848    cx.assert_excerpts_with_selections(indoc! {"
16849        [EXCERPT]
16850        [FOLDED]
16851        [EXCERPT]
16852        a1
16853        ˇb1
16854        [EXCERPT]
16855        [FOLDED]
16856        [EXCERPT]
16857        [FOLDED]
16858        "
16859    });
16860    cx.simulate_keystroke("down");
16861    cx.assert_excerpts_with_selections(indoc! {"
16862        [EXCERPT]
16863        [FOLDED]
16864        [EXCERPT]
16865        a1
16866        b1
16867        ˇ[EXCERPT]
16868        [FOLDED]
16869        [EXCERPT]
16870        [FOLDED]
16871        "
16872    });
16873    cx.simulate_keystroke("down");
16874    cx.assert_excerpts_with_selections(indoc! {"
16875        [EXCERPT]
16876        [FOLDED]
16877        [EXCERPT]
16878        a1
16879        b1
16880        [EXCERPT]
16881        ˇ[FOLDED]
16882        [EXCERPT]
16883        [FOLDED]
16884        "
16885    });
16886    for _ in 0..5 {
16887        cx.simulate_keystroke("down");
16888        cx.assert_excerpts_with_selections(indoc! {"
16889            [EXCERPT]
16890            [FOLDED]
16891            [EXCERPT]
16892            a1
16893            b1
16894            [EXCERPT]
16895            [FOLDED]
16896            [EXCERPT]
16897            ˇ[FOLDED]
16898            "
16899        });
16900    }
16901
16902    cx.simulate_keystroke("up");
16903    cx.assert_excerpts_with_selections(indoc! {"
16904        [EXCERPT]
16905        [FOLDED]
16906        [EXCERPT]
16907        a1
16908        b1
16909        [EXCERPT]
16910        ˇ[FOLDED]
16911        [EXCERPT]
16912        [FOLDED]
16913        "
16914    });
16915    cx.simulate_keystroke("up");
16916    cx.assert_excerpts_with_selections(indoc! {"
16917        [EXCERPT]
16918        [FOLDED]
16919        [EXCERPT]
16920        a1
16921        b1
16922        ˇ[EXCERPT]
16923        [FOLDED]
16924        [EXCERPT]
16925        [FOLDED]
16926        "
16927    });
16928    cx.simulate_keystroke("up");
16929    cx.assert_excerpts_with_selections(indoc! {"
16930        [EXCERPT]
16931        [FOLDED]
16932        [EXCERPT]
16933        a1
16934        ˇb1
16935        [EXCERPT]
16936        [FOLDED]
16937        [EXCERPT]
16938        [FOLDED]
16939        "
16940    });
16941    cx.simulate_keystroke("up");
16942    cx.assert_excerpts_with_selections(indoc! {"
16943        [EXCERPT]
16944        [FOLDED]
16945        [EXCERPT]
16946        ˇa1
16947        b1
16948        [EXCERPT]
16949        [FOLDED]
16950        [EXCERPT]
16951        [FOLDED]
16952        "
16953    });
16954    for _ in 0..5 {
16955        cx.simulate_keystroke("up");
16956        cx.assert_excerpts_with_selections(indoc! {"
16957            [EXCERPT]
16958            ˇ[FOLDED]
16959            [EXCERPT]
16960            a1
16961            b1
16962            [EXCERPT]
16963            [FOLDED]
16964            [EXCERPT]
16965            [FOLDED]
16966            "
16967        });
16968    }
16969}
16970
16971#[gpui::test]
16972async fn test_inline_completion_text(cx: &mut TestAppContext) {
16973    init_test(cx, |_| {});
16974
16975    // Simple insertion
16976    assert_highlighted_edits(
16977        "Hello, world!",
16978        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
16979        true,
16980        cx,
16981        |highlighted_edits, cx| {
16982            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
16983            assert_eq!(highlighted_edits.highlights.len(), 1);
16984            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
16985            assert_eq!(
16986                highlighted_edits.highlights[0].1.background_color,
16987                Some(cx.theme().status().created_background)
16988            );
16989        },
16990    )
16991    .await;
16992
16993    // Replacement
16994    assert_highlighted_edits(
16995        "This is a test.",
16996        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
16997        false,
16998        cx,
16999        |highlighted_edits, cx| {
17000            assert_eq!(highlighted_edits.text, "That is a test.");
17001            assert_eq!(highlighted_edits.highlights.len(), 1);
17002            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17003            assert_eq!(
17004                highlighted_edits.highlights[0].1.background_color,
17005                Some(cx.theme().status().created_background)
17006            );
17007        },
17008    )
17009    .await;
17010
17011    // Multiple edits
17012    assert_highlighted_edits(
17013        "Hello, world!",
17014        vec![
17015            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17016            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17017        ],
17018        false,
17019        cx,
17020        |highlighted_edits, cx| {
17021            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17022            assert_eq!(highlighted_edits.highlights.len(), 2);
17023            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17024            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17025            assert_eq!(
17026                highlighted_edits.highlights[0].1.background_color,
17027                Some(cx.theme().status().created_background)
17028            );
17029            assert_eq!(
17030                highlighted_edits.highlights[1].1.background_color,
17031                Some(cx.theme().status().created_background)
17032            );
17033        },
17034    )
17035    .await;
17036
17037    // Multiple lines with edits
17038    assert_highlighted_edits(
17039        "First line\nSecond line\nThird line\nFourth line",
17040        vec![
17041            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17042            (
17043                Point::new(2, 0)..Point::new(2, 10),
17044                "New third line".to_string(),
17045            ),
17046            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17047        ],
17048        false,
17049        cx,
17050        |highlighted_edits, cx| {
17051            assert_eq!(
17052                highlighted_edits.text,
17053                "Second modified\nNew third line\nFourth updated line"
17054            );
17055            assert_eq!(highlighted_edits.highlights.len(), 3);
17056            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17057            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17058            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17059            for highlight in &highlighted_edits.highlights {
17060                assert_eq!(
17061                    highlight.1.background_color,
17062                    Some(cx.theme().status().created_background)
17063                );
17064            }
17065        },
17066    )
17067    .await;
17068}
17069
17070#[gpui::test]
17071async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17072    init_test(cx, |_| {});
17073
17074    // Deletion
17075    assert_highlighted_edits(
17076        "Hello, world!",
17077        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17078        true,
17079        cx,
17080        |highlighted_edits, cx| {
17081            assert_eq!(highlighted_edits.text, "Hello, world!");
17082            assert_eq!(highlighted_edits.highlights.len(), 1);
17083            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17084            assert_eq!(
17085                highlighted_edits.highlights[0].1.background_color,
17086                Some(cx.theme().status().deleted_background)
17087            );
17088        },
17089    )
17090    .await;
17091
17092    // Insertion
17093    assert_highlighted_edits(
17094        "Hello, world!",
17095        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17096        true,
17097        cx,
17098        |highlighted_edits, cx| {
17099            assert_eq!(highlighted_edits.highlights.len(), 1);
17100            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17101            assert_eq!(
17102                highlighted_edits.highlights[0].1.background_color,
17103                Some(cx.theme().status().created_background)
17104            );
17105        },
17106    )
17107    .await;
17108}
17109
17110async fn assert_highlighted_edits(
17111    text: &str,
17112    edits: Vec<(Range<Point>, String)>,
17113    include_deletions: bool,
17114    cx: &mut TestAppContext,
17115    assertion_fn: impl Fn(HighlightedText, &App),
17116) {
17117    let window = cx.add_window(|window, cx| {
17118        let buffer = MultiBuffer::build_simple(text, cx);
17119        Editor::new(EditorMode::Full, buffer, None, window, cx)
17120    });
17121    let cx = &mut VisualTestContext::from_window(*window, cx);
17122
17123    let (buffer, snapshot) = window
17124        .update(cx, |editor, _window, cx| {
17125            (
17126                editor.buffer().clone(),
17127                editor.buffer().read(cx).snapshot(cx),
17128            )
17129        })
17130        .unwrap();
17131
17132    let edits = edits
17133        .into_iter()
17134        .map(|(range, edit)| {
17135            (
17136                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17137                edit,
17138            )
17139        })
17140        .collect::<Vec<_>>();
17141
17142    let text_anchor_edits = edits
17143        .clone()
17144        .into_iter()
17145        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17146        .collect::<Vec<_>>();
17147
17148    let edit_preview = window
17149        .update(cx, |_, _window, cx| {
17150            buffer
17151                .read(cx)
17152                .as_singleton()
17153                .unwrap()
17154                .read(cx)
17155                .preview_edits(text_anchor_edits.into(), cx)
17156        })
17157        .unwrap()
17158        .await;
17159
17160    cx.update(|_window, cx| {
17161        let highlighted_edits = inline_completion_edit_text(
17162            &snapshot.as_singleton().unwrap().2,
17163            &edits,
17164            &edit_preview,
17165            include_deletions,
17166            cx,
17167        );
17168        assertion_fn(highlighted_edits, cx)
17169    });
17170}
17171
17172#[track_caller]
17173fn assert_breakpoint(
17174    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17175    path: &Arc<Path>,
17176    expected: Vec<(u32, Breakpoint)>,
17177) {
17178    if expected.len() == 0usize {
17179        assert!(!breakpoints.contains_key(path), "{}", path.display());
17180    } else {
17181        let mut breakpoint = breakpoints
17182            .get(path)
17183            .unwrap()
17184            .into_iter()
17185            .map(|breakpoint| {
17186                (
17187                    breakpoint.row,
17188                    Breakpoint {
17189                        message: breakpoint.message.clone(),
17190                        state: breakpoint.state,
17191                        condition: breakpoint.condition.clone(),
17192                        hit_condition: breakpoint.hit_condition.clone(),
17193                    },
17194                )
17195            })
17196            .collect::<Vec<_>>();
17197
17198        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17199
17200        assert_eq!(expected, breakpoint);
17201    }
17202}
17203
17204fn add_log_breakpoint_at_cursor(
17205    editor: &mut Editor,
17206    log_message: &str,
17207    window: &mut Window,
17208    cx: &mut Context<Editor>,
17209) {
17210    let (anchor, bp) = editor
17211        .breakpoint_at_cursor_head(window, cx)
17212        .unwrap_or_else(|| {
17213            let cursor_position: Point = editor.selections.newest(cx).head();
17214
17215            let breakpoint_position = editor
17216                .snapshot(window, cx)
17217                .display_snapshot
17218                .buffer_snapshot
17219                .anchor_before(Point::new(cursor_position.row, 0));
17220
17221            (breakpoint_position, Breakpoint::new_log(&log_message))
17222        });
17223
17224    editor.edit_breakpoint_at_anchor(
17225        anchor,
17226        bp,
17227        BreakpointEditAction::EditLogMessage(log_message.into()),
17228        cx,
17229    );
17230}
17231
17232#[gpui::test]
17233async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17234    init_test(cx, |_| {});
17235
17236    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17237    let fs = FakeFs::new(cx.executor());
17238    fs.insert_tree(
17239        path!("/a"),
17240        json!({
17241            "main.rs": sample_text,
17242        }),
17243    )
17244    .await;
17245    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17246    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17247    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17248
17249    let fs = FakeFs::new(cx.executor());
17250    fs.insert_tree(
17251        path!("/a"),
17252        json!({
17253            "main.rs": sample_text,
17254        }),
17255    )
17256    .await;
17257    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17258    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17259    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17260    let worktree_id = workspace
17261        .update(cx, |workspace, _window, cx| {
17262            workspace.project().update(cx, |project, cx| {
17263                project.worktrees(cx).next().unwrap().read(cx).id()
17264            })
17265        })
17266        .unwrap();
17267
17268    let buffer = project
17269        .update(cx, |project, cx| {
17270            project.open_buffer((worktree_id, "main.rs"), cx)
17271        })
17272        .await
17273        .unwrap();
17274
17275    let (editor, cx) = cx.add_window_view(|window, cx| {
17276        Editor::new(
17277            EditorMode::Full,
17278            MultiBuffer::build_from_buffer(buffer, cx),
17279            Some(project.clone()),
17280            window,
17281            cx,
17282        )
17283    });
17284
17285    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17286    let abs_path = project.read_with(cx, |project, cx| {
17287        project
17288            .absolute_path(&project_path, cx)
17289            .map(|path_buf| Arc::from(path_buf.to_owned()))
17290            .unwrap()
17291    });
17292
17293    // assert we can add breakpoint on the first line
17294    editor.update_in(cx, |editor, window, cx| {
17295        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17296        editor.move_to_end(&MoveToEnd, window, cx);
17297        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17298    });
17299
17300    let breakpoints = editor.update(cx, |editor, cx| {
17301        editor
17302            .breakpoint_store()
17303            .as_ref()
17304            .unwrap()
17305            .read(cx)
17306            .all_breakpoints(cx)
17307            .clone()
17308    });
17309
17310    assert_eq!(1, breakpoints.len());
17311    assert_breakpoint(
17312        &breakpoints,
17313        &abs_path,
17314        vec![
17315            (0, Breakpoint::new_standard()),
17316            (3, Breakpoint::new_standard()),
17317        ],
17318    );
17319
17320    editor.update_in(cx, |editor, window, cx| {
17321        editor.move_to_beginning(&MoveToBeginning, window, cx);
17322        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17323    });
17324
17325    let breakpoints = editor.update(cx, |editor, cx| {
17326        editor
17327            .breakpoint_store()
17328            .as_ref()
17329            .unwrap()
17330            .read(cx)
17331            .all_breakpoints(cx)
17332            .clone()
17333    });
17334
17335    assert_eq!(1, breakpoints.len());
17336    assert_breakpoint(
17337        &breakpoints,
17338        &abs_path,
17339        vec![(3, Breakpoint::new_standard())],
17340    );
17341
17342    editor.update_in(cx, |editor, window, cx| {
17343        editor.move_to_end(&MoveToEnd, window, cx);
17344        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17345    });
17346
17347    let breakpoints = editor.update(cx, |editor, cx| {
17348        editor
17349            .breakpoint_store()
17350            .as_ref()
17351            .unwrap()
17352            .read(cx)
17353            .all_breakpoints(cx)
17354            .clone()
17355    });
17356
17357    assert_eq!(0, breakpoints.len());
17358    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17359}
17360
17361#[gpui::test]
17362async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17363    init_test(cx, |_| {});
17364
17365    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17366
17367    let fs = FakeFs::new(cx.executor());
17368    fs.insert_tree(
17369        path!("/a"),
17370        json!({
17371            "main.rs": sample_text,
17372        }),
17373    )
17374    .await;
17375    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17376    let (workspace, cx) =
17377        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17378
17379    let worktree_id = workspace.update(cx, |workspace, cx| {
17380        workspace.project().update(cx, |project, cx| {
17381            project.worktrees(cx).next().unwrap().read(cx).id()
17382        })
17383    });
17384
17385    let buffer = project
17386        .update(cx, |project, cx| {
17387            project.open_buffer((worktree_id, "main.rs"), cx)
17388        })
17389        .await
17390        .unwrap();
17391
17392    let (editor, cx) = cx.add_window_view(|window, cx| {
17393        Editor::new(
17394            EditorMode::Full,
17395            MultiBuffer::build_from_buffer(buffer, cx),
17396            Some(project.clone()),
17397            window,
17398            cx,
17399        )
17400    });
17401
17402    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17403    let abs_path = project.read_with(cx, |project, cx| {
17404        project
17405            .absolute_path(&project_path, cx)
17406            .map(|path_buf| Arc::from(path_buf.to_owned()))
17407            .unwrap()
17408    });
17409
17410    editor.update_in(cx, |editor, window, cx| {
17411        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17412    });
17413
17414    let breakpoints = editor.update(cx, |editor, cx| {
17415        editor
17416            .breakpoint_store()
17417            .as_ref()
17418            .unwrap()
17419            .read(cx)
17420            .all_breakpoints(cx)
17421            .clone()
17422    });
17423
17424    assert_breakpoint(
17425        &breakpoints,
17426        &abs_path,
17427        vec![(0, Breakpoint::new_log("hello world"))],
17428    );
17429
17430    // Removing a log message from a log breakpoint should remove it
17431    editor.update_in(cx, |editor, window, cx| {
17432        add_log_breakpoint_at_cursor(editor, "", window, cx);
17433    });
17434
17435    let breakpoints = editor.update(cx, |editor, cx| {
17436        editor
17437            .breakpoint_store()
17438            .as_ref()
17439            .unwrap()
17440            .read(cx)
17441            .all_breakpoints(cx)
17442            .clone()
17443    });
17444
17445    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17446
17447    editor.update_in(cx, |editor, window, cx| {
17448        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17449        editor.move_to_end(&MoveToEnd, window, cx);
17450        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17451        // Not adding a log message to a standard breakpoint shouldn't remove it
17452        add_log_breakpoint_at_cursor(editor, "", window, cx);
17453    });
17454
17455    let breakpoints = editor.update(cx, |editor, cx| {
17456        editor
17457            .breakpoint_store()
17458            .as_ref()
17459            .unwrap()
17460            .read(cx)
17461            .all_breakpoints(cx)
17462            .clone()
17463    });
17464
17465    assert_breakpoint(
17466        &breakpoints,
17467        &abs_path,
17468        vec![
17469            (0, Breakpoint::new_standard()),
17470            (3, Breakpoint::new_standard()),
17471        ],
17472    );
17473
17474    editor.update_in(cx, |editor, window, cx| {
17475        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17476    });
17477
17478    let breakpoints = editor.update(cx, |editor, cx| {
17479        editor
17480            .breakpoint_store()
17481            .as_ref()
17482            .unwrap()
17483            .read(cx)
17484            .all_breakpoints(cx)
17485            .clone()
17486    });
17487
17488    assert_breakpoint(
17489        &breakpoints,
17490        &abs_path,
17491        vec![
17492            (0, Breakpoint::new_standard()),
17493            (3, Breakpoint::new_log("hello world")),
17494        ],
17495    );
17496
17497    editor.update_in(cx, |editor, window, cx| {
17498        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17499    });
17500
17501    let breakpoints = editor.update(cx, |editor, cx| {
17502        editor
17503            .breakpoint_store()
17504            .as_ref()
17505            .unwrap()
17506            .read(cx)
17507            .all_breakpoints(cx)
17508            .clone()
17509    });
17510
17511    assert_breakpoint(
17512        &breakpoints,
17513        &abs_path,
17514        vec![
17515            (0, Breakpoint::new_standard()),
17516            (3, Breakpoint::new_log("hello Earth!!")),
17517        ],
17518    );
17519}
17520
17521/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17522/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17523/// or when breakpoints were placed out of order. This tests for a regression too
17524#[gpui::test]
17525async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17526    init_test(cx, |_| {});
17527
17528    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17529    let fs = FakeFs::new(cx.executor());
17530    fs.insert_tree(
17531        path!("/a"),
17532        json!({
17533            "main.rs": sample_text,
17534        }),
17535    )
17536    .await;
17537    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17538    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17539    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17540
17541    let fs = FakeFs::new(cx.executor());
17542    fs.insert_tree(
17543        path!("/a"),
17544        json!({
17545            "main.rs": sample_text,
17546        }),
17547    )
17548    .await;
17549    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17550    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17551    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17552    let worktree_id = workspace
17553        .update(cx, |workspace, _window, cx| {
17554            workspace.project().update(cx, |project, cx| {
17555                project.worktrees(cx).next().unwrap().read(cx).id()
17556            })
17557        })
17558        .unwrap();
17559
17560    let buffer = project
17561        .update(cx, |project, cx| {
17562            project.open_buffer((worktree_id, "main.rs"), cx)
17563        })
17564        .await
17565        .unwrap();
17566
17567    let (editor, cx) = cx.add_window_view(|window, cx| {
17568        Editor::new(
17569            EditorMode::Full,
17570            MultiBuffer::build_from_buffer(buffer, cx),
17571            Some(project.clone()),
17572            window,
17573            cx,
17574        )
17575    });
17576
17577    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17578    let abs_path = project.read_with(cx, |project, cx| {
17579        project
17580            .absolute_path(&project_path, cx)
17581            .map(|path_buf| Arc::from(path_buf.to_owned()))
17582            .unwrap()
17583    });
17584
17585    // assert we can add breakpoint on the first line
17586    editor.update_in(cx, |editor, window, cx| {
17587        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17588        editor.move_to_end(&MoveToEnd, window, cx);
17589        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17590        editor.move_up(&MoveUp, window, cx);
17591        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17592    });
17593
17594    let breakpoints = editor.update(cx, |editor, cx| {
17595        editor
17596            .breakpoint_store()
17597            .as_ref()
17598            .unwrap()
17599            .read(cx)
17600            .all_breakpoints(cx)
17601            .clone()
17602    });
17603
17604    assert_eq!(1, breakpoints.len());
17605    assert_breakpoint(
17606        &breakpoints,
17607        &abs_path,
17608        vec![
17609            (0, Breakpoint::new_standard()),
17610            (2, Breakpoint::new_standard()),
17611            (3, Breakpoint::new_standard()),
17612        ],
17613    );
17614
17615    editor.update_in(cx, |editor, window, cx| {
17616        editor.move_to_beginning(&MoveToBeginning, window, cx);
17617        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17618        editor.move_to_end(&MoveToEnd, window, cx);
17619        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17620        // Disabling a breakpoint that doesn't exist should do nothing
17621        editor.move_up(&MoveUp, window, cx);
17622        editor.move_up(&MoveUp, window, cx);
17623        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17624    });
17625
17626    let breakpoints = editor.update(cx, |editor, cx| {
17627        editor
17628            .breakpoint_store()
17629            .as_ref()
17630            .unwrap()
17631            .read(cx)
17632            .all_breakpoints(cx)
17633            .clone()
17634    });
17635
17636    let disable_breakpoint = {
17637        let mut bp = Breakpoint::new_standard();
17638        bp.state = BreakpointState::Disabled;
17639        bp
17640    };
17641
17642    assert_eq!(1, breakpoints.len());
17643    assert_breakpoint(
17644        &breakpoints,
17645        &abs_path,
17646        vec![
17647            (0, disable_breakpoint.clone()),
17648            (2, Breakpoint::new_standard()),
17649            (3, disable_breakpoint.clone()),
17650        ],
17651    );
17652
17653    editor.update_in(cx, |editor, window, cx| {
17654        editor.move_to_beginning(&MoveToBeginning, window, cx);
17655        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17656        editor.move_to_end(&MoveToEnd, window, cx);
17657        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
17658        editor.move_up(&MoveUp, window, cx);
17659        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17660    });
17661
17662    let breakpoints = editor.update(cx, |editor, cx| {
17663        editor
17664            .breakpoint_store()
17665            .as_ref()
17666            .unwrap()
17667            .read(cx)
17668            .all_breakpoints(cx)
17669            .clone()
17670    });
17671
17672    assert_eq!(1, breakpoints.len());
17673    assert_breakpoint(
17674        &breakpoints,
17675        &abs_path,
17676        vec![
17677            (0, Breakpoint::new_standard()),
17678            (2, disable_breakpoint),
17679            (3, Breakpoint::new_standard()),
17680        ],
17681    );
17682}
17683
17684#[gpui::test]
17685async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
17686    init_test(cx, |_| {});
17687    let capabilities = lsp::ServerCapabilities {
17688        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
17689            prepare_provider: Some(true),
17690            work_done_progress_options: Default::default(),
17691        })),
17692        ..Default::default()
17693    };
17694    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17695
17696    cx.set_state(indoc! {"
17697        struct Fˇoo {}
17698    "});
17699
17700    cx.update_editor(|editor, _, cx| {
17701        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17702        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17703        editor.highlight_background::<DocumentHighlightRead>(
17704            &[highlight_range],
17705            |c| c.editor_document_highlight_read_background,
17706            cx,
17707        );
17708    });
17709
17710    let mut prepare_rename_handler = cx
17711        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
17712            move |_, _, _| async move {
17713                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
17714                    start: lsp::Position {
17715                        line: 0,
17716                        character: 7,
17717                    },
17718                    end: lsp::Position {
17719                        line: 0,
17720                        character: 10,
17721                    },
17722                })))
17723            },
17724        );
17725    let prepare_rename_task = cx
17726        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17727        .expect("Prepare rename was not started");
17728    prepare_rename_handler.next().await.unwrap();
17729    prepare_rename_task.await.expect("Prepare rename failed");
17730
17731    let mut rename_handler =
17732        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17733            let edit = lsp::TextEdit {
17734                range: lsp::Range {
17735                    start: lsp::Position {
17736                        line: 0,
17737                        character: 7,
17738                    },
17739                    end: lsp::Position {
17740                        line: 0,
17741                        character: 10,
17742                    },
17743                },
17744                new_text: "FooRenamed".to_string(),
17745            };
17746            Ok(Some(lsp::WorkspaceEdit::new(
17747                // Specify the same edit twice
17748                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
17749            )))
17750        });
17751    let rename_task = cx
17752        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17753        .expect("Confirm rename was not started");
17754    rename_handler.next().await.unwrap();
17755    rename_task.await.expect("Confirm rename failed");
17756    cx.run_until_parked();
17757
17758    // Despite two edits, only one is actually applied as those are identical
17759    cx.assert_editor_state(indoc! {"
17760        struct FooRenamedˇ {}
17761    "});
17762}
17763
17764#[gpui::test]
17765async fn test_rename_without_prepare(cx: &mut TestAppContext) {
17766    init_test(cx, |_| {});
17767    // These capabilities indicate that the server does not support prepare rename.
17768    let capabilities = lsp::ServerCapabilities {
17769        rename_provider: Some(lsp::OneOf::Left(true)),
17770        ..Default::default()
17771    };
17772    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
17773
17774    cx.set_state(indoc! {"
17775        struct Fˇoo {}
17776    "});
17777
17778    cx.update_editor(|editor, _window, cx| {
17779        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
17780        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
17781        editor.highlight_background::<DocumentHighlightRead>(
17782            &[highlight_range],
17783            |c| c.editor_document_highlight_read_background,
17784            cx,
17785        );
17786    });
17787
17788    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
17789        .expect("Prepare rename was not started")
17790        .await
17791        .expect("Prepare rename failed");
17792
17793    let mut rename_handler =
17794        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
17795            let edit = lsp::TextEdit {
17796                range: lsp::Range {
17797                    start: lsp::Position {
17798                        line: 0,
17799                        character: 7,
17800                    },
17801                    end: lsp::Position {
17802                        line: 0,
17803                        character: 10,
17804                    },
17805                },
17806                new_text: "FooRenamed".to_string(),
17807            };
17808            Ok(Some(lsp::WorkspaceEdit::new(
17809                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
17810            )))
17811        });
17812    let rename_task = cx
17813        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
17814        .expect("Confirm rename was not started");
17815    rename_handler.next().await.unwrap();
17816    rename_task.await.expect("Confirm rename failed");
17817    cx.run_until_parked();
17818
17819    // Correct range is renamed, as `surrounding_word` is used to find it.
17820    cx.assert_editor_state(indoc! {"
17821        struct FooRenamedˇ {}
17822    "});
17823}
17824
17825#[gpui::test]
17826async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
17827    init_test(cx, |_| {});
17828    let mut cx = EditorTestContext::new(cx).await;
17829
17830    let language = Arc::new(
17831        Language::new(
17832            LanguageConfig::default(),
17833            Some(tree_sitter_html::LANGUAGE.into()),
17834        )
17835        .with_brackets_query(
17836            r#"
17837            ("<" @open "/>" @close)
17838            ("</" @open ">" @close)
17839            ("<" @open ">" @close)
17840            ("\"" @open "\"" @close)
17841            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
17842        "#,
17843        )
17844        .unwrap(),
17845    );
17846    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
17847
17848    cx.set_state(indoc! {"
17849        <span>ˇ</span>
17850    "});
17851    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17852    cx.assert_editor_state(indoc! {"
17853        <span>
17854        ˇ
17855        </span>
17856    "});
17857
17858    cx.set_state(indoc! {"
17859        <span><span></span>ˇ</span>
17860    "});
17861    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17862    cx.assert_editor_state(indoc! {"
17863        <span><span></span>
17864        ˇ</span>
17865    "});
17866
17867    cx.set_state(indoc! {"
17868        <span>ˇ
17869        </span>
17870    "});
17871    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
17872    cx.assert_editor_state(indoc! {"
17873        <span>
17874        ˇ
17875        </span>
17876    "});
17877}
17878
17879#[gpui::test(iterations = 10)]
17880async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
17881    init_test(cx, |_| {});
17882
17883    let fs = FakeFs::new(cx.executor());
17884    fs.insert_tree(
17885        path!("/dir"),
17886        json!({
17887            "a.ts": "a",
17888        }),
17889    )
17890    .await;
17891
17892    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
17893    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17894    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17895
17896    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
17897    language_registry.add(Arc::new(Language::new(
17898        LanguageConfig {
17899            name: "TypeScript".into(),
17900            matcher: LanguageMatcher {
17901                path_suffixes: vec!["ts".to_string()],
17902                ..Default::default()
17903            },
17904            ..Default::default()
17905        },
17906        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
17907    )));
17908    let mut fake_language_servers = language_registry.register_fake_lsp(
17909        "TypeScript",
17910        FakeLspAdapter {
17911            capabilities: lsp::ServerCapabilities {
17912                code_lens_provider: Some(lsp::CodeLensOptions {
17913                    resolve_provider: Some(true),
17914                }),
17915                execute_command_provider: Some(lsp::ExecuteCommandOptions {
17916                    commands: vec!["_the/command".to_string()],
17917                    ..lsp::ExecuteCommandOptions::default()
17918                }),
17919                ..lsp::ServerCapabilities::default()
17920            },
17921            ..FakeLspAdapter::default()
17922        },
17923    );
17924
17925    let (buffer, _handle) = project
17926        .update(cx, |p, cx| {
17927            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
17928        })
17929        .await
17930        .unwrap();
17931    cx.executor().run_until_parked();
17932
17933    let fake_server = fake_language_servers.next().await.unwrap();
17934
17935    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
17936    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
17937    drop(buffer_snapshot);
17938    let actions = cx
17939        .update_window(*workspace, |_, window, cx| {
17940            project.code_actions(&buffer, anchor..anchor, window, cx)
17941        })
17942        .unwrap();
17943
17944    fake_server
17945        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
17946            Ok(Some(vec![
17947                lsp::CodeLens {
17948                    range: lsp::Range::default(),
17949                    command: Some(lsp::Command {
17950                        title: "Code lens command".to_owned(),
17951                        command: "_the/command".to_owned(),
17952                        arguments: None,
17953                    }),
17954                    data: None,
17955                },
17956                lsp::CodeLens {
17957                    range: lsp::Range::default(),
17958                    command: Some(lsp::Command {
17959                        title: "Command not in capabilities".to_owned(),
17960                        command: "not in capabilities".to_owned(),
17961                        arguments: None,
17962                    }),
17963                    data: None,
17964                },
17965                lsp::CodeLens {
17966                    range: lsp::Range {
17967                        start: lsp::Position {
17968                            line: 1,
17969                            character: 1,
17970                        },
17971                        end: lsp::Position {
17972                            line: 1,
17973                            character: 1,
17974                        },
17975                    },
17976                    command: Some(lsp::Command {
17977                        title: "Command not in range".to_owned(),
17978                        command: "_the/command".to_owned(),
17979                        arguments: None,
17980                    }),
17981                    data: None,
17982                },
17983            ]))
17984        })
17985        .next()
17986        .await;
17987
17988    let actions = actions.await.unwrap();
17989    assert_eq!(
17990        actions.len(),
17991        1,
17992        "Should have only one valid action for the 0..0 range"
17993    );
17994    let action = actions[0].clone();
17995    let apply = project.update(cx, |project, cx| {
17996        project.apply_code_action(buffer.clone(), action, true, cx)
17997    });
17998
17999    // Resolving the code action does not populate its edits. In absence of
18000    // edits, we must execute the given command.
18001    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18002        |mut lens, _| async move {
18003            let lens_command = lens.command.as_mut().expect("should have a command");
18004            assert_eq!(lens_command.title, "Code lens command");
18005            lens_command.arguments = Some(vec![json!("the-argument")]);
18006            Ok(lens)
18007        },
18008    );
18009
18010    // While executing the command, the language server sends the editor
18011    // a `workspaceEdit` request.
18012    fake_server
18013        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18014            let fake = fake_server.clone();
18015            move |params, _| {
18016                assert_eq!(params.command, "_the/command");
18017                let fake = fake.clone();
18018                async move {
18019                    fake.server
18020                        .request::<lsp::request::ApplyWorkspaceEdit>(
18021                            lsp::ApplyWorkspaceEditParams {
18022                                label: None,
18023                                edit: lsp::WorkspaceEdit {
18024                                    changes: Some(
18025                                        [(
18026                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18027                                            vec![lsp::TextEdit {
18028                                                range: lsp::Range::new(
18029                                                    lsp::Position::new(0, 0),
18030                                                    lsp::Position::new(0, 0),
18031                                                ),
18032                                                new_text: "X".into(),
18033                                            }],
18034                                        )]
18035                                        .into_iter()
18036                                        .collect(),
18037                                    ),
18038                                    ..Default::default()
18039                                },
18040                            },
18041                        )
18042                        .await
18043                        .unwrap();
18044                    Ok(Some(json!(null)))
18045                }
18046            }
18047        })
18048        .next()
18049        .await;
18050
18051    // Applying the code lens command returns a project transaction containing the edits
18052    // sent by the language server in its `workspaceEdit` request.
18053    let transaction = apply.await.unwrap();
18054    assert!(transaction.0.contains_key(&buffer));
18055    buffer.update(cx, |buffer, cx| {
18056        assert_eq!(buffer.text(), "Xa");
18057        buffer.undo(cx);
18058        assert_eq!(buffer.text(), "a");
18059    });
18060}
18061
18062#[gpui::test]
18063async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18064    init_test(cx, |_| {});
18065
18066    let fs = FakeFs::new(cx.executor());
18067    let main_text = r#"fn main() {
18068println!("1");
18069println!("2");
18070println!("3");
18071println!("4");
18072println!("5");
18073}"#;
18074    let lib_text = "mod foo {}";
18075    fs.insert_tree(
18076        path!("/a"),
18077        json!({
18078            "lib.rs": lib_text,
18079            "main.rs": main_text,
18080        }),
18081    )
18082    .await;
18083
18084    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18085    let (workspace, cx) =
18086        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18087    let worktree_id = workspace.update(cx, |workspace, cx| {
18088        workspace.project().update(cx, |project, cx| {
18089            project.worktrees(cx).next().unwrap().read(cx).id()
18090        })
18091    });
18092
18093    let expected_ranges = vec![
18094        Point::new(0, 0)..Point::new(0, 0),
18095        Point::new(1, 0)..Point::new(1, 1),
18096        Point::new(2, 0)..Point::new(2, 2),
18097        Point::new(3, 0)..Point::new(3, 3),
18098    ];
18099
18100    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18101    let editor_1 = workspace
18102        .update_in(cx, |workspace, window, cx| {
18103            workspace.open_path(
18104                (worktree_id, "main.rs"),
18105                Some(pane_1.downgrade()),
18106                true,
18107                window,
18108                cx,
18109            )
18110        })
18111        .unwrap()
18112        .await
18113        .downcast::<Editor>()
18114        .unwrap();
18115    pane_1.update(cx, |pane, cx| {
18116        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18117        open_editor.update(cx, |editor, cx| {
18118            assert_eq!(
18119                editor.display_text(cx),
18120                main_text,
18121                "Original main.rs text on initial open",
18122            );
18123            assert_eq!(
18124                editor
18125                    .selections
18126                    .all::<Point>(cx)
18127                    .into_iter()
18128                    .map(|s| s.range())
18129                    .collect::<Vec<_>>(),
18130                vec![Point::zero()..Point::zero()],
18131                "Default selections on initial open",
18132            );
18133        })
18134    });
18135    editor_1.update_in(cx, |editor, window, cx| {
18136        editor.change_selections(None, window, cx, |s| {
18137            s.select_ranges(expected_ranges.clone());
18138        });
18139    });
18140
18141    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18142        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18143    });
18144    let editor_2 = workspace
18145        .update_in(cx, |workspace, window, cx| {
18146            workspace.open_path(
18147                (worktree_id, "main.rs"),
18148                Some(pane_2.downgrade()),
18149                true,
18150                window,
18151                cx,
18152            )
18153        })
18154        .unwrap()
18155        .await
18156        .downcast::<Editor>()
18157        .unwrap();
18158    pane_2.update(cx, |pane, cx| {
18159        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18160        open_editor.update(cx, |editor, cx| {
18161            assert_eq!(
18162                editor.display_text(cx),
18163                main_text,
18164                "Original main.rs text on initial open in another panel",
18165            );
18166            assert_eq!(
18167                editor
18168                    .selections
18169                    .all::<Point>(cx)
18170                    .into_iter()
18171                    .map(|s| s.range())
18172                    .collect::<Vec<_>>(),
18173                vec![Point::zero()..Point::zero()],
18174                "Default selections on initial open in another panel",
18175            );
18176        })
18177    });
18178
18179    editor_2.update_in(cx, |editor, window, cx| {
18180        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18181    });
18182
18183    let _other_editor_1 = workspace
18184        .update_in(cx, |workspace, window, cx| {
18185            workspace.open_path(
18186                (worktree_id, "lib.rs"),
18187                Some(pane_1.downgrade()),
18188                true,
18189                window,
18190                cx,
18191            )
18192        })
18193        .unwrap()
18194        .await
18195        .downcast::<Editor>()
18196        .unwrap();
18197    pane_1
18198        .update_in(cx, |pane, window, cx| {
18199            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18200                .unwrap()
18201        })
18202        .await
18203        .unwrap();
18204    drop(editor_1);
18205    pane_1.update(cx, |pane, cx| {
18206        pane.active_item()
18207            .unwrap()
18208            .downcast::<Editor>()
18209            .unwrap()
18210            .update(cx, |editor, cx| {
18211                assert_eq!(
18212                    editor.display_text(cx),
18213                    lib_text,
18214                    "Other file should be open and active",
18215                );
18216            });
18217        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18218    });
18219
18220    let _other_editor_2 = workspace
18221        .update_in(cx, |workspace, window, cx| {
18222            workspace.open_path(
18223                (worktree_id, "lib.rs"),
18224                Some(pane_2.downgrade()),
18225                true,
18226                window,
18227                cx,
18228            )
18229        })
18230        .unwrap()
18231        .await
18232        .downcast::<Editor>()
18233        .unwrap();
18234    pane_2
18235        .update_in(cx, |pane, window, cx| {
18236            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18237                .unwrap()
18238        })
18239        .await
18240        .unwrap();
18241    drop(editor_2);
18242    pane_2.update(cx, |pane, cx| {
18243        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18244        open_editor.update(cx, |editor, cx| {
18245            assert_eq!(
18246                editor.display_text(cx),
18247                lib_text,
18248                "Other file should be open and active in another panel too",
18249            );
18250        });
18251        assert_eq!(
18252            pane.items().count(),
18253            1,
18254            "No other editors should be open in another pane",
18255        );
18256    });
18257
18258    let _editor_1_reopened = workspace
18259        .update_in(cx, |workspace, window, cx| {
18260            workspace.open_path(
18261                (worktree_id, "main.rs"),
18262                Some(pane_1.downgrade()),
18263                true,
18264                window,
18265                cx,
18266            )
18267        })
18268        .unwrap()
18269        .await
18270        .downcast::<Editor>()
18271        .unwrap();
18272    let _editor_2_reopened = workspace
18273        .update_in(cx, |workspace, window, cx| {
18274            workspace.open_path(
18275                (worktree_id, "main.rs"),
18276                Some(pane_2.downgrade()),
18277                true,
18278                window,
18279                cx,
18280            )
18281        })
18282        .unwrap()
18283        .await
18284        .downcast::<Editor>()
18285        .unwrap();
18286    pane_1.update(cx, |pane, cx| {
18287        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18288        open_editor.update(cx, |editor, cx| {
18289            assert_eq!(
18290                editor.display_text(cx),
18291                main_text,
18292                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18293            );
18294            assert_eq!(
18295                editor
18296                    .selections
18297                    .all::<Point>(cx)
18298                    .into_iter()
18299                    .map(|s| s.range())
18300                    .collect::<Vec<_>>(),
18301                expected_ranges,
18302                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18303            );
18304        })
18305    });
18306    pane_2.update(cx, |pane, cx| {
18307        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18308        open_editor.update(cx, |editor, cx| {
18309            assert_eq!(
18310                editor.display_text(cx),
18311                r#"fn main() {
18312⋯rintln!("1");
18313⋯intln!("2");
18314⋯ntln!("3");
18315println!("4");
18316println!("5");
18317}"#,
18318                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18319            );
18320            assert_eq!(
18321                editor
18322                    .selections
18323                    .all::<Point>(cx)
18324                    .into_iter()
18325                    .map(|s| s.range())
18326                    .collect::<Vec<_>>(),
18327                vec![Point::zero()..Point::zero()],
18328                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18329            );
18330        })
18331    });
18332}
18333
18334#[gpui::test]
18335async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18336    init_test(cx, |_| {});
18337
18338    let fs = FakeFs::new(cx.executor());
18339    let main_text = r#"fn main() {
18340println!("1");
18341println!("2");
18342println!("3");
18343println!("4");
18344println!("5");
18345}"#;
18346    let lib_text = "mod foo {}";
18347    fs.insert_tree(
18348        path!("/a"),
18349        json!({
18350            "lib.rs": lib_text,
18351            "main.rs": main_text,
18352        }),
18353    )
18354    .await;
18355
18356    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18357    let (workspace, cx) =
18358        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18359    let worktree_id = workspace.update(cx, |workspace, cx| {
18360        workspace.project().update(cx, |project, cx| {
18361            project.worktrees(cx).next().unwrap().read(cx).id()
18362        })
18363    });
18364
18365    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18366    let editor = workspace
18367        .update_in(cx, |workspace, window, cx| {
18368            workspace.open_path(
18369                (worktree_id, "main.rs"),
18370                Some(pane.downgrade()),
18371                true,
18372                window,
18373                cx,
18374            )
18375        })
18376        .unwrap()
18377        .await
18378        .downcast::<Editor>()
18379        .unwrap();
18380    pane.update(cx, |pane, cx| {
18381        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18382        open_editor.update(cx, |editor, cx| {
18383            assert_eq!(
18384                editor.display_text(cx),
18385                main_text,
18386                "Original main.rs text on initial open",
18387            );
18388        })
18389    });
18390    editor.update_in(cx, |editor, window, cx| {
18391        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18392    });
18393
18394    cx.update_global(|store: &mut SettingsStore, cx| {
18395        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18396            s.restore_on_file_reopen = Some(false);
18397        });
18398    });
18399    editor.update_in(cx, |editor, window, cx| {
18400        editor.fold_ranges(
18401            vec![
18402                Point::new(1, 0)..Point::new(1, 1),
18403                Point::new(2, 0)..Point::new(2, 2),
18404                Point::new(3, 0)..Point::new(3, 3),
18405            ],
18406            false,
18407            window,
18408            cx,
18409        );
18410    });
18411    pane.update_in(cx, |pane, window, cx| {
18412        pane.close_all_items(&CloseAllItems::default(), window, cx)
18413            .unwrap()
18414    })
18415    .await
18416    .unwrap();
18417    pane.update(cx, |pane, _| {
18418        assert!(pane.active_item().is_none());
18419    });
18420    cx.update_global(|store: &mut SettingsStore, cx| {
18421        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18422            s.restore_on_file_reopen = Some(true);
18423        });
18424    });
18425
18426    let _editor_reopened = workspace
18427        .update_in(cx, |workspace, window, cx| {
18428            workspace.open_path(
18429                (worktree_id, "main.rs"),
18430                Some(pane.downgrade()),
18431                true,
18432                window,
18433                cx,
18434            )
18435        })
18436        .unwrap()
18437        .await
18438        .downcast::<Editor>()
18439        .unwrap();
18440    pane.update(cx, |pane, cx| {
18441        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18442        open_editor.update(cx, |editor, cx| {
18443            assert_eq!(
18444                editor.display_text(cx),
18445                main_text,
18446                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18447            );
18448        })
18449    });
18450}
18451
18452fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18453    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18454    point..point
18455}
18456
18457fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18458    let (text, ranges) = marked_text_ranges(marked_text, true);
18459    assert_eq!(editor.text(cx), text);
18460    assert_eq!(
18461        editor.selections.ranges(cx),
18462        ranges,
18463        "Assert selections are {}",
18464        marked_text
18465    );
18466}
18467
18468pub fn handle_signature_help_request(
18469    cx: &mut EditorLspTestContext,
18470    mocked_response: lsp::SignatureHelp,
18471) -> impl Future<Output = ()> + use<> {
18472    let mut request =
18473        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18474            let mocked_response = mocked_response.clone();
18475            async move { Ok(Some(mocked_response)) }
18476        });
18477
18478    async move {
18479        request.next().await;
18480    }
18481}
18482
18483/// Handle completion request passing a marked string specifying where the completion
18484/// should be triggered from using '|' character, what range should be replaced, and what completions
18485/// should be returned using '<' and '>' to delimit the range
18486pub fn handle_completion_request(
18487    cx: &mut EditorLspTestContext,
18488    marked_string: &str,
18489    completions: Vec<&'static str>,
18490    counter: Arc<AtomicUsize>,
18491) -> impl Future<Output = ()> {
18492    let complete_from_marker: TextRangeMarker = '|'.into();
18493    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18494    let (_, mut marked_ranges) = marked_text_ranges_by(
18495        marked_string,
18496        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18497    );
18498
18499    let complete_from_position =
18500        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18501    let replace_range =
18502        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18503
18504    let mut request =
18505        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18506            let completions = completions.clone();
18507            counter.fetch_add(1, atomic::Ordering::Release);
18508            async move {
18509                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18510                assert_eq!(
18511                    params.text_document_position.position,
18512                    complete_from_position
18513                );
18514                Ok(Some(lsp::CompletionResponse::Array(
18515                    completions
18516                        .iter()
18517                        .map(|completion_text| lsp::CompletionItem {
18518                            label: completion_text.to_string(),
18519                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18520                                range: replace_range,
18521                                new_text: completion_text.to_string(),
18522                            })),
18523                            ..Default::default()
18524                        })
18525                        .collect(),
18526                )))
18527            }
18528        });
18529
18530    async move {
18531        request.next().await;
18532    }
18533}
18534
18535fn handle_resolve_completion_request(
18536    cx: &mut EditorLspTestContext,
18537    edits: Option<Vec<(&'static str, &'static str)>>,
18538) -> impl Future<Output = ()> {
18539    let edits = edits.map(|edits| {
18540        edits
18541            .iter()
18542            .map(|(marked_string, new_text)| {
18543                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18544                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18545                lsp::TextEdit::new(replace_range, new_text.to_string())
18546            })
18547            .collect::<Vec<_>>()
18548    });
18549
18550    let mut request =
18551        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18552            let edits = edits.clone();
18553            async move {
18554                Ok(lsp::CompletionItem {
18555                    additional_text_edits: edits,
18556                    ..Default::default()
18557                })
18558            }
18559        });
18560
18561    async move {
18562        request.next().await;
18563    }
18564}
18565
18566pub(crate) fn update_test_language_settings(
18567    cx: &mut TestAppContext,
18568    f: impl Fn(&mut AllLanguageSettingsContent),
18569) {
18570    cx.update(|cx| {
18571        SettingsStore::update_global(cx, |store, cx| {
18572            store.update_user_settings::<AllLanguageSettings>(cx, f);
18573        });
18574    });
18575}
18576
18577pub(crate) fn update_test_project_settings(
18578    cx: &mut TestAppContext,
18579    f: impl Fn(&mut ProjectSettings),
18580) {
18581    cx.update(|cx| {
18582        SettingsStore::update_global(cx, |store, cx| {
18583            store.update_user_settings::<ProjectSettings>(cx, f);
18584        });
18585    });
18586}
18587
18588pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
18589    cx.update(|cx| {
18590        assets::Assets.load_test_fonts(cx);
18591        let store = SettingsStore::test(cx);
18592        cx.set_global(store);
18593        theme::init(theme::LoadThemes::JustBase, cx);
18594        release_channel::init(SemanticVersion::default(), cx);
18595        client::init_settings(cx);
18596        language::init(cx);
18597        Project::init_settings(cx);
18598        workspace::init_settings(cx);
18599        crate::init(cx);
18600    });
18601
18602    update_test_language_settings(cx, f);
18603}
18604
18605#[track_caller]
18606fn assert_hunk_revert(
18607    not_reverted_text_with_selections: &str,
18608    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
18609    expected_reverted_text_with_selections: &str,
18610    base_text: &str,
18611    cx: &mut EditorLspTestContext,
18612) {
18613    cx.set_state(not_reverted_text_with_selections);
18614    cx.set_head_text(base_text);
18615    cx.executor().run_until_parked();
18616
18617    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
18618        let snapshot = editor.snapshot(window, cx);
18619        let reverted_hunk_statuses = snapshot
18620            .buffer_snapshot
18621            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
18622            .map(|hunk| hunk.status().kind)
18623            .collect::<Vec<_>>();
18624
18625        editor.git_restore(&Default::default(), window, cx);
18626        reverted_hunk_statuses
18627    });
18628    cx.executor().run_until_parked();
18629    cx.assert_editor_state(expected_reverted_text_with_selections);
18630    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
18631}