editor_tests.rs

    1use super::*;
    2use crate::{
    3    JoinLines,
    4    scroll::scroll_amount::ScrollAmount,
    5    test::{
    6        assert_text_with_selections, build_editor,
    7        editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
    8        editor_test_context::EditorTestContext,
    9        select_ranges,
   10    },
   11};
   12use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind};
   13use futures::StreamExt;
   14use gpui::{
   15    BackgroundExecutor, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext,
   16    WindowBounds, WindowOptions, div,
   17};
   18use indoc::indoc;
   19use language::{
   20    BracketPairConfig,
   21    Capability::ReadWrite,
   22    FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
   23    Override, Point,
   24    language_settings::{
   25        AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings,
   26        LanguageSettingsContent, LspInsertMode, PrettierSettings,
   27    },
   28};
   29use language_settings::{Formatter, FormatterList, IndentGuideSettings};
   30use lsp::CompletionParams;
   31use multi_buffer::{IndentGuide, PathKey};
   32use parking_lot::Mutex;
   33use pretty_assertions::{assert_eq, assert_ne};
   34use project::{
   35    FakeFs,
   36    debugger::breakpoint_store::{BreakpointState, SourceBreakpoint},
   37    project_settings::{LspSettings, ProjectSettings},
   38};
   39use serde_json::{self, json};
   40use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant};
   41use std::{
   42    iter,
   43    sync::atomic::{self, AtomicUsize},
   44};
   45use test::{build_editor_with_project, editor_lsp_test_context::rust_lang};
   46use text::ToPoint as _;
   47use unindent::Unindent;
   48use util::{
   49    assert_set_eq, path,
   50    test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
   51    uri,
   52};
   53use workspace::{
   54    CloseAllItems, CloseInactiveItems, NavigationEntry, ViewId,
   55    item::{FollowEvent, FollowableItem, Item, ItemHandle},
   56};
   57
   58#[gpui::test]
   59fn test_edit_events(cx: &mut TestAppContext) {
   60    init_test(cx, |_| {});
   61
   62    let buffer = cx.new(|cx| {
   63        let mut buffer = language::Buffer::local("123456", cx);
   64        buffer.set_group_interval(Duration::from_secs(1));
   65        buffer
   66    });
   67
   68    let events = Rc::new(RefCell::new(Vec::new()));
   69    let editor1 = cx.add_window({
   70        let events = events.clone();
   71        |window, cx| {
   72            let entity = cx.entity().clone();
   73            cx.subscribe_in(
   74                &entity,
   75                window,
   76                move |_, _, event: &EditorEvent, _, _| match event {
   77                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor1", "edited")),
   78                    EditorEvent::BufferEdited => {
   79                        events.borrow_mut().push(("editor1", "buffer edited"))
   80                    }
   81                    _ => {}
   82                },
   83            )
   84            .detach();
   85            Editor::for_buffer(buffer.clone(), None, window, cx)
   86        }
   87    });
   88
   89    let editor2 = cx.add_window({
   90        let events = events.clone();
   91        |window, cx| {
   92            cx.subscribe_in(
   93                &cx.entity().clone(),
   94                window,
   95                move |_, _, event: &EditorEvent, _, _| match event {
   96                    EditorEvent::Edited { .. } => events.borrow_mut().push(("editor2", "edited")),
   97                    EditorEvent::BufferEdited => {
   98                        events.borrow_mut().push(("editor2", "buffer edited"))
   99                    }
  100                    _ => {}
  101                },
  102            )
  103            .detach();
  104            Editor::for_buffer(buffer.clone(), None, window, cx)
  105        }
  106    });
  107
  108    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  109
  110    // Mutating editor 1 will emit an `Edited` event only for that editor.
  111    _ = editor1.update(cx, |editor, window, cx| editor.insert("X", window, cx));
  112    assert_eq!(
  113        mem::take(&mut *events.borrow_mut()),
  114        [
  115            ("editor1", "edited"),
  116            ("editor1", "buffer edited"),
  117            ("editor2", "buffer edited"),
  118        ]
  119    );
  120
  121    // Mutating editor 2 will emit an `Edited` event only for that editor.
  122    _ = editor2.update(cx, |editor, window, cx| editor.delete(&Delete, window, cx));
  123    assert_eq!(
  124        mem::take(&mut *events.borrow_mut()),
  125        [
  126            ("editor2", "edited"),
  127            ("editor1", "buffer edited"),
  128            ("editor2", "buffer edited"),
  129        ]
  130    );
  131
  132    // Undoing on editor 1 will emit an `Edited` event only for that editor.
  133    _ = editor1.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  134    assert_eq!(
  135        mem::take(&mut *events.borrow_mut()),
  136        [
  137            ("editor1", "edited"),
  138            ("editor1", "buffer edited"),
  139            ("editor2", "buffer edited"),
  140        ]
  141    );
  142
  143    // Redoing on editor 1 will emit an `Edited` event only for that editor.
  144    _ = editor1.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  145    assert_eq!(
  146        mem::take(&mut *events.borrow_mut()),
  147        [
  148            ("editor1", "edited"),
  149            ("editor1", "buffer edited"),
  150            ("editor2", "buffer edited"),
  151        ]
  152    );
  153
  154    // Undoing on editor 2 will emit an `Edited` event only for that editor.
  155    _ = editor2.update(cx, |editor, window, cx| editor.undo(&Undo, window, cx));
  156    assert_eq!(
  157        mem::take(&mut *events.borrow_mut()),
  158        [
  159            ("editor2", "edited"),
  160            ("editor1", "buffer edited"),
  161            ("editor2", "buffer edited"),
  162        ]
  163    );
  164
  165    // Redoing on editor 2 will emit an `Edited` event only for that editor.
  166    _ = editor2.update(cx, |editor, window, cx| editor.redo(&Redo, window, cx));
  167    assert_eq!(
  168        mem::take(&mut *events.borrow_mut()),
  169        [
  170            ("editor2", "edited"),
  171            ("editor1", "buffer edited"),
  172            ("editor2", "buffer edited"),
  173        ]
  174    );
  175
  176    // No event is emitted when the mutation is a no-op.
  177    _ = editor2.update(cx, |editor, window, cx| {
  178        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
  179
  180        editor.backspace(&Backspace, window, cx);
  181    });
  182    assert_eq!(mem::take(&mut *events.borrow_mut()), []);
  183}
  184
  185#[gpui::test]
  186fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
  187    init_test(cx, |_| {});
  188
  189    let mut now = Instant::now();
  190    let group_interval = Duration::from_millis(1);
  191    let buffer = cx.new(|cx| {
  192        let mut buf = language::Buffer::local("123456", cx);
  193        buf.set_group_interval(group_interval);
  194        buf
  195    });
  196    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  197    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
  198
  199    _ = editor.update(cx, |editor, window, cx| {
  200        editor.start_transaction_at(now, window, cx);
  201        editor.change_selections(None, window, cx, |s| s.select_ranges([2..4]));
  202
  203        editor.insert("cd", window, cx);
  204        editor.end_transaction_at(now, cx);
  205        assert_eq!(editor.text(cx), "12cd56");
  206        assert_eq!(editor.selections.ranges(cx), vec![4..4]);
  207
  208        editor.start_transaction_at(now, window, cx);
  209        editor.change_selections(None, window, cx, |s| s.select_ranges([4..5]));
  210        editor.insert("e", window, cx);
  211        editor.end_transaction_at(now, cx);
  212        assert_eq!(editor.text(cx), "12cde6");
  213        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  214
  215        now += group_interval + Duration::from_millis(1);
  216        editor.change_selections(None, window, cx, |s| s.select_ranges([2..2]));
  217
  218        // Simulate an edit in another editor
  219        buffer.update(cx, |buffer, cx| {
  220            buffer.start_transaction_at(now, cx);
  221            buffer.edit([(0..1, "a")], None, cx);
  222            buffer.edit([(1..1, "b")], None, cx);
  223            buffer.end_transaction_at(now, cx);
  224        });
  225
  226        assert_eq!(editor.text(cx), "ab2cde6");
  227        assert_eq!(editor.selections.ranges(cx), vec![3..3]);
  228
  229        // Last transaction happened past the group interval in a different editor.
  230        // Undo it individually and don't restore selections.
  231        editor.undo(&Undo, window, cx);
  232        assert_eq!(editor.text(cx), "12cde6");
  233        assert_eq!(editor.selections.ranges(cx), vec![2..2]);
  234
  235        // First two transactions happened within the group interval in this editor.
  236        // Undo them together and restore selections.
  237        editor.undo(&Undo, window, cx);
  238        editor.undo(&Undo, window, cx); // Undo stack is empty here, so this is a no-op.
  239        assert_eq!(editor.text(cx), "123456");
  240        assert_eq!(editor.selections.ranges(cx), vec![0..0]);
  241
  242        // Redo the first two transactions together.
  243        editor.redo(&Redo, window, cx);
  244        assert_eq!(editor.text(cx), "12cde6");
  245        assert_eq!(editor.selections.ranges(cx), vec![5..5]);
  246
  247        // Redo the last transaction on its own.
  248        editor.redo(&Redo, window, cx);
  249        assert_eq!(editor.text(cx), "ab2cde6");
  250        assert_eq!(editor.selections.ranges(cx), vec![6..6]);
  251
  252        // Test empty transactions.
  253        editor.start_transaction_at(now, window, cx);
  254        editor.end_transaction_at(now, cx);
  255        editor.undo(&Undo, window, cx);
  256        assert_eq!(editor.text(cx), "12cde6");
  257    });
  258}
  259
  260#[gpui::test]
  261fn test_ime_composition(cx: &mut TestAppContext) {
  262    init_test(cx, |_| {});
  263
  264    let buffer = cx.new(|cx| {
  265        let mut buffer = language::Buffer::local("abcde", cx);
  266        // Ensure automatic grouping doesn't occur.
  267        buffer.set_group_interval(Duration::ZERO);
  268        buffer
  269    });
  270
  271    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
  272    cx.add_window(|window, cx| {
  273        let mut editor = build_editor(buffer.clone(), window, cx);
  274
  275        // Start a new IME composition.
  276        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  277        editor.replace_and_mark_text_in_range(Some(0..1), "á", None, window, cx);
  278        editor.replace_and_mark_text_in_range(Some(0..1), "ä", None, window, cx);
  279        assert_eq!(editor.text(cx), "äbcde");
  280        assert_eq!(
  281            editor.marked_text_ranges(cx),
  282            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  283        );
  284
  285        // Finalize IME composition.
  286        editor.replace_text_in_range(None, "ā", window, cx);
  287        assert_eq!(editor.text(cx), "ābcde");
  288        assert_eq!(editor.marked_text_ranges(cx), None);
  289
  290        // IME composition edits are grouped and are undone/redone at once.
  291        editor.undo(&Default::default(), window, cx);
  292        assert_eq!(editor.text(cx), "abcde");
  293        assert_eq!(editor.marked_text_ranges(cx), None);
  294        editor.redo(&Default::default(), window, cx);
  295        assert_eq!(editor.text(cx), "ābcde");
  296        assert_eq!(editor.marked_text_ranges(cx), None);
  297
  298        // Start a new IME composition.
  299        editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx);
  300        assert_eq!(
  301            editor.marked_text_ranges(cx),
  302            Some(vec![OffsetUtf16(0)..OffsetUtf16(1)])
  303        );
  304
  305        // Undoing during an IME composition cancels it.
  306        editor.undo(&Default::default(), window, cx);
  307        assert_eq!(editor.text(cx), "ābcde");
  308        assert_eq!(editor.marked_text_ranges(cx), None);
  309
  310        // Start a new IME composition with an invalid marked range, ensuring it gets clipped.
  311        editor.replace_and_mark_text_in_range(Some(4..999), "è", None, window, cx);
  312        assert_eq!(editor.text(cx), "ābcdè");
  313        assert_eq!(
  314            editor.marked_text_ranges(cx),
  315            Some(vec![OffsetUtf16(4)..OffsetUtf16(5)])
  316        );
  317
  318        // Finalize IME composition with an invalid replacement range, ensuring it gets clipped.
  319        editor.replace_text_in_range(Some(4..999), "ę", window, cx);
  320        assert_eq!(editor.text(cx), "ābcdę");
  321        assert_eq!(editor.marked_text_ranges(cx), None);
  322
  323        // Start a new IME composition with multiple cursors.
  324        editor.change_selections(None, window, cx, |s| {
  325            s.select_ranges([
  326                OffsetUtf16(1)..OffsetUtf16(1),
  327                OffsetUtf16(3)..OffsetUtf16(3),
  328                OffsetUtf16(5)..OffsetUtf16(5),
  329            ])
  330        });
  331        editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx);
  332        assert_eq!(editor.text(cx), "XYZbXYZdXYZ");
  333        assert_eq!(
  334            editor.marked_text_ranges(cx),
  335            Some(vec![
  336                OffsetUtf16(0)..OffsetUtf16(3),
  337                OffsetUtf16(4)..OffsetUtf16(7),
  338                OffsetUtf16(8)..OffsetUtf16(11)
  339            ])
  340        );
  341
  342        // Ensure the newly-marked range gets treated as relative to the previously-marked ranges.
  343        editor.replace_and_mark_text_in_range(Some(1..2), "1", None, window, cx);
  344        assert_eq!(editor.text(cx), "X1ZbX1ZdX1Z");
  345        assert_eq!(
  346            editor.marked_text_ranges(cx),
  347            Some(vec![
  348                OffsetUtf16(1)..OffsetUtf16(2),
  349                OffsetUtf16(5)..OffsetUtf16(6),
  350                OffsetUtf16(9)..OffsetUtf16(10)
  351            ])
  352        );
  353
  354        // Finalize IME composition with multiple cursors.
  355        editor.replace_text_in_range(Some(9..10), "2", window, cx);
  356        assert_eq!(editor.text(cx), "X2ZbX2ZdX2Z");
  357        assert_eq!(editor.marked_text_ranges(cx), None);
  358
  359        editor
  360    });
  361}
  362
  363#[gpui::test]
  364fn test_selection_with_mouse(cx: &mut TestAppContext) {
  365    init_test(cx, |_| {});
  366
  367    let editor = cx.add_window(|window, cx| {
  368        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  369        build_editor(buffer, window, cx)
  370    });
  371
  372    _ = editor.update(cx, |editor, window, cx| {
  373        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  374    });
  375    assert_eq!(
  376        editor
  377            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  378            .unwrap(),
  379        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  380    );
  381
  382    _ = editor.update(cx, |editor, window, cx| {
  383        editor.update_selection(
  384            DisplayPoint::new(DisplayRow(3), 3),
  385            0,
  386            gpui::Point::<f32>::default(),
  387            window,
  388            cx,
  389        );
  390    });
  391
  392    assert_eq!(
  393        editor
  394            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  395            .unwrap(),
  396        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  397    );
  398
  399    _ = editor.update(cx, |editor, window, cx| {
  400        editor.update_selection(
  401            DisplayPoint::new(DisplayRow(1), 1),
  402            0,
  403            gpui::Point::<f32>::default(),
  404            window,
  405            cx,
  406        );
  407    });
  408
  409    assert_eq!(
  410        editor
  411            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  412            .unwrap(),
  413        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  414    );
  415
  416    _ = editor.update(cx, |editor, window, cx| {
  417        editor.end_selection(window, cx);
  418        editor.update_selection(
  419            DisplayPoint::new(DisplayRow(3), 3),
  420            0,
  421            gpui::Point::<f32>::default(),
  422            window,
  423            cx,
  424        );
  425    });
  426
  427    assert_eq!(
  428        editor
  429            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  430            .unwrap(),
  431        [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1)]
  432    );
  433
  434    _ = editor.update(cx, |editor, window, cx| {
  435        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 3), true, 1, window, cx);
  436        editor.update_selection(
  437            DisplayPoint::new(DisplayRow(0), 0),
  438            0,
  439            gpui::Point::<f32>::default(),
  440            window,
  441            cx,
  442        );
  443    });
  444
  445    assert_eq!(
  446        editor
  447            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  448            .unwrap(),
  449        [
  450            DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(1), 1),
  451            DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)
  452        ]
  453    );
  454
  455    _ = editor.update(cx, |editor, window, cx| {
  456        editor.end_selection(window, cx);
  457    });
  458
  459    assert_eq!(
  460        editor
  461            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  462            .unwrap(),
  463        [DisplayPoint::new(DisplayRow(3), 3)..DisplayPoint::new(DisplayRow(0), 0)]
  464    );
  465}
  466
  467#[gpui::test]
  468fn test_multiple_cursor_removal(cx: &mut TestAppContext) {
  469    init_test(cx, |_| {});
  470
  471    let editor = cx.add_window(|window, cx| {
  472        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
  473        build_editor(buffer, window, cx)
  474    });
  475
  476    _ = editor.update(cx, |editor, window, cx| {
  477        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), false, 1, window, cx);
  478    });
  479
  480    _ = editor.update(cx, |editor, window, cx| {
  481        editor.end_selection(window, cx);
  482    });
  483
  484    _ = editor.update(cx, |editor, window, cx| {
  485        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 2), true, 1, window, cx);
  486    });
  487
  488    _ = editor.update(cx, |editor, window, cx| {
  489        editor.end_selection(window, cx);
  490    });
  491
  492    assert_eq!(
  493        editor
  494            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  495            .unwrap(),
  496        [
  497            DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
  498            DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)
  499        ]
  500    );
  501
  502    _ = editor.update(cx, |editor, window, cx| {
  503        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 1), true, 1, window, cx);
  504    });
  505
  506    _ = editor.update(cx, |editor, window, cx| {
  507        editor.end_selection(window, cx);
  508    });
  509
  510    assert_eq!(
  511        editor
  512            .update(cx, |editor, _, cx| editor.selections.display_ranges(cx))
  513            .unwrap(),
  514        [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  515    );
  516}
  517
  518#[gpui::test]
  519fn test_canceling_pending_selection(cx: &mut TestAppContext) {
  520    init_test(cx, |_| {});
  521
  522    let editor = cx.add_window(|window, cx| {
  523        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  524        build_editor(buffer, window, cx)
  525    });
  526
  527    _ = editor.update(cx, |editor, window, cx| {
  528        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  529        assert_eq!(
  530            editor.selections.display_ranges(cx),
  531            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  532        );
  533    });
  534
  535    _ = editor.update(cx, |editor, window, cx| {
  536        editor.update_selection(
  537            DisplayPoint::new(DisplayRow(3), 3),
  538            0,
  539            gpui::Point::<f32>::default(),
  540            window,
  541            cx,
  542        );
  543        assert_eq!(
  544            editor.selections.display_ranges(cx),
  545            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  546        );
  547    });
  548
  549    _ = editor.update(cx, |editor, window, cx| {
  550        editor.cancel(&Cancel, window, cx);
  551        editor.update_selection(
  552            DisplayPoint::new(DisplayRow(1), 1),
  553            0,
  554            gpui::Point::<f32>::default(),
  555            window,
  556            cx,
  557        );
  558        assert_eq!(
  559            editor.selections.display_ranges(cx),
  560            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3)]
  561        );
  562    });
  563}
  564
  565#[gpui::test]
  566fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) {
  567    init_test(cx, |_| {});
  568
  569    let editor = cx.add_window(|window, cx| {
  570        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  571        build_editor(buffer, window, cx)
  572    });
  573
  574    _ = editor.update(cx, |editor, window, cx| {
  575        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  576        assert_eq!(
  577            editor.selections.display_ranges(cx),
  578            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  579        );
  580
  581        editor.move_down(&Default::default(), window, cx);
  582        assert_eq!(
  583            editor.selections.display_ranges(cx),
  584            [DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 2)]
  585        );
  586
  587        editor.begin_selection(DisplayPoint::new(DisplayRow(2), 2), false, 1, window, cx);
  588        assert_eq!(
  589            editor.selections.display_ranges(cx),
  590            [DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(2), 2)]
  591        );
  592
  593        editor.move_up(&Default::default(), window, cx);
  594        assert_eq!(
  595            editor.selections.display_ranges(cx),
  596            [DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2)]
  597        );
  598    });
  599}
  600
  601#[gpui::test]
  602fn test_clone(cx: &mut TestAppContext) {
  603    init_test(cx, |_| {});
  604
  605    let (text, selection_ranges) = marked_text_ranges(
  606        indoc! {"
  607            one
  608            two
  609            threeˇ
  610            four
  611            fiveˇ
  612        "},
  613        true,
  614    );
  615
  616    let editor = cx.add_window(|window, cx| {
  617        let buffer = MultiBuffer::build_simple(&text, cx);
  618        build_editor(buffer, window, cx)
  619    });
  620
  621    _ = editor.update(cx, |editor, window, cx| {
  622        editor.change_selections(None, window, cx, |s| {
  623            s.select_ranges(selection_ranges.clone())
  624        });
  625        editor.fold_creases(
  626            vec![
  627                Crease::simple(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
  628                Crease::simple(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
  629            ],
  630            true,
  631            window,
  632            cx,
  633        );
  634    });
  635
  636    let cloned_editor = editor
  637        .update(cx, |editor, _, cx| {
  638            cx.open_window(Default::default(), |window, cx| {
  639                cx.new(|cx| editor.clone(window, cx))
  640            })
  641        })
  642        .unwrap()
  643        .unwrap();
  644
  645    let snapshot = editor
  646        .update(cx, |e, window, cx| e.snapshot(window, cx))
  647        .unwrap();
  648    let cloned_snapshot = cloned_editor
  649        .update(cx, |e, window, cx| e.snapshot(window, cx))
  650        .unwrap();
  651
  652    assert_eq!(
  653        cloned_editor
  654            .update(cx, |e, _, cx| e.display_text(cx))
  655            .unwrap(),
  656        editor.update(cx, |e, _, cx| e.display_text(cx)).unwrap()
  657    );
  658    assert_eq!(
  659        cloned_snapshot
  660            .folds_in_range(0..text.len())
  661            .collect::<Vec<_>>(),
  662        snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
  663    );
  664    assert_set_eq!(
  665        cloned_editor
  666            .update(cx, |editor, _, cx| editor.selections.ranges::<Point>(cx))
  667            .unwrap(),
  668        editor
  669            .update(cx, |editor, _, cx| editor.selections.ranges(cx))
  670            .unwrap()
  671    );
  672    assert_set_eq!(
  673        cloned_editor
  674            .update(cx, |e, _window, cx| e.selections.display_ranges(cx))
  675            .unwrap(),
  676        editor
  677            .update(cx, |e, _, cx| e.selections.display_ranges(cx))
  678            .unwrap()
  679    );
  680}
  681
  682#[gpui::test]
  683async fn test_navigation_history(cx: &mut TestAppContext) {
  684    init_test(cx, |_| {});
  685
  686    use workspace::item::Item;
  687
  688    let fs = FakeFs::new(cx.executor());
  689    let project = Project::test(fs, [], cx).await;
  690    let workspace = cx.add_window(|window, cx| Workspace::test_new(project, window, cx));
  691    let pane = workspace
  692        .update(cx, |workspace, _, _| workspace.active_pane().clone())
  693        .unwrap();
  694
  695    _ = workspace.update(cx, |_v, window, cx| {
  696        cx.new(|cx| {
  697            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
  698            let mut editor = build_editor(buffer.clone(), window, cx);
  699            let handle = cx.entity();
  700            editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
  701
  702            fn pop_history(editor: &mut Editor, cx: &mut App) -> Option<NavigationEntry> {
  703                editor.nav_history.as_mut().unwrap().pop_backward(cx)
  704            }
  705
  706            // Move the cursor a small distance.
  707            // Nothing is added to the navigation history.
  708            editor.change_selections(None, window, cx, |s| {
  709                s.select_display_ranges([
  710                    DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)
  711                ])
  712            });
  713            editor.change_selections(None, window, cx, |s| {
  714                s.select_display_ranges([
  715                    DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)
  716                ])
  717            });
  718            assert!(pop_history(&mut editor, cx).is_none());
  719
  720            // Move the cursor a large distance.
  721            // The history can jump back to the previous position.
  722            editor.change_selections(None, window, cx, |s| {
  723                s.select_display_ranges([
  724                    DisplayPoint::new(DisplayRow(13), 0)..DisplayPoint::new(DisplayRow(13), 3)
  725                ])
  726            });
  727            let nav_entry = pop_history(&mut editor, cx).unwrap();
  728            editor.navigate(nav_entry.data.unwrap(), window, cx);
  729            assert_eq!(nav_entry.item.id(), cx.entity_id());
  730            assert_eq!(
  731                editor.selections.display_ranges(cx),
  732                &[DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0)]
  733            );
  734            assert!(pop_history(&mut editor, cx).is_none());
  735
  736            // Move the cursor a small distance via the mouse.
  737            // Nothing is added to the navigation history.
  738            editor.begin_selection(DisplayPoint::new(DisplayRow(5), 0), false, 1, window, cx);
  739            editor.end_selection(window, cx);
  740            assert_eq!(
  741                editor.selections.display_ranges(cx),
  742                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  743            );
  744            assert!(pop_history(&mut editor, cx).is_none());
  745
  746            // Move the cursor a large distance via the mouse.
  747            // The history can jump back to the previous position.
  748            editor.begin_selection(DisplayPoint::new(DisplayRow(15), 0), false, 1, window, cx);
  749            editor.end_selection(window, cx);
  750            assert_eq!(
  751                editor.selections.display_ranges(cx),
  752                &[DisplayPoint::new(DisplayRow(15), 0)..DisplayPoint::new(DisplayRow(15), 0)]
  753            );
  754            let nav_entry = pop_history(&mut editor, cx).unwrap();
  755            editor.navigate(nav_entry.data.unwrap(), window, cx);
  756            assert_eq!(nav_entry.item.id(), cx.entity_id());
  757            assert_eq!(
  758                editor.selections.display_ranges(cx),
  759                &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0)]
  760            );
  761            assert!(pop_history(&mut editor, cx).is_none());
  762
  763            // Set scroll position to check later
  764            editor.set_scroll_position(gpui::Point::<f32>::new(5.5, 5.5), window, cx);
  765            let original_scroll_position = editor.scroll_manager.anchor();
  766
  767            // Jump to the end of the document and adjust scroll
  768            editor.move_to_end(&MoveToEnd, window, cx);
  769            editor.set_scroll_position(gpui::Point::<f32>::new(-2.5, -0.5), window, cx);
  770            assert_ne!(editor.scroll_manager.anchor(), original_scroll_position);
  771
  772            let nav_entry = pop_history(&mut editor, cx).unwrap();
  773            editor.navigate(nav_entry.data.unwrap(), window, cx);
  774            assert_eq!(editor.scroll_manager.anchor(), original_scroll_position);
  775
  776            // Ensure we don't panic when navigation data contains invalid anchors *and* points.
  777            let mut invalid_anchor = editor.scroll_manager.anchor().anchor;
  778            invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
  779            let invalid_point = Point::new(9999, 0);
  780            editor.navigate(
  781                Box::new(NavigationData {
  782                    cursor_anchor: invalid_anchor,
  783                    cursor_position: invalid_point,
  784                    scroll_anchor: ScrollAnchor {
  785                        anchor: invalid_anchor,
  786                        offset: Default::default(),
  787                    },
  788                    scroll_top_row: invalid_point.row,
  789                }),
  790                window,
  791                cx,
  792            );
  793            assert_eq!(
  794                editor.selections.display_ranges(cx),
  795                &[editor.max_point(cx)..editor.max_point(cx)]
  796            );
  797            assert_eq!(
  798                editor.scroll_position(cx),
  799                gpui::Point::new(0., editor.max_point(cx).row().as_f32())
  800            );
  801
  802            editor
  803        })
  804    });
  805}
  806
  807#[gpui::test]
  808fn test_cancel(cx: &mut TestAppContext) {
  809    init_test(cx, |_| {});
  810
  811    let editor = cx.add_window(|window, cx| {
  812        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
  813        build_editor(buffer, window, cx)
  814    });
  815
  816    _ = editor.update(cx, |editor, window, cx| {
  817        editor.begin_selection(DisplayPoint::new(DisplayRow(3), 4), false, 1, window, cx);
  818        editor.update_selection(
  819            DisplayPoint::new(DisplayRow(1), 1),
  820            0,
  821            gpui::Point::<f32>::default(),
  822            window,
  823            cx,
  824        );
  825        editor.end_selection(window, cx);
  826
  827        editor.begin_selection(DisplayPoint::new(DisplayRow(0), 1), true, 1, window, cx);
  828        editor.update_selection(
  829            DisplayPoint::new(DisplayRow(0), 3),
  830            0,
  831            gpui::Point::<f32>::default(),
  832            window,
  833            cx,
  834        );
  835        editor.end_selection(window, cx);
  836        assert_eq!(
  837            editor.selections.display_ranges(cx),
  838            [
  839                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 3),
  840                DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1),
  841            ]
  842        );
  843    });
  844
  845    _ = editor.update(cx, |editor, window, cx| {
  846        editor.cancel(&Cancel, window, cx);
  847        assert_eq!(
  848            editor.selections.display_ranges(cx),
  849            [DisplayPoint::new(DisplayRow(3), 4)..DisplayPoint::new(DisplayRow(1), 1)]
  850        );
  851    });
  852
  853    _ = editor.update(cx, |editor, window, cx| {
  854        editor.cancel(&Cancel, window, cx);
  855        assert_eq!(
  856            editor.selections.display_ranges(cx),
  857            [DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1)]
  858        );
  859    });
  860}
  861
  862#[gpui::test]
  863fn test_fold_action(cx: &mut TestAppContext) {
  864    init_test(cx, |_| {});
  865
  866    let editor = cx.add_window(|window, cx| {
  867        let buffer = MultiBuffer::build_simple(
  868            &"
  869                impl Foo {
  870                    // Hello!
  871
  872                    fn a() {
  873                        1
  874                    }
  875
  876                    fn b() {
  877                        2
  878                    }
  879
  880                    fn c() {
  881                        3
  882                    }
  883                }
  884            "
  885            .unindent(),
  886            cx,
  887        );
  888        build_editor(buffer.clone(), window, cx)
  889    });
  890
  891    _ = editor.update(cx, |editor, window, cx| {
  892        editor.change_selections(None, window, cx, |s| {
  893            s.select_display_ranges([
  894                DisplayPoint::new(DisplayRow(7), 0)..DisplayPoint::new(DisplayRow(12), 0)
  895            ]);
  896        });
  897        editor.fold(&Fold, window, cx);
  898        assert_eq!(
  899            editor.display_text(cx),
  900            "
  901                impl Foo {
  902                    // Hello!
  903
  904                    fn a() {
  905                        1
  906                    }
  907
  908                    fn b() {⋯
  909                    }
  910
  911                    fn c() {⋯
  912                    }
  913                }
  914            "
  915            .unindent(),
  916        );
  917
  918        editor.fold(&Fold, window, cx);
  919        assert_eq!(
  920            editor.display_text(cx),
  921            "
  922                impl Foo {⋯
  923                }
  924            "
  925            .unindent(),
  926        );
  927
  928        editor.unfold_lines(&UnfoldLines, window, cx);
  929        assert_eq!(
  930            editor.display_text(cx),
  931            "
  932                impl Foo {
  933                    // Hello!
  934
  935                    fn a() {
  936                        1
  937                    }
  938
  939                    fn b() {⋯
  940                    }
  941
  942                    fn c() {⋯
  943                    }
  944                }
  945            "
  946            .unindent(),
  947        );
  948
  949        editor.unfold_lines(&UnfoldLines, window, cx);
  950        assert_eq!(
  951            editor.display_text(cx),
  952            editor.buffer.read(cx).read(cx).text()
  953        );
  954    });
  955}
  956
  957#[gpui::test]
  958fn test_fold_action_whitespace_sensitive_language(cx: &mut TestAppContext) {
  959    init_test(cx, |_| {});
  960
  961    let editor = cx.add_window(|window, cx| {
  962        let buffer = MultiBuffer::build_simple(
  963            &"
  964                class Foo:
  965                    # Hello!
  966
  967                    def a():
  968                        print(1)
  969
  970                    def b():
  971                        print(2)
  972
  973                    def c():
  974                        print(3)
  975            "
  976            .unindent(),
  977            cx,
  978        );
  979        build_editor(buffer.clone(), window, cx)
  980    });
  981
  982    _ = editor.update(cx, |editor, window, cx| {
  983        editor.change_selections(None, window, cx, |s| {
  984            s.select_display_ranges([
  985                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(10), 0)
  986            ]);
  987        });
  988        editor.fold(&Fold, window, cx);
  989        assert_eq!(
  990            editor.display_text(cx),
  991            "
  992                class Foo:
  993                    # Hello!
  994
  995                    def a():
  996                        print(1)
  997
  998                    def b():⋯
  999
 1000                    def c():⋯
 1001            "
 1002            .unindent(),
 1003        );
 1004
 1005        editor.fold(&Fold, window, cx);
 1006        assert_eq!(
 1007            editor.display_text(cx),
 1008            "
 1009                class Foo:⋯
 1010            "
 1011            .unindent(),
 1012        );
 1013
 1014        editor.unfold_lines(&UnfoldLines, window, cx);
 1015        assert_eq!(
 1016            editor.display_text(cx),
 1017            "
 1018                class Foo:
 1019                    # Hello!
 1020
 1021                    def a():
 1022                        print(1)
 1023
 1024                    def b():⋯
 1025
 1026                    def c():⋯
 1027            "
 1028            .unindent(),
 1029        );
 1030
 1031        editor.unfold_lines(&UnfoldLines, window, cx);
 1032        assert_eq!(
 1033            editor.display_text(cx),
 1034            editor.buffer.read(cx).read(cx).text()
 1035        );
 1036    });
 1037}
 1038
 1039#[gpui::test]
 1040fn test_fold_action_multiple_line_breaks(cx: &mut TestAppContext) {
 1041    init_test(cx, |_| {});
 1042
 1043    let editor = cx.add_window(|window, cx| {
 1044        let buffer = MultiBuffer::build_simple(
 1045            &"
 1046                class Foo:
 1047                    # Hello!
 1048
 1049                    def a():
 1050                        print(1)
 1051
 1052                    def b():
 1053                        print(2)
 1054
 1055
 1056                    def c():
 1057                        print(3)
 1058
 1059
 1060            "
 1061            .unindent(),
 1062            cx,
 1063        );
 1064        build_editor(buffer.clone(), window, cx)
 1065    });
 1066
 1067    _ = editor.update(cx, |editor, window, cx| {
 1068        editor.change_selections(None, window, cx, |s| {
 1069            s.select_display_ranges([
 1070                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(11), 0)
 1071            ]);
 1072        });
 1073        editor.fold(&Fold, window, cx);
 1074        assert_eq!(
 1075            editor.display_text(cx),
 1076            "
 1077                class Foo:
 1078                    # Hello!
 1079
 1080                    def a():
 1081                        print(1)
 1082
 1083                    def b():⋯
 1084
 1085
 1086                    def c():⋯
 1087
 1088
 1089            "
 1090            .unindent(),
 1091        );
 1092
 1093        editor.fold(&Fold, window, cx);
 1094        assert_eq!(
 1095            editor.display_text(cx),
 1096            "
 1097                class Foo:⋯
 1098
 1099
 1100            "
 1101            .unindent(),
 1102        );
 1103
 1104        editor.unfold_lines(&UnfoldLines, window, cx);
 1105        assert_eq!(
 1106            editor.display_text(cx),
 1107            "
 1108                class Foo:
 1109                    # Hello!
 1110
 1111                    def a():
 1112                        print(1)
 1113
 1114                    def b():⋯
 1115
 1116
 1117                    def c():⋯
 1118
 1119
 1120            "
 1121            .unindent(),
 1122        );
 1123
 1124        editor.unfold_lines(&UnfoldLines, window, cx);
 1125        assert_eq!(
 1126            editor.display_text(cx),
 1127            editor.buffer.read(cx).read(cx).text()
 1128        );
 1129    });
 1130}
 1131
 1132#[gpui::test]
 1133fn test_fold_at_level(cx: &mut TestAppContext) {
 1134    init_test(cx, |_| {});
 1135
 1136    let editor = cx.add_window(|window, cx| {
 1137        let buffer = MultiBuffer::build_simple(
 1138            &"
 1139                class Foo:
 1140                    # Hello!
 1141
 1142                    def a():
 1143                        print(1)
 1144
 1145                    def b():
 1146                        print(2)
 1147
 1148
 1149                class Bar:
 1150                    # World!
 1151
 1152                    def a():
 1153                        print(1)
 1154
 1155                    def b():
 1156                        print(2)
 1157
 1158
 1159            "
 1160            .unindent(),
 1161            cx,
 1162        );
 1163        build_editor(buffer.clone(), window, cx)
 1164    });
 1165
 1166    _ = editor.update(cx, |editor, window, cx| {
 1167        editor.fold_at_level(&FoldAtLevel(2), window, cx);
 1168        assert_eq!(
 1169            editor.display_text(cx),
 1170            "
 1171                class Foo:
 1172                    # Hello!
 1173
 1174                    def a():⋯
 1175
 1176                    def b():⋯
 1177
 1178
 1179                class Bar:
 1180                    # World!
 1181
 1182                    def a():⋯
 1183
 1184                    def b():⋯
 1185
 1186
 1187            "
 1188            .unindent(),
 1189        );
 1190
 1191        editor.fold_at_level(&FoldAtLevel(1), window, cx);
 1192        assert_eq!(
 1193            editor.display_text(cx),
 1194            "
 1195                class Foo:⋯
 1196
 1197
 1198                class Bar:⋯
 1199
 1200
 1201            "
 1202            .unindent(),
 1203        );
 1204
 1205        editor.unfold_all(&UnfoldAll, window, cx);
 1206        editor.fold_at_level(&FoldAtLevel(0), window, cx);
 1207        assert_eq!(
 1208            editor.display_text(cx),
 1209            "
 1210                class Foo:
 1211                    # Hello!
 1212
 1213                    def a():
 1214                        print(1)
 1215
 1216                    def b():
 1217                        print(2)
 1218
 1219
 1220                class Bar:
 1221                    # World!
 1222
 1223                    def a():
 1224                        print(1)
 1225
 1226                    def b():
 1227                        print(2)
 1228
 1229
 1230            "
 1231            .unindent(),
 1232        );
 1233
 1234        assert_eq!(
 1235            editor.display_text(cx),
 1236            editor.buffer.read(cx).read(cx).text()
 1237        );
 1238    });
 1239}
 1240
 1241#[gpui::test]
 1242fn test_move_cursor(cx: &mut TestAppContext) {
 1243    init_test(cx, |_| {});
 1244
 1245    let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
 1246    let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
 1247
 1248    buffer.update(cx, |buffer, cx| {
 1249        buffer.edit(
 1250            vec![
 1251                (Point::new(1, 0)..Point::new(1, 0), "\t"),
 1252                (Point::new(1, 1)..Point::new(1, 1), "\t"),
 1253            ],
 1254            None,
 1255            cx,
 1256        );
 1257    });
 1258    _ = editor.update(cx, |editor, window, cx| {
 1259        assert_eq!(
 1260            editor.selections.display_ranges(cx),
 1261            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1262        );
 1263
 1264        editor.move_down(&MoveDown, window, cx);
 1265        assert_eq!(
 1266            editor.selections.display_ranges(cx),
 1267            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1268        );
 1269
 1270        editor.move_right(&MoveRight, window, cx);
 1271        assert_eq!(
 1272            editor.selections.display_ranges(cx),
 1273            &[DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4)]
 1274        );
 1275
 1276        editor.move_left(&MoveLeft, window, cx);
 1277        assert_eq!(
 1278            editor.selections.display_ranges(cx),
 1279            &[DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)]
 1280        );
 1281
 1282        editor.move_up(&MoveUp, window, cx);
 1283        assert_eq!(
 1284            editor.selections.display_ranges(cx),
 1285            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1286        );
 1287
 1288        editor.move_to_end(&MoveToEnd, window, cx);
 1289        assert_eq!(
 1290            editor.selections.display_ranges(cx),
 1291            &[DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 6)]
 1292        );
 1293
 1294        editor.move_to_beginning(&MoveToBeginning, window, cx);
 1295        assert_eq!(
 1296            editor.selections.display_ranges(cx),
 1297            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 1298        );
 1299
 1300        editor.change_selections(None, window, cx, |s| {
 1301            s.select_display_ranges([
 1302                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 2)
 1303            ]);
 1304        });
 1305        editor.select_to_beginning(&SelectToBeginning, window, cx);
 1306        assert_eq!(
 1307            editor.selections.display_ranges(cx),
 1308            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 0)]
 1309        );
 1310
 1311        editor.select_to_end(&SelectToEnd, window, cx);
 1312        assert_eq!(
 1313            editor.selections.display_ranges(cx),
 1314            &[DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(5), 6)]
 1315        );
 1316    });
 1317}
 1318
 1319// TODO: Re-enable this test
 1320#[cfg(target_os = "macos")]
 1321#[gpui::test]
 1322fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1323    init_test(cx, |_| {});
 1324
 1325    let editor = cx.add_window(|window, cx| {
 1326        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1327        build_editor(buffer.clone(), window, cx)
 1328    });
 1329
 1330    assert_eq!('🟥'.len_utf8(), 4);
 1331    assert_eq!('α'.len_utf8(), 2);
 1332
 1333    _ = editor.update(cx, |editor, window, cx| {
 1334        editor.fold_creases(
 1335            vec![
 1336                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1337                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1338                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1339            ],
 1340            true,
 1341            window,
 1342            cx,
 1343        );
 1344        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1345
 1346        editor.move_right(&MoveRight, window, cx);
 1347        assert_eq!(
 1348            editor.selections.display_ranges(cx),
 1349            &[empty_range(0, "🟥".len())]
 1350        );
 1351        editor.move_right(&MoveRight, window, cx);
 1352        assert_eq!(
 1353            editor.selections.display_ranges(cx),
 1354            &[empty_range(0, "🟥🟧".len())]
 1355        );
 1356        editor.move_right(&MoveRight, window, cx);
 1357        assert_eq!(
 1358            editor.selections.display_ranges(cx),
 1359            &[empty_range(0, "🟥🟧⋯".len())]
 1360        );
 1361
 1362        editor.move_down(&MoveDown, window, cx);
 1363        assert_eq!(
 1364            editor.selections.display_ranges(cx),
 1365            &[empty_range(1, "ab⋯e".len())]
 1366        );
 1367        editor.move_left(&MoveLeft, window, cx);
 1368        assert_eq!(
 1369            editor.selections.display_ranges(cx),
 1370            &[empty_range(1, "ab⋯".len())]
 1371        );
 1372        editor.move_left(&MoveLeft, window, cx);
 1373        assert_eq!(
 1374            editor.selections.display_ranges(cx),
 1375            &[empty_range(1, "ab".len())]
 1376        );
 1377        editor.move_left(&MoveLeft, window, cx);
 1378        assert_eq!(
 1379            editor.selections.display_ranges(cx),
 1380            &[empty_range(1, "a".len())]
 1381        );
 1382
 1383        editor.move_down(&MoveDown, window, cx);
 1384        assert_eq!(
 1385            editor.selections.display_ranges(cx),
 1386            &[empty_range(2, "α".len())]
 1387        );
 1388        editor.move_right(&MoveRight, window, cx);
 1389        assert_eq!(
 1390            editor.selections.display_ranges(cx),
 1391            &[empty_range(2, "αβ".len())]
 1392        );
 1393        editor.move_right(&MoveRight, window, cx);
 1394        assert_eq!(
 1395            editor.selections.display_ranges(cx),
 1396            &[empty_range(2, "αβ⋯".len())]
 1397        );
 1398        editor.move_right(&MoveRight, window, cx);
 1399        assert_eq!(
 1400            editor.selections.display_ranges(cx),
 1401            &[empty_range(2, "αβ⋯ε".len())]
 1402        );
 1403
 1404        editor.move_up(&MoveUp, window, cx);
 1405        assert_eq!(
 1406            editor.selections.display_ranges(cx),
 1407            &[empty_range(1, "ab⋯e".len())]
 1408        );
 1409        editor.move_down(&MoveDown, window, cx);
 1410        assert_eq!(
 1411            editor.selections.display_ranges(cx),
 1412            &[empty_range(2, "αβ⋯ε".len())]
 1413        );
 1414        editor.move_up(&MoveUp, window, cx);
 1415        assert_eq!(
 1416            editor.selections.display_ranges(cx),
 1417            &[empty_range(1, "ab⋯e".len())]
 1418        );
 1419
 1420        editor.move_up(&MoveUp, window, cx);
 1421        assert_eq!(
 1422            editor.selections.display_ranges(cx),
 1423            &[empty_range(0, "🟥🟧".len())]
 1424        );
 1425        editor.move_left(&MoveLeft, window, cx);
 1426        assert_eq!(
 1427            editor.selections.display_ranges(cx),
 1428            &[empty_range(0, "🟥".len())]
 1429        );
 1430        editor.move_left(&MoveLeft, window, cx);
 1431        assert_eq!(
 1432            editor.selections.display_ranges(cx),
 1433            &[empty_range(0, "".len())]
 1434        );
 1435    });
 1436}
 1437
 1438#[gpui::test]
 1439fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1440    init_test(cx, |_| {});
 1441
 1442    let editor = cx.add_window(|window, cx| {
 1443        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1444        build_editor(buffer.clone(), window, cx)
 1445    });
 1446    _ = editor.update(cx, |editor, window, cx| {
 1447        editor.change_selections(None, window, cx, |s| {
 1448            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1449        });
 1450
 1451        // moving above start of document should move selection to start of document,
 1452        // but the next move down should still be at the original goal_x
 1453        editor.move_up(&MoveUp, window, cx);
 1454        assert_eq!(
 1455            editor.selections.display_ranges(cx),
 1456            &[empty_range(0, "".len())]
 1457        );
 1458
 1459        editor.move_down(&MoveDown, window, cx);
 1460        assert_eq!(
 1461            editor.selections.display_ranges(cx),
 1462            &[empty_range(1, "abcd".len())]
 1463        );
 1464
 1465        editor.move_down(&MoveDown, window, cx);
 1466        assert_eq!(
 1467            editor.selections.display_ranges(cx),
 1468            &[empty_range(2, "αβγ".len())]
 1469        );
 1470
 1471        editor.move_down(&MoveDown, window, cx);
 1472        assert_eq!(
 1473            editor.selections.display_ranges(cx),
 1474            &[empty_range(3, "abcd".len())]
 1475        );
 1476
 1477        editor.move_down(&MoveDown, window, cx);
 1478        assert_eq!(
 1479            editor.selections.display_ranges(cx),
 1480            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1481        );
 1482
 1483        // moving past end of document should not change goal_x
 1484        editor.move_down(&MoveDown, window, cx);
 1485        assert_eq!(
 1486            editor.selections.display_ranges(cx),
 1487            &[empty_range(5, "".len())]
 1488        );
 1489
 1490        editor.move_down(&MoveDown, window, cx);
 1491        assert_eq!(
 1492            editor.selections.display_ranges(cx),
 1493            &[empty_range(5, "".len())]
 1494        );
 1495
 1496        editor.move_up(&MoveUp, window, cx);
 1497        assert_eq!(
 1498            editor.selections.display_ranges(cx),
 1499            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1500        );
 1501
 1502        editor.move_up(&MoveUp, window, cx);
 1503        assert_eq!(
 1504            editor.selections.display_ranges(cx),
 1505            &[empty_range(3, "abcd".len())]
 1506        );
 1507
 1508        editor.move_up(&MoveUp, window, cx);
 1509        assert_eq!(
 1510            editor.selections.display_ranges(cx),
 1511            &[empty_range(2, "αβγ".len())]
 1512        );
 1513    });
 1514}
 1515
 1516#[gpui::test]
 1517fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1518    init_test(cx, |_| {});
 1519    let move_to_beg = MoveToBeginningOfLine {
 1520        stop_at_soft_wraps: true,
 1521        stop_at_indent: true,
 1522    };
 1523
 1524    let delete_to_beg = DeleteToBeginningOfLine {
 1525        stop_at_indent: false,
 1526    };
 1527
 1528    let move_to_end = MoveToEndOfLine {
 1529        stop_at_soft_wraps: true,
 1530    };
 1531
 1532    let editor = cx.add_window(|window, cx| {
 1533        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1534        build_editor(buffer, window, cx)
 1535    });
 1536    _ = editor.update(cx, |editor, window, cx| {
 1537        editor.change_selections(None, window, cx, |s| {
 1538            s.select_display_ranges([
 1539                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1540                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1541            ]);
 1542        });
 1543    });
 1544
 1545    _ = editor.update(cx, |editor, window, cx| {
 1546        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1547        assert_eq!(
 1548            editor.selections.display_ranges(cx),
 1549            &[
 1550                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1551                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1552            ]
 1553        );
 1554    });
 1555
 1556    _ = editor.update(cx, |editor, window, cx| {
 1557        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1558        assert_eq!(
 1559            editor.selections.display_ranges(cx),
 1560            &[
 1561                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1562                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1563            ]
 1564        );
 1565    });
 1566
 1567    _ = editor.update(cx, |editor, window, cx| {
 1568        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1569        assert_eq!(
 1570            editor.selections.display_ranges(cx),
 1571            &[
 1572                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1573                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1574            ]
 1575        );
 1576    });
 1577
 1578    _ = editor.update(cx, |editor, window, cx| {
 1579        editor.move_to_end_of_line(&move_to_end, window, cx);
 1580        assert_eq!(
 1581            editor.selections.display_ranges(cx),
 1582            &[
 1583                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1584                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1585            ]
 1586        );
 1587    });
 1588
 1589    // Moving to the end of line again is a no-op.
 1590    _ = editor.update(cx, |editor, window, cx| {
 1591        editor.move_to_end_of_line(&move_to_end, window, cx);
 1592        assert_eq!(
 1593            editor.selections.display_ranges(cx),
 1594            &[
 1595                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1596                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1597            ]
 1598        );
 1599    });
 1600
 1601    _ = editor.update(cx, |editor, window, cx| {
 1602        editor.move_left(&MoveLeft, window, cx);
 1603        editor.select_to_beginning_of_line(
 1604            &SelectToBeginningOfLine {
 1605                stop_at_soft_wraps: true,
 1606                stop_at_indent: true,
 1607            },
 1608            window,
 1609            cx,
 1610        );
 1611        assert_eq!(
 1612            editor.selections.display_ranges(cx),
 1613            &[
 1614                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1615                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1616            ]
 1617        );
 1618    });
 1619
 1620    _ = editor.update(cx, |editor, window, cx| {
 1621        editor.select_to_beginning_of_line(
 1622            &SelectToBeginningOfLine {
 1623                stop_at_soft_wraps: true,
 1624                stop_at_indent: true,
 1625            },
 1626            window,
 1627            cx,
 1628        );
 1629        assert_eq!(
 1630            editor.selections.display_ranges(cx),
 1631            &[
 1632                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1633                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1634            ]
 1635        );
 1636    });
 1637
 1638    _ = editor.update(cx, |editor, window, cx| {
 1639        editor.select_to_beginning_of_line(
 1640            &SelectToBeginningOfLine {
 1641                stop_at_soft_wraps: true,
 1642                stop_at_indent: true,
 1643            },
 1644            window,
 1645            cx,
 1646        );
 1647        assert_eq!(
 1648            editor.selections.display_ranges(cx),
 1649            &[
 1650                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1651                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1652            ]
 1653        );
 1654    });
 1655
 1656    _ = editor.update(cx, |editor, window, cx| {
 1657        editor.select_to_end_of_line(
 1658            &SelectToEndOfLine {
 1659                stop_at_soft_wraps: true,
 1660            },
 1661            window,
 1662            cx,
 1663        );
 1664        assert_eq!(
 1665            editor.selections.display_ranges(cx),
 1666            &[
 1667                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1668                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1669            ]
 1670        );
 1671    });
 1672
 1673    _ = editor.update(cx, |editor, window, cx| {
 1674        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1675        assert_eq!(editor.display_text(cx), "ab\n  de");
 1676        assert_eq!(
 1677            editor.selections.display_ranges(cx),
 1678            &[
 1679                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1680                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1681            ]
 1682        );
 1683    });
 1684
 1685    _ = editor.update(cx, |editor, window, cx| {
 1686        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1687        assert_eq!(editor.display_text(cx), "\n");
 1688        assert_eq!(
 1689            editor.selections.display_ranges(cx),
 1690            &[
 1691                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1692                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1693            ]
 1694        );
 1695    });
 1696}
 1697
 1698#[gpui::test]
 1699fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1700    init_test(cx, |_| {});
 1701    let move_to_beg = MoveToBeginningOfLine {
 1702        stop_at_soft_wraps: false,
 1703        stop_at_indent: false,
 1704    };
 1705
 1706    let move_to_end = MoveToEndOfLine {
 1707        stop_at_soft_wraps: false,
 1708    };
 1709
 1710    let editor = cx.add_window(|window, cx| {
 1711        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1712        build_editor(buffer, window, cx)
 1713    });
 1714
 1715    _ = editor.update(cx, |editor, window, cx| {
 1716        editor.set_wrap_width(Some(140.0.into()), cx);
 1717
 1718        // We expect the following lines after wrapping
 1719        // ```
 1720        // thequickbrownfox
 1721        // jumpedoverthelazydo
 1722        // gs
 1723        // ```
 1724        // The final `gs` was soft-wrapped onto a new line.
 1725        assert_eq!(
 1726            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1727            editor.display_text(cx),
 1728        );
 1729
 1730        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1731        // Start the cursor at the `k` on the first line
 1732        editor.change_selections(None, window, cx, |s| {
 1733            s.select_display_ranges([
 1734                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1735            ]);
 1736        });
 1737
 1738        // Moving to the beginning of the line should put us at the beginning of the line.
 1739        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1740        assert_eq!(
 1741            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1742            editor.selections.display_ranges(cx)
 1743        );
 1744
 1745        // Moving to the end of the line should put us at the end of the line.
 1746        editor.move_to_end_of_line(&move_to_end, window, cx);
 1747        assert_eq!(
 1748            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1749            editor.selections.display_ranges(cx)
 1750        );
 1751
 1752        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1753        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1754        editor.change_selections(None, window, cx, |s| {
 1755            s.select_display_ranges([
 1756                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1757            ]);
 1758        });
 1759
 1760        // Moving to the beginning of the line should put us at the start of the second line of
 1761        // display text, i.e., the `j`.
 1762        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1763        assert_eq!(
 1764            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1765            editor.selections.display_ranges(cx)
 1766        );
 1767
 1768        // Moving to the beginning of the line again should be a no-op.
 1769        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1770        assert_eq!(
 1771            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1772            editor.selections.display_ranges(cx)
 1773        );
 1774
 1775        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1776        // next display line.
 1777        editor.move_to_end_of_line(&move_to_end, window, cx);
 1778        assert_eq!(
 1779            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1780            editor.selections.display_ranges(cx)
 1781        );
 1782
 1783        // Moving to the end of the line again should be a no-op.
 1784        editor.move_to_end_of_line(&move_to_end, window, cx);
 1785        assert_eq!(
 1786            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1787            editor.selections.display_ranges(cx)
 1788        );
 1789    });
 1790}
 1791
 1792#[gpui::test]
 1793fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1794    init_test(cx, |_| {});
 1795
 1796    let move_to_beg = MoveToBeginningOfLine {
 1797        stop_at_soft_wraps: true,
 1798        stop_at_indent: true,
 1799    };
 1800
 1801    let select_to_beg = SelectToBeginningOfLine {
 1802        stop_at_soft_wraps: true,
 1803        stop_at_indent: true,
 1804    };
 1805
 1806    let delete_to_beg = DeleteToBeginningOfLine {
 1807        stop_at_indent: true,
 1808    };
 1809
 1810    let move_to_end = MoveToEndOfLine {
 1811        stop_at_soft_wraps: false,
 1812    };
 1813
 1814    let editor = cx.add_window(|window, cx| {
 1815        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1816        build_editor(buffer, window, cx)
 1817    });
 1818
 1819    _ = editor.update(cx, |editor, window, cx| {
 1820        editor.change_selections(None, window, cx, |s| {
 1821            s.select_display_ranges([
 1822                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1823                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1824            ]);
 1825        });
 1826
 1827        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1828        // and the second cursor at the first non-whitespace character in the line.
 1829        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1830        assert_eq!(
 1831            editor.selections.display_ranges(cx),
 1832            &[
 1833                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1834                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1835            ]
 1836        );
 1837
 1838        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1839        // and should move the second cursor to the beginning of the line.
 1840        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1841        assert_eq!(
 1842            editor.selections.display_ranges(cx),
 1843            &[
 1844                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1845                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1846            ]
 1847        );
 1848
 1849        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1850        // and should move the second cursor back to the first non-whitespace character in the line.
 1851        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1852        assert_eq!(
 1853            editor.selections.display_ranges(cx),
 1854            &[
 1855                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1856                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1857            ]
 1858        );
 1859
 1860        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1861        // and to the first non-whitespace character in the line for the second cursor.
 1862        editor.move_to_end_of_line(&move_to_end, window, cx);
 1863        editor.move_left(&MoveLeft, window, cx);
 1864        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1865        assert_eq!(
 1866            editor.selections.display_ranges(cx),
 1867            &[
 1868                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1869                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1870            ]
 1871        );
 1872
 1873        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1874        // and should select to the beginning of the line for the second cursor.
 1875        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1876        assert_eq!(
 1877            editor.selections.display_ranges(cx),
 1878            &[
 1879                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1880                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1881            ]
 1882        );
 1883
 1884        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1885        // and should delete to the first non-whitespace character in the line for the second cursor.
 1886        editor.move_to_end_of_line(&move_to_end, window, cx);
 1887        editor.move_left(&MoveLeft, window, cx);
 1888        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1889        assert_eq!(editor.text(cx), "c\n  f");
 1890    });
 1891}
 1892
 1893#[gpui::test]
 1894fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1895    init_test(cx, |_| {});
 1896
 1897    let editor = cx.add_window(|window, cx| {
 1898        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1899        build_editor(buffer, window, cx)
 1900    });
 1901    _ = editor.update(cx, |editor, window, cx| {
 1902        editor.change_selections(None, window, cx, |s| {
 1903            s.select_display_ranges([
 1904                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1905                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1906            ])
 1907        });
 1908
 1909        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1910        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1911
 1912        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1913        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1914
 1915        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1916        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1917
 1918        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1919        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1920
 1921        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1922        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1923
 1924        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1925        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1926
 1927        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1928        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1929
 1930        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1931        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1932
 1933        editor.move_right(&MoveRight, window, cx);
 1934        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1935        assert_selection_ranges(
 1936            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1937            editor,
 1938            cx,
 1939        );
 1940
 1941        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1942        assert_selection_ranges(
 1943            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1944            editor,
 1945            cx,
 1946        );
 1947
 1948        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1949        assert_selection_ranges(
 1950            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1951            editor,
 1952            cx,
 1953        );
 1954    });
 1955}
 1956
 1957#[gpui::test]
 1958fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1959    init_test(cx, |_| {});
 1960
 1961    let editor = cx.add_window(|window, cx| {
 1962        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1963        build_editor(buffer, window, cx)
 1964    });
 1965
 1966    _ = editor.update(cx, |editor, window, cx| {
 1967        editor.set_wrap_width(Some(140.0.into()), cx);
 1968        assert_eq!(
 1969            editor.display_text(cx),
 1970            "use one::{\n    two::three::\n    four::five\n};"
 1971        );
 1972
 1973        editor.change_selections(None, window, cx, |s| {
 1974            s.select_display_ranges([
 1975                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1976            ]);
 1977        });
 1978
 1979        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1980        assert_eq!(
 1981            editor.selections.display_ranges(cx),
 1982            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1983        );
 1984
 1985        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1986        assert_eq!(
 1987            editor.selections.display_ranges(cx),
 1988            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1989        );
 1990
 1991        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1992        assert_eq!(
 1993            editor.selections.display_ranges(cx),
 1994            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1995        );
 1996
 1997        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1998        assert_eq!(
 1999            editor.selections.display_ranges(cx),
 2000            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 2001        );
 2002
 2003        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2004        assert_eq!(
 2005            editor.selections.display_ranges(cx),
 2006            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2007        );
 2008
 2009        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2010        assert_eq!(
 2011            editor.selections.display_ranges(cx),
 2012            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2013        );
 2014    });
 2015}
 2016
 2017#[gpui::test]
 2018async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2019    init_test(cx, |_| {});
 2020    let mut cx = EditorTestContext::new(cx).await;
 2021
 2022    let line_height = cx.editor(|editor, window, _| {
 2023        editor
 2024            .style()
 2025            .unwrap()
 2026            .text
 2027            .line_height_in_pixels(window.rem_size())
 2028    });
 2029    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2030
 2031    cx.set_state(
 2032        &r#"ˇone
 2033        two
 2034
 2035        three
 2036        fourˇ
 2037        five
 2038
 2039        six"#
 2040            .unindent(),
 2041    );
 2042
 2043    cx.update_editor(|editor, window, cx| {
 2044        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2045    });
 2046    cx.assert_editor_state(
 2047        &r#"one
 2048        two
 2049        ˇ
 2050        three
 2051        four
 2052        five
 2053        ˇ
 2054        six"#
 2055            .unindent(),
 2056    );
 2057
 2058    cx.update_editor(|editor, window, cx| {
 2059        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2060    });
 2061    cx.assert_editor_state(
 2062        &r#"one
 2063        two
 2064
 2065        three
 2066        four
 2067        five
 2068        ˇ
 2069        sixˇ"#
 2070            .unindent(),
 2071    );
 2072
 2073    cx.update_editor(|editor, window, cx| {
 2074        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2075    });
 2076    cx.assert_editor_state(
 2077        &r#"one
 2078        two
 2079
 2080        three
 2081        four
 2082        five
 2083
 2084        sixˇ"#
 2085            .unindent(),
 2086    );
 2087
 2088    cx.update_editor(|editor, window, cx| {
 2089        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2090    });
 2091    cx.assert_editor_state(
 2092        &r#"one
 2093        two
 2094
 2095        three
 2096        four
 2097        five
 2098        ˇ
 2099        six"#
 2100            .unindent(),
 2101    );
 2102
 2103    cx.update_editor(|editor, window, cx| {
 2104        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2105    });
 2106    cx.assert_editor_state(
 2107        &r#"one
 2108        two
 2109        ˇ
 2110        three
 2111        four
 2112        five
 2113
 2114        six"#
 2115            .unindent(),
 2116    );
 2117
 2118    cx.update_editor(|editor, window, cx| {
 2119        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2120    });
 2121    cx.assert_editor_state(
 2122        &r#"ˇone
 2123        two
 2124
 2125        three
 2126        four
 2127        five
 2128
 2129        six"#
 2130            .unindent(),
 2131    );
 2132}
 2133
 2134#[gpui::test]
 2135async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2136    init_test(cx, |_| {});
 2137    let mut cx = EditorTestContext::new(cx).await;
 2138    let line_height = cx.editor(|editor, window, _| {
 2139        editor
 2140            .style()
 2141            .unwrap()
 2142            .text
 2143            .line_height_in_pixels(window.rem_size())
 2144    });
 2145    let window = cx.window;
 2146    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2147
 2148    cx.set_state(
 2149        r#"ˇone
 2150        two
 2151        three
 2152        four
 2153        five
 2154        six
 2155        seven
 2156        eight
 2157        nine
 2158        ten
 2159        "#,
 2160    );
 2161
 2162    cx.update_editor(|editor, window, cx| {
 2163        assert_eq!(
 2164            editor.snapshot(window, cx).scroll_position(),
 2165            gpui::Point::new(0., 0.)
 2166        );
 2167        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2168        assert_eq!(
 2169            editor.snapshot(window, cx).scroll_position(),
 2170            gpui::Point::new(0., 3.)
 2171        );
 2172        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2173        assert_eq!(
 2174            editor.snapshot(window, cx).scroll_position(),
 2175            gpui::Point::new(0., 6.)
 2176        );
 2177        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2178        assert_eq!(
 2179            editor.snapshot(window, cx).scroll_position(),
 2180            gpui::Point::new(0., 3.)
 2181        );
 2182
 2183        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2184        assert_eq!(
 2185            editor.snapshot(window, cx).scroll_position(),
 2186            gpui::Point::new(0., 1.)
 2187        );
 2188        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2189        assert_eq!(
 2190            editor.snapshot(window, cx).scroll_position(),
 2191            gpui::Point::new(0., 3.)
 2192        );
 2193    });
 2194}
 2195
 2196#[gpui::test]
 2197async fn test_autoscroll(cx: &mut TestAppContext) {
 2198    init_test(cx, |_| {});
 2199    let mut cx = EditorTestContext::new(cx).await;
 2200
 2201    let line_height = cx.update_editor(|editor, window, cx| {
 2202        editor.set_vertical_scroll_margin(2, cx);
 2203        editor
 2204            .style()
 2205            .unwrap()
 2206            .text
 2207            .line_height_in_pixels(window.rem_size())
 2208    });
 2209    let window = cx.window;
 2210    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2211
 2212    cx.set_state(
 2213        r#"ˇone
 2214            two
 2215            three
 2216            four
 2217            five
 2218            six
 2219            seven
 2220            eight
 2221            nine
 2222            ten
 2223        "#,
 2224    );
 2225    cx.update_editor(|editor, window, cx| {
 2226        assert_eq!(
 2227            editor.snapshot(window, cx).scroll_position(),
 2228            gpui::Point::new(0., 0.0)
 2229        );
 2230    });
 2231
 2232    // Add a cursor below the visible area. Since both cursors cannot fit
 2233    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2234    // allows the vertical scroll margin below that cursor.
 2235    cx.update_editor(|editor, window, cx| {
 2236        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2237            selections.select_ranges([
 2238                Point::new(0, 0)..Point::new(0, 0),
 2239                Point::new(6, 0)..Point::new(6, 0),
 2240            ]);
 2241        })
 2242    });
 2243    cx.update_editor(|editor, window, cx| {
 2244        assert_eq!(
 2245            editor.snapshot(window, cx).scroll_position(),
 2246            gpui::Point::new(0., 3.0)
 2247        );
 2248    });
 2249
 2250    // Move down. The editor cursor scrolls down to track the newest cursor.
 2251    cx.update_editor(|editor, window, cx| {
 2252        editor.move_down(&Default::default(), window, cx);
 2253    });
 2254    cx.update_editor(|editor, window, cx| {
 2255        assert_eq!(
 2256            editor.snapshot(window, cx).scroll_position(),
 2257            gpui::Point::new(0., 4.0)
 2258        );
 2259    });
 2260
 2261    // Add a cursor above the visible area. Since both cursors fit on screen,
 2262    // the editor scrolls to show both.
 2263    cx.update_editor(|editor, window, cx| {
 2264        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2265            selections.select_ranges([
 2266                Point::new(1, 0)..Point::new(1, 0),
 2267                Point::new(6, 0)..Point::new(6, 0),
 2268            ]);
 2269        })
 2270    });
 2271    cx.update_editor(|editor, window, cx| {
 2272        assert_eq!(
 2273            editor.snapshot(window, cx).scroll_position(),
 2274            gpui::Point::new(0., 1.0)
 2275        );
 2276    });
 2277}
 2278
 2279#[gpui::test]
 2280async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2281    init_test(cx, |_| {});
 2282    let mut cx = EditorTestContext::new(cx).await;
 2283
 2284    let line_height = cx.editor(|editor, window, _cx| {
 2285        editor
 2286            .style()
 2287            .unwrap()
 2288            .text
 2289            .line_height_in_pixels(window.rem_size())
 2290    });
 2291    let window = cx.window;
 2292    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2293    cx.set_state(
 2294        &r#"
 2295        ˇone
 2296        two
 2297        threeˇ
 2298        four
 2299        five
 2300        six
 2301        seven
 2302        eight
 2303        nine
 2304        ten
 2305        "#
 2306        .unindent(),
 2307    );
 2308
 2309    cx.update_editor(|editor, window, cx| {
 2310        editor.move_page_down(&MovePageDown::default(), window, cx)
 2311    });
 2312    cx.assert_editor_state(
 2313        &r#"
 2314        one
 2315        two
 2316        three
 2317        ˇfour
 2318        five
 2319        sixˇ
 2320        seven
 2321        eight
 2322        nine
 2323        ten
 2324        "#
 2325        .unindent(),
 2326    );
 2327
 2328    cx.update_editor(|editor, window, cx| {
 2329        editor.move_page_down(&MovePageDown::default(), window, cx)
 2330    });
 2331    cx.assert_editor_state(
 2332        &r#"
 2333        one
 2334        two
 2335        three
 2336        four
 2337        five
 2338        six
 2339        ˇseven
 2340        eight
 2341        nineˇ
 2342        ten
 2343        "#
 2344        .unindent(),
 2345    );
 2346
 2347    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2348    cx.assert_editor_state(
 2349        &r#"
 2350        one
 2351        two
 2352        three
 2353        ˇfour
 2354        five
 2355        sixˇ
 2356        seven
 2357        eight
 2358        nine
 2359        ten
 2360        "#
 2361        .unindent(),
 2362    );
 2363
 2364    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2365    cx.assert_editor_state(
 2366        &r#"
 2367        ˇone
 2368        two
 2369        threeˇ
 2370        four
 2371        five
 2372        six
 2373        seven
 2374        eight
 2375        nine
 2376        ten
 2377        "#
 2378        .unindent(),
 2379    );
 2380
 2381    // Test select collapsing
 2382    cx.update_editor(|editor, window, cx| {
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384        editor.move_page_down(&MovePageDown::default(), window, cx);
 2385        editor.move_page_down(&MovePageDown::default(), window, cx);
 2386    });
 2387    cx.assert_editor_state(
 2388        &r#"
 2389        one
 2390        two
 2391        three
 2392        four
 2393        five
 2394        six
 2395        seven
 2396        eight
 2397        nine
 2398        ˇten
 2399        ˇ"#
 2400        .unindent(),
 2401    );
 2402}
 2403
 2404#[gpui::test]
 2405async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2406    init_test(cx, |_| {});
 2407    let mut cx = EditorTestContext::new(cx).await;
 2408    cx.set_state("one «two threeˇ» four");
 2409    cx.update_editor(|editor, window, cx| {
 2410        editor.delete_to_beginning_of_line(
 2411            &DeleteToBeginningOfLine {
 2412                stop_at_indent: false,
 2413            },
 2414            window,
 2415            cx,
 2416        );
 2417        assert_eq!(editor.text(cx), " four");
 2418    });
 2419}
 2420
 2421#[gpui::test]
 2422fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2423    init_test(cx, |_| {});
 2424
 2425    let editor = cx.add_window(|window, cx| {
 2426        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2427        build_editor(buffer.clone(), window, cx)
 2428    });
 2429
 2430    _ = editor.update(cx, |editor, window, cx| {
 2431        editor.change_selections(None, window, cx, |s| {
 2432            s.select_display_ranges([
 2433                // an empty selection - the preceding word fragment is deleted
 2434                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2435                // characters selected - they are deleted
 2436                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2437            ])
 2438        });
 2439        editor.delete_to_previous_word_start(
 2440            &DeleteToPreviousWordStart {
 2441                ignore_newlines: false,
 2442            },
 2443            window,
 2444            cx,
 2445        );
 2446        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2447    });
 2448
 2449    _ = editor.update(cx, |editor, window, cx| {
 2450        editor.change_selections(None, window, cx, |s| {
 2451            s.select_display_ranges([
 2452                // an empty selection - the following word fragment is deleted
 2453                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2454                // characters selected - they are deleted
 2455                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2456            ])
 2457        });
 2458        editor.delete_to_next_word_end(
 2459            &DeleteToNextWordEnd {
 2460                ignore_newlines: false,
 2461            },
 2462            window,
 2463            cx,
 2464        );
 2465        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2466    });
 2467}
 2468
 2469#[gpui::test]
 2470fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2471    init_test(cx, |_| {});
 2472
 2473    let editor = cx.add_window(|window, cx| {
 2474        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2475        build_editor(buffer.clone(), window, cx)
 2476    });
 2477    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2478        ignore_newlines: false,
 2479    };
 2480    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2481        ignore_newlines: true,
 2482    };
 2483
 2484    _ = editor.update(cx, |editor, window, cx| {
 2485        editor.change_selections(None, window, cx, |s| {
 2486            s.select_display_ranges([
 2487                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2488            ])
 2489        });
 2490        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2491        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 2492        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2493        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree");
 2494        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2495        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\n");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2");
 2498        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2499        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 2500        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2501        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2502    });
 2503}
 2504
 2505#[gpui::test]
 2506fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2507    init_test(cx, |_| {});
 2508
 2509    let editor = cx.add_window(|window, cx| {
 2510        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2511        build_editor(buffer.clone(), window, cx)
 2512    });
 2513    let del_to_next_word_end = DeleteToNextWordEnd {
 2514        ignore_newlines: false,
 2515    };
 2516    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2517        ignore_newlines: true,
 2518    };
 2519
 2520    _ = editor.update(cx, |editor, window, cx| {
 2521        editor.change_selections(None, window, cx, |s| {
 2522            s.select_display_ranges([
 2523                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2524            ])
 2525        });
 2526        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2527        assert_eq!(
 2528            editor.buffer.read(cx).read(cx).text(),
 2529            "one\n   two\nthree\n   four"
 2530        );
 2531        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2532        assert_eq!(
 2533            editor.buffer.read(cx).read(cx).text(),
 2534            "\n   two\nthree\n   four"
 2535        );
 2536        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2537        assert_eq!(
 2538            editor.buffer.read(cx).read(cx).text(),
 2539            "two\nthree\n   four"
 2540        );
 2541        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2543        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2544        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\n   four");
 2545        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2546        assert_eq!(editor.buffer.read(cx).read(cx).text(), "");
 2547    });
 2548}
 2549
 2550#[gpui::test]
 2551fn test_newline(cx: &mut TestAppContext) {
 2552    init_test(cx, |_| {});
 2553
 2554    let editor = cx.add_window(|window, cx| {
 2555        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2556        build_editor(buffer.clone(), window, cx)
 2557    });
 2558
 2559    _ = editor.update(cx, |editor, window, cx| {
 2560        editor.change_selections(None, window, cx, |s| {
 2561            s.select_display_ranges([
 2562                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2563                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2564                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2565            ])
 2566        });
 2567
 2568        editor.newline(&Newline, window, cx);
 2569        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2570    });
 2571}
 2572
 2573#[gpui::test]
 2574fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2575    init_test(cx, |_| {});
 2576
 2577    let editor = cx.add_window(|window, cx| {
 2578        let buffer = MultiBuffer::build_simple(
 2579            "
 2580                a
 2581                b(
 2582                    X
 2583                )
 2584                c(
 2585                    X
 2586                )
 2587            "
 2588            .unindent()
 2589            .as_str(),
 2590            cx,
 2591        );
 2592        let mut editor = build_editor(buffer.clone(), window, cx);
 2593        editor.change_selections(None, window, cx, |s| {
 2594            s.select_ranges([
 2595                Point::new(2, 4)..Point::new(2, 5),
 2596                Point::new(5, 4)..Point::new(5, 5),
 2597            ])
 2598        });
 2599        editor
 2600    });
 2601
 2602    _ = editor.update(cx, |editor, window, cx| {
 2603        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2604        editor.buffer.update(cx, |buffer, cx| {
 2605            buffer.edit(
 2606                [
 2607                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2608                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2609                ],
 2610                None,
 2611                cx,
 2612            );
 2613            assert_eq!(
 2614                buffer.read(cx).text(),
 2615                "
 2616                    a
 2617                    b()
 2618                    c()
 2619                "
 2620                .unindent()
 2621            );
 2622        });
 2623        assert_eq!(
 2624            editor.selections.ranges(cx),
 2625            &[
 2626                Point::new(1, 2)..Point::new(1, 2),
 2627                Point::new(2, 2)..Point::new(2, 2),
 2628            ],
 2629        );
 2630
 2631        editor.newline(&Newline, window, cx);
 2632        assert_eq!(
 2633            editor.text(cx),
 2634            "
 2635                a
 2636                b(
 2637                )
 2638                c(
 2639                )
 2640            "
 2641            .unindent()
 2642        );
 2643
 2644        // The selections are moved after the inserted newlines
 2645        assert_eq!(
 2646            editor.selections.ranges(cx),
 2647            &[
 2648                Point::new(2, 0)..Point::new(2, 0),
 2649                Point::new(4, 0)..Point::new(4, 0),
 2650            ],
 2651        );
 2652    });
 2653}
 2654
 2655#[gpui::test]
 2656async fn test_newline_above(cx: &mut TestAppContext) {
 2657    init_test(cx, |settings| {
 2658        settings.defaults.tab_size = NonZeroU32::new(4)
 2659    });
 2660
 2661    let language = Arc::new(
 2662        Language::new(
 2663            LanguageConfig::default(),
 2664            Some(tree_sitter_rust::LANGUAGE.into()),
 2665        )
 2666        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2667        .unwrap(),
 2668    );
 2669
 2670    let mut cx = EditorTestContext::new(cx).await;
 2671    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2672    cx.set_state(indoc! {"
 2673        const a: ˇA = (
 2674 2675                «const_functionˇ»(ˇ),
 2676                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2677 2678        ˇ);ˇ
 2679    "});
 2680
 2681    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2682    cx.assert_editor_state(indoc! {"
 2683        ˇ
 2684        const a: A = (
 2685            ˇ
 2686            (
 2687                ˇ
 2688                ˇ
 2689                const_function(),
 2690                ˇ
 2691                ˇ
 2692                ˇ
 2693                ˇ
 2694                something_else,
 2695                ˇ
 2696            )
 2697            ˇ
 2698            ˇ
 2699        );
 2700    "});
 2701}
 2702
 2703#[gpui::test]
 2704async fn test_newline_below(cx: &mut TestAppContext) {
 2705    init_test(cx, |settings| {
 2706        settings.defaults.tab_size = NonZeroU32::new(4)
 2707    });
 2708
 2709    let language = Arc::new(
 2710        Language::new(
 2711            LanguageConfig::default(),
 2712            Some(tree_sitter_rust::LANGUAGE.into()),
 2713        )
 2714        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2715        .unwrap(),
 2716    );
 2717
 2718    let mut cx = EditorTestContext::new(cx).await;
 2719    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2720    cx.set_state(indoc! {"
 2721        const a: ˇA = (
 2722 2723                «const_functionˇ»(ˇ),
 2724                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2725 2726        ˇ);ˇ
 2727    "});
 2728
 2729    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2730    cx.assert_editor_state(indoc! {"
 2731        const a: A = (
 2732            ˇ
 2733            (
 2734                ˇ
 2735                const_function(),
 2736                ˇ
 2737                ˇ
 2738                something_else,
 2739                ˇ
 2740                ˇ
 2741                ˇ
 2742                ˇ
 2743            )
 2744            ˇ
 2745        );
 2746        ˇ
 2747        ˇ
 2748    "});
 2749}
 2750
 2751#[gpui::test]
 2752async fn test_newline_comments(cx: &mut TestAppContext) {
 2753    init_test(cx, |settings| {
 2754        settings.defaults.tab_size = NonZeroU32::new(4)
 2755    });
 2756
 2757    let language = Arc::new(Language::new(
 2758        LanguageConfig {
 2759            line_comments: vec!["//".into()],
 2760            ..LanguageConfig::default()
 2761        },
 2762        None,
 2763    ));
 2764    {
 2765        let mut cx = EditorTestContext::new(cx).await;
 2766        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2767        cx.set_state(indoc! {"
 2768        // Fooˇ
 2769    "});
 2770
 2771        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2772        cx.assert_editor_state(indoc! {"
 2773        // Foo
 2774        //ˇ
 2775    "});
 2776        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2777        cx.set_state(indoc! {"
 2778        ˇ// Foo
 2779    "});
 2780        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2781        cx.assert_editor_state(indoc! {"
 2782
 2783        ˇ// Foo
 2784    "});
 2785    }
 2786    // Ensure that comment continuations can be disabled.
 2787    update_test_language_settings(cx, |settings| {
 2788        settings.defaults.extend_comment_on_newline = Some(false);
 2789    });
 2790    let mut cx = EditorTestContext::new(cx).await;
 2791    cx.set_state(indoc! {"
 2792        // Fooˇ
 2793    "});
 2794    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2795    cx.assert_editor_state(indoc! {"
 2796        // Foo
 2797        ˇ
 2798    "});
 2799}
 2800
 2801#[gpui::test]
 2802fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2803    init_test(cx, |_| {});
 2804
 2805    let editor = cx.add_window(|window, cx| {
 2806        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2807        let mut editor = build_editor(buffer.clone(), window, cx);
 2808        editor.change_selections(None, window, cx, |s| {
 2809            s.select_ranges([3..4, 11..12, 19..20])
 2810        });
 2811        editor
 2812    });
 2813
 2814    _ = editor.update(cx, |editor, window, cx| {
 2815        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2816        editor.buffer.update(cx, |buffer, cx| {
 2817            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2818            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2819        });
 2820        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2821
 2822        editor.insert("Z", window, cx);
 2823        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2824
 2825        // The selections are moved after the inserted characters
 2826        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2827    });
 2828}
 2829
 2830#[gpui::test]
 2831async fn test_tab(cx: &mut TestAppContext) {
 2832    init_test(cx, |settings| {
 2833        settings.defaults.tab_size = NonZeroU32::new(3)
 2834    });
 2835
 2836    let mut cx = EditorTestContext::new(cx).await;
 2837    cx.set_state(indoc! {"
 2838        ˇabˇc
 2839        ˇ🏀ˇ🏀ˇefg
 2840 2841    "});
 2842    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2843    cx.assert_editor_state(indoc! {"
 2844           ˇab ˇc
 2845           ˇ🏀  ˇ🏀  ˇefg
 2846        d  ˇ
 2847    "});
 2848
 2849    cx.set_state(indoc! {"
 2850        a
 2851        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2852    "});
 2853    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2854    cx.assert_editor_state(indoc! {"
 2855        a
 2856           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2857    "});
 2858}
 2859
 2860#[gpui::test]
 2861async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2862    init_test(cx, |_| {});
 2863
 2864    let mut cx = EditorTestContext::new(cx).await;
 2865    let language = Arc::new(
 2866        Language::new(
 2867            LanguageConfig::default(),
 2868            Some(tree_sitter_rust::LANGUAGE.into()),
 2869        )
 2870        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2871        .unwrap(),
 2872    );
 2873    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2874
 2875    // cursors that are already at the suggested indent level insert
 2876    // a soft tab. cursors that are to the left of the suggested indent
 2877    // auto-indent their line.
 2878    cx.set_state(indoc! {"
 2879        ˇ
 2880        const a: B = (
 2881            c(
 2882                d(
 2883        ˇ
 2884                )
 2885        ˇ
 2886        ˇ    )
 2887        );
 2888    "});
 2889    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2890    cx.assert_editor_state(indoc! {"
 2891            ˇ
 2892        const a: B = (
 2893            c(
 2894                d(
 2895                    ˇ
 2896                )
 2897                ˇ
 2898            ˇ)
 2899        );
 2900    "});
 2901
 2902    // handle auto-indent when there are multiple cursors on the same line
 2903    cx.set_state(indoc! {"
 2904        const a: B = (
 2905            c(
 2906        ˇ    ˇ
 2907        ˇ    )
 2908        );
 2909    "});
 2910    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2911    cx.assert_editor_state(indoc! {"
 2912        const a: B = (
 2913            c(
 2914                ˇ
 2915            ˇ)
 2916        );
 2917    "});
 2918}
 2919
 2920#[gpui::test]
 2921async fn test_tab_with_mixed_whitespace(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: Some(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: Some(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            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_mode(cx: &mut TestAppContext) {
 9203    init_test(cx, |_| {});
 9204    let mut cx = EditorLspTestContext::new_rust(
 9205        lsp::ServerCapabilities {
 9206            completion_provider: Some(lsp::CompletionOptions {
 9207                resolve_provider: Some(true),
 9208                ..Default::default()
 9209            }),
 9210            ..Default::default()
 9211        },
 9212        cx,
 9213    )
 9214    .await;
 9215
 9216    struct Run {
 9217        run_description: &'static str,
 9218        initial_state: String,
 9219        buffer_marked_text: String,
 9220        completion_text: &'static str,
 9221        expected_with_insert_mode: String,
 9222        expected_with_replace_mode: String,
 9223        expected_with_replace_subsequence_mode: String,
 9224        expected_with_replace_suffix_mode: String,
 9225    }
 9226
 9227    let runs = [
 9228        Run {
 9229            run_description: "Start of word matches completion text",
 9230            initial_state: "before ediˇ after".into(),
 9231            buffer_marked_text: "before <edi|> after".into(),
 9232            completion_text: "editor",
 9233            expected_with_insert_mode: "before editorˇ after".into(),
 9234            expected_with_replace_mode: "before editorˇ after".into(),
 9235            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9236            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9237        },
 9238        Run {
 9239            run_description: "Accept same text at the middle of the word",
 9240            initial_state: "before ediˇtor after".into(),
 9241            buffer_marked_text: "before <edi|tor> after".into(),
 9242            completion_text: "editor",
 9243            expected_with_insert_mode: "before editorˇtor after".into(),
 9244            expected_with_replace_mode: "before ediˇtor after".into(),
 9245            expected_with_replace_subsequence_mode: "before ediˇtor after".into(),
 9246            expected_with_replace_suffix_mode: "before ediˇtor after".into(),
 9247        },
 9248        Run {
 9249            run_description: "End of word matches completion text -- cursor at end",
 9250            initial_state: "before torˇ after".into(),
 9251            buffer_marked_text: "before <tor|> after".into(),
 9252            completion_text: "editor",
 9253            expected_with_insert_mode: "before editorˇ after".into(),
 9254            expected_with_replace_mode: "before editorˇ after".into(),
 9255            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9256            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9257        },
 9258        Run {
 9259            run_description: "End of word matches completion text -- cursor at start",
 9260            initial_state: "before ˇtor after".into(),
 9261            buffer_marked_text: "before <|tor> after".into(),
 9262            completion_text: "editor",
 9263            expected_with_insert_mode: "before editorˇtor after".into(),
 9264            expected_with_replace_mode: "before editorˇ after".into(),
 9265            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9266            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9267        },
 9268        Run {
 9269            run_description: "Prepend text containing whitespace",
 9270            initial_state: "pˇfield: bool".into(),
 9271            buffer_marked_text: "<p|field>: bool".into(),
 9272            completion_text: "pub ",
 9273            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9274            expected_with_replace_mode: "pub ˇ: bool".into(),
 9275            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9276            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9277        },
 9278        Run {
 9279            run_description: "Add element to start of list",
 9280            initial_state: "[element_ˇelement_2]".into(),
 9281            buffer_marked_text: "[<element_|element_2>]".into(),
 9282            completion_text: "element_1",
 9283            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9284            expected_with_replace_mode: "[element_1ˇ]".into(),
 9285            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9286            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9287        },
 9288        Run {
 9289            run_description: "Add element to start of list -- first and second elements are equal",
 9290            initial_state: "[elˇelement]".into(),
 9291            buffer_marked_text: "[<el|element>]".into(),
 9292            completion_text: "element",
 9293            expected_with_insert_mode: "[elementˇelement]".into(),
 9294            expected_with_replace_mode: "[elˇement]".into(),
 9295            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9296            expected_with_replace_suffix_mode: "[elˇement]".into(),
 9297        },
 9298        Run {
 9299            run_description: "Ends with matching suffix",
 9300            initial_state: "SubˇError".into(),
 9301            buffer_marked_text: "<Sub|Error>".into(),
 9302            completion_text: "SubscriptionError",
 9303            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9304            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9305            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9306            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9307        },
 9308        Run {
 9309            run_description: "Suffix is a subsequence -- contiguous",
 9310            initial_state: "SubˇErr".into(),
 9311            buffer_marked_text: "<Sub|Err>".into(),
 9312            completion_text: "SubscriptionError",
 9313            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9314            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9315            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9316            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9317        },
 9318        Run {
 9319            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9320            initial_state: "Suˇscrirr".into(),
 9321            buffer_marked_text: "<Su|scrirr>".into(),
 9322            completion_text: "SubscriptionError",
 9323            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9324            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9325            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9326            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9327        },
 9328        Run {
 9329            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9330            initial_state: "foo(indˇix)".into(),
 9331            buffer_marked_text: "foo(<ind|ix>)".into(),
 9332            completion_text: "node_index",
 9333            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9334            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9335            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9336            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9337        },
 9338    ];
 9339
 9340    for run in runs {
 9341        let run_variations = [
 9342            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9343            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9344            (
 9345                LspInsertMode::ReplaceSubsequence,
 9346                run.expected_with_replace_subsequence_mode,
 9347            ),
 9348            (
 9349                LspInsertMode::ReplaceSuffix,
 9350                run.expected_with_replace_suffix_mode,
 9351            ),
 9352        ];
 9353
 9354        for (lsp_insert_mode, expected_text) in run_variations {
 9355            eprintln!(
 9356                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9357                run.run_description,
 9358            );
 9359
 9360            update_test_language_settings(&mut cx, |settings| {
 9361                settings.defaults.completions = Some(CompletionSettings {
 9362                    lsp_insert_mode,
 9363                    words: WordsCompletionMode::Disabled,
 9364                    lsp: true,
 9365                    lsp_fetch_timeout_ms: 0,
 9366                });
 9367            });
 9368
 9369            cx.set_state(&run.initial_state);
 9370            cx.update_editor(|editor, window, cx| {
 9371                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9372            });
 9373
 9374            let counter = Arc::new(AtomicUsize::new(0));
 9375            handle_completion_request_with_insert_and_replace(
 9376                &mut cx,
 9377                &run.buffer_marked_text,
 9378                vec![run.completion_text],
 9379                counter.clone(),
 9380            )
 9381            .await;
 9382            cx.condition(|editor, _| editor.context_menu_visible())
 9383                .await;
 9384            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9385
 9386            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9387                editor
 9388                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9389                    .unwrap()
 9390            });
 9391            cx.assert_editor_state(&expected_text);
 9392            handle_resolve_completion_request(&mut cx, None).await;
 9393            apply_additional_edits.await.unwrap();
 9394        }
 9395    }
 9396}
 9397
 9398#[gpui::test]
 9399async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9400    init_test(cx, |_| {});
 9401    let mut cx = EditorLspTestContext::new_rust(
 9402        lsp::ServerCapabilities {
 9403            completion_provider: Some(lsp::CompletionOptions {
 9404                resolve_provider: Some(true),
 9405                ..Default::default()
 9406            }),
 9407            ..Default::default()
 9408        },
 9409        cx,
 9410    )
 9411    .await;
 9412
 9413    let initial_state = "SubˇError";
 9414    let buffer_marked_text = "<Sub|Error>";
 9415    let completion_text = "SubscriptionError";
 9416    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9417    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9418
 9419    update_test_language_settings(&mut cx, |settings| {
 9420        settings.defaults.completions = Some(CompletionSettings {
 9421            words: WordsCompletionMode::Disabled,
 9422            // set the opposite here to ensure that the action is overriding the default behavior
 9423            lsp_insert_mode: LspInsertMode::Insert,
 9424            lsp: true,
 9425            lsp_fetch_timeout_ms: 0,
 9426        });
 9427    });
 9428
 9429    cx.set_state(initial_state);
 9430    cx.update_editor(|editor, window, cx| {
 9431        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9432    });
 9433
 9434    let counter = Arc::new(AtomicUsize::new(0));
 9435    handle_completion_request_with_insert_and_replace(
 9436        &mut cx,
 9437        &buffer_marked_text,
 9438        vec![completion_text],
 9439        counter.clone(),
 9440    )
 9441    .await;
 9442    cx.condition(|editor, _| editor.context_menu_visible())
 9443        .await;
 9444    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9445
 9446    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9447        editor
 9448            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9449            .unwrap()
 9450    });
 9451    cx.assert_editor_state(&expected_with_replace_mode);
 9452    handle_resolve_completion_request(&mut cx, None).await;
 9453    apply_additional_edits.await.unwrap();
 9454
 9455    update_test_language_settings(&mut cx, |settings| {
 9456        settings.defaults.completions = Some(CompletionSettings {
 9457            words: WordsCompletionMode::Disabled,
 9458            // set the opposite here to ensure that the action is overriding the default behavior
 9459            lsp_insert_mode: LspInsertMode::Replace,
 9460            lsp: true,
 9461            lsp_fetch_timeout_ms: 0,
 9462        });
 9463    });
 9464
 9465    cx.set_state(initial_state);
 9466    cx.update_editor(|editor, window, cx| {
 9467        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9468    });
 9469    handle_completion_request_with_insert_and_replace(
 9470        &mut cx,
 9471        &buffer_marked_text,
 9472        vec![completion_text],
 9473        counter.clone(),
 9474    )
 9475    .await;
 9476    cx.condition(|editor, _| editor.context_menu_visible())
 9477        .await;
 9478    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9479
 9480    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9481        editor
 9482            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9483            .unwrap()
 9484    });
 9485    cx.assert_editor_state(&expected_with_insert_mode);
 9486    handle_resolve_completion_request(&mut cx, None).await;
 9487    apply_additional_edits.await.unwrap();
 9488}
 9489
 9490#[gpui::test]
 9491async fn test_completion(cx: &mut TestAppContext) {
 9492    init_test(cx, |_| {});
 9493
 9494    let mut cx = EditorLspTestContext::new_rust(
 9495        lsp::ServerCapabilities {
 9496            completion_provider: Some(lsp::CompletionOptions {
 9497                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9498                resolve_provider: Some(true),
 9499                ..Default::default()
 9500            }),
 9501            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9502            ..Default::default()
 9503        },
 9504        cx,
 9505    )
 9506    .await;
 9507    let counter = Arc::new(AtomicUsize::new(0));
 9508
 9509    cx.set_state(indoc! {"
 9510        oneˇ
 9511        two
 9512        three
 9513    "});
 9514    cx.simulate_keystroke(".");
 9515    handle_completion_request(
 9516        &mut cx,
 9517        indoc! {"
 9518            one.|<>
 9519            two
 9520            three
 9521        "},
 9522        vec!["first_completion", "second_completion"],
 9523        counter.clone(),
 9524    )
 9525    .await;
 9526    cx.condition(|editor, _| editor.context_menu_visible())
 9527        .await;
 9528    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9529
 9530    let _handler = handle_signature_help_request(
 9531        &mut cx,
 9532        lsp::SignatureHelp {
 9533            signatures: vec![lsp::SignatureInformation {
 9534                label: "test signature".to_string(),
 9535                documentation: None,
 9536                parameters: Some(vec![lsp::ParameterInformation {
 9537                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
 9538                    documentation: None,
 9539                }]),
 9540                active_parameter: None,
 9541            }],
 9542            active_signature: None,
 9543            active_parameter: None,
 9544        },
 9545    );
 9546    cx.update_editor(|editor, window, cx| {
 9547        assert!(
 9548            !editor.signature_help_state.is_shown(),
 9549            "No signature help was called for"
 9550        );
 9551        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9552    });
 9553    cx.run_until_parked();
 9554    cx.update_editor(|editor, _, _| {
 9555        assert!(
 9556            !editor.signature_help_state.is_shown(),
 9557            "No signature help should be shown when completions menu is open"
 9558        );
 9559    });
 9560
 9561    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9562        editor.context_menu_next(&Default::default(), window, cx);
 9563        editor
 9564            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9565            .unwrap()
 9566    });
 9567    cx.assert_editor_state(indoc! {"
 9568        one.second_completionˇ
 9569        two
 9570        three
 9571    "});
 9572
 9573    handle_resolve_completion_request(
 9574        &mut cx,
 9575        Some(vec![
 9576            (
 9577                //This overlaps with the primary completion edit which is
 9578                //misbehavior from the LSP spec, test that we filter it out
 9579                indoc! {"
 9580                    one.second_ˇcompletion
 9581                    two
 9582                    threeˇ
 9583                "},
 9584                "overlapping additional edit",
 9585            ),
 9586            (
 9587                indoc! {"
 9588                    one.second_completion
 9589                    two
 9590                    threeˇ
 9591                "},
 9592                "\nadditional edit",
 9593            ),
 9594        ]),
 9595    )
 9596    .await;
 9597    apply_additional_edits.await.unwrap();
 9598    cx.assert_editor_state(indoc! {"
 9599        one.second_completionˇ
 9600        two
 9601        three
 9602        additional edit
 9603    "});
 9604
 9605    cx.set_state(indoc! {"
 9606        one.second_completion
 9607        twoˇ
 9608        threeˇ
 9609        additional edit
 9610    "});
 9611    cx.simulate_keystroke(" ");
 9612    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9613    cx.simulate_keystroke("s");
 9614    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9615
 9616    cx.assert_editor_state(indoc! {"
 9617        one.second_completion
 9618        two sˇ
 9619        three sˇ
 9620        additional edit
 9621    "});
 9622    handle_completion_request(
 9623        &mut cx,
 9624        indoc! {"
 9625            one.second_completion
 9626            two s
 9627            three <s|>
 9628            additional edit
 9629        "},
 9630        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9631        counter.clone(),
 9632    )
 9633    .await;
 9634    cx.condition(|editor, _| editor.context_menu_visible())
 9635        .await;
 9636    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9637
 9638    cx.simulate_keystroke("i");
 9639
 9640    handle_completion_request(
 9641        &mut cx,
 9642        indoc! {"
 9643            one.second_completion
 9644            two si
 9645            three <si|>
 9646            additional edit
 9647        "},
 9648        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
 9649        counter.clone(),
 9650    )
 9651    .await;
 9652    cx.condition(|editor, _| editor.context_menu_visible())
 9653        .await;
 9654    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
 9655
 9656    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9657        editor
 9658            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9659            .unwrap()
 9660    });
 9661    cx.assert_editor_state(indoc! {"
 9662        one.second_completion
 9663        two sixth_completionˇ
 9664        three sixth_completionˇ
 9665        additional edit
 9666    "});
 9667
 9668    apply_additional_edits.await.unwrap();
 9669
 9670    update_test_language_settings(&mut cx, |settings| {
 9671        settings.defaults.show_completions_on_input = Some(false);
 9672    });
 9673    cx.set_state("editorˇ");
 9674    cx.simulate_keystroke(".");
 9675    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9676    cx.simulate_keystrokes("c l o");
 9677    cx.assert_editor_state("editor.cloˇ");
 9678    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
 9679    cx.update_editor(|editor, window, cx| {
 9680        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9681    });
 9682    handle_completion_request(
 9683        &mut cx,
 9684        "editor.<clo|>",
 9685        vec!["close", "clobber"],
 9686        counter.clone(),
 9687    )
 9688    .await;
 9689    cx.condition(|editor, _| editor.context_menu_visible())
 9690        .await;
 9691    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
 9692
 9693    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9694        editor
 9695            .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9696            .unwrap()
 9697    });
 9698    cx.assert_editor_state("editor.closeˇ");
 9699    handle_resolve_completion_request(&mut cx, None).await;
 9700    apply_additional_edits.await.unwrap();
 9701}
 9702
 9703#[gpui::test]
 9704async fn test_word_completion(cx: &mut TestAppContext) {
 9705    let lsp_fetch_timeout_ms = 10;
 9706    init_test(cx, |language_settings| {
 9707        language_settings.defaults.completions = Some(CompletionSettings {
 9708            words: WordsCompletionMode::Fallback,
 9709            lsp: true,
 9710            lsp_fetch_timeout_ms: 10,
 9711            lsp_insert_mode: LspInsertMode::Insert,
 9712        });
 9713    });
 9714
 9715    let mut cx = EditorLspTestContext::new_rust(
 9716        lsp::ServerCapabilities {
 9717            completion_provider: Some(lsp::CompletionOptions {
 9718                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9719                ..lsp::CompletionOptions::default()
 9720            }),
 9721            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9722            ..lsp::ServerCapabilities::default()
 9723        },
 9724        cx,
 9725    )
 9726    .await;
 9727
 9728    let throttle_completions = Arc::new(AtomicBool::new(false));
 9729
 9730    let lsp_throttle_completions = throttle_completions.clone();
 9731    let _completion_requests_handler =
 9732        cx.lsp
 9733            .server
 9734            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
 9735                let lsp_throttle_completions = lsp_throttle_completions.clone();
 9736                let cx = cx.clone();
 9737                async move {
 9738                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
 9739                        cx.background_executor()
 9740                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
 9741                            .await;
 9742                    }
 9743                    Ok(Some(lsp::CompletionResponse::Array(vec![
 9744                        lsp::CompletionItem {
 9745                            label: "first".into(),
 9746                            ..lsp::CompletionItem::default()
 9747                        },
 9748                        lsp::CompletionItem {
 9749                            label: "last".into(),
 9750                            ..lsp::CompletionItem::default()
 9751                        },
 9752                    ])))
 9753                }
 9754            });
 9755
 9756    cx.set_state(indoc! {"
 9757        oneˇ
 9758        two
 9759        three
 9760    "});
 9761    cx.simulate_keystroke(".");
 9762    cx.executor().run_until_parked();
 9763    cx.condition(|editor, _| editor.context_menu_visible())
 9764        .await;
 9765    cx.update_editor(|editor, window, cx| {
 9766        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9767        {
 9768            assert_eq!(
 9769                completion_menu_entries(&menu),
 9770                &["first", "last"],
 9771                "When LSP server is fast to reply, no fallback word completions are used"
 9772            );
 9773        } else {
 9774            panic!("expected completion menu to be open");
 9775        }
 9776        editor.cancel(&Cancel, window, cx);
 9777    });
 9778    cx.executor().run_until_parked();
 9779    cx.condition(|editor, _| !editor.context_menu_visible())
 9780        .await;
 9781
 9782    throttle_completions.store(true, atomic::Ordering::Release);
 9783    cx.simulate_keystroke(".");
 9784    cx.executor()
 9785        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
 9786    cx.executor().run_until_parked();
 9787    cx.condition(|editor, _| editor.context_menu_visible())
 9788        .await;
 9789    cx.update_editor(|editor, _, _| {
 9790        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9791        {
 9792            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
 9793                "When LSP server is slow, document words can be shown instead, if configured accordingly");
 9794        } else {
 9795            panic!("expected completion menu to be open");
 9796        }
 9797    });
 9798}
 9799
 9800#[gpui::test]
 9801async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
 9802    init_test(cx, |language_settings| {
 9803        language_settings.defaults.completions = Some(CompletionSettings {
 9804            words: WordsCompletionMode::Enabled,
 9805            lsp: true,
 9806            lsp_fetch_timeout_ms: 0,
 9807            lsp_insert_mode: LspInsertMode::Insert,
 9808        });
 9809    });
 9810
 9811    let mut cx = EditorLspTestContext::new_rust(
 9812        lsp::ServerCapabilities {
 9813            completion_provider: Some(lsp::CompletionOptions {
 9814                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9815                ..lsp::CompletionOptions::default()
 9816            }),
 9817            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9818            ..lsp::ServerCapabilities::default()
 9819        },
 9820        cx,
 9821    )
 9822    .await;
 9823
 9824    let _completion_requests_handler =
 9825        cx.lsp
 9826            .server
 9827            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9828                Ok(Some(lsp::CompletionResponse::Array(vec![
 9829                    lsp::CompletionItem {
 9830                        label: "first".into(),
 9831                        ..lsp::CompletionItem::default()
 9832                    },
 9833                    lsp::CompletionItem {
 9834                        label: "last".into(),
 9835                        ..lsp::CompletionItem::default()
 9836                    },
 9837                ])))
 9838            });
 9839
 9840    cx.set_state(indoc! {"ˇ
 9841        first
 9842        last
 9843        second
 9844    "});
 9845    cx.simulate_keystroke(".");
 9846    cx.executor().run_until_parked();
 9847    cx.condition(|editor, _| editor.context_menu_visible())
 9848        .await;
 9849    cx.update_editor(|editor, _, _| {
 9850        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9851        {
 9852            assert_eq!(
 9853                completion_menu_entries(&menu),
 9854                &["first", "last", "second"],
 9855                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
 9856            );
 9857        } else {
 9858            panic!("expected completion menu to be open");
 9859        }
 9860    });
 9861}
 9862
 9863#[gpui::test]
 9864async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
 9865    init_test(cx, |language_settings| {
 9866        language_settings.defaults.completions = Some(CompletionSettings {
 9867            words: WordsCompletionMode::Disabled,
 9868            lsp: true,
 9869            lsp_fetch_timeout_ms: 0,
 9870            lsp_insert_mode: LspInsertMode::Insert,
 9871        });
 9872    });
 9873
 9874    let mut cx = EditorLspTestContext::new_rust(
 9875        lsp::ServerCapabilities {
 9876            completion_provider: Some(lsp::CompletionOptions {
 9877                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
 9878                ..lsp::CompletionOptions::default()
 9879            }),
 9880            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
 9881            ..lsp::ServerCapabilities::default()
 9882        },
 9883        cx,
 9884    )
 9885    .await;
 9886
 9887    let _completion_requests_handler =
 9888        cx.lsp
 9889            .server
 9890            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
 9891                panic!("LSP completions should not be queried when dealing with word completions")
 9892            });
 9893
 9894    cx.set_state(indoc! {"ˇ
 9895        first
 9896        last
 9897        second
 9898    "});
 9899    cx.update_editor(|editor, window, cx| {
 9900        editor.show_word_completions(&ShowWordCompletions, window, cx);
 9901    });
 9902    cx.executor().run_until_parked();
 9903    cx.condition(|editor, _| editor.context_menu_visible())
 9904        .await;
 9905    cx.update_editor(|editor, _, _| {
 9906        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9907        {
 9908            assert_eq!(
 9909                completion_menu_entries(&menu),
 9910                &["first", "last", "second"],
 9911                "`ShowWordCompletions` action should show word completions"
 9912            );
 9913        } else {
 9914            panic!("expected completion menu to be open");
 9915        }
 9916    });
 9917
 9918    cx.simulate_keystroke("l");
 9919    cx.executor().run_until_parked();
 9920    cx.condition(|editor, _| editor.context_menu_visible())
 9921        .await;
 9922    cx.update_editor(|editor, _, _| {
 9923        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9924        {
 9925            assert_eq!(
 9926                completion_menu_entries(&menu),
 9927                &["last"],
 9928                "After showing word completions, further editing should filter them and not query the LSP"
 9929            );
 9930        } else {
 9931            panic!("expected completion menu to be open");
 9932        }
 9933    });
 9934}
 9935
 9936#[gpui::test]
 9937async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
 9938    init_test(cx, |language_settings| {
 9939        language_settings.defaults.completions = Some(CompletionSettings {
 9940            words: WordsCompletionMode::Fallback,
 9941            lsp: false,
 9942            lsp_fetch_timeout_ms: 0,
 9943            lsp_insert_mode: LspInsertMode::Insert,
 9944        });
 9945    });
 9946
 9947    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
 9948
 9949    cx.set_state(indoc! {"ˇ
 9950        0_usize
 9951        let
 9952        33
 9953        4.5f32
 9954    "});
 9955    cx.update_editor(|editor, window, cx| {
 9956        editor.show_completions(&ShowCompletions::default(), window, cx);
 9957    });
 9958    cx.executor().run_until_parked();
 9959    cx.condition(|editor, _| editor.context_menu_visible())
 9960        .await;
 9961    cx.update_editor(|editor, window, cx| {
 9962        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9963        {
 9964            assert_eq!(
 9965                completion_menu_entries(&menu),
 9966                &["let"],
 9967                "With no digits in the completion query, no digits should be in the word completions"
 9968            );
 9969        } else {
 9970            panic!("expected completion menu to be open");
 9971        }
 9972        editor.cancel(&Cancel, window, cx);
 9973    });
 9974
 9975    cx.set_state(indoc! {" 9976        0_usize
 9977        let
 9978        3
 9979        33.35f32
 9980    "});
 9981    cx.update_editor(|editor, window, cx| {
 9982        editor.show_completions(&ShowCompletions::default(), window, cx);
 9983    });
 9984    cx.executor().run_until_parked();
 9985    cx.condition(|editor, _| editor.context_menu_visible())
 9986        .await;
 9987    cx.update_editor(|editor, _, _| {
 9988        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
 9989        {
 9990            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
 9991                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
 9992        } else {
 9993            panic!("expected completion menu to be open");
 9994        }
 9995    });
 9996}
 9997
 9998fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
 9999    let position = || lsp::Position {
10000        line: params.text_document_position.position.line,
10001        character: params.text_document_position.position.character,
10002    };
10003    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10004        range: lsp::Range {
10005            start: position(),
10006            end: position(),
10007        },
10008        new_text: text.to_string(),
10009    }))
10010}
10011
10012#[gpui::test]
10013async fn test_multiline_completion(cx: &mut TestAppContext) {
10014    init_test(cx, |_| {});
10015
10016    let fs = FakeFs::new(cx.executor());
10017    fs.insert_tree(
10018        path!("/a"),
10019        json!({
10020            "main.ts": "a",
10021        }),
10022    )
10023    .await;
10024
10025    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10026    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10027    let typescript_language = Arc::new(Language::new(
10028        LanguageConfig {
10029            name: "TypeScript".into(),
10030            matcher: LanguageMatcher {
10031                path_suffixes: vec!["ts".to_string()],
10032                ..LanguageMatcher::default()
10033            },
10034            line_comments: vec!["// ".into()],
10035            ..LanguageConfig::default()
10036        },
10037        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10038    ));
10039    language_registry.add(typescript_language.clone());
10040    let mut fake_servers = language_registry.register_fake_lsp(
10041        "TypeScript",
10042        FakeLspAdapter {
10043            capabilities: lsp::ServerCapabilities {
10044                completion_provider: Some(lsp::CompletionOptions {
10045                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10046                    ..lsp::CompletionOptions::default()
10047                }),
10048                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10049                ..lsp::ServerCapabilities::default()
10050            },
10051            // Emulate vtsls label generation
10052            label_for_completion: Some(Box::new(|item, _| {
10053                let text = if let Some(description) = item
10054                    .label_details
10055                    .as_ref()
10056                    .and_then(|label_details| label_details.description.as_ref())
10057                {
10058                    format!("{} {}", item.label, description)
10059                } else if let Some(detail) = &item.detail {
10060                    format!("{} {}", item.label, detail)
10061                } else {
10062                    item.label.clone()
10063                };
10064                let len = text.len();
10065                Some(language::CodeLabel {
10066                    text,
10067                    runs: Vec::new(),
10068                    filter_range: 0..len,
10069                })
10070            })),
10071            ..FakeLspAdapter::default()
10072        },
10073    );
10074    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10075    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10076    let worktree_id = workspace
10077        .update(cx, |workspace, _window, cx| {
10078            workspace.project().update(cx, |project, cx| {
10079                project.worktrees(cx).next().unwrap().read(cx).id()
10080            })
10081        })
10082        .unwrap();
10083    let _buffer = project
10084        .update(cx, |project, cx| {
10085            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10086        })
10087        .await
10088        .unwrap();
10089    let editor = workspace
10090        .update(cx, |workspace, window, cx| {
10091            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10092        })
10093        .unwrap()
10094        .await
10095        .unwrap()
10096        .downcast::<Editor>()
10097        .unwrap();
10098    let fake_server = fake_servers.next().await.unwrap();
10099
10100    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10101    let multiline_label_2 = "a\nb\nc\n";
10102    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10103    let multiline_description = "d\ne\nf\n";
10104    let multiline_detail_2 = "g\nh\ni\n";
10105
10106    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10107        move |params, _| async move {
10108            Ok(Some(lsp::CompletionResponse::Array(vec![
10109                lsp::CompletionItem {
10110                    label: multiline_label.to_string(),
10111                    text_edit: gen_text_edit(&params, "new_text_1"),
10112                    ..lsp::CompletionItem::default()
10113                },
10114                lsp::CompletionItem {
10115                    label: "single line label 1".to_string(),
10116                    detail: Some(multiline_detail.to_string()),
10117                    text_edit: gen_text_edit(&params, "new_text_2"),
10118                    ..lsp::CompletionItem::default()
10119                },
10120                lsp::CompletionItem {
10121                    label: "single line label 2".to_string(),
10122                    label_details: Some(lsp::CompletionItemLabelDetails {
10123                        description: Some(multiline_description.to_string()),
10124                        detail: None,
10125                    }),
10126                    text_edit: gen_text_edit(&params, "new_text_2"),
10127                    ..lsp::CompletionItem::default()
10128                },
10129                lsp::CompletionItem {
10130                    label: multiline_label_2.to_string(),
10131                    detail: Some(multiline_detail_2.to_string()),
10132                    text_edit: gen_text_edit(&params, "new_text_3"),
10133                    ..lsp::CompletionItem::default()
10134                },
10135                lsp::CompletionItem {
10136                    label: "Label with many     spaces and \t but without newlines".to_string(),
10137                    detail: Some(
10138                        "Details with many     spaces and \t but without newlines".to_string(),
10139                    ),
10140                    text_edit: gen_text_edit(&params, "new_text_4"),
10141                    ..lsp::CompletionItem::default()
10142                },
10143            ])))
10144        },
10145    );
10146
10147    editor.update_in(cx, |editor, window, cx| {
10148        cx.focus_self(window);
10149        editor.move_to_end(&MoveToEnd, window, cx);
10150        editor.handle_input(".", window, cx);
10151    });
10152    cx.run_until_parked();
10153    completion_handle.next().await.unwrap();
10154
10155    editor.update(cx, |editor, _| {
10156        assert!(editor.context_menu_visible());
10157        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10158        {
10159            let completion_labels = menu
10160                .completions
10161                .borrow()
10162                .iter()
10163                .map(|c| c.label.text.clone())
10164                .collect::<Vec<_>>();
10165            assert_eq!(
10166                completion_labels,
10167                &[
10168                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10169                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10170                    "single line label 2 d e f ",
10171                    "a b c g h i ",
10172                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10173                ],
10174                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10175            );
10176
10177            for completion in menu
10178                .completions
10179                .borrow()
10180                .iter() {
10181                    assert_eq!(
10182                        completion.label.filter_range,
10183                        0..completion.label.text.len(),
10184                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10185                    );
10186                }
10187        } else {
10188            panic!("expected completion menu to be open");
10189        }
10190    });
10191}
10192
10193#[gpui::test]
10194async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10195    init_test(cx, |_| {});
10196    let mut cx = EditorLspTestContext::new_rust(
10197        lsp::ServerCapabilities {
10198            completion_provider: Some(lsp::CompletionOptions {
10199                trigger_characters: Some(vec![".".to_string()]),
10200                ..Default::default()
10201            }),
10202            ..Default::default()
10203        },
10204        cx,
10205    )
10206    .await;
10207    cx.lsp
10208        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10209            Ok(Some(lsp::CompletionResponse::Array(vec![
10210                lsp::CompletionItem {
10211                    label: "first".into(),
10212                    ..Default::default()
10213                },
10214                lsp::CompletionItem {
10215                    label: "last".into(),
10216                    ..Default::default()
10217                },
10218            ])))
10219        });
10220    cx.set_state("variableˇ");
10221    cx.simulate_keystroke(".");
10222    cx.executor().run_until_parked();
10223
10224    cx.update_editor(|editor, _, _| {
10225        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10226        {
10227            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
10228        } else {
10229            panic!("expected completion menu to be open");
10230        }
10231    });
10232
10233    cx.update_editor(|editor, window, cx| {
10234        editor.move_page_down(&MovePageDown::default(), window, cx);
10235        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10236        {
10237            assert!(
10238                menu.selected_item == 1,
10239                "expected PageDown to select the last item from the context menu"
10240            );
10241        } else {
10242            panic!("expected completion menu to stay open after PageDown");
10243        }
10244    });
10245
10246    cx.update_editor(|editor, window, cx| {
10247        editor.move_page_up(&MovePageUp::default(), window, cx);
10248        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10249        {
10250            assert!(
10251                menu.selected_item == 0,
10252                "expected PageUp to select the first item from the context menu"
10253            );
10254        } else {
10255            panic!("expected completion menu to stay open after PageUp");
10256        }
10257    });
10258}
10259
10260#[gpui::test]
10261async fn test_completion_sort(cx: &mut TestAppContext) {
10262    init_test(cx, |_| {});
10263    let mut cx = EditorLspTestContext::new_rust(
10264        lsp::ServerCapabilities {
10265            completion_provider: Some(lsp::CompletionOptions {
10266                trigger_characters: Some(vec![".".to_string()]),
10267                ..Default::default()
10268            }),
10269            ..Default::default()
10270        },
10271        cx,
10272    )
10273    .await;
10274    cx.lsp
10275        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10276            Ok(Some(lsp::CompletionResponse::Array(vec![
10277                lsp::CompletionItem {
10278                    label: "Range".into(),
10279                    sort_text: Some("a".into()),
10280                    ..Default::default()
10281                },
10282                lsp::CompletionItem {
10283                    label: "r".into(),
10284                    sort_text: Some("b".into()),
10285                    ..Default::default()
10286                },
10287                lsp::CompletionItem {
10288                    label: "ret".into(),
10289                    sort_text: Some("c".into()),
10290                    ..Default::default()
10291                },
10292                lsp::CompletionItem {
10293                    label: "return".into(),
10294                    sort_text: Some("d".into()),
10295                    ..Default::default()
10296                },
10297                lsp::CompletionItem {
10298                    label: "slice".into(),
10299                    sort_text: Some("d".into()),
10300                    ..Default::default()
10301                },
10302            ])))
10303        });
10304    cx.set_state("");
10305    cx.executor().run_until_parked();
10306    cx.update_editor(|editor, window, cx| {
10307        editor.show_completions(
10308            &ShowCompletions {
10309                trigger: Some("r".into()),
10310            },
10311            window,
10312            cx,
10313        );
10314    });
10315    cx.executor().run_until_parked();
10316
10317    cx.update_editor(|editor, _, _| {
10318        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10319        {
10320            assert_eq!(
10321                completion_menu_entries(&menu),
10322                &["r", "ret", "Range", "return"]
10323            );
10324        } else {
10325            panic!("expected completion menu to be open");
10326        }
10327    });
10328}
10329
10330#[gpui::test]
10331async fn test_as_is_completions(cx: &mut TestAppContext) {
10332    init_test(cx, |_| {});
10333    let mut cx = EditorLspTestContext::new_rust(
10334        lsp::ServerCapabilities {
10335            completion_provider: Some(lsp::CompletionOptions {
10336                ..Default::default()
10337            }),
10338            ..Default::default()
10339        },
10340        cx,
10341    )
10342    .await;
10343    cx.lsp
10344        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10345            Ok(Some(lsp::CompletionResponse::Array(vec![
10346                lsp::CompletionItem {
10347                    label: "unsafe".into(),
10348                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10349                        range: lsp::Range {
10350                            start: lsp::Position {
10351                                line: 1,
10352                                character: 2,
10353                            },
10354                            end: lsp::Position {
10355                                line: 1,
10356                                character: 3,
10357                            },
10358                        },
10359                        new_text: "unsafe".to_string(),
10360                    })),
10361                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
10362                    ..Default::default()
10363                },
10364            ])))
10365        });
10366    cx.set_state("fn a() {}\n");
10367    cx.executor().run_until_parked();
10368    cx.update_editor(|editor, window, cx| {
10369        editor.show_completions(
10370            &ShowCompletions {
10371                trigger: Some("\n".into()),
10372            },
10373            window,
10374            cx,
10375        );
10376    });
10377    cx.executor().run_until_parked();
10378
10379    cx.update_editor(|editor, window, cx| {
10380        editor.confirm_completion(&Default::default(), window, cx)
10381    });
10382    cx.executor().run_until_parked();
10383    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
10384}
10385
10386#[gpui::test]
10387async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
10388    init_test(cx, |_| {});
10389
10390    let mut cx = EditorLspTestContext::new_rust(
10391        lsp::ServerCapabilities {
10392            completion_provider: Some(lsp::CompletionOptions {
10393                trigger_characters: Some(vec![".".to_string()]),
10394                resolve_provider: Some(true),
10395                ..Default::default()
10396            }),
10397            ..Default::default()
10398        },
10399        cx,
10400    )
10401    .await;
10402
10403    cx.set_state("fn main() { let a = 2ˇ; }");
10404    cx.simulate_keystroke(".");
10405    let completion_item = lsp::CompletionItem {
10406        label: "Some".into(),
10407        kind: Some(lsp::CompletionItemKind::SNIPPET),
10408        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
10409        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
10410            kind: lsp::MarkupKind::Markdown,
10411            value: "```rust\nSome(2)\n```".to_string(),
10412        })),
10413        deprecated: Some(false),
10414        sort_text: Some("Some".to_string()),
10415        filter_text: Some("Some".to_string()),
10416        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
10417        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10418            range: lsp::Range {
10419                start: lsp::Position {
10420                    line: 0,
10421                    character: 22,
10422                },
10423                end: lsp::Position {
10424                    line: 0,
10425                    character: 22,
10426                },
10427            },
10428            new_text: "Some(2)".to_string(),
10429        })),
10430        additional_text_edits: Some(vec![lsp::TextEdit {
10431            range: lsp::Range {
10432                start: lsp::Position {
10433                    line: 0,
10434                    character: 20,
10435                },
10436                end: lsp::Position {
10437                    line: 0,
10438                    character: 22,
10439                },
10440            },
10441            new_text: "".to_string(),
10442        }]),
10443        ..Default::default()
10444    };
10445
10446    let closure_completion_item = completion_item.clone();
10447    let counter = Arc::new(AtomicUsize::new(0));
10448    let counter_clone = counter.clone();
10449    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
10450        let task_completion_item = closure_completion_item.clone();
10451        counter_clone.fetch_add(1, atomic::Ordering::Release);
10452        async move {
10453            Ok(Some(lsp::CompletionResponse::Array(vec![
10454                task_completion_item,
10455            ])))
10456        }
10457    });
10458
10459    cx.condition(|editor, _| editor.context_menu_visible())
10460        .await;
10461    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
10462    assert!(request.next().await.is_some());
10463    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10464
10465    cx.simulate_keystrokes("S o m");
10466    cx.condition(|editor, _| editor.context_menu_visible())
10467        .await;
10468    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
10469    assert!(request.next().await.is_some());
10470    assert!(request.next().await.is_some());
10471    assert!(request.next().await.is_some());
10472    request.close();
10473    assert!(request.next().await.is_none());
10474    assert_eq!(
10475        counter.load(atomic::Ordering::Acquire),
10476        4,
10477        "With the completions menu open, only one LSP request should happen per input"
10478    );
10479}
10480
10481#[gpui::test]
10482async fn test_toggle_comment(cx: &mut TestAppContext) {
10483    init_test(cx, |_| {});
10484    let mut cx = EditorTestContext::new(cx).await;
10485    let language = Arc::new(Language::new(
10486        LanguageConfig {
10487            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10488            ..Default::default()
10489        },
10490        Some(tree_sitter_rust::LANGUAGE.into()),
10491    ));
10492    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10493
10494    // If multiple selections intersect a line, the line is only toggled once.
10495    cx.set_state(indoc! {"
10496        fn a() {
10497            «//b();
10498            ˇ»// «c();
10499            //ˇ»  d();
10500        }
10501    "});
10502
10503    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10504
10505    cx.assert_editor_state(indoc! {"
10506        fn a() {
10507            «b();
10508            c();
10509            ˇ» d();
10510        }
10511    "});
10512
10513    // The comment prefix is inserted at the same column for every line in a
10514    // selection.
10515    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10516
10517    cx.assert_editor_state(indoc! {"
10518        fn a() {
10519            // «b();
10520            // c();
10521            ˇ»//  d();
10522        }
10523    "});
10524
10525    // If a selection ends at the beginning of a line, that line is not toggled.
10526    cx.set_selections_state(indoc! {"
10527        fn a() {
10528            // b();
10529            «// c();
10530        ˇ»    //  d();
10531        }
10532    "});
10533
10534    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10535
10536    cx.assert_editor_state(indoc! {"
10537        fn a() {
10538            // b();
10539            «c();
10540        ˇ»    //  d();
10541        }
10542    "});
10543
10544    // If a selection span a single line and is empty, the line is toggled.
10545    cx.set_state(indoc! {"
10546        fn a() {
10547            a();
10548            b();
10549        ˇ
10550        }
10551    "});
10552
10553    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10554
10555    cx.assert_editor_state(indoc! {"
10556        fn a() {
10557            a();
10558            b();
10559        //•ˇ
10560        }
10561    "});
10562
10563    // If a selection span multiple lines, empty lines are not toggled.
10564    cx.set_state(indoc! {"
10565        fn a() {
10566            «a();
10567
10568            c();ˇ»
10569        }
10570    "});
10571
10572    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10573
10574    cx.assert_editor_state(indoc! {"
10575        fn a() {
10576            // «a();
10577
10578            // c();ˇ»
10579        }
10580    "});
10581
10582    // If a selection includes multiple comment prefixes, all lines are uncommented.
10583    cx.set_state(indoc! {"
10584        fn a() {
10585            «// a();
10586            /// b();
10587            //! c();ˇ»
10588        }
10589    "});
10590
10591    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
10592
10593    cx.assert_editor_state(indoc! {"
10594        fn a() {
10595            «a();
10596            b();
10597            c();ˇ»
10598        }
10599    "});
10600}
10601
10602#[gpui::test]
10603async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
10604    init_test(cx, |_| {});
10605    let mut cx = EditorTestContext::new(cx).await;
10606    let language = Arc::new(Language::new(
10607        LanguageConfig {
10608            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
10609            ..Default::default()
10610        },
10611        Some(tree_sitter_rust::LANGUAGE.into()),
10612    ));
10613    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
10614
10615    let toggle_comments = &ToggleComments {
10616        advance_downwards: false,
10617        ignore_indent: true,
10618    };
10619
10620    // If multiple selections intersect a line, the line is only toggled once.
10621    cx.set_state(indoc! {"
10622        fn a() {
10623        //    «b();
10624        //    c();
10625        //    ˇ» d();
10626        }
10627    "});
10628
10629    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10630
10631    cx.assert_editor_state(indoc! {"
10632        fn a() {
10633            «b();
10634            c();
10635            ˇ» d();
10636        }
10637    "});
10638
10639    // The comment prefix is inserted at the beginning of each line
10640    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10641
10642    cx.assert_editor_state(indoc! {"
10643        fn a() {
10644        //    «b();
10645        //    c();
10646        //    ˇ» d();
10647        }
10648    "});
10649
10650    // If a selection ends at the beginning of a line, that line is not toggled.
10651    cx.set_selections_state(indoc! {"
10652        fn a() {
10653        //    b();
10654        //    «c();
10655        ˇ»//     d();
10656        }
10657    "});
10658
10659    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10660
10661    cx.assert_editor_state(indoc! {"
10662        fn a() {
10663        //    b();
10664            «c();
10665        ˇ»//     d();
10666        }
10667    "});
10668
10669    // If a selection span a single line and is empty, the line is toggled.
10670    cx.set_state(indoc! {"
10671        fn a() {
10672            a();
10673            b();
10674        ˇ
10675        }
10676    "});
10677
10678    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10679
10680    cx.assert_editor_state(indoc! {"
10681        fn a() {
10682            a();
10683            b();
10684        //ˇ
10685        }
10686    "});
10687
10688    // If a selection span multiple lines, empty lines are not toggled.
10689    cx.set_state(indoc! {"
10690        fn a() {
10691            «a();
10692
10693            c();ˇ»
10694        }
10695    "});
10696
10697    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10698
10699    cx.assert_editor_state(indoc! {"
10700        fn a() {
10701        //    «a();
10702
10703        //    c();ˇ»
10704        }
10705    "});
10706
10707    // If a selection includes multiple comment prefixes, all lines are uncommented.
10708    cx.set_state(indoc! {"
10709        fn a() {
10710        //    «a();
10711        ///    b();
10712        //!    c();ˇ»
10713        }
10714    "});
10715
10716    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
10717
10718    cx.assert_editor_state(indoc! {"
10719        fn a() {
10720            «a();
10721            b();
10722            c();ˇ»
10723        }
10724    "});
10725}
10726
10727#[gpui::test]
10728async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
10729    init_test(cx, |_| {});
10730
10731    let language = Arc::new(Language::new(
10732        LanguageConfig {
10733            line_comments: vec!["// ".into()],
10734            ..Default::default()
10735        },
10736        Some(tree_sitter_rust::LANGUAGE.into()),
10737    ));
10738
10739    let mut cx = EditorTestContext::new(cx).await;
10740
10741    cx.language_registry().add(language.clone());
10742    cx.update_buffer(|buffer, cx| {
10743        buffer.set_language(Some(language), cx);
10744    });
10745
10746    let toggle_comments = &ToggleComments {
10747        advance_downwards: true,
10748        ignore_indent: false,
10749    };
10750
10751    // Single cursor on one line -> advance
10752    // Cursor moves horizontally 3 characters as well on non-blank line
10753    cx.set_state(indoc!(
10754        "fn a() {
10755             ˇdog();
10756             cat();
10757        }"
10758    ));
10759    cx.update_editor(|editor, window, cx| {
10760        editor.toggle_comments(toggle_comments, window, cx);
10761    });
10762    cx.assert_editor_state(indoc!(
10763        "fn a() {
10764             // dog();
10765             catˇ();
10766        }"
10767    ));
10768
10769    // Single selection on one line -> don't advance
10770    cx.set_state(indoc!(
10771        "fn a() {
10772             «dog()ˇ»;
10773             cat();
10774        }"
10775    ));
10776    cx.update_editor(|editor, window, cx| {
10777        editor.toggle_comments(toggle_comments, window, cx);
10778    });
10779    cx.assert_editor_state(indoc!(
10780        "fn a() {
10781             // «dog()ˇ»;
10782             cat();
10783        }"
10784    ));
10785
10786    // Multiple cursors on one line -> advance
10787    cx.set_state(indoc!(
10788        "fn a() {
10789             ˇdˇog();
10790             cat();
10791        }"
10792    ));
10793    cx.update_editor(|editor, window, cx| {
10794        editor.toggle_comments(toggle_comments, window, cx);
10795    });
10796    cx.assert_editor_state(indoc!(
10797        "fn a() {
10798             // dog();
10799             catˇ(ˇ);
10800        }"
10801    ));
10802
10803    // Multiple cursors on one line, with selection -> don't advance
10804    cx.set_state(indoc!(
10805        "fn a() {
10806             ˇdˇog«()ˇ»;
10807             cat();
10808        }"
10809    ));
10810    cx.update_editor(|editor, window, cx| {
10811        editor.toggle_comments(toggle_comments, window, cx);
10812    });
10813    cx.assert_editor_state(indoc!(
10814        "fn a() {
10815             // ˇdˇog«()ˇ»;
10816             cat();
10817        }"
10818    ));
10819
10820    // Single cursor on one line -> advance
10821    // Cursor moves to column 0 on blank line
10822    cx.set_state(indoc!(
10823        "fn a() {
10824             ˇdog();
10825
10826             cat();
10827        }"
10828    ));
10829    cx.update_editor(|editor, window, cx| {
10830        editor.toggle_comments(toggle_comments, window, cx);
10831    });
10832    cx.assert_editor_state(indoc!(
10833        "fn a() {
10834             // dog();
10835        ˇ
10836             cat();
10837        }"
10838    ));
10839
10840    // Single cursor on one line -> advance
10841    // Cursor starts and ends at column 0
10842    cx.set_state(indoc!(
10843        "fn a() {
10844         ˇ    dog();
10845             cat();
10846        }"
10847    ));
10848    cx.update_editor(|editor, window, cx| {
10849        editor.toggle_comments(toggle_comments, window, cx);
10850    });
10851    cx.assert_editor_state(indoc!(
10852        "fn a() {
10853             // dog();
10854         ˇ    cat();
10855        }"
10856    ));
10857}
10858
10859#[gpui::test]
10860async fn test_toggle_block_comment(cx: &mut TestAppContext) {
10861    init_test(cx, |_| {});
10862
10863    let mut cx = EditorTestContext::new(cx).await;
10864
10865    let html_language = Arc::new(
10866        Language::new(
10867            LanguageConfig {
10868                name: "HTML".into(),
10869                block_comment: Some(("<!-- ".into(), " -->".into())),
10870                ..Default::default()
10871            },
10872            Some(tree_sitter_html::LANGUAGE.into()),
10873        )
10874        .with_injection_query(
10875            r#"
10876            (script_element
10877                (raw_text) @injection.content
10878                (#set! injection.language "javascript"))
10879            "#,
10880        )
10881        .unwrap(),
10882    );
10883
10884    let javascript_language = Arc::new(Language::new(
10885        LanguageConfig {
10886            name: "JavaScript".into(),
10887            line_comments: vec!["// ".into()],
10888            ..Default::default()
10889        },
10890        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
10891    ));
10892
10893    cx.language_registry().add(html_language.clone());
10894    cx.language_registry().add(javascript_language.clone());
10895    cx.update_buffer(|buffer, cx| {
10896        buffer.set_language(Some(html_language), cx);
10897    });
10898
10899    // Toggle comments for empty selections
10900    cx.set_state(
10901        &r#"
10902            <p>A</p>ˇ
10903            <p>B</p>ˇ
10904            <p>C</p>ˇ
10905        "#
10906        .unindent(),
10907    );
10908    cx.update_editor(|editor, window, cx| {
10909        editor.toggle_comments(&ToggleComments::default(), window, cx)
10910    });
10911    cx.assert_editor_state(
10912        &r#"
10913            <!-- <p>A</p>ˇ -->
10914            <!-- <p>B</p>ˇ -->
10915            <!-- <p>C</p>ˇ -->
10916        "#
10917        .unindent(),
10918    );
10919    cx.update_editor(|editor, window, cx| {
10920        editor.toggle_comments(&ToggleComments::default(), window, cx)
10921    });
10922    cx.assert_editor_state(
10923        &r#"
10924            <p>A</p>ˇ
10925            <p>B</p>ˇ
10926            <p>C</p>ˇ
10927        "#
10928        .unindent(),
10929    );
10930
10931    // Toggle comments for mixture of empty and non-empty selections, where
10932    // multiple selections occupy a given line.
10933    cx.set_state(
10934        &r#"
10935            <p>A«</p>
10936            <p>ˇ»B</p>ˇ
10937            <p>C«</p>
10938            <p>ˇ»D</p>ˇ
10939        "#
10940        .unindent(),
10941    );
10942
10943    cx.update_editor(|editor, window, cx| {
10944        editor.toggle_comments(&ToggleComments::default(), window, cx)
10945    });
10946    cx.assert_editor_state(
10947        &r#"
10948            <!-- <p>A«</p>
10949            <p>ˇ»B</p>ˇ -->
10950            <!-- <p>C«</p>
10951            <p>ˇ»D</p>ˇ -->
10952        "#
10953        .unindent(),
10954    );
10955    cx.update_editor(|editor, window, cx| {
10956        editor.toggle_comments(&ToggleComments::default(), window, cx)
10957    });
10958    cx.assert_editor_state(
10959        &r#"
10960            <p>A«</p>
10961            <p>ˇ»B</p>ˇ
10962            <p>C«</p>
10963            <p>ˇ»D</p>ˇ
10964        "#
10965        .unindent(),
10966    );
10967
10968    // Toggle comments when different languages are active for different
10969    // selections.
10970    cx.set_state(
10971        &r#"
10972            ˇ<script>
10973                ˇvar x = new Y();
10974            ˇ</script>
10975        "#
10976        .unindent(),
10977    );
10978    cx.executor().run_until_parked();
10979    cx.update_editor(|editor, window, cx| {
10980        editor.toggle_comments(&ToggleComments::default(), window, cx)
10981    });
10982    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
10983    // Uncommenting and commenting from this position brings in even more wrong artifacts.
10984    cx.assert_editor_state(
10985        &r#"
10986            <!-- ˇ<script> -->
10987                // ˇvar x = new Y();
10988            <!-- ˇ</script> -->
10989        "#
10990        .unindent(),
10991    );
10992}
10993
10994#[gpui::test]
10995fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
10996    init_test(cx, |_| {});
10997
10998    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
10999    let multibuffer = cx.new(|cx| {
11000        let mut multibuffer = MultiBuffer::new(ReadWrite);
11001        multibuffer.push_excerpts(
11002            buffer.clone(),
11003            [
11004                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11005                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11006            ],
11007            cx,
11008        );
11009        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11010        multibuffer
11011    });
11012
11013    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11014    editor.update_in(cx, |editor, window, cx| {
11015        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11016        editor.change_selections(None, window, cx, |s| {
11017            s.select_ranges([
11018                Point::new(0, 0)..Point::new(0, 0),
11019                Point::new(1, 0)..Point::new(1, 0),
11020            ])
11021        });
11022
11023        editor.handle_input("X", window, cx);
11024        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11025        assert_eq!(
11026            editor.selections.ranges(cx),
11027            [
11028                Point::new(0, 1)..Point::new(0, 1),
11029                Point::new(1, 1)..Point::new(1, 1),
11030            ]
11031        );
11032
11033        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11034        editor.change_selections(None, window, cx, |s| {
11035            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11036        });
11037        editor.backspace(&Default::default(), window, cx);
11038        assert_eq!(editor.text(cx), "Xa\nbbb");
11039        assert_eq!(
11040            editor.selections.ranges(cx),
11041            [Point::new(1, 0)..Point::new(1, 0)]
11042        );
11043
11044        editor.change_selections(None, window, cx, |s| {
11045            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11046        });
11047        editor.backspace(&Default::default(), window, cx);
11048        assert_eq!(editor.text(cx), "X\nbb");
11049        assert_eq!(
11050            editor.selections.ranges(cx),
11051            [Point::new(0, 1)..Point::new(0, 1)]
11052        );
11053    });
11054}
11055
11056#[gpui::test]
11057fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11058    init_test(cx, |_| {});
11059
11060    let markers = vec![('[', ']').into(), ('(', ')').into()];
11061    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11062        indoc! {"
11063            [aaaa
11064            (bbbb]
11065            cccc)",
11066        },
11067        markers.clone(),
11068    );
11069    let excerpt_ranges = markers.into_iter().map(|marker| {
11070        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11071        ExcerptRange::new(context.clone())
11072    });
11073    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11074    let multibuffer = cx.new(|cx| {
11075        let mut multibuffer = MultiBuffer::new(ReadWrite);
11076        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11077        multibuffer
11078    });
11079
11080    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11081    editor.update_in(cx, |editor, window, cx| {
11082        let (expected_text, selection_ranges) = marked_text_ranges(
11083            indoc! {"
11084                aaaa
11085                bˇbbb
11086                bˇbbˇb
11087                cccc"
11088            },
11089            true,
11090        );
11091        assert_eq!(editor.text(cx), expected_text);
11092        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11093
11094        editor.handle_input("X", window, cx);
11095
11096        let (expected_text, expected_selections) = marked_text_ranges(
11097            indoc! {"
11098                aaaa
11099                bXˇbbXb
11100                bXˇbbXˇb
11101                cccc"
11102            },
11103            false,
11104        );
11105        assert_eq!(editor.text(cx), expected_text);
11106        assert_eq!(editor.selections.ranges(cx), expected_selections);
11107
11108        editor.newline(&Newline, window, cx);
11109        let (expected_text, expected_selections) = marked_text_ranges(
11110            indoc! {"
11111                aaaa
11112                bX
11113                ˇbbX
11114                b
11115                bX
11116                ˇbbX
11117                ˇb
11118                cccc"
11119            },
11120            false,
11121        );
11122        assert_eq!(editor.text(cx), expected_text);
11123        assert_eq!(editor.selections.ranges(cx), expected_selections);
11124    });
11125}
11126
11127#[gpui::test]
11128fn test_refresh_selections(cx: &mut TestAppContext) {
11129    init_test(cx, |_| {});
11130
11131    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11132    let mut excerpt1_id = None;
11133    let multibuffer = cx.new(|cx| {
11134        let mut multibuffer = MultiBuffer::new(ReadWrite);
11135        excerpt1_id = multibuffer
11136            .push_excerpts(
11137                buffer.clone(),
11138                [
11139                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11140                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11141                ],
11142                cx,
11143            )
11144            .into_iter()
11145            .next();
11146        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11147        multibuffer
11148    });
11149
11150    let editor = cx.add_window(|window, cx| {
11151        let mut editor = build_editor(multibuffer.clone(), window, cx);
11152        let snapshot = editor.snapshot(window, cx);
11153        editor.change_selections(None, window, cx, |s| {
11154            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11155        });
11156        editor.begin_selection(
11157            Point::new(2, 1).to_display_point(&snapshot),
11158            true,
11159            1,
11160            window,
11161            cx,
11162        );
11163        assert_eq!(
11164            editor.selections.ranges(cx),
11165            [
11166                Point::new(1, 3)..Point::new(1, 3),
11167                Point::new(2, 1)..Point::new(2, 1),
11168            ]
11169        );
11170        editor
11171    });
11172
11173    // Refreshing selections is a no-op when excerpts haven't changed.
11174    _ = editor.update(cx, |editor, window, cx| {
11175        editor.change_selections(None, window, cx, |s| s.refresh());
11176        assert_eq!(
11177            editor.selections.ranges(cx),
11178            [
11179                Point::new(1, 3)..Point::new(1, 3),
11180                Point::new(2, 1)..Point::new(2, 1),
11181            ]
11182        );
11183    });
11184
11185    multibuffer.update(cx, |multibuffer, cx| {
11186        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11187    });
11188    _ = editor.update(cx, |editor, window, cx| {
11189        // Removing an excerpt causes the first selection to become degenerate.
11190        assert_eq!(
11191            editor.selections.ranges(cx),
11192            [
11193                Point::new(0, 0)..Point::new(0, 0),
11194                Point::new(0, 1)..Point::new(0, 1)
11195            ]
11196        );
11197
11198        // Refreshing selections will relocate the first selection to the original buffer
11199        // location.
11200        editor.change_selections(None, window, cx, |s| s.refresh());
11201        assert_eq!(
11202            editor.selections.ranges(cx),
11203            [
11204                Point::new(0, 1)..Point::new(0, 1),
11205                Point::new(0, 3)..Point::new(0, 3)
11206            ]
11207        );
11208        assert!(editor.selections.pending_anchor().is_some());
11209    });
11210}
11211
11212#[gpui::test]
11213fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
11214    init_test(cx, |_| {});
11215
11216    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11217    let mut excerpt1_id = None;
11218    let multibuffer = cx.new(|cx| {
11219        let mut multibuffer = MultiBuffer::new(ReadWrite);
11220        excerpt1_id = multibuffer
11221            .push_excerpts(
11222                buffer.clone(),
11223                [
11224                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11225                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11226                ],
11227                cx,
11228            )
11229            .into_iter()
11230            .next();
11231        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11232        multibuffer
11233    });
11234
11235    let editor = cx.add_window(|window, cx| {
11236        let mut editor = build_editor(multibuffer.clone(), window, cx);
11237        let snapshot = editor.snapshot(window, cx);
11238        editor.begin_selection(
11239            Point::new(1, 3).to_display_point(&snapshot),
11240            false,
11241            1,
11242            window,
11243            cx,
11244        );
11245        assert_eq!(
11246            editor.selections.ranges(cx),
11247            [Point::new(1, 3)..Point::new(1, 3)]
11248        );
11249        editor
11250    });
11251
11252    multibuffer.update(cx, |multibuffer, cx| {
11253        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11254    });
11255    _ = editor.update(cx, |editor, window, cx| {
11256        assert_eq!(
11257            editor.selections.ranges(cx),
11258            [Point::new(0, 0)..Point::new(0, 0)]
11259        );
11260
11261        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
11262        editor.change_selections(None, window, cx, |s| s.refresh());
11263        assert_eq!(
11264            editor.selections.ranges(cx),
11265            [Point::new(0, 3)..Point::new(0, 3)]
11266        );
11267        assert!(editor.selections.pending_anchor().is_some());
11268    });
11269}
11270
11271#[gpui::test]
11272async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
11273    init_test(cx, |_| {});
11274
11275    let language = Arc::new(
11276        Language::new(
11277            LanguageConfig {
11278                brackets: BracketPairConfig {
11279                    pairs: vec![
11280                        BracketPair {
11281                            start: "{".to_string(),
11282                            end: "}".to_string(),
11283                            close: true,
11284                            surround: true,
11285                            newline: true,
11286                        },
11287                        BracketPair {
11288                            start: "/* ".to_string(),
11289                            end: " */".to_string(),
11290                            close: true,
11291                            surround: true,
11292                            newline: true,
11293                        },
11294                    ],
11295                    ..Default::default()
11296                },
11297                ..Default::default()
11298            },
11299            Some(tree_sitter_rust::LANGUAGE.into()),
11300        )
11301        .with_indents_query("")
11302        .unwrap(),
11303    );
11304
11305    let text = concat!(
11306        "{   }\n",     //
11307        "  x\n",       //
11308        "  /*   */\n", //
11309        "x\n",         //
11310        "{{} }\n",     //
11311    );
11312
11313    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
11314    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
11315    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
11316    editor
11317        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
11318        .await;
11319
11320    editor.update_in(cx, |editor, window, cx| {
11321        editor.change_selections(None, window, cx, |s| {
11322            s.select_display_ranges([
11323                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
11324                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
11325                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
11326            ])
11327        });
11328        editor.newline(&Newline, window, cx);
11329
11330        assert_eq!(
11331            editor.buffer().read(cx).read(cx).text(),
11332            concat!(
11333                "{ \n",    // Suppress rustfmt
11334                "\n",      //
11335                "}\n",     //
11336                "  x\n",   //
11337                "  /* \n", //
11338                "  \n",    //
11339                "  */\n",  //
11340                "x\n",     //
11341                "{{} \n",  //
11342                "}\n",     //
11343            )
11344        );
11345    });
11346}
11347
11348#[gpui::test]
11349fn test_highlighted_ranges(cx: &mut TestAppContext) {
11350    init_test(cx, |_| {});
11351
11352    let editor = cx.add_window(|window, cx| {
11353        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
11354        build_editor(buffer.clone(), window, cx)
11355    });
11356
11357    _ = editor.update(cx, |editor, window, cx| {
11358        struct Type1;
11359        struct Type2;
11360
11361        let buffer = editor.buffer.read(cx).snapshot(cx);
11362
11363        let anchor_range =
11364            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
11365
11366        editor.highlight_background::<Type1>(
11367            &[
11368                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
11369                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
11370                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
11371                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
11372            ],
11373            |_| Hsla::red(),
11374            cx,
11375        );
11376        editor.highlight_background::<Type2>(
11377            &[
11378                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
11379                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
11380                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
11381                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
11382            ],
11383            |_| Hsla::green(),
11384            cx,
11385        );
11386
11387        let snapshot = editor.snapshot(window, cx);
11388        let mut highlighted_ranges = editor.background_highlights_in_range(
11389            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
11390            &snapshot,
11391            cx.theme().colors(),
11392        );
11393        // Enforce a consistent ordering based on color without relying on the ordering of the
11394        // highlight's `TypeId` which is non-executor.
11395        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
11396        assert_eq!(
11397            highlighted_ranges,
11398            &[
11399                (
11400                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
11401                    Hsla::red(),
11402                ),
11403                (
11404                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11405                    Hsla::red(),
11406                ),
11407                (
11408                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
11409                    Hsla::green(),
11410                ),
11411                (
11412                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
11413                    Hsla::green(),
11414                ),
11415            ]
11416        );
11417        assert_eq!(
11418            editor.background_highlights_in_range(
11419                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
11420                &snapshot,
11421                cx.theme().colors(),
11422            ),
11423            &[(
11424                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
11425                Hsla::red(),
11426            )]
11427        );
11428    });
11429}
11430
11431#[gpui::test]
11432async fn test_following(cx: &mut TestAppContext) {
11433    init_test(cx, |_| {});
11434
11435    let fs = FakeFs::new(cx.executor());
11436    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11437
11438    let buffer = project.update(cx, |project, cx| {
11439        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
11440        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
11441    });
11442    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
11443    let follower = cx.update(|cx| {
11444        cx.open_window(
11445            WindowOptions {
11446                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
11447                    gpui::Point::new(px(0.), px(0.)),
11448                    gpui::Point::new(px(10.), px(80.)),
11449                ))),
11450                ..Default::default()
11451            },
11452            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
11453        )
11454        .unwrap()
11455    });
11456
11457    let is_still_following = Rc::new(RefCell::new(true));
11458    let follower_edit_event_count = Rc::new(RefCell::new(0));
11459    let pending_update = Rc::new(RefCell::new(None));
11460    let leader_entity = leader.root(cx).unwrap();
11461    let follower_entity = follower.root(cx).unwrap();
11462    _ = follower.update(cx, {
11463        let update = pending_update.clone();
11464        let is_still_following = is_still_following.clone();
11465        let follower_edit_event_count = follower_edit_event_count.clone();
11466        |_, window, cx| {
11467            cx.subscribe_in(
11468                &leader_entity,
11469                window,
11470                move |_, leader, event, window, cx| {
11471                    leader.read(cx).add_event_to_update_proto(
11472                        event,
11473                        &mut update.borrow_mut(),
11474                        window,
11475                        cx,
11476                    );
11477                },
11478            )
11479            .detach();
11480
11481            cx.subscribe_in(
11482                &follower_entity,
11483                window,
11484                move |_, _, event: &EditorEvent, _window, _cx| {
11485                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
11486                        *is_still_following.borrow_mut() = false;
11487                    }
11488
11489                    if let EditorEvent::BufferEdited = event {
11490                        *follower_edit_event_count.borrow_mut() += 1;
11491                    }
11492                },
11493            )
11494            .detach();
11495        }
11496    });
11497
11498    // Update the selections only
11499    _ = leader.update(cx, |leader, window, cx| {
11500        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11501    });
11502    follower
11503        .update(cx, |follower, window, cx| {
11504            follower.apply_update_proto(
11505                &project,
11506                pending_update.borrow_mut().take().unwrap(),
11507                window,
11508                cx,
11509            )
11510        })
11511        .unwrap()
11512        .await
11513        .unwrap();
11514    _ = follower.update(cx, |follower, _, cx| {
11515        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
11516    });
11517    assert!(*is_still_following.borrow());
11518    assert_eq!(*follower_edit_event_count.borrow(), 0);
11519
11520    // Update the scroll position only
11521    _ = leader.update(cx, |leader, window, cx| {
11522        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11523    });
11524    follower
11525        .update(cx, |follower, window, cx| {
11526            follower.apply_update_proto(
11527                &project,
11528                pending_update.borrow_mut().take().unwrap(),
11529                window,
11530                cx,
11531            )
11532        })
11533        .unwrap()
11534        .await
11535        .unwrap();
11536    assert_eq!(
11537        follower
11538            .update(cx, |follower, _, cx| follower.scroll_position(cx))
11539            .unwrap(),
11540        gpui::Point::new(1.5, 3.5)
11541    );
11542    assert!(*is_still_following.borrow());
11543    assert_eq!(*follower_edit_event_count.borrow(), 0);
11544
11545    // Update the selections and scroll position. The follower's scroll position is updated
11546    // via autoscroll, not via the leader's exact scroll position.
11547    _ = leader.update(cx, |leader, window, cx| {
11548        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
11549        leader.request_autoscroll(Autoscroll::newest(), cx);
11550        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
11551    });
11552    follower
11553        .update(cx, |follower, window, cx| {
11554            follower.apply_update_proto(
11555                &project,
11556                pending_update.borrow_mut().take().unwrap(),
11557                window,
11558                cx,
11559            )
11560        })
11561        .unwrap()
11562        .await
11563        .unwrap();
11564    _ = follower.update(cx, |follower, _, cx| {
11565        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
11566        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
11567    });
11568    assert!(*is_still_following.borrow());
11569
11570    // Creating a pending selection that precedes another selection
11571    _ = leader.update(cx, |leader, window, cx| {
11572        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
11573        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
11574    });
11575    follower
11576        .update(cx, |follower, window, cx| {
11577            follower.apply_update_proto(
11578                &project,
11579                pending_update.borrow_mut().take().unwrap(),
11580                window,
11581                cx,
11582            )
11583        })
11584        .unwrap()
11585        .await
11586        .unwrap();
11587    _ = follower.update(cx, |follower, _, cx| {
11588        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
11589    });
11590    assert!(*is_still_following.borrow());
11591
11592    // Extend the pending selection so that it surrounds another selection
11593    _ = leader.update(cx, |leader, window, cx| {
11594        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
11595    });
11596    follower
11597        .update(cx, |follower, window, cx| {
11598            follower.apply_update_proto(
11599                &project,
11600                pending_update.borrow_mut().take().unwrap(),
11601                window,
11602                cx,
11603            )
11604        })
11605        .unwrap()
11606        .await
11607        .unwrap();
11608    _ = follower.update(cx, |follower, _, cx| {
11609        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
11610    });
11611
11612    // Scrolling locally breaks the follow
11613    _ = follower.update(cx, |follower, window, cx| {
11614        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
11615        follower.set_scroll_anchor(
11616            ScrollAnchor {
11617                anchor: top_anchor,
11618                offset: gpui::Point::new(0.0, 0.5),
11619            },
11620            window,
11621            cx,
11622        );
11623    });
11624    assert!(!(*is_still_following.borrow()));
11625}
11626
11627#[gpui::test]
11628async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
11629    init_test(cx, |_| {});
11630
11631    let fs = FakeFs::new(cx.executor());
11632    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
11633    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
11634    let pane = workspace
11635        .update(cx, |workspace, _, _| workspace.active_pane().clone())
11636        .unwrap();
11637
11638    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
11639
11640    let leader = pane.update_in(cx, |_, window, cx| {
11641        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
11642        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
11643    });
11644
11645    // Start following the editor when it has no excerpts.
11646    let mut state_message =
11647        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11648    let workspace_entity = workspace.root(cx).unwrap();
11649    let follower_1 = cx
11650        .update_window(*workspace.deref(), |_, window, cx| {
11651            Editor::from_state_proto(
11652                workspace_entity,
11653                ViewId {
11654                    creator: Default::default(),
11655                    id: 0,
11656                },
11657                &mut state_message,
11658                window,
11659                cx,
11660            )
11661        })
11662        .unwrap()
11663        .unwrap()
11664        .await
11665        .unwrap();
11666
11667    let update_message = Rc::new(RefCell::new(None));
11668    follower_1.update_in(cx, {
11669        let update = update_message.clone();
11670        |_, window, cx| {
11671            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
11672                leader.read(cx).add_event_to_update_proto(
11673                    event,
11674                    &mut update.borrow_mut(),
11675                    window,
11676                    cx,
11677                );
11678            })
11679            .detach();
11680        }
11681    });
11682
11683    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
11684        (
11685            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
11686            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
11687        )
11688    });
11689
11690    // Insert some excerpts.
11691    leader.update(cx, |leader, cx| {
11692        leader.buffer.update(cx, |multibuffer, cx| {
11693            let excerpt_ids = multibuffer.push_excerpts(
11694                buffer_1.clone(),
11695                [
11696                    ExcerptRange::new(1..6),
11697                    ExcerptRange::new(12..15),
11698                    ExcerptRange::new(0..3),
11699                ],
11700                cx,
11701            );
11702            multibuffer.insert_excerpts_after(
11703                excerpt_ids[0],
11704                buffer_2.clone(),
11705                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
11706                cx,
11707            );
11708        });
11709    });
11710
11711    // Apply the update of adding the excerpts.
11712    follower_1
11713        .update_in(cx, |follower, window, cx| {
11714            follower.apply_update_proto(
11715                &project,
11716                update_message.borrow().clone().unwrap(),
11717                window,
11718                cx,
11719            )
11720        })
11721        .await
11722        .unwrap();
11723    assert_eq!(
11724        follower_1.update(cx, |editor, cx| editor.text(cx)),
11725        leader.update(cx, |editor, cx| editor.text(cx))
11726    );
11727    update_message.borrow_mut().take();
11728
11729    // Start following separately after it already has excerpts.
11730    let mut state_message =
11731        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
11732    let workspace_entity = workspace.root(cx).unwrap();
11733    let follower_2 = cx
11734        .update_window(*workspace.deref(), |_, window, cx| {
11735            Editor::from_state_proto(
11736                workspace_entity,
11737                ViewId {
11738                    creator: Default::default(),
11739                    id: 0,
11740                },
11741                &mut state_message,
11742                window,
11743                cx,
11744            )
11745        })
11746        .unwrap()
11747        .unwrap()
11748        .await
11749        .unwrap();
11750    assert_eq!(
11751        follower_2.update(cx, |editor, cx| editor.text(cx)),
11752        leader.update(cx, |editor, cx| editor.text(cx))
11753    );
11754
11755    // Remove some excerpts.
11756    leader.update(cx, |leader, cx| {
11757        leader.buffer.update(cx, |multibuffer, cx| {
11758            let excerpt_ids = multibuffer.excerpt_ids();
11759            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
11760            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
11761        });
11762    });
11763
11764    // Apply the update of removing the excerpts.
11765    follower_1
11766        .update_in(cx, |follower, window, cx| {
11767            follower.apply_update_proto(
11768                &project,
11769                update_message.borrow().clone().unwrap(),
11770                window,
11771                cx,
11772            )
11773        })
11774        .await
11775        .unwrap();
11776    follower_2
11777        .update_in(cx, |follower, window, cx| {
11778            follower.apply_update_proto(
11779                &project,
11780                update_message.borrow().clone().unwrap(),
11781                window,
11782                cx,
11783            )
11784        })
11785        .await
11786        .unwrap();
11787    update_message.borrow_mut().take();
11788    assert_eq!(
11789        follower_1.update(cx, |editor, cx| editor.text(cx)),
11790        leader.update(cx, |editor, cx| editor.text(cx))
11791    );
11792}
11793
11794#[gpui::test]
11795async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
11796    init_test(cx, |_| {});
11797
11798    let mut cx = EditorTestContext::new(cx).await;
11799    let lsp_store =
11800        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11801
11802    cx.set_state(indoc! {"
11803        ˇfn func(abc def: i32) -> u32 {
11804        }
11805    "});
11806
11807    cx.update(|_, cx| {
11808        lsp_store.update(cx, |lsp_store, cx| {
11809            lsp_store
11810                .update_diagnostics(
11811                    LanguageServerId(0),
11812                    lsp::PublishDiagnosticsParams {
11813                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11814                        version: None,
11815                        diagnostics: vec![
11816                            lsp::Diagnostic {
11817                                range: lsp::Range::new(
11818                                    lsp::Position::new(0, 11),
11819                                    lsp::Position::new(0, 12),
11820                                ),
11821                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11822                                ..Default::default()
11823                            },
11824                            lsp::Diagnostic {
11825                                range: lsp::Range::new(
11826                                    lsp::Position::new(0, 12),
11827                                    lsp::Position::new(0, 15),
11828                                ),
11829                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11830                                ..Default::default()
11831                            },
11832                            lsp::Diagnostic {
11833                                range: lsp::Range::new(
11834                                    lsp::Position::new(0, 25),
11835                                    lsp::Position::new(0, 28),
11836                                ),
11837                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11838                                ..Default::default()
11839                            },
11840                        ],
11841                    },
11842                    &[],
11843                    cx,
11844                )
11845                .unwrap()
11846        });
11847    });
11848
11849    executor.run_until_parked();
11850
11851    cx.update_editor(|editor, window, cx| {
11852        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11853    });
11854
11855    cx.assert_editor_state(indoc! {"
11856        fn func(abc def: i32) -> ˇu32 {
11857        }
11858    "});
11859
11860    cx.update_editor(|editor, window, cx| {
11861        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11862    });
11863
11864    cx.assert_editor_state(indoc! {"
11865        fn func(abc ˇdef: i32) -> u32 {
11866        }
11867    "});
11868
11869    cx.update_editor(|editor, window, cx| {
11870        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11871    });
11872
11873    cx.assert_editor_state(indoc! {"
11874        fn func(abcˇ def: i32) -> u32 {
11875        }
11876    "});
11877
11878    cx.update_editor(|editor, window, cx| {
11879        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11880    });
11881
11882    cx.assert_editor_state(indoc! {"
11883        fn func(abc def: i32) -> ˇu32 {
11884        }
11885    "});
11886}
11887
11888#[gpui::test]
11889async fn cycle_through_same_place_diagnostics(
11890    executor: BackgroundExecutor,
11891    cx: &mut TestAppContext,
11892) {
11893    init_test(cx, |_| {});
11894
11895    let mut cx = EditorTestContext::new(cx).await;
11896    let lsp_store =
11897        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
11898
11899    cx.set_state(indoc! {"
11900        ˇfn func(abc def: i32) -> u32 {
11901        }
11902    "});
11903
11904    cx.update(|_, cx| {
11905        lsp_store.update(cx, |lsp_store, cx| {
11906            lsp_store
11907                .update_diagnostics(
11908                    LanguageServerId(0),
11909                    lsp::PublishDiagnosticsParams {
11910                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
11911                        version: None,
11912                        diagnostics: vec![
11913                            lsp::Diagnostic {
11914                                range: lsp::Range::new(
11915                                    lsp::Position::new(0, 11),
11916                                    lsp::Position::new(0, 12),
11917                                ),
11918                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11919                                ..Default::default()
11920                            },
11921                            lsp::Diagnostic {
11922                                range: lsp::Range::new(
11923                                    lsp::Position::new(0, 12),
11924                                    lsp::Position::new(0, 15),
11925                                ),
11926                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11927                                ..Default::default()
11928                            },
11929                            lsp::Diagnostic {
11930                                range: lsp::Range::new(
11931                                    lsp::Position::new(0, 12),
11932                                    lsp::Position::new(0, 15),
11933                                ),
11934                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11935                                ..Default::default()
11936                            },
11937                            lsp::Diagnostic {
11938                                range: lsp::Range::new(
11939                                    lsp::Position::new(0, 25),
11940                                    lsp::Position::new(0, 28),
11941                                ),
11942                                severity: Some(lsp::DiagnosticSeverity::ERROR),
11943                                ..Default::default()
11944                            },
11945                        ],
11946                    },
11947                    &[],
11948                    cx,
11949                )
11950                .unwrap()
11951        });
11952    });
11953    executor.run_until_parked();
11954
11955    //// Backward
11956
11957    // Fourth diagnostic
11958    cx.update_editor(|editor, window, cx| {
11959        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11960    });
11961    cx.assert_editor_state(indoc! {"
11962        fn func(abc def: i32) -> ˇu32 {
11963        }
11964    "});
11965
11966    // Third diagnostic
11967    cx.update_editor(|editor, window, cx| {
11968        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11969    });
11970    cx.assert_editor_state(indoc! {"
11971        fn func(abc ˇdef: i32) -> u32 {
11972        }
11973    "});
11974
11975    // Second diagnostic, same place
11976    cx.update_editor(|editor, window, cx| {
11977        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11978    });
11979    cx.assert_editor_state(indoc! {"
11980        fn func(abc ˇdef: i32) -> u32 {
11981        }
11982    "});
11983
11984    // First diagnostic
11985    cx.update_editor(|editor, window, cx| {
11986        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11987    });
11988    cx.assert_editor_state(indoc! {"
11989        fn func(abcˇ def: i32) -> u32 {
11990        }
11991    "});
11992
11993    // Wrapped over, fourth diagnostic
11994    cx.update_editor(|editor, window, cx| {
11995        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
11996    });
11997    cx.assert_editor_state(indoc! {"
11998        fn func(abc def: i32) -> ˇu32 {
11999        }
12000    "});
12001
12002    cx.update_editor(|editor, window, cx| {
12003        editor.move_to_beginning(&MoveToBeginning, window, cx);
12004    });
12005    cx.assert_editor_state(indoc! {"
12006        ˇfn func(abc def: i32) -> u32 {
12007        }
12008    "});
12009
12010    //// Forward
12011
12012    // First diagnostic
12013    cx.update_editor(|editor, window, cx| {
12014        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12015    });
12016    cx.assert_editor_state(indoc! {"
12017        fn func(abcˇ def: i32) -> u32 {
12018        }
12019    "});
12020
12021    // Second diagnostic
12022    cx.update_editor(|editor, window, cx| {
12023        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12024    });
12025    cx.assert_editor_state(indoc! {"
12026        fn func(abc ˇdef: i32) -> u32 {
12027        }
12028    "});
12029
12030    // Third diagnostic, same place
12031    cx.update_editor(|editor, window, cx| {
12032        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12033    });
12034    cx.assert_editor_state(indoc! {"
12035        fn func(abc ˇdef: i32) -> u32 {
12036        }
12037    "});
12038
12039    // Fourth diagnostic
12040    cx.update_editor(|editor, window, cx| {
12041        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12042    });
12043    cx.assert_editor_state(indoc! {"
12044        fn func(abc def: i32) -> ˇu32 {
12045        }
12046    "});
12047
12048    // Wrapped around, first diagnostic
12049    cx.update_editor(|editor, window, cx| {
12050        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12051    });
12052    cx.assert_editor_state(indoc! {"
12053        fn func(abcˇ def: i32) -> u32 {
12054        }
12055    "});
12056}
12057
12058#[gpui::test]
12059async fn active_diagnostics_dismiss_after_invalidation(
12060    executor: BackgroundExecutor,
12061    cx: &mut TestAppContext,
12062) {
12063    init_test(cx, |_| {});
12064
12065    let mut cx = EditorTestContext::new(cx).await;
12066    let lsp_store =
12067        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12068
12069    cx.set_state(indoc! {"
12070        ˇfn func(abc def: i32) -> u32 {
12071        }
12072    "});
12073
12074    let message = "Something's wrong!";
12075    cx.update(|_, cx| {
12076        lsp_store.update(cx, |lsp_store, cx| {
12077            lsp_store
12078                .update_diagnostics(
12079                    LanguageServerId(0),
12080                    lsp::PublishDiagnosticsParams {
12081                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12082                        version: None,
12083                        diagnostics: vec![lsp::Diagnostic {
12084                            range: lsp::Range::new(
12085                                lsp::Position::new(0, 11),
12086                                lsp::Position::new(0, 12),
12087                            ),
12088                            severity: Some(lsp::DiagnosticSeverity::ERROR),
12089                            message: message.to_string(),
12090                            ..Default::default()
12091                        }],
12092                    },
12093                    &[],
12094                    cx,
12095                )
12096                .unwrap()
12097        });
12098    });
12099    executor.run_until_parked();
12100
12101    cx.update_editor(|editor, window, cx| {
12102        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12103        assert_eq!(
12104            editor
12105                .active_diagnostics
12106                .as_ref()
12107                .map(|diagnostics_group| diagnostics_group.primary_message.as_str()),
12108            Some(message),
12109            "Should have a diagnostics group activated"
12110        );
12111    });
12112    cx.assert_editor_state(indoc! {"
12113        fn func(abcˇ def: i32) -> u32 {
12114        }
12115    "});
12116
12117    cx.update(|_, cx| {
12118        lsp_store.update(cx, |lsp_store, cx| {
12119            lsp_store
12120                .update_diagnostics(
12121                    LanguageServerId(0),
12122                    lsp::PublishDiagnosticsParams {
12123                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12124                        version: None,
12125                        diagnostics: Vec::new(),
12126                    },
12127                    &[],
12128                    cx,
12129                )
12130                .unwrap()
12131        });
12132    });
12133    executor.run_until_parked();
12134    cx.update_editor(|editor, _, _| {
12135        assert_eq!(
12136            editor.active_diagnostics, None,
12137            "After no diagnostics set to the editor, no diagnostics should be active"
12138        );
12139    });
12140    cx.assert_editor_state(indoc! {"
12141        fn func(abcˇ def: i32) -> u32 {
12142        }
12143    "});
12144
12145    cx.update_editor(|editor, window, cx| {
12146        editor.go_to_diagnostic(&GoToDiagnostic, window, cx);
12147        assert_eq!(
12148            editor.active_diagnostics, None,
12149            "Should be no diagnostics to go to and activate"
12150        );
12151    });
12152    cx.assert_editor_state(indoc! {"
12153        fn func(abcˇ def: i32) -> u32 {
12154        }
12155    "});
12156}
12157
12158#[gpui::test]
12159async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12160    init_test(cx, |_| {});
12161
12162    let mut cx = EditorTestContext::new(cx).await;
12163
12164    cx.set_state(indoc! {"
12165        fn func(abˇc def: i32) -> u32 {
12166        }
12167    "});
12168    let lsp_store =
12169        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12170
12171    cx.update(|_, cx| {
12172        lsp_store.update(cx, |lsp_store, cx| {
12173            lsp_store.update_diagnostics(
12174                LanguageServerId(0),
12175                lsp::PublishDiagnosticsParams {
12176                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12177                    version: None,
12178                    diagnostics: vec![lsp::Diagnostic {
12179                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12180                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12181                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12182                        ..Default::default()
12183                    }],
12184                },
12185                &[],
12186                cx,
12187            )
12188        })
12189    }).unwrap();
12190    cx.run_until_parked();
12191    cx.update_editor(|editor, window, cx| {
12192        hover_popover::hover(editor, &Default::default(), window, cx)
12193    });
12194    cx.run_until_parked();
12195    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12196}
12197
12198#[gpui::test]
12199async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12200    init_test(cx, |_| {});
12201
12202    let mut cx = EditorTestContext::new(cx).await;
12203
12204    let diff_base = r#"
12205        use some::mod;
12206
12207        const A: u32 = 42;
12208
12209        fn main() {
12210            println!("hello");
12211
12212            println!("world");
12213        }
12214        "#
12215    .unindent();
12216
12217    // Edits are modified, removed, modified, added
12218    cx.set_state(
12219        &r#"
12220        use some::modified;
12221
12222        ˇ
12223        fn main() {
12224            println!("hello there");
12225
12226            println!("around the");
12227            println!("world");
12228        }
12229        "#
12230        .unindent(),
12231    );
12232
12233    cx.set_head_text(&diff_base);
12234    executor.run_until_parked();
12235
12236    cx.update_editor(|editor, window, cx| {
12237        //Wrap around the bottom of the buffer
12238        for _ in 0..3 {
12239            editor.go_to_next_hunk(&GoToHunk, window, cx);
12240        }
12241    });
12242
12243    cx.assert_editor_state(
12244        &r#"
12245        ˇuse some::modified;
12246
12247
12248        fn main() {
12249            println!("hello there");
12250
12251            println!("around the");
12252            println!("world");
12253        }
12254        "#
12255        .unindent(),
12256    );
12257
12258    cx.update_editor(|editor, window, cx| {
12259        //Wrap around the top of the buffer
12260        for _ in 0..2 {
12261            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12262        }
12263    });
12264
12265    cx.assert_editor_state(
12266        &r#"
12267        use some::modified;
12268
12269
12270        fn main() {
12271        ˇ    println!("hello there");
12272
12273            println!("around the");
12274            println!("world");
12275        }
12276        "#
12277        .unindent(),
12278    );
12279
12280    cx.update_editor(|editor, window, cx| {
12281        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12282    });
12283
12284    cx.assert_editor_state(
12285        &r#"
12286        use some::modified;
12287
12288        ˇ
12289        fn main() {
12290            println!("hello there");
12291
12292            println!("around the");
12293            println!("world");
12294        }
12295        "#
12296        .unindent(),
12297    );
12298
12299    cx.update_editor(|editor, window, cx| {
12300        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12301    });
12302
12303    cx.assert_editor_state(
12304        &r#"
12305        ˇuse some::modified;
12306
12307
12308        fn main() {
12309            println!("hello there");
12310
12311            println!("around the");
12312            println!("world");
12313        }
12314        "#
12315        .unindent(),
12316    );
12317
12318    cx.update_editor(|editor, window, cx| {
12319        for _ in 0..2 {
12320            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12321        }
12322    });
12323
12324    cx.assert_editor_state(
12325        &r#"
12326        use some::modified;
12327
12328
12329        fn main() {
12330        ˇ    println!("hello there");
12331
12332            println!("around the");
12333            println!("world");
12334        }
12335        "#
12336        .unindent(),
12337    );
12338
12339    cx.update_editor(|editor, window, cx| {
12340        editor.fold(&Fold, window, cx);
12341    });
12342
12343    cx.update_editor(|editor, window, cx| {
12344        editor.go_to_next_hunk(&GoToHunk, window, cx);
12345    });
12346
12347    cx.assert_editor_state(
12348        &r#"
12349        ˇuse some::modified;
12350
12351
12352        fn main() {
12353            println!("hello there");
12354
12355            println!("around the");
12356            println!("world");
12357        }
12358        "#
12359        .unindent(),
12360    );
12361}
12362
12363#[test]
12364fn test_split_words() {
12365    fn split(text: &str) -> Vec<&str> {
12366        split_words(text).collect()
12367    }
12368
12369    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12370    assert_eq!(split("hello_world"), &["hello_", "world"]);
12371    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12372    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12373    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12374    assert_eq!(split("helloworld"), &["helloworld"]);
12375
12376    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12377}
12378
12379#[gpui::test]
12380async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12381    init_test(cx, |_| {});
12382
12383    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12384    let mut assert = |before, after| {
12385        let _state_context = cx.set_state(before);
12386        cx.run_until_parked();
12387        cx.update_editor(|editor, window, cx| {
12388            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12389        });
12390        cx.run_until_parked();
12391        cx.assert_editor_state(after);
12392    };
12393
12394    // Outside bracket jumps to outside of matching bracket
12395    assert("console.logˇ(var);", "console.log(var)ˇ;");
12396    assert("console.log(var)ˇ;", "console.logˇ(var);");
12397
12398    // Inside bracket jumps to inside of matching bracket
12399    assert("console.log(ˇvar);", "console.log(varˇ);");
12400    assert("console.log(varˇ);", "console.log(ˇvar);");
12401
12402    // When outside a bracket and inside, favor jumping to the inside bracket
12403    assert(
12404        "console.log('foo', [1, 2, 3]ˇ);",
12405        "console.log(ˇ'foo', [1, 2, 3]);",
12406    );
12407    assert(
12408        "console.log(ˇ'foo', [1, 2, 3]);",
12409        "console.log('foo', [1, 2, 3]ˇ);",
12410    );
12411
12412    // Bias forward if two options are equally likely
12413    assert(
12414        "let result = curried_fun()ˇ();",
12415        "let result = curried_fun()()ˇ;",
12416    );
12417
12418    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12419    assert(
12420        indoc! {"
12421            function test() {
12422                console.log('test')ˇ
12423            }"},
12424        indoc! {"
12425            function test() {
12426                console.logˇ('test')
12427            }"},
12428    );
12429}
12430
12431#[gpui::test]
12432async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12433    init_test(cx, |_| {});
12434
12435    let fs = FakeFs::new(cx.executor());
12436    fs.insert_tree(
12437        path!("/a"),
12438        json!({
12439            "main.rs": "fn main() { let a = 5; }",
12440            "other.rs": "// Test file",
12441        }),
12442    )
12443    .await;
12444    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12445
12446    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12447    language_registry.add(Arc::new(Language::new(
12448        LanguageConfig {
12449            name: "Rust".into(),
12450            matcher: LanguageMatcher {
12451                path_suffixes: vec!["rs".to_string()],
12452                ..Default::default()
12453            },
12454            brackets: BracketPairConfig {
12455                pairs: vec![BracketPair {
12456                    start: "{".to_string(),
12457                    end: "}".to_string(),
12458                    close: true,
12459                    surround: true,
12460                    newline: true,
12461                }],
12462                disabled_scopes_by_bracket_ix: Vec::new(),
12463            },
12464            ..Default::default()
12465        },
12466        Some(tree_sitter_rust::LANGUAGE.into()),
12467    )));
12468    let mut fake_servers = language_registry.register_fake_lsp(
12469        "Rust",
12470        FakeLspAdapter {
12471            capabilities: lsp::ServerCapabilities {
12472                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
12473                    first_trigger_character: "{".to_string(),
12474                    more_trigger_character: None,
12475                }),
12476                ..Default::default()
12477            },
12478            ..Default::default()
12479        },
12480    );
12481
12482    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12483
12484    let cx = &mut VisualTestContext::from_window(*workspace, cx);
12485
12486    let worktree_id = workspace
12487        .update(cx, |workspace, _, cx| {
12488            workspace.project().update(cx, |project, cx| {
12489                project.worktrees(cx).next().unwrap().read(cx).id()
12490            })
12491        })
12492        .unwrap();
12493
12494    let buffer = project
12495        .update(cx, |project, cx| {
12496            project.open_local_buffer(path!("/a/main.rs"), cx)
12497        })
12498        .await
12499        .unwrap();
12500    let editor_handle = workspace
12501        .update(cx, |workspace, window, cx| {
12502            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
12503        })
12504        .unwrap()
12505        .await
12506        .unwrap()
12507        .downcast::<Editor>()
12508        .unwrap();
12509
12510    cx.executor().start_waiting();
12511    let fake_server = fake_servers.next().await.unwrap();
12512
12513    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
12514        |params, _| async move {
12515            assert_eq!(
12516                params.text_document_position.text_document.uri,
12517                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
12518            );
12519            assert_eq!(
12520                params.text_document_position.position,
12521                lsp::Position::new(0, 21),
12522            );
12523
12524            Ok(Some(vec![lsp::TextEdit {
12525                new_text: "]".to_string(),
12526                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12527            }]))
12528        },
12529    );
12530
12531    editor_handle.update_in(cx, |editor, window, cx| {
12532        window.focus(&editor.focus_handle(cx));
12533        editor.change_selections(None, window, cx, |s| {
12534            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
12535        });
12536        editor.handle_input("{", window, cx);
12537    });
12538
12539    cx.executor().run_until_parked();
12540
12541    buffer.update(cx, |buffer, _| {
12542        assert_eq!(
12543            buffer.text(),
12544            "fn main() { let a = {5}; }",
12545            "No extra braces from on type formatting should appear in the buffer"
12546        )
12547    });
12548}
12549
12550#[gpui::test]
12551async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
12552    init_test(cx, |_| {});
12553
12554    let fs = FakeFs::new(cx.executor());
12555    fs.insert_tree(
12556        path!("/a"),
12557        json!({
12558            "main.rs": "fn main() { let a = 5; }",
12559            "other.rs": "// Test file",
12560        }),
12561    )
12562    .await;
12563
12564    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12565
12566    let server_restarts = Arc::new(AtomicUsize::new(0));
12567    let closure_restarts = Arc::clone(&server_restarts);
12568    let language_server_name = "test language server";
12569    let language_name: LanguageName = "Rust".into();
12570
12571    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12572    language_registry.add(Arc::new(Language::new(
12573        LanguageConfig {
12574            name: language_name.clone(),
12575            matcher: LanguageMatcher {
12576                path_suffixes: vec!["rs".to_string()],
12577                ..Default::default()
12578            },
12579            ..Default::default()
12580        },
12581        Some(tree_sitter_rust::LANGUAGE.into()),
12582    )));
12583    let mut fake_servers = language_registry.register_fake_lsp(
12584        "Rust",
12585        FakeLspAdapter {
12586            name: language_server_name,
12587            initialization_options: Some(json!({
12588                "testOptionValue": true
12589            })),
12590            initializer: Some(Box::new(move |fake_server| {
12591                let task_restarts = Arc::clone(&closure_restarts);
12592                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
12593                    task_restarts.fetch_add(1, atomic::Ordering::Release);
12594                    futures::future::ready(Ok(()))
12595                });
12596            })),
12597            ..Default::default()
12598        },
12599    );
12600
12601    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12602    let _buffer = project
12603        .update(cx, |project, cx| {
12604            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
12605        })
12606        .await
12607        .unwrap();
12608    let _fake_server = fake_servers.next().await.unwrap();
12609    update_test_language_settings(cx, |language_settings| {
12610        language_settings.languages.insert(
12611            language_name.clone(),
12612            LanguageSettingsContent {
12613                tab_size: NonZeroU32::new(8),
12614                ..Default::default()
12615            },
12616        );
12617    });
12618    cx.executor().run_until_parked();
12619    assert_eq!(
12620        server_restarts.load(atomic::Ordering::Acquire),
12621        0,
12622        "Should not restart LSP server on an unrelated change"
12623    );
12624
12625    update_test_project_settings(cx, |project_settings| {
12626        project_settings.lsp.insert(
12627            "Some other server name".into(),
12628            LspSettings {
12629                binary: None,
12630                settings: None,
12631                initialization_options: Some(json!({
12632                    "some other init value": false
12633                })),
12634                enable_lsp_tasks: false,
12635            },
12636        );
12637    });
12638    cx.executor().run_until_parked();
12639    assert_eq!(
12640        server_restarts.load(atomic::Ordering::Acquire),
12641        0,
12642        "Should not restart LSP server on an unrelated LSP settings change"
12643    );
12644
12645    update_test_project_settings(cx, |project_settings| {
12646        project_settings.lsp.insert(
12647            language_server_name.into(),
12648            LspSettings {
12649                binary: None,
12650                settings: None,
12651                initialization_options: Some(json!({
12652                    "anotherInitValue": false
12653                })),
12654                enable_lsp_tasks: false,
12655            },
12656        );
12657    });
12658    cx.executor().run_until_parked();
12659    assert_eq!(
12660        server_restarts.load(atomic::Ordering::Acquire),
12661        1,
12662        "Should restart LSP server on a related LSP settings change"
12663    );
12664
12665    update_test_project_settings(cx, |project_settings| {
12666        project_settings.lsp.insert(
12667            language_server_name.into(),
12668            LspSettings {
12669                binary: None,
12670                settings: None,
12671                initialization_options: Some(json!({
12672                    "anotherInitValue": false
12673                })),
12674                enable_lsp_tasks: false,
12675            },
12676        );
12677    });
12678    cx.executor().run_until_parked();
12679    assert_eq!(
12680        server_restarts.load(atomic::Ordering::Acquire),
12681        1,
12682        "Should not restart LSP server on a related LSP settings change that is the same"
12683    );
12684
12685    update_test_project_settings(cx, |project_settings| {
12686        project_settings.lsp.insert(
12687            language_server_name.into(),
12688            LspSettings {
12689                binary: None,
12690                settings: None,
12691                initialization_options: None,
12692                enable_lsp_tasks: false,
12693            },
12694        );
12695    });
12696    cx.executor().run_until_parked();
12697    assert_eq!(
12698        server_restarts.load(atomic::Ordering::Acquire),
12699        2,
12700        "Should restart LSP server on another related LSP settings change"
12701    );
12702}
12703
12704#[gpui::test]
12705async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
12706    init_test(cx, |_| {});
12707
12708    let mut cx = EditorLspTestContext::new_rust(
12709        lsp::ServerCapabilities {
12710            completion_provider: Some(lsp::CompletionOptions {
12711                trigger_characters: Some(vec![".".to_string()]),
12712                resolve_provider: Some(true),
12713                ..Default::default()
12714            }),
12715            ..Default::default()
12716        },
12717        cx,
12718    )
12719    .await;
12720
12721    cx.set_state("fn main() { let a = 2ˇ; }");
12722    cx.simulate_keystroke(".");
12723    let completion_item = lsp::CompletionItem {
12724        label: "some".into(),
12725        kind: Some(lsp::CompletionItemKind::SNIPPET),
12726        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
12727        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
12728            kind: lsp::MarkupKind::Markdown,
12729            value: "```rust\nSome(2)\n```".to_string(),
12730        })),
12731        deprecated: Some(false),
12732        sort_text: Some("fffffff2".to_string()),
12733        filter_text: Some("some".to_string()),
12734        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
12735        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12736            range: lsp::Range {
12737                start: lsp::Position {
12738                    line: 0,
12739                    character: 22,
12740                },
12741                end: lsp::Position {
12742                    line: 0,
12743                    character: 22,
12744                },
12745            },
12746            new_text: "Some(2)".to_string(),
12747        })),
12748        additional_text_edits: Some(vec![lsp::TextEdit {
12749            range: lsp::Range {
12750                start: lsp::Position {
12751                    line: 0,
12752                    character: 20,
12753                },
12754                end: lsp::Position {
12755                    line: 0,
12756                    character: 22,
12757                },
12758            },
12759            new_text: "".to_string(),
12760        }]),
12761        ..Default::default()
12762    };
12763
12764    let closure_completion_item = completion_item.clone();
12765    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
12766        let task_completion_item = closure_completion_item.clone();
12767        async move {
12768            Ok(Some(lsp::CompletionResponse::Array(vec![
12769                task_completion_item,
12770            ])))
12771        }
12772    });
12773
12774    request.next().await;
12775
12776    cx.condition(|editor, _| editor.context_menu_visible())
12777        .await;
12778    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
12779        editor
12780            .confirm_completion(&ConfirmCompletion::default(), window, cx)
12781            .unwrap()
12782    });
12783    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
12784
12785    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
12786        let task_completion_item = completion_item.clone();
12787        async move { Ok(task_completion_item) }
12788    })
12789    .next()
12790    .await
12791    .unwrap();
12792    apply_additional_edits.await.unwrap();
12793    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
12794}
12795
12796#[gpui::test]
12797async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
12798    init_test(cx, |_| {});
12799
12800    let mut cx = EditorLspTestContext::new_rust(
12801        lsp::ServerCapabilities {
12802            completion_provider: Some(lsp::CompletionOptions {
12803                trigger_characters: Some(vec![".".to_string()]),
12804                resolve_provider: Some(true),
12805                ..Default::default()
12806            }),
12807            ..Default::default()
12808        },
12809        cx,
12810    )
12811    .await;
12812
12813    cx.set_state("fn main() { let a = 2ˇ; }");
12814    cx.simulate_keystroke(".");
12815
12816    let item1 = lsp::CompletionItem {
12817        label: "method id()".to_string(),
12818        filter_text: Some("id".to_string()),
12819        detail: None,
12820        documentation: None,
12821        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12822            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12823            new_text: ".id".to_string(),
12824        })),
12825        ..lsp::CompletionItem::default()
12826    };
12827
12828    let item2 = lsp::CompletionItem {
12829        label: "other".to_string(),
12830        filter_text: Some("other".to_string()),
12831        detail: None,
12832        documentation: None,
12833        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12834            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12835            new_text: ".other".to_string(),
12836        })),
12837        ..lsp::CompletionItem::default()
12838    };
12839
12840    let item1 = item1.clone();
12841    cx.set_request_handler::<lsp::request::Completion, _, _>({
12842        let item1 = item1.clone();
12843        move |_, _, _| {
12844            let item1 = item1.clone();
12845            let item2 = item2.clone();
12846            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
12847        }
12848    })
12849    .next()
12850    .await;
12851
12852    cx.condition(|editor, _| editor.context_menu_visible())
12853        .await;
12854    cx.update_editor(|editor, _, _| {
12855        let context_menu = editor.context_menu.borrow_mut();
12856        let context_menu = context_menu
12857            .as_ref()
12858            .expect("Should have the context menu deployed");
12859        match context_menu {
12860            CodeContextMenu::Completions(completions_menu) => {
12861                let completions = completions_menu.completions.borrow_mut();
12862                assert_eq!(
12863                    completions
12864                        .iter()
12865                        .map(|completion| &completion.label.text)
12866                        .collect::<Vec<_>>(),
12867                    vec!["method id()", "other"]
12868                )
12869            }
12870            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12871        }
12872    });
12873
12874    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
12875        let item1 = item1.clone();
12876        move |_, item_to_resolve, _| {
12877            let item1 = item1.clone();
12878            async move {
12879                if item1 == item_to_resolve {
12880                    Ok(lsp::CompletionItem {
12881                        label: "method id()".to_string(),
12882                        filter_text: Some("id".to_string()),
12883                        detail: Some("Now resolved!".to_string()),
12884                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
12885                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12886                            range: lsp::Range::new(
12887                                lsp::Position::new(0, 22),
12888                                lsp::Position::new(0, 22),
12889                            ),
12890                            new_text: ".id".to_string(),
12891                        })),
12892                        ..lsp::CompletionItem::default()
12893                    })
12894                } else {
12895                    Ok(item_to_resolve)
12896                }
12897            }
12898        }
12899    })
12900    .next()
12901    .await
12902    .unwrap();
12903    cx.run_until_parked();
12904
12905    cx.update_editor(|editor, window, cx| {
12906        editor.context_menu_next(&Default::default(), window, cx);
12907    });
12908
12909    cx.update_editor(|editor, _, _| {
12910        let context_menu = editor.context_menu.borrow_mut();
12911        let context_menu = context_menu
12912            .as_ref()
12913            .expect("Should have the context menu deployed");
12914        match context_menu {
12915            CodeContextMenu::Completions(completions_menu) => {
12916                let completions = completions_menu.completions.borrow_mut();
12917                assert_eq!(
12918                    completions
12919                        .iter()
12920                        .map(|completion| &completion.label.text)
12921                        .collect::<Vec<_>>(),
12922                    vec!["method id() Now resolved!", "other"],
12923                    "Should update first completion label, but not second as the filter text did not match."
12924                );
12925            }
12926            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
12927        }
12928    });
12929}
12930
12931#[gpui::test]
12932async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
12933    init_test(cx, |_| {});
12934
12935    let mut cx = EditorLspTestContext::new_rust(
12936        lsp::ServerCapabilities {
12937            completion_provider: Some(lsp::CompletionOptions {
12938                trigger_characters: Some(vec![".".to_string()]),
12939                resolve_provider: Some(true),
12940                ..Default::default()
12941            }),
12942            ..Default::default()
12943        },
12944        cx,
12945    )
12946    .await;
12947
12948    cx.set_state("fn main() { let a = 2ˇ; }");
12949    cx.simulate_keystroke(".");
12950
12951    let unresolved_item_1 = lsp::CompletionItem {
12952        label: "id".to_string(),
12953        filter_text: Some("id".to_string()),
12954        detail: None,
12955        documentation: None,
12956        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12957            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12958            new_text: ".id".to_string(),
12959        })),
12960        ..lsp::CompletionItem::default()
12961    };
12962    let resolved_item_1 = lsp::CompletionItem {
12963        additional_text_edits: Some(vec![lsp::TextEdit {
12964            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12965            new_text: "!!".to_string(),
12966        }]),
12967        ..unresolved_item_1.clone()
12968    };
12969    let unresolved_item_2 = lsp::CompletionItem {
12970        label: "other".to_string(),
12971        filter_text: Some("other".to_string()),
12972        detail: None,
12973        documentation: None,
12974        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
12975            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
12976            new_text: ".other".to_string(),
12977        })),
12978        ..lsp::CompletionItem::default()
12979    };
12980    let resolved_item_2 = lsp::CompletionItem {
12981        additional_text_edits: Some(vec![lsp::TextEdit {
12982            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
12983            new_text: "??".to_string(),
12984        }]),
12985        ..unresolved_item_2.clone()
12986    };
12987
12988    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
12989    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
12990    cx.lsp
12991        .server
12992        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
12993            let unresolved_item_1 = unresolved_item_1.clone();
12994            let resolved_item_1 = resolved_item_1.clone();
12995            let unresolved_item_2 = unresolved_item_2.clone();
12996            let resolved_item_2 = resolved_item_2.clone();
12997            let resolve_requests_1 = resolve_requests_1.clone();
12998            let resolve_requests_2 = resolve_requests_2.clone();
12999            move |unresolved_request, _| {
13000                let unresolved_item_1 = unresolved_item_1.clone();
13001                let resolved_item_1 = resolved_item_1.clone();
13002                let unresolved_item_2 = unresolved_item_2.clone();
13003                let resolved_item_2 = resolved_item_2.clone();
13004                let resolve_requests_1 = resolve_requests_1.clone();
13005                let resolve_requests_2 = resolve_requests_2.clone();
13006                async move {
13007                    if unresolved_request == unresolved_item_1 {
13008                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13009                        Ok(resolved_item_1.clone())
13010                    } else if unresolved_request == unresolved_item_2 {
13011                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13012                        Ok(resolved_item_2.clone())
13013                    } else {
13014                        panic!("Unexpected completion item {unresolved_request:?}")
13015                    }
13016                }
13017            }
13018        })
13019        .detach();
13020
13021    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13022        let unresolved_item_1 = unresolved_item_1.clone();
13023        let unresolved_item_2 = unresolved_item_2.clone();
13024        async move {
13025            Ok(Some(lsp::CompletionResponse::Array(vec![
13026                unresolved_item_1,
13027                unresolved_item_2,
13028            ])))
13029        }
13030    })
13031    .next()
13032    .await;
13033
13034    cx.condition(|editor, _| editor.context_menu_visible())
13035        .await;
13036    cx.update_editor(|editor, _, _| {
13037        let context_menu = editor.context_menu.borrow_mut();
13038        let context_menu = context_menu
13039            .as_ref()
13040            .expect("Should have the context menu deployed");
13041        match context_menu {
13042            CodeContextMenu::Completions(completions_menu) => {
13043                let completions = completions_menu.completions.borrow_mut();
13044                assert_eq!(
13045                    completions
13046                        .iter()
13047                        .map(|completion| &completion.label.text)
13048                        .collect::<Vec<_>>(),
13049                    vec!["id", "other"]
13050                )
13051            }
13052            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13053        }
13054    });
13055    cx.run_until_parked();
13056
13057    cx.update_editor(|editor, window, cx| {
13058        editor.context_menu_next(&ContextMenuNext, window, cx);
13059    });
13060    cx.run_until_parked();
13061    cx.update_editor(|editor, window, cx| {
13062        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13063    });
13064    cx.run_until_parked();
13065    cx.update_editor(|editor, window, cx| {
13066        editor.context_menu_next(&ContextMenuNext, window, cx);
13067    });
13068    cx.run_until_parked();
13069    cx.update_editor(|editor, window, cx| {
13070        editor
13071            .compose_completion(&ComposeCompletion::default(), window, cx)
13072            .expect("No task returned")
13073    })
13074    .await
13075    .expect("Completion failed");
13076    cx.run_until_parked();
13077
13078    cx.update_editor(|editor, _, cx| {
13079        assert_eq!(
13080            resolve_requests_1.load(atomic::Ordering::Acquire),
13081            1,
13082            "Should always resolve once despite multiple selections"
13083        );
13084        assert_eq!(
13085            resolve_requests_2.load(atomic::Ordering::Acquire),
13086            1,
13087            "Should always resolve once after multiple selections and applying the completion"
13088        );
13089        assert_eq!(
13090            editor.text(cx),
13091            "fn main() { let a = ??.other; }",
13092            "Should use resolved data when applying the completion"
13093        );
13094    });
13095}
13096
13097#[gpui::test]
13098async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13099    init_test(cx, |_| {});
13100
13101    let item_0 = lsp::CompletionItem {
13102        label: "abs".into(),
13103        insert_text: Some("abs".into()),
13104        data: Some(json!({ "very": "special"})),
13105        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13106        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13107            lsp::InsertReplaceEdit {
13108                new_text: "abs".to_string(),
13109                insert: lsp::Range::default(),
13110                replace: lsp::Range::default(),
13111            },
13112        )),
13113        ..lsp::CompletionItem::default()
13114    };
13115    let items = iter::once(item_0.clone())
13116        .chain((11..51).map(|i| lsp::CompletionItem {
13117            label: format!("item_{}", i),
13118            insert_text: Some(format!("item_{}", i)),
13119            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13120            ..lsp::CompletionItem::default()
13121        }))
13122        .collect::<Vec<_>>();
13123
13124    let default_commit_characters = vec!["?".to_string()];
13125    let default_data = json!({ "default": "data"});
13126    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13127    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13128    let default_edit_range = lsp::Range {
13129        start: lsp::Position {
13130            line: 0,
13131            character: 5,
13132        },
13133        end: lsp::Position {
13134            line: 0,
13135            character: 5,
13136        },
13137    };
13138
13139    let mut cx = EditorLspTestContext::new_rust(
13140        lsp::ServerCapabilities {
13141            completion_provider: Some(lsp::CompletionOptions {
13142                trigger_characters: Some(vec![".".to_string()]),
13143                resolve_provider: Some(true),
13144                ..Default::default()
13145            }),
13146            ..Default::default()
13147        },
13148        cx,
13149    )
13150    .await;
13151
13152    cx.set_state("fn main() { let a = 2ˇ; }");
13153    cx.simulate_keystroke(".");
13154
13155    let completion_data = default_data.clone();
13156    let completion_characters = default_commit_characters.clone();
13157    let completion_items = items.clone();
13158    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13159        let default_data = completion_data.clone();
13160        let default_commit_characters = completion_characters.clone();
13161        let items = completion_items.clone();
13162        async move {
13163            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13164                items,
13165                item_defaults: Some(lsp::CompletionListItemDefaults {
13166                    data: Some(default_data.clone()),
13167                    commit_characters: Some(default_commit_characters.clone()),
13168                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13169                        default_edit_range,
13170                    )),
13171                    insert_text_format: Some(default_insert_text_format),
13172                    insert_text_mode: Some(default_insert_text_mode),
13173                }),
13174                ..lsp::CompletionList::default()
13175            })))
13176        }
13177    })
13178    .next()
13179    .await;
13180
13181    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13182    cx.lsp
13183        .server
13184        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13185            let closure_resolved_items = resolved_items.clone();
13186            move |item_to_resolve, _| {
13187                let closure_resolved_items = closure_resolved_items.clone();
13188                async move {
13189                    closure_resolved_items.lock().push(item_to_resolve.clone());
13190                    Ok(item_to_resolve)
13191                }
13192            }
13193        })
13194        .detach();
13195
13196    cx.condition(|editor, _| editor.context_menu_visible())
13197        .await;
13198    cx.run_until_parked();
13199    cx.update_editor(|editor, _, _| {
13200        let menu = editor.context_menu.borrow_mut();
13201        match menu.as_ref().expect("should have the completions menu") {
13202            CodeContextMenu::Completions(completions_menu) => {
13203                assert_eq!(
13204                    completions_menu
13205                        .entries
13206                        .borrow()
13207                        .iter()
13208                        .map(|mat| mat.string.clone())
13209                        .collect::<Vec<String>>(),
13210                    items
13211                        .iter()
13212                        .map(|completion| completion.label.clone())
13213                        .collect::<Vec<String>>()
13214                );
13215            }
13216            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13217        }
13218    });
13219    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13220    // with 4 from the end.
13221    assert_eq!(
13222        *resolved_items.lock(),
13223        [&items[0..16], &items[items.len() - 4..items.len()]]
13224            .concat()
13225            .iter()
13226            .cloned()
13227            .map(|mut item| {
13228                if item.data.is_none() {
13229                    item.data = Some(default_data.clone());
13230                }
13231                item
13232            })
13233            .collect::<Vec<lsp::CompletionItem>>(),
13234        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13235    );
13236    resolved_items.lock().clear();
13237
13238    cx.update_editor(|editor, window, cx| {
13239        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13240    });
13241    cx.run_until_parked();
13242    // Completions that have already been resolved are skipped.
13243    assert_eq!(
13244        *resolved_items.lock(),
13245        items[items.len() - 16..items.len() - 4]
13246            .iter()
13247            .cloned()
13248            .map(|mut item| {
13249                if item.data.is_none() {
13250                    item.data = Some(default_data.clone());
13251                }
13252                item
13253            })
13254            .collect::<Vec<lsp::CompletionItem>>()
13255    );
13256    resolved_items.lock().clear();
13257}
13258
13259#[gpui::test]
13260async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13261    init_test(cx, |_| {});
13262
13263    let mut cx = EditorLspTestContext::new(
13264        Language::new(
13265            LanguageConfig {
13266                matcher: LanguageMatcher {
13267                    path_suffixes: vec!["jsx".into()],
13268                    ..Default::default()
13269                },
13270                overrides: [(
13271                    "element".into(),
13272                    LanguageConfigOverride {
13273                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13274                        ..Default::default()
13275                    },
13276                )]
13277                .into_iter()
13278                .collect(),
13279                ..Default::default()
13280            },
13281            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13282        )
13283        .with_override_query("(jsx_self_closing_element) @element")
13284        .unwrap(),
13285        lsp::ServerCapabilities {
13286            completion_provider: Some(lsp::CompletionOptions {
13287                trigger_characters: Some(vec![":".to_string()]),
13288                ..Default::default()
13289            }),
13290            ..Default::default()
13291        },
13292        cx,
13293    )
13294    .await;
13295
13296    cx.lsp
13297        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13298            Ok(Some(lsp::CompletionResponse::Array(vec![
13299                lsp::CompletionItem {
13300                    label: "bg-blue".into(),
13301                    ..Default::default()
13302                },
13303                lsp::CompletionItem {
13304                    label: "bg-red".into(),
13305                    ..Default::default()
13306                },
13307                lsp::CompletionItem {
13308                    label: "bg-yellow".into(),
13309                    ..Default::default()
13310                },
13311            ])))
13312        });
13313
13314    cx.set_state(r#"<p class="bgˇ" />"#);
13315
13316    // Trigger completion when typing a dash, because the dash is an extra
13317    // word character in the 'element' scope, which contains the cursor.
13318    cx.simulate_keystroke("-");
13319    cx.executor().run_until_parked();
13320    cx.update_editor(|editor, _, _| {
13321        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13322        {
13323            assert_eq!(
13324                completion_menu_entries(&menu),
13325                &["bg-red", "bg-blue", "bg-yellow"]
13326            );
13327        } else {
13328            panic!("expected completion menu to be open");
13329        }
13330    });
13331
13332    cx.simulate_keystroke("l");
13333    cx.executor().run_until_parked();
13334    cx.update_editor(|editor, _, _| {
13335        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13336        {
13337            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13338        } else {
13339            panic!("expected completion menu to be open");
13340        }
13341    });
13342
13343    // When filtering completions, consider the character after the '-' to
13344    // be the start of a subword.
13345    cx.set_state(r#"<p class="yelˇ" />"#);
13346    cx.simulate_keystroke("l");
13347    cx.executor().run_until_parked();
13348    cx.update_editor(|editor, _, _| {
13349        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13350        {
13351            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13352        } else {
13353            panic!("expected completion menu to be open");
13354        }
13355    });
13356}
13357
13358fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13359    let entries = menu.entries.borrow();
13360    entries.iter().map(|mat| mat.string.clone()).collect()
13361}
13362
13363#[gpui::test]
13364async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13365    init_test(cx, |settings| {
13366        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13367            FormatterList(vec![Formatter::Prettier].into()),
13368        ))
13369    });
13370
13371    let fs = FakeFs::new(cx.executor());
13372    fs.insert_file(path!("/file.ts"), Default::default()).await;
13373
13374    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13375    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13376
13377    language_registry.add(Arc::new(Language::new(
13378        LanguageConfig {
13379            name: "TypeScript".into(),
13380            matcher: LanguageMatcher {
13381                path_suffixes: vec!["ts".to_string()],
13382                ..Default::default()
13383            },
13384            ..Default::default()
13385        },
13386        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13387    )));
13388    update_test_language_settings(cx, |settings| {
13389        settings.defaults.prettier = Some(PrettierSettings {
13390            allowed: true,
13391            ..PrettierSettings::default()
13392        });
13393    });
13394
13395    let test_plugin = "test_plugin";
13396    let _ = language_registry.register_fake_lsp(
13397        "TypeScript",
13398        FakeLspAdapter {
13399            prettier_plugins: vec![test_plugin],
13400            ..Default::default()
13401        },
13402    );
13403
13404    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13405    let buffer = project
13406        .update(cx, |project, cx| {
13407            project.open_local_buffer(path!("/file.ts"), cx)
13408        })
13409        .await
13410        .unwrap();
13411
13412    let buffer_text = "one\ntwo\nthree\n";
13413    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13414    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13415    editor.update_in(cx, |editor, window, cx| {
13416        editor.set_text(buffer_text, window, cx)
13417    });
13418
13419    editor
13420        .update_in(cx, |editor, window, cx| {
13421            editor.perform_format(
13422                project.clone(),
13423                FormatTrigger::Manual,
13424                FormatTarget::Buffers,
13425                window,
13426                cx,
13427            )
13428        })
13429        .unwrap()
13430        .await;
13431    assert_eq!(
13432        editor.update(cx, |editor, cx| editor.text(cx)),
13433        buffer_text.to_string() + prettier_format_suffix,
13434        "Test prettier formatting was not applied to the original buffer text",
13435    );
13436
13437    update_test_language_settings(cx, |settings| {
13438        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13439    });
13440    let format = editor.update_in(cx, |editor, window, cx| {
13441        editor.perform_format(
13442            project.clone(),
13443            FormatTrigger::Manual,
13444            FormatTarget::Buffers,
13445            window,
13446            cx,
13447        )
13448    });
13449    format.await.unwrap();
13450    assert_eq!(
13451        editor.update(cx, |editor, cx| editor.text(cx)),
13452        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13453        "Autoformatting (via test prettier) was not applied to the original buffer text",
13454    );
13455}
13456
13457#[gpui::test]
13458async fn test_addition_reverts(cx: &mut TestAppContext) {
13459    init_test(cx, |_| {});
13460    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13461    let base_text = indoc! {r#"
13462        struct Row;
13463        struct Row1;
13464        struct Row2;
13465
13466        struct Row4;
13467        struct Row5;
13468        struct Row6;
13469
13470        struct Row8;
13471        struct Row9;
13472        struct Row10;"#};
13473
13474    // When addition hunks are not adjacent to carets, no hunk revert is performed
13475    assert_hunk_revert(
13476        indoc! {r#"struct Row;
13477                   struct Row1;
13478                   struct Row1.1;
13479                   struct Row1.2;
13480                   struct Row2;ˇ
13481
13482                   struct Row4;
13483                   struct Row5;
13484                   struct Row6;
13485
13486                   struct Row8;
13487                   ˇstruct Row9;
13488                   struct Row9.1;
13489                   struct Row9.2;
13490                   struct Row9.3;
13491                   struct Row10;"#},
13492        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13493        indoc! {r#"struct Row;
13494                   struct Row1;
13495                   struct Row1.1;
13496                   struct Row1.2;
13497                   struct Row2;ˇ
13498
13499                   struct Row4;
13500                   struct Row5;
13501                   struct Row6;
13502
13503                   struct Row8;
13504                   ˇstruct Row9;
13505                   struct Row9.1;
13506                   struct Row9.2;
13507                   struct Row9.3;
13508                   struct Row10;"#},
13509        base_text,
13510        &mut cx,
13511    );
13512    // Same for selections
13513    assert_hunk_revert(
13514        indoc! {r#"struct Row;
13515                   struct Row1;
13516                   struct Row2;
13517                   struct Row2.1;
13518                   struct Row2.2;
13519                   «ˇ
13520                   struct Row4;
13521                   struct» Row5;
13522                   «struct Row6;
13523                   ˇ»
13524                   struct Row9.1;
13525                   struct Row9.2;
13526                   struct Row9.3;
13527                   struct Row8;
13528                   struct Row9;
13529                   struct Row10;"#},
13530        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
13531        indoc! {r#"struct Row;
13532                   struct Row1;
13533                   struct Row2;
13534                   struct Row2.1;
13535                   struct Row2.2;
13536                   «ˇ
13537                   struct Row4;
13538                   struct» Row5;
13539                   «struct Row6;
13540                   ˇ»
13541                   struct Row9.1;
13542                   struct Row9.2;
13543                   struct Row9.3;
13544                   struct Row8;
13545                   struct Row9;
13546                   struct Row10;"#},
13547        base_text,
13548        &mut cx,
13549    );
13550
13551    // When carets and selections intersect the addition hunks, those are reverted.
13552    // Adjacent carets got merged.
13553    assert_hunk_revert(
13554        indoc! {r#"struct Row;
13555                   ˇ// something on the top
13556                   struct Row1;
13557                   struct Row2;
13558                   struct Roˇw3.1;
13559                   struct Row2.2;
13560                   struct Row2.3;ˇ
13561
13562                   struct Row4;
13563                   struct ˇRow5.1;
13564                   struct Row5.2;
13565                   struct «Rowˇ»5.3;
13566                   struct Row5;
13567                   struct Row6;
13568                   ˇ
13569                   struct Row9.1;
13570                   struct «Rowˇ»9.2;
13571                   struct «ˇRow»9.3;
13572                   struct Row8;
13573                   struct Row9;
13574                   «ˇ// something on bottom»
13575                   struct Row10;"#},
13576        vec![
13577            DiffHunkStatusKind::Added,
13578            DiffHunkStatusKind::Added,
13579            DiffHunkStatusKind::Added,
13580            DiffHunkStatusKind::Added,
13581            DiffHunkStatusKind::Added,
13582        ],
13583        indoc! {r#"struct Row;
13584                   ˇstruct Row1;
13585                   struct Row2;
13586                   ˇ
13587                   struct Row4;
13588                   ˇstruct Row5;
13589                   struct Row6;
13590                   ˇ
13591                   ˇstruct Row8;
13592                   struct Row9;
13593                   ˇstruct Row10;"#},
13594        base_text,
13595        &mut cx,
13596    );
13597}
13598
13599#[gpui::test]
13600async fn test_modification_reverts(cx: &mut TestAppContext) {
13601    init_test(cx, |_| {});
13602    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13603    let base_text = indoc! {r#"
13604        struct Row;
13605        struct Row1;
13606        struct Row2;
13607
13608        struct Row4;
13609        struct Row5;
13610        struct Row6;
13611
13612        struct Row8;
13613        struct Row9;
13614        struct Row10;"#};
13615
13616    // Modification hunks behave the same as the addition ones.
13617    assert_hunk_revert(
13618        indoc! {r#"struct Row;
13619                   struct Row1;
13620                   struct Row33;
13621                   ˇ
13622                   struct Row4;
13623                   struct Row5;
13624                   struct Row6;
13625                   ˇ
13626                   struct Row99;
13627                   struct Row9;
13628                   struct Row10;"#},
13629        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13630        indoc! {r#"struct Row;
13631                   struct Row1;
13632                   struct Row33;
13633                   ˇ
13634                   struct Row4;
13635                   struct Row5;
13636                   struct Row6;
13637                   ˇ
13638                   struct Row99;
13639                   struct Row9;
13640                   struct Row10;"#},
13641        base_text,
13642        &mut cx,
13643    );
13644    assert_hunk_revert(
13645        indoc! {r#"struct Row;
13646                   struct Row1;
13647                   struct Row33;
13648                   «ˇ
13649                   struct Row4;
13650                   struct» Row5;
13651                   «struct Row6;
13652                   ˇ»
13653                   struct Row99;
13654                   struct Row9;
13655                   struct Row10;"#},
13656        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
13657        indoc! {r#"struct Row;
13658                   struct Row1;
13659                   struct Row33;
13660                   «ˇ
13661                   struct Row4;
13662                   struct» Row5;
13663                   «struct Row6;
13664                   ˇ»
13665                   struct Row99;
13666                   struct Row9;
13667                   struct Row10;"#},
13668        base_text,
13669        &mut cx,
13670    );
13671
13672    assert_hunk_revert(
13673        indoc! {r#"ˇstruct Row1.1;
13674                   struct Row1;
13675                   «ˇstr»uct Row22;
13676
13677                   struct ˇRow44;
13678                   struct Row5;
13679                   struct «Rˇ»ow66;ˇ
13680
13681                   «struˇ»ct Row88;
13682                   struct Row9;
13683                   struct Row1011;ˇ"#},
13684        vec![
13685            DiffHunkStatusKind::Modified,
13686            DiffHunkStatusKind::Modified,
13687            DiffHunkStatusKind::Modified,
13688            DiffHunkStatusKind::Modified,
13689            DiffHunkStatusKind::Modified,
13690            DiffHunkStatusKind::Modified,
13691        ],
13692        indoc! {r#"struct Row;
13693                   ˇstruct Row1;
13694                   struct Row2;
13695                   ˇ
13696                   struct Row4;
13697                   ˇstruct Row5;
13698                   struct Row6;
13699                   ˇ
13700                   struct Row8;
13701                   ˇstruct Row9;
13702                   struct Row10;ˇ"#},
13703        base_text,
13704        &mut cx,
13705    );
13706}
13707
13708#[gpui::test]
13709async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
13710    init_test(cx, |_| {});
13711    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13712    let base_text = indoc! {r#"
13713        one
13714
13715        two
13716        three
13717        "#};
13718
13719    cx.set_head_text(base_text);
13720    cx.set_state("\nˇ\n");
13721    cx.executor().run_until_parked();
13722    cx.update_editor(|editor, _window, cx| {
13723        editor.expand_selected_diff_hunks(cx);
13724    });
13725    cx.executor().run_until_parked();
13726    cx.update_editor(|editor, window, cx| {
13727        editor.backspace(&Default::default(), window, cx);
13728    });
13729    cx.run_until_parked();
13730    cx.assert_state_with_diff(
13731        indoc! {r#"
13732
13733        - two
13734        - threeˇ
13735        +
13736        "#}
13737        .to_string(),
13738    );
13739}
13740
13741#[gpui::test]
13742async fn test_deletion_reverts(cx: &mut TestAppContext) {
13743    init_test(cx, |_| {});
13744    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13745    let base_text = indoc! {r#"struct Row;
13746struct Row1;
13747struct Row2;
13748
13749struct Row4;
13750struct Row5;
13751struct Row6;
13752
13753struct Row8;
13754struct Row9;
13755struct Row10;"#};
13756
13757    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
13758    assert_hunk_revert(
13759        indoc! {r#"struct Row;
13760                   struct Row2;
13761
13762                   ˇstruct Row4;
13763                   struct Row5;
13764                   struct Row6;
13765                   ˇ
13766                   struct Row8;
13767                   struct Row10;"#},
13768        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13769        indoc! {r#"struct Row;
13770                   struct Row2;
13771
13772                   ˇstruct Row4;
13773                   struct Row5;
13774                   struct Row6;
13775                   ˇ
13776                   struct Row8;
13777                   struct Row10;"#},
13778        base_text,
13779        &mut cx,
13780    );
13781    assert_hunk_revert(
13782        indoc! {r#"struct Row;
13783                   struct Row2;
13784
13785                   «ˇstruct Row4;
13786                   struct» Row5;
13787                   «struct Row6;
13788                   ˇ»
13789                   struct Row8;
13790                   struct Row10;"#},
13791        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13792        indoc! {r#"struct Row;
13793                   struct Row2;
13794
13795                   «ˇstruct Row4;
13796                   struct» Row5;
13797                   «struct Row6;
13798                   ˇ»
13799                   struct Row8;
13800                   struct Row10;"#},
13801        base_text,
13802        &mut cx,
13803    );
13804
13805    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
13806    assert_hunk_revert(
13807        indoc! {r#"struct Row;
13808                   ˇstruct Row2;
13809
13810                   struct Row4;
13811                   struct Row5;
13812                   struct Row6;
13813
13814                   struct Row8;ˇ
13815                   struct Row10;"#},
13816        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
13817        indoc! {r#"struct Row;
13818                   struct Row1;
13819                   ˇstruct Row2;
13820
13821                   struct Row4;
13822                   struct Row5;
13823                   struct Row6;
13824
13825                   struct Row8;ˇ
13826                   struct Row9;
13827                   struct Row10;"#},
13828        base_text,
13829        &mut cx,
13830    );
13831    assert_hunk_revert(
13832        indoc! {r#"struct Row;
13833                   struct Row2«ˇ;
13834                   struct Row4;
13835                   struct» Row5;
13836                   «struct Row6;
13837
13838                   struct Row8;ˇ»
13839                   struct Row10;"#},
13840        vec![
13841            DiffHunkStatusKind::Deleted,
13842            DiffHunkStatusKind::Deleted,
13843            DiffHunkStatusKind::Deleted,
13844        ],
13845        indoc! {r#"struct Row;
13846                   struct Row1;
13847                   struct Row2«ˇ;
13848
13849                   struct Row4;
13850                   struct» Row5;
13851                   «struct Row6;
13852
13853                   struct Row8;ˇ»
13854                   struct Row9;
13855                   struct Row10;"#},
13856        base_text,
13857        &mut cx,
13858    );
13859}
13860
13861#[gpui::test]
13862async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
13863    init_test(cx, |_| {});
13864
13865    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
13866    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
13867    let base_text_3 =
13868        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
13869
13870    let text_1 = edit_first_char_of_every_line(base_text_1);
13871    let text_2 = edit_first_char_of_every_line(base_text_2);
13872    let text_3 = edit_first_char_of_every_line(base_text_3);
13873
13874    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
13875    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
13876    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
13877
13878    let multibuffer = cx.new(|cx| {
13879        let mut multibuffer = MultiBuffer::new(ReadWrite);
13880        multibuffer.push_excerpts(
13881            buffer_1.clone(),
13882            [
13883                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13884                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13885                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13886            ],
13887            cx,
13888        );
13889        multibuffer.push_excerpts(
13890            buffer_2.clone(),
13891            [
13892                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13893                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13894                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13895            ],
13896            cx,
13897        );
13898        multibuffer.push_excerpts(
13899            buffer_3.clone(),
13900            [
13901                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
13902                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
13903                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
13904            ],
13905            cx,
13906        );
13907        multibuffer
13908    });
13909
13910    let fs = FakeFs::new(cx.executor());
13911    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
13912    let (editor, cx) = cx
13913        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
13914    editor.update_in(cx, |editor, _window, cx| {
13915        for (buffer, diff_base) in [
13916            (buffer_1.clone(), base_text_1),
13917            (buffer_2.clone(), base_text_2),
13918            (buffer_3.clone(), base_text_3),
13919        ] {
13920            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
13921            editor
13922                .buffer
13923                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
13924        }
13925    });
13926    cx.executor().run_until_parked();
13927
13928    editor.update_in(cx, |editor, window, cx| {
13929        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}");
13930        editor.select_all(&SelectAll, window, cx);
13931        editor.git_restore(&Default::default(), window, cx);
13932    });
13933    cx.executor().run_until_parked();
13934
13935    // When all ranges are selected, all buffer hunks are reverted.
13936    editor.update(cx, |editor, cx| {
13937        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");
13938    });
13939    buffer_1.update(cx, |buffer, _| {
13940        assert_eq!(buffer.text(), base_text_1);
13941    });
13942    buffer_2.update(cx, |buffer, _| {
13943        assert_eq!(buffer.text(), base_text_2);
13944    });
13945    buffer_3.update(cx, |buffer, _| {
13946        assert_eq!(buffer.text(), base_text_3);
13947    });
13948
13949    editor.update_in(cx, |editor, window, cx| {
13950        editor.undo(&Default::default(), window, cx);
13951    });
13952
13953    editor.update_in(cx, |editor, window, cx| {
13954        editor.change_selections(None, window, cx, |s| {
13955            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
13956        });
13957        editor.git_restore(&Default::default(), window, cx);
13958    });
13959
13960    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
13961    // but not affect buffer_2 and its related excerpts.
13962    editor.update(cx, |editor, cx| {
13963        assert_eq!(
13964            editor.text(cx),
13965            "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}"
13966        );
13967    });
13968    buffer_1.update(cx, |buffer, _| {
13969        assert_eq!(buffer.text(), base_text_1);
13970    });
13971    buffer_2.update(cx, |buffer, _| {
13972        assert_eq!(
13973            buffer.text(),
13974            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
13975        );
13976    });
13977    buffer_3.update(cx, |buffer, _| {
13978        assert_eq!(
13979            buffer.text(),
13980            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
13981        );
13982    });
13983
13984    fn edit_first_char_of_every_line(text: &str) -> String {
13985        text.split('\n')
13986            .map(|line| format!("X{}", &line[1..]))
13987            .collect::<Vec<_>>()
13988            .join("\n")
13989    }
13990}
13991
13992#[gpui::test]
13993async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
13994    init_test(cx, |_| {});
13995
13996    let cols = 4;
13997    let rows = 10;
13998    let sample_text_1 = sample_text(rows, cols, 'a');
13999    assert_eq!(
14000        sample_text_1,
14001        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14002    );
14003    let sample_text_2 = sample_text(rows, cols, 'l');
14004    assert_eq!(
14005        sample_text_2,
14006        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14007    );
14008    let sample_text_3 = sample_text(rows, cols, 'v');
14009    assert_eq!(
14010        sample_text_3,
14011        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14012    );
14013
14014    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14015    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14016    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14017
14018    let multi_buffer = cx.new(|cx| {
14019        let mut multibuffer = MultiBuffer::new(ReadWrite);
14020        multibuffer.push_excerpts(
14021            buffer_1.clone(),
14022            [
14023                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14024                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14025                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14026            ],
14027            cx,
14028        );
14029        multibuffer.push_excerpts(
14030            buffer_2.clone(),
14031            [
14032                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14033                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14034                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14035            ],
14036            cx,
14037        );
14038        multibuffer.push_excerpts(
14039            buffer_3.clone(),
14040            [
14041                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14042                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14043                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14044            ],
14045            cx,
14046        );
14047        multibuffer
14048    });
14049
14050    let fs = FakeFs::new(cx.executor());
14051    fs.insert_tree(
14052        "/a",
14053        json!({
14054            "main.rs": sample_text_1,
14055            "other.rs": sample_text_2,
14056            "lib.rs": sample_text_3,
14057        }),
14058    )
14059    .await;
14060    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14061    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14062    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14063    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14064        Editor::new(
14065            EditorMode::Full,
14066            multi_buffer,
14067            Some(project.clone()),
14068            window,
14069            cx,
14070        )
14071    });
14072    let multibuffer_item_id = workspace
14073        .update(cx, |workspace, window, cx| {
14074            assert!(
14075                workspace.active_item(cx).is_none(),
14076                "active item should be None before the first item is added"
14077            );
14078            workspace.add_item_to_active_pane(
14079                Box::new(multi_buffer_editor.clone()),
14080                None,
14081                true,
14082                window,
14083                cx,
14084            );
14085            let active_item = workspace
14086                .active_item(cx)
14087                .expect("should have an active item after adding the multi buffer");
14088            assert!(
14089                !active_item.is_singleton(cx),
14090                "A multi buffer was expected to active after adding"
14091            );
14092            active_item.item_id()
14093        })
14094        .unwrap();
14095    cx.executor().run_until_parked();
14096
14097    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14098        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14099            s.select_ranges(Some(1..2))
14100        });
14101        editor.open_excerpts(&OpenExcerpts, window, cx);
14102    });
14103    cx.executor().run_until_parked();
14104    let first_item_id = workspace
14105        .update(cx, |workspace, window, cx| {
14106            let active_item = workspace
14107                .active_item(cx)
14108                .expect("should have an active item after navigating into the 1st buffer");
14109            let first_item_id = active_item.item_id();
14110            assert_ne!(
14111                first_item_id, multibuffer_item_id,
14112                "Should navigate into the 1st buffer and activate it"
14113            );
14114            assert!(
14115                active_item.is_singleton(cx),
14116                "New active item should be a singleton buffer"
14117            );
14118            assert_eq!(
14119                active_item
14120                    .act_as::<Editor>(cx)
14121                    .expect("should have navigated into an editor for the 1st buffer")
14122                    .read(cx)
14123                    .text(cx),
14124                sample_text_1
14125            );
14126
14127            workspace
14128                .go_back(workspace.active_pane().downgrade(), window, cx)
14129                .detach_and_log_err(cx);
14130
14131            first_item_id
14132        })
14133        .unwrap();
14134    cx.executor().run_until_parked();
14135    workspace
14136        .update(cx, |workspace, _, cx| {
14137            let active_item = workspace
14138                .active_item(cx)
14139                .expect("should have an active item after navigating back");
14140            assert_eq!(
14141                active_item.item_id(),
14142                multibuffer_item_id,
14143                "Should navigate back to the multi buffer"
14144            );
14145            assert!(!active_item.is_singleton(cx));
14146        })
14147        .unwrap();
14148
14149    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14150        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14151            s.select_ranges(Some(39..40))
14152        });
14153        editor.open_excerpts(&OpenExcerpts, window, cx);
14154    });
14155    cx.executor().run_until_parked();
14156    let second_item_id = workspace
14157        .update(cx, |workspace, window, cx| {
14158            let active_item = workspace
14159                .active_item(cx)
14160                .expect("should have an active item after navigating into the 2nd buffer");
14161            let second_item_id = active_item.item_id();
14162            assert_ne!(
14163                second_item_id, multibuffer_item_id,
14164                "Should navigate away from the multibuffer"
14165            );
14166            assert_ne!(
14167                second_item_id, first_item_id,
14168                "Should navigate into the 2nd buffer and activate it"
14169            );
14170            assert!(
14171                active_item.is_singleton(cx),
14172                "New active item should be a singleton buffer"
14173            );
14174            assert_eq!(
14175                active_item
14176                    .act_as::<Editor>(cx)
14177                    .expect("should have navigated into an editor")
14178                    .read(cx)
14179                    .text(cx),
14180                sample_text_2
14181            );
14182
14183            workspace
14184                .go_back(workspace.active_pane().downgrade(), window, cx)
14185                .detach_and_log_err(cx);
14186
14187            second_item_id
14188        })
14189        .unwrap();
14190    cx.executor().run_until_parked();
14191    workspace
14192        .update(cx, |workspace, _, cx| {
14193            let active_item = workspace
14194                .active_item(cx)
14195                .expect("should have an active item after navigating back from the 2nd buffer");
14196            assert_eq!(
14197                active_item.item_id(),
14198                multibuffer_item_id,
14199                "Should navigate back from the 2nd buffer to the multi buffer"
14200            );
14201            assert!(!active_item.is_singleton(cx));
14202        })
14203        .unwrap();
14204
14205    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14206        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14207            s.select_ranges(Some(70..70))
14208        });
14209        editor.open_excerpts(&OpenExcerpts, window, cx);
14210    });
14211    cx.executor().run_until_parked();
14212    workspace
14213        .update(cx, |workspace, window, cx| {
14214            let active_item = workspace
14215                .active_item(cx)
14216                .expect("should have an active item after navigating into the 3rd buffer");
14217            let third_item_id = active_item.item_id();
14218            assert_ne!(
14219                third_item_id, multibuffer_item_id,
14220                "Should navigate into the 3rd buffer and activate it"
14221            );
14222            assert_ne!(third_item_id, first_item_id);
14223            assert_ne!(third_item_id, second_item_id);
14224            assert!(
14225                active_item.is_singleton(cx),
14226                "New active item should be a singleton buffer"
14227            );
14228            assert_eq!(
14229                active_item
14230                    .act_as::<Editor>(cx)
14231                    .expect("should have navigated into an editor")
14232                    .read(cx)
14233                    .text(cx),
14234                sample_text_3
14235            );
14236
14237            workspace
14238                .go_back(workspace.active_pane().downgrade(), window, cx)
14239                .detach_and_log_err(cx);
14240        })
14241        .unwrap();
14242    cx.executor().run_until_parked();
14243    workspace
14244        .update(cx, |workspace, _, cx| {
14245            let active_item = workspace
14246                .active_item(cx)
14247                .expect("should have an active item after navigating back from the 3rd buffer");
14248            assert_eq!(
14249                active_item.item_id(),
14250                multibuffer_item_id,
14251                "Should navigate back from the 3rd buffer to the multi buffer"
14252            );
14253            assert!(!active_item.is_singleton(cx));
14254        })
14255        .unwrap();
14256}
14257
14258#[gpui::test]
14259async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14260    init_test(cx, |_| {});
14261
14262    let mut cx = EditorTestContext::new(cx).await;
14263
14264    let diff_base = r#"
14265        use some::mod;
14266
14267        const A: u32 = 42;
14268
14269        fn main() {
14270            println!("hello");
14271
14272            println!("world");
14273        }
14274        "#
14275    .unindent();
14276
14277    cx.set_state(
14278        &r#"
14279        use some::modified;
14280
14281        ˇ
14282        fn main() {
14283            println!("hello there");
14284
14285            println!("around the");
14286            println!("world");
14287        }
14288        "#
14289        .unindent(),
14290    );
14291
14292    cx.set_head_text(&diff_base);
14293    executor.run_until_parked();
14294
14295    cx.update_editor(|editor, window, cx| {
14296        editor.go_to_next_hunk(&GoToHunk, window, cx);
14297        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14298    });
14299    executor.run_until_parked();
14300    cx.assert_state_with_diff(
14301        r#"
14302          use some::modified;
14303
14304
14305          fn main() {
14306        -     println!("hello");
14307        + ˇ    println!("hello there");
14308
14309              println!("around the");
14310              println!("world");
14311          }
14312        "#
14313        .unindent(),
14314    );
14315
14316    cx.update_editor(|editor, window, cx| {
14317        for _ in 0..2 {
14318            editor.go_to_next_hunk(&GoToHunk, window, cx);
14319            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14320        }
14321    });
14322    executor.run_until_parked();
14323    cx.assert_state_with_diff(
14324        r#"
14325        - use some::mod;
14326        + ˇuse some::modified;
14327
14328
14329          fn main() {
14330        -     println!("hello");
14331        +     println!("hello there");
14332
14333        +     println!("around the");
14334              println!("world");
14335          }
14336        "#
14337        .unindent(),
14338    );
14339
14340    cx.update_editor(|editor, window, cx| {
14341        editor.go_to_next_hunk(&GoToHunk, window, cx);
14342        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14343    });
14344    executor.run_until_parked();
14345    cx.assert_state_with_diff(
14346        r#"
14347        - use some::mod;
14348        + use some::modified;
14349
14350        - const A: u32 = 42;
14351          ˇ
14352          fn main() {
14353        -     println!("hello");
14354        +     println!("hello there");
14355
14356        +     println!("around the");
14357              println!("world");
14358          }
14359        "#
14360        .unindent(),
14361    );
14362
14363    cx.update_editor(|editor, window, cx| {
14364        editor.cancel(&Cancel, window, cx);
14365    });
14366
14367    cx.assert_state_with_diff(
14368        r#"
14369          use some::modified;
14370
14371          ˇ
14372          fn main() {
14373              println!("hello there");
14374
14375              println!("around the");
14376              println!("world");
14377          }
14378        "#
14379        .unindent(),
14380    );
14381}
14382
14383#[gpui::test]
14384async fn test_diff_base_change_with_expanded_diff_hunks(
14385    executor: BackgroundExecutor,
14386    cx: &mut TestAppContext,
14387) {
14388    init_test(cx, |_| {});
14389
14390    let mut cx = EditorTestContext::new(cx).await;
14391
14392    let diff_base = r#"
14393        use some::mod1;
14394        use some::mod2;
14395
14396        const A: u32 = 42;
14397        const B: u32 = 42;
14398        const C: u32 = 42;
14399
14400        fn main() {
14401            println!("hello");
14402
14403            println!("world");
14404        }
14405        "#
14406    .unindent();
14407
14408    cx.set_state(
14409        &r#"
14410        use some::mod2;
14411
14412        const A: u32 = 42;
14413        const C: u32 = 42;
14414
14415        fn main(ˇ) {
14416            //println!("hello");
14417
14418            println!("world");
14419            //
14420            //
14421        }
14422        "#
14423        .unindent(),
14424    );
14425
14426    cx.set_head_text(&diff_base);
14427    executor.run_until_parked();
14428
14429    cx.update_editor(|editor, window, cx| {
14430        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14431    });
14432    executor.run_until_parked();
14433    cx.assert_state_with_diff(
14434        r#"
14435        - use some::mod1;
14436          use some::mod2;
14437
14438          const A: u32 = 42;
14439        - const B: u32 = 42;
14440          const C: u32 = 42;
14441
14442          fn main(ˇ) {
14443        -     println!("hello");
14444        +     //println!("hello");
14445
14446              println!("world");
14447        +     //
14448        +     //
14449          }
14450        "#
14451        .unindent(),
14452    );
14453
14454    cx.set_head_text("new diff base!");
14455    executor.run_until_parked();
14456    cx.assert_state_with_diff(
14457        r#"
14458        - new diff base!
14459        + use some::mod2;
14460        +
14461        + const A: u32 = 42;
14462        + const C: u32 = 42;
14463        +
14464        + fn main(ˇ) {
14465        +     //println!("hello");
14466        +
14467        +     println!("world");
14468        +     //
14469        +     //
14470        + }
14471        "#
14472        .unindent(),
14473    );
14474}
14475
14476#[gpui::test]
14477async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
14478    init_test(cx, |_| {});
14479
14480    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14481    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
14482    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14483    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
14484    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
14485    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
14486
14487    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
14488    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
14489    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
14490
14491    let multi_buffer = cx.new(|cx| {
14492        let mut multibuffer = MultiBuffer::new(ReadWrite);
14493        multibuffer.push_excerpts(
14494            buffer_1.clone(),
14495            [
14496                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14497                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14498                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14499            ],
14500            cx,
14501        );
14502        multibuffer.push_excerpts(
14503            buffer_2.clone(),
14504            [
14505                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14506                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14507                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14508            ],
14509            cx,
14510        );
14511        multibuffer.push_excerpts(
14512            buffer_3.clone(),
14513            [
14514                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14515                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14516                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
14517            ],
14518            cx,
14519        );
14520        multibuffer
14521    });
14522
14523    let editor =
14524        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14525    editor
14526        .update(cx, |editor, _window, cx| {
14527            for (buffer, diff_base) in [
14528                (buffer_1.clone(), file_1_old),
14529                (buffer_2.clone(), file_2_old),
14530                (buffer_3.clone(), file_3_old),
14531            ] {
14532                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14533                editor
14534                    .buffer
14535                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14536            }
14537        })
14538        .unwrap();
14539
14540    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14541    cx.run_until_parked();
14542
14543    cx.assert_editor_state(
14544        &"
14545            ˇaaa
14546            ccc
14547            ddd
14548
14549            ggg
14550            hhh
14551
14552
14553            lll
14554            mmm
14555            NNN
14556
14557            qqq
14558            rrr
14559
14560            uuu
14561            111
14562            222
14563            333
14564
14565            666
14566            777
14567
14568            000
14569            !!!"
14570        .unindent(),
14571    );
14572
14573    cx.update_editor(|editor, window, cx| {
14574        editor.select_all(&SelectAll, window, cx);
14575        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14576    });
14577    cx.executor().run_until_parked();
14578
14579    cx.assert_state_with_diff(
14580        "
14581            «aaa
14582          - bbb
14583            ccc
14584            ddd
14585
14586            ggg
14587            hhh
14588
14589
14590            lll
14591            mmm
14592          - nnn
14593          + NNN
14594
14595            qqq
14596            rrr
14597
14598            uuu
14599            111
14600            222
14601            333
14602
14603          + 666
14604            777
14605
14606            000
14607            !!!ˇ»"
14608            .unindent(),
14609    );
14610}
14611
14612#[gpui::test]
14613async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
14614    init_test(cx, |_| {});
14615
14616    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
14617    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
14618
14619    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
14620    let multi_buffer = cx.new(|cx| {
14621        let mut multibuffer = MultiBuffer::new(ReadWrite);
14622        multibuffer.push_excerpts(
14623            buffer.clone(),
14624            [
14625                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
14626                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
14627                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
14628            ],
14629            cx,
14630        );
14631        multibuffer
14632    });
14633
14634    let editor =
14635        cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx));
14636    editor
14637        .update(cx, |editor, _window, cx| {
14638            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
14639            editor
14640                .buffer
14641                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
14642        })
14643        .unwrap();
14644
14645    let mut cx = EditorTestContext::for_editor(editor, cx).await;
14646    cx.run_until_parked();
14647
14648    cx.update_editor(|editor, window, cx| {
14649        editor.expand_all_diff_hunks(&Default::default(), window, cx)
14650    });
14651    cx.executor().run_until_parked();
14652
14653    // When the start of a hunk coincides with the start of its excerpt,
14654    // the hunk is expanded. When the start of a a hunk is earlier than
14655    // the start of its excerpt, the hunk is not expanded.
14656    cx.assert_state_with_diff(
14657        "
14658            ˇaaa
14659          - bbb
14660          + BBB
14661
14662          - ddd
14663          - eee
14664          + DDD
14665          + EEE
14666            fff
14667
14668            iii
14669        "
14670        .unindent(),
14671    );
14672}
14673
14674#[gpui::test]
14675async fn test_edits_around_expanded_insertion_hunks(
14676    executor: BackgroundExecutor,
14677    cx: &mut TestAppContext,
14678) {
14679    init_test(cx, |_| {});
14680
14681    let mut cx = EditorTestContext::new(cx).await;
14682
14683    let diff_base = r#"
14684        use some::mod1;
14685        use some::mod2;
14686
14687        const A: u32 = 42;
14688
14689        fn main() {
14690            println!("hello");
14691
14692            println!("world");
14693        }
14694        "#
14695    .unindent();
14696    executor.run_until_parked();
14697    cx.set_state(
14698        &r#"
14699        use some::mod1;
14700        use some::mod2;
14701
14702        const A: u32 = 42;
14703        const B: u32 = 42;
14704        const C: u32 = 42;
14705        ˇ
14706
14707        fn main() {
14708            println!("hello");
14709
14710            println!("world");
14711        }
14712        "#
14713        .unindent(),
14714    );
14715
14716    cx.set_head_text(&diff_base);
14717    executor.run_until_parked();
14718
14719    cx.update_editor(|editor, window, cx| {
14720        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14721    });
14722    executor.run_until_parked();
14723
14724    cx.assert_state_with_diff(
14725        r#"
14726        use some::mod1;
14727        use some::mod2;
14728
14729        const A: u32 = 42;
14730      + const B: u32 = 42;
14731      + const C: u32 = 42;
14732      + ˇ
14733
14734        fn main() {
14735            println!("hello");
14736
14737            println!("world");
14738        }
14739      "#
14740        .unindent(),
14741    );
14742
14743    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
14744    executor.run_until_parked();
14745
14746    cx.assert_state_with_diff(
14747        r#"
14748        use some::mod1;
14749        use some::mod2;
14750
14751        const A: u32 = 42;
14752      + const B: u32 = 42;
14753      + const C: u32 = 42;
14754      + const D: u32 = 42;
14755      + ˇ
14756
14757        fn main() {
14758            println!("hello");
14759
14760            println!("world");
14761        }
14762      "#
14763        .unindent(),
14764    );
14765
14766    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
14767    executor.run_until_parked();
14768
14769    cx.assert_state_with_diff(
14770        r#"
14771        use some::mod1;
14772        use some::mod2;
14773
14774        const A: u32 = 42;
14775      + const B: u32 = 42;
14776      + const C: u32 = 42;
14777      + const D: u32 = 42;
14778      + const E: u32 = 42;
14779      + ˇ
14780
14781        fn main() {
14782            println!("hello");
14783
14784            println!("world");
14785        }
14786      "#
14787        .unindent(),
14788    );
14789
14790    cx.update_editor(|editor, window, cx| {
14791        editor.delete_line(&DeleteLine, window, cx);
14792    });
14793    executor.run_until_parked();
14794
14795    cx.assert_state_with_diff(
14796        r#"
14797        use some::mod1;
14798        use some::mod2;
14799
14800        const A: u32 = 42;
14801      + const B: u32 = 42;
14802      + const C: u32 = 42;
14803      + const D: u32 = 42;
14804      + const E: u32 = 42;
14805        ˇ
14806        fn main() {
14807            println!("hello");
14808
14809            println!("world");
14810        }
14811      "#
14812        .unindent(),
14813    );
14814
14815    cx.update_editor(|editor, window, cx| {
14816        editor.move_up(&MoveUp, window, cx);
14817        editor.delete_line(&DeleteLine, window, cx);
14818        editor.move_up(&MoveUp, window, cx);
14819        editor.delete_line(&DeleteLine, window, cx);
14820        editor.move_up(&MoveUp, window, cx);
14821        editor.delete_line(&DeleteLine, window, cx);
14822    });
14823    executor.run_until_parked();
14824    cx.assert_state_with_diff(
14825        r#"
14826        use some::mod1;
14827        use some::mod2;
14828
14829        const A: u32 = 42;
14830      + const B: u32 = 42;
14831        ˇ
14832        fn main() {
14833            println!("hello");
14834
14835            println!("world");
14836        }
14837      "#
14838        .unindent(),
14839    );
14840
14841    cx.update_editor(|editor, window, cx| {
14842        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
14843        editor.delete_line(&DeleteLine, window, cx);
14844    });
14845    executor.run_until_parked();
14846    cx.assert_state_with_diff(
14847        r#"
14848        ˇ
14849        fn main() {
14850            println!("hello");
14851
14852            println!("world");
14853        }
14854      "#
14855        .unindent(),
14856    );
14857}
14858
14859#[gpui::test]
14860async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
14861    init_test(cx, |_| {});
14862
14863    let mut cx = EditorTestContext::new(cx).await;
14864    cx.set_head_text(indoc! { "
14865        one
14866        two
14867        three
14868        four
14869        five
14870        "
14871    });
14872    cx.set_state(indoc! { "
14873        one
14874        ˇthree
14875        five
14876    "});
14877    cx.run_until_parked();
14878    cx.update_editor(|editor, window, cx| {
14879        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14880    });
14881    cx.assert_state_with_diff(
14882        indoc! { "
14883        one
14884      - two
14885        ˇthree
14886      - four
14887        five
14888    "}
14889        .to_string(),
14890    );
14891    cx.update_editor(|editor, window, cx| {
14892        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14893    });
14894
14895    cx.assert_state_with_diff(
14896        indoc! { "
14897        one
14898        ˇthree
14899        five
14900    "}
14901        .to_string(),
14902    );
14903
14904    cx.set_state(indoc! { "
14905        one
14906        ˇTWO
14907        three
14908        four
14909        five
14910    "});
14911    cx.run_until_parked();
14912    cx.update_editor(|editor, window, cx| {
14913        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14914    });
14915
14916    cx.assert_state_with_diff(
14917        indoc! { "
14918            one
14919          - two
14920          + ˇTWO
14921            three
14922            four
14923            five
14924        "}
14925        .to_string(),
14926    );
14927    cx.update_editor(|editor, window, cx| {
14928        editor.move_up(&Default::default(), window, cx);
14929        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
14930    });
14931    cx.assert_state_with_diff(
14932        indoc! { "
14933            one
14934            ˇTWO
14935            three
14936            four
14937            five
14938        "}
14939        .to_string(),
14940    );
14941}
14942
14943#[gpui::test]
14944async fn test_edits_around_expanded_deletion_hunks(
14945    executor: BackgroundExecutor,
14946    cx: &mut TestAppContext,
14947) {
14948    init_test(cx, |_| {});
14949
14950    let mut cx = EditorTestContext::new(cx).await;
14951
14952    let diff_base = r#"
14953        use some::mod1;
14954        use some::mod2;
14955
14956        const A: u32 = 42;
14957        const B: u32 = 42;
14958        const C: u32 = 42;
14959
14960
14961        fn main() {
14962            println!("hello");
14963
14964            println!("world");
14965        }
14966    "#
14967    .unindent();
14968    executor.run_until_parked();
14969    cx.set_state(
14970        &r#"
14971        use some::mod1;
14972        use some::mod2;
14973
14974        ˇconst B: u32 = 42;
14975        const C: u32 = 42;
14976
14977
14978        fn main() {
14979            println!("hello");
14980
14981            println!("world");
14982        }
14983        "#
14984        .unindent(),
14985    );
14986
14987    cx.set_head_text(&diff_base);
14988    executor.run_until_parked();
14989
14990    cx.update_editor(|editor, window, cx| {
14991        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14992    });
14993    executor.run_until_parked();
14994
14995    cx.assert_state_with_diff(
14996        r#"
14997        use some::mod1;
14998        use some::mod2;
14999
15000      - const A: u32 = 42;
15001        ˇconst B: u32 = 42;
15002        const C: u32 = 42;
15003
15004
15005        fn main() {
15006            println!("hello");
15007
15008            println!("world");
15009        }
15010      "#
15011        .unindent(),
15012    );
15013
15014    cx.update_editor(|editor, window, cx| {
15015        editor.delete_line(&DeleteLine, window, cx);
15016    });
15017    executor.run_until_parked();
15018    cx.assert_state_with_diff(
15019        r#"
15020        use some::mod1;
15021        use some::mod2;
15022
15023      - const A: u32 = 42;
15024      - const B: u32 = 42;
15025        ˇconst C: u32 = 42;
15026
15027
15028        fn main() {
15029            println!("hello");
15030
15031            println!("world");
15032        }
15033      "#
15034        .unindent(),
15035    );
15036
15037    cx.update_editor(|editor, window, cx| {
15038        editor.delete_line(&DeleteLine, window, cx);
15039    });
15040    executor.run_until_parked();
15041    cx.assert_state_with_diff(
15042        r#"
15043        use some::mod1;
15044        use some::mod2;
15045
15046      - const A: u32 = 42;
15047      - const B: u32 = 42;
15048      - const C: u32 = 42;
15049        ˇ
15050
15051        fn main() {
15052            println!("hello");
15053
15054            println!("world");
15055        }
15056      "#
15057        .unindent(),
15058    );
15059
15060    cx.update_editor(|editor, window, cx| {
15061        editor.handle_input("replacement", window, cx);
15062    });
15063    executor.run_until_parked();
15064    cx.assert_state_with_diff(
15065        r#"
15066        use some::mod1;
15067        use some::mod2;
15068
15069      - const A: u32 = 42;
15070      - const B: u32 = 42;
15071      - const C: u32 = 42;
15072      -
15073      + replacementˇ
15074
15075        fn main() {
15076            println!("hello");
15077
15078            println!("world");
15079        }
15080      "#
15081        .unindent(),
15082    );
15083}
15084
15085#[gpui::test]
15086async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15087    init_test(cx, |_| {});
15088
15089    let mut cx = EditorTestContext::new(cx).await;
15090
15091    let base_text = r#"
15092        one
15093        two
15094        three
15095        four
15096        five
15097    "#
15098    .unindent();
15099    executor.run_until_parked();
15100    cx.set_state(
15101        &r#"
15102        one
15103        two
15104        fˇour
15105        five
15106        "#
15107        .unindent(),
15108    );
15109
15110    cx.set_head_text(&base_text);
15111    executor.run_until_parked();
15112
15113    cx.update_editor(|editor, window, cx| {
15114        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15115    });
15116    executor.run_until_parked();
15117
15118    cx.assert_state_with_diff(
15119        r#"
15120          one
15121          two
15122        - three
15123          fˇour
15124          five
15125        "#
15126        .unindent(),
15127    );
15128
15129    cx.update_editor(|editor, window, cx| {
15130        editor.backspace(&Backspace, window, cx);
15131        editor.backspace(&Backspace, window, cx);
15132    });
15133    executor.run_until_parked();
15134    cx.assert_state_with_diff(
15135        r#"
15136          one
15137          two
15138        - threeˇ
15139        - four
15140        + our
15141          five
15142        "#
15143        .unindent(),
15144    );
15145}
15146
15147#[gpui::test]
15148async fn test_edit_after_expanded_modification_hunk(
15149    executor: BackgroundExecutor,
15150    cx: &mut TestAppContext,
15151) {
15152    init_test(cx, |_| {});
15153
15154    let mut cx = EditorTestContext::new(cx).await;
15155
15156    let diff_base = r#"
15157        use some::mod1;
15158        use some::mod2;
15159
15160        const A: u32 = 42;
15161        const B: u32 = 42;
15162        const C: u32 = 42;
15163        const D: u32 = 42;
15164
15165
15166        fn main() {
15167            println!("hello");
15168
15169            println!("world");
15170        }"#
15171    .unindent();
15172
15173    cx.set_state(
15174        &r#"
15175        use some::mod1;
15176        use some::mod2;
15177
15178        const A: u32 = 42;
15179        const B: u32 = 42;
15180        const C: u32 = 43ˇ
15181        const D: u32 = 42;
15182
15183
15184        fn main() {
15185            println!("hello");
15186
15187            println!("world");
15188        }"#
15189        .unindent(),
15190    );
15191
15192    cx.set_head_text(&diff_base);
15193    executor.run_until_parked();
15194    cx.update_editor(|editor, window, cx| {
15195        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15196    });
15197    executor.run_until_parked();
15198
15199    cx.assert_state_with_diff(
15200        r#"
15201        use some::mod1;
15202        use some::mod2;
15203
15204        const A: u32 = 42;
15205        const B: u32 = 42;
15206      - const C: u32 = 42;
15207      + const C: u32 = 43ˇ
15208        const D: u32 = 42;
15209
15210
15211        fn main() {
15212            println!("hello");
15213
15214            println!("world");
15215        }"#
15216        .unindent(),
15217    );
15218
15219    cx.update_editor(|editor, window, cx| {
15220        editor.handle_input("\nnew_line\n", window, cx);
15221    });
15222    executor.run_until_parked();
15223
15224    cx.assert_state_with_diff(
15225        r#"
15226        use some::mod1;
15227        use some::mod2;
15228
15229        const A: u32 = 42;
15230        const B: u32 = 42;
15231      - const C: u32 = 42;
15232      + const C: u32 = 43
15233      + new_line
15234      + ˇ
15235        const D: u32 = 42;
15236
15237
15238        fn main() {
15239            println!("hello");
15240
15241            println!("world");
15242        }"#
15243        .unindent(),
15244    );
15245}
15246
15247#[gpui::test]
15248async fn test_stage_and_unstage_added_file_hunk(
15249    executor: BackgroundExecutor,
15250    cx: &mut TestAppContext,
15251) {
15252    init_test(cx, |_| {});
15253
15254    let mut cx = EditorTestContext::new(cx).await;
15255    cx.update_editor(|editor, _, cx| {
15256        editor.set_expand_all_diff_hunks(cx);
15257    });
15258
15259    let working_copy = r#"
15260            ˇfn main() {
15261                println!("hello, world!");
15262            }
15263        "#
15264    .unindent();
15265
15266    cx.set_state(&working_copy);
15267    executor.run_until_parked();
15268
15269    cx.assert_state_with_diff(
15270        r#"
15271            + ˇfn main() {
15272            +     println!("hello, world!");
15273            + }
15274        "#
15275        .unindent(),
15276    );
15277    cx.assert_index_text(None);
15278
15279    cx.update_editor(|editor, window, cx| {
15280        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15281    });
15282    executor.run_until_parked();
15283    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15284    cx.assert_state_with_diff(
15285        r#"
15286            + ˇfn main() {
15287            +     println!("hello, world!");
15288            + }
15289        "#
15290        .unindent(),
15291    );
15292
15293    cx.update_editor(|editor, window, cx| {
15294        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15295    });
15296    executor.run_until_parked();
15297    cx.assert_index_text(None);
15298}
15299
15300async fn setup_indent_guides_editor(
15301    text: &str,
15302    cx: &mut TestAppContext,
15303) -> (BufferId, EditorTestContext) {
15304    init_test(cx, |_| {});
15305
15306    let mut cx = EditorTestContext::new(cx).await;
15307
15308    let buffer_id = cx.update_editor(|editor, window, cx| {
15309        editor.set_text(text, window, cx);
15310        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15311
15312        buffer_ids[0]
15313    });
15314
15315    (buffer_id, cx)
15316}
15317
15318fn assert_indent_guides(
15319    range: Range<u32>,
15320    expected: Vec<IndentGuide>,
15321    active_indices: Option<Vec<usize>>,
15322    cx: &mut EditorTestContext,
15323) {
15324    let indent_guides = cx.update_editor(|editor, window, cx| {
15325        let snapshot = editor.snapshot(window, cx).display_snapshot;
15326        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15327            editor,
15328            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15329            true,
15330            &snapshot,
15331            cx,
15332        );
15333
15334        indent_guides.sort_by(|a, b| {
15335            a.depth.cmp(&b.depth).then(
15336                a.start_row
15337                    .cmp(&b.start_row)
15338                    .then(a.end_row.cmp(&b.end_row)),
15339            )
15340        });
15341        indent_guides
15342    });
15343
15344    if let Some(expected) = active_indices {
15345        let active_indices = cx.update_editor(|editor, window, cx| {
15346            let snapshot = editor.snapshot(window, cx).display_snapshot;
15347            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15348        });
15349
15350        assert_eq!(
15351            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15352            expected,
15353            "Active indent guide indices do not match"
15354        );
15355    }
15356
15357    assert_eq!(indent_guides, expected, "Indent guides do not match");
15358}
15359
15360fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15361    IndentGuide {
15362        buffer_id,
15363        start_row: MultiBufferRow(start_row),
15364        end_row: MultiBufferRow(end_row),
15365        depth,
15366        tab_size: 4,
15367        settings: IndentGuideSettings {
15368            enabled: true,
15369            line_width: 1,
15370            active_line_width: 1,
15371            ..Default::default()
15372        },
15373    }
15374}
15375
15376#[gpui::test]
15377async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15378    let (buffer_id, mut cx) = setup_indent_guides_editor(
15379        &"
15380    fn main() {
15381        let a = 1;
15382    }"
15383        .unindent(),
15384        cx,
15385    )
15386    .await;
15387
15388    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15389}
15390
15391#[gpui::test]
15392async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15393    let (buffer_id, mut cx) = setup_indent_guides_editor(
15394        &"
15395    fn main() {
15396        let a = 1;
15397        let b = 2;
15398    }"
15399        .unindent(),
15400        cx,
15401    )
15402    .await;
15403
15404    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15405}
15406
15407#[gpui::test]
15408async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15409    let (buffer_id, mut cx) = setup_indent_guides_editor(
15410        &"
15411    fn main() {
15412        let a = 1;
15413        if a == 3 {
15414            let b = 2;
15415        } else {
15416            let c = 3;
15417        }
15418    }"
15419        .unindent(),
15420        cx,
15421    )
15422    .await;
15423
15424    assert_indent_guides(
15425        0..8,
15426        vec![
15427            indent_guide(buffer_id, 1, 6, 0),
15428            indent_guide(buffer_id, 3, 3, 1),
15429            indent_guide(buffer_id, 5, 5, 1),
15430        ],
15431        None,
15432        &mut cx,
15433    );
15434}
15435
15436#[gpui::test]
15437async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15438    let (buffer_id, mut cx) = setup_indent_guides_editor(
15439        &"
15440    fn main() {
15441        let a = 1;
15442            let b = 2;
15443        let c = 3;
15444    }"
15445        .unindent(),
15446        cx,
15447    )
15448    .await;
15449
15450    assert_indent_guides(
15451        0..5,
15452        vec![
15453            indent_guide(buffer_id, 1, 3, 0),
15454            indent_guide(buffer_id, 2, 2, 1),
15455        ],
15456        None,
15457        &mut cx,
15458    );
15459}
15460
15461#[gpui::test]
15462async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15463    let (buffer_id, mut cx) = setup_indent_guides_editor(
15464        &"
15465        fn main() {
15466            let a = 1;
15467
15468            let c = 3;
15469        }"
15470        .unindent(),
15471        cx,
15472    )
15473    .await;
15474
15475    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
15476}
15477
15478#[gpui::test]
15479async fn test_indent_guide_complex(cx: &mut TestAppContext) {
15480    let (buffer_id, mut cx) = setup_indent_guides_editor(
15481        &"
15482        fn main() {
15483            let a = 1;
15484
15485            let c = 3;
15486
15487            if a == 3 {
15488                let b = 2;
15489            } else {
15490                let c = 3;
15491            }
15492        }"
15493        .unindent(),
15494        cx,
15495    )
15496    .await;
15497
15498    assert_indent_guides(
15499        0..11,
15500        vec![
15501            indent_guide(buffer_id, 1, 9, 0),
15502            indent_guide(buffer_id, 6, 6, 1),
15503            indent_guide(buffer_id, 8, 8, 1),
15504        ],
15505        None,
15506        &mut cx,
15507    );
15508}
15509
15510#[gpui::test]
15511async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
15512    let (buffer_id, mut cx) = setup_indent_guides_editor(
15513        &"
15514        fn main() {
15515            let a = 1;
15516
15517            let c = 3;
15518
15519            if a == 3 {
15520                let b = 2;
15521            } else {
15522                let c = 3;
15523            }
15524        }"
15525        .unindent(),
15526        cx,
15527    )
15528    .await;
15529
15530    assert_indent_guides(
15531        1..11,
15532        vec![
15533            indent_guide(buffer_id, 1, 9, 0),
15534            indent_guide(buffer_id, 6, 6, 1),
15535            indent_guide(buffer_id, 8, 8, 1),
15536        ],
15537        None,
15538        &mut cx,
15539    );
15540}
15541
15542#[gpui::test]
15543async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
15544    let (buffer_id, mut cx) = setup_indent_guides_editor(
15545        &"
15546        fn main() {
15547            let a = 1;
15548
15549            let c = 3;
15550
15551            if a == 3 {
15552                let b = 2;
15553            } else {
15554                let c = 3;
15555            }
15556        }"
15557        .unindent(),
15558        cx,
15559    )
15560    .await;
15561
15562    assert_indent_guides(
15563        1..10,
15564        vec![
15565            indent_guide(buffer_id, 1, 9, 0),
15566            indent_guide(buffer_id, 6, 6, 1),
15567            indent_guide(buffer_id, 8, 8, 1),
15568        ],
15569        None,
15570        &mut cx,
15571    );
15572}
15573
15574#[gpui::test]
15575async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
15576    let (buffer_id, mut cx) = setup_indent_guides_editor(
15577        &"
15578        block1
15579            block2
15580                block3
15581                    block4
15582            block2
15583        block1
15584        block1"
15585            .unindent(),
15586        cx,
15587    )
15588    .await;
15589
15590    assert_indent_guides(
15591        1..10,
15592        vec![
15593            indent_guide(buffer_id, 1, 4, 0),
15594            indent_guide(buffer_id, 2, 3, 1),
15595            indent_guide(buffer_id, 3, 3, 2),
15596        ],
15597        None,
15598        &mut cx,
15599    );
15600}
15601
15602#[gpui::test]
15603async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
15604    let (buffer_id, mut cx) = setup_indent_guides_editor(
15605        &"
15606        block1
15607            block2
15608                block3
15609
15610        block1
15611        block1"
15612            .unindent(),
15613        cx,
15614    )
15615    .await;
15616
15617    assert_indent_guides(
15618        0..6,
15619        vec![
15620            indent_guide(buffer_id, 1, 2, 0),
15621            indent_guide(buffer_id, 2, 2, 1),
15622        ],
15623        None,
15624        &mut cx,
15625    );
15626}
15627
15628#[gpui::test]
15629async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
15630    let (buffer_id, mut cx) = setup_indent_guides_editor(
15631        &"
15632        block1
15633
15634
15635
15636            block2
15637        "
15638        .unindent(),
15639        cx,
15640    )
15641    .await;
15642
15643    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15644}
15645
15646#[gpui::test]
15647async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
15648    let (buffer_id, mut cx) = setup_indent_guides_editor(
15649        &"
15650        def a:
15651        \tb = 3
15652        \tif True:
15653        \t\tc = 4
15654        \t\td = 5
15655        \tprint(b)
15656        "
15657        .unindent(),
15658        cx,
15659    )
15660    .await;
15661
15662    assert_indent_guides(
15663        0..6,
15664        vec![
15665            indent_guide(buffer_id, 1, 6, 0),
15666            indent_guide(buffer_id, 3, 4, 1),
15667        ],
15668        None,
15669        &mut cx,
15670    );
15671}
15672
15673#[gpui::test]
15674async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
15675    let (buffer_id, mut cx) = setup_indent_guides_editor(
15676        &"
15677    fn main() {
15678        let a = 1;
15679    }"
15680        .unindent(),
15681        cx,
15682    )
15683    .await;
15684
15685    cx.update_editor(|editor, window, cx| {
15686        editor.change_selections(None, window, cx, |s| {
15687            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15688        });
15689    });
15690
15691    assert_indent_guides(
15692        0..3,
15693        vec![indent_guide(buffer_id, 1, 1, 0)],
15694        Some(vec![0]),
15695        &mut cx,
15696    );
15697}
15698
15699#[gpui::test]
15700async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
15701    let (buffer_id, mut cx) = setup_indent_guides_editor(
15702        &"
15703    fn main() {
15704        if 1 == 2 {
15705            let a = 1;
15706        }
15707    }"
15708        .unindent(),
15709        cx,
15710    )
15711    .await;
15712
15713    cx.update_editor(|editor, window, cx| {
15714        editor.change_selections(None, window, cx, |s| {
15715            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15716        });
15717    });
15718
15719    assert_indent_guides(
15720        0..4,
15721        vec![
15722            indent_guide(buffer_id, 1, 3, 0),
15723            indent_guide(buffer_id, 2, 2, 1),
15724        ],
15725        Some(vec![1]),
15726        &mut cx,
15727    );
15728
15729    cx.update_editor(|editor, window, cx| {
15730        editor.change_selections(None, window, cx, |s| {
15731            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15732        });
15733    });
15734
15735    assert_indent_guides(
15736        0..4,
15737        vec![
15738            indent_guide(buffer_id, 1, 3, 0),
15739            indent_guide(buffer_id, 2, 2, 1),
15740        ],
15741        Some(vec![1]),
15742        &mut cx,
15743    );
15744
15745    cx.update_editor(|editor, window, cx| {
15746        editor.change_selections(None, window, cx, |s| {
15747            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
15748        });
15749    });
15750
15751    assert_indent_guides(
15752        0..4,
15753        vec![
15754            indent_guide(buffer_id, 1, 3, 0),
15755            indent_guide(buffer_id, 2, 2, 1),
15756        ],
15757        Some(vec![0]),
15758        &mut cx,
15759    );
15760}
15761
15762#[gpui::test]
15763async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
15764    let (buffer_id, mut cx) = setup_indent_guides_editor(
15765        &"
15766    fn main() {
15767        let a = 1;
15768
15769        let b = 2;
15770    }"
15771        .unindent(),
15772        cx,
15773    )
15774    .await;
15775
15776    cx.update_editor(|editor, window, cx| {
15777        editor.change_selections(None, window, cx, |s| {
15778            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
15779        });
15780    });
15781
15782    assert_indent_guides(
15783        0..5,
15784        vec![indent_guide(buffer_id, 1, 3, 0)],
15785        Some(vec![0]),
15786        &mut cx,
15787    );
15788}
15789
15790#[gpui::test]
15791async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
15792    let (buffer_id, mut cx) = setup_indent_guides_editor(
15793        &"
15794    def m:
15795        a = 1
15796        pass"
15797            .unindent(),
15798        cx,
15799    )
15800    .await;
15801
15802    cx.update_editor(|editor, window, cx| {
15803        editor.change_selections(None, window, cx, |s| {
15804            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
15805        });
15806    });
15807
15808    assert_indent_guides(
15809        0..3,
15810        vec![indent_guide(buffer_id, 1, 2, 0)],
15811        Some(vec![0]),
15812        &mut cx,
15813    );
15814}
15815
15816#[gpui::test]
15817async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
15818    init_test(cx, |_| {});
15819    let mut cx = EditorTestContext::new(cx).await;
15820    let text = indoc! {
15821        "
15822        impl A {
15823            fn b() {
15824                0;
15825                3;
15826                5;
15827                6;
15828                7;
15829            }
15830        }
15831        "
15832    };
15833    let base_text = indoc! {
15834        "
15835        impl A {
15836            fn b() {
15837                0;
15838                1;
15839                2;
15840                3;
15841                4;
15842            }
15843            fn c() {
15844                5;
15845                6;
15846                7;
15847            }
15848        }
15849        "
15850    };
15851
15852    cx.update_editor(|editor, window, cx| {
15853        editor.set_text(text, window, cx);
15854
15855        editor.buffer().update(cx, |multibuffer, cx| {
15856            let buffer = multibuffer.as_singleton().unwrap();
15857            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
15858
15859            multibuffer.set_all_diff_hunks_expanded(cx);
15860            multibuffer.add_diff(diff, cx);
15861
15862            buffer.read(cx).remote_id()
15863        })
15864    });
15865    cx.run_until_parked();
15866
15867    cx.assert_state_with_diff(
15868        indoc! { "
15869          impl A {
15870              fn b() {
15871                  0;
15872        -         1;
15873        -         2;
15874                  3;
15875        -         4;
15876        -     }
15877        -     fn c() {
15878                  5;
15879                  6;
15880                  7;
15881              }
15882          }
15883          ˇ"
15884        }
15885        .to_string(),
15886    );
15887
15888    let mut actual_guides = cx.update_editor(|editor, window, cx| {
15889        editor
15890            .snapshot(window, cx)
15891            .buffer_snapshot
15892            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
15893            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
15894            .collect::<Vec<_>>()
15895    });
15896    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
15897    assert_eq!(
15898        actual_guides,
15899        vec![
15900            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
15901            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
15902            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
15903        ]
15904    );
15905}
15906
15907#[gpui::test]
15908async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15909    init_test(cx, |_| {});
15910    let mut cx = EditorTestContext::new(cx).await;
15911
15912    let diff_base = r#"
15913        a
15914        b
15915        c
15916        "#
15917    .unindent();
15918
15919    cx.set_state(
15920        &r#"
15921        ˇA
15922        b
15923        C
15924        "#
15925        .unindent(),
15926    );
15927    cx.set_head_text(&diff_base);
15928    cx.update_editor(|editor, window, cx| {
15929        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15930    });
15931    executor.run_until_parked();
15932
15933    let both_hunks_expanded = r#"
15934        - a
15935        + ˇA
15936          b
15937        - c
15938        + C
15939        "#
15940    .unindent();
15941
15942    cx.assert_state_with_diff(both_hunks_expanded.clone());
15943
15944    let hunk_ranges = cx.update_editor(|editor, window, cx| {
15945        let snapshot = editor.snapshot(window, cx);
15946        let hunks = editor
15947            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
15948            .collect::<Vec<_>>();
15949        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
15950        let buffer_id = hunks[0].buffer_id;
15951        hunks
15952            .into_iter()
15953            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
15954            .collect::<Vec<_>>()
15955    });
15956    assert_eq!(hunk_ranges.len(), 2);
15957
15958    cx.update_editor(|editor, _, cx| {
15959        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15960    });
15961    executor.run_until_parked();
15962
15963    let second_hunk_expanded = r#"
15964          ˇA
15965          b
15966        - c
15967        + C
15968        "#
15969    .unindent();
15970
15971    cx.assert_state_with_diff(second_hunk_expanded);
15972
15973    cx.update_editor(|editor, _, cx| {
15974        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
15975    });
15976    executor.run_until_parked();
15977
15978    cx.assert_state_with_diff(both_hunks_expanded.clone());
15979
15980    cx.update_editor(|editor, _, cx| {
15981        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15982    });
15983    executor.run_until_parked();
15984
15985    let first_hunk_expanded = r#"
15986        - a
15987        + ˇA
15988          b
15989          C
15990        "#
15991    .unindent();
15992
15993    cx.assert_state_with_diff(first_hunk_expanded);
15994
15995    cx.update_editor(|editor, _, cx| {
15996        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
15997    });
15998    executor.run_until_parked();
15999
16000    cx.assert_state_with_diff(both_hunks_expanded);
16001
16002    cx.set_state(
16003        &r#"
16004        ˇA
16005        b
16006        "#
16007        .unindent(),
16008    );
16009    cx.run_until_parked();
16010
16011    // TODO this cursor position seems bad
16012    cx.assert_state_with_diff(
16013        r#"
16014        - ˇa
16015        + A
16016          b
16017        "#
16018        .unindent(),
16019    );
16020
16021    cx.update_editor(|editor, window, cx| {
16022        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16023    });
16024
16025    cx.assert_state_with_diff(
16026        r#"
16027            - ˇa
16028            + A
16029              b
16030            - c
16031            "#
16032        .unindent(),
16033    );
16034
16035    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16036        let snapshot = editor.snapshot(window, cx);
16037        let hunks = editor
16038            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16039            .collect::<Vec<_>>();
16040        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16041        let buffer_id = hunks[0].buffer_id;
16042        hunks
16043            .into_iter()
16044            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16045            .collect::<Vec<_>>()
16046    });
16047    assert_eq!(hunk_ranges.len(), 2);
16048
16049    cx.update_editor(|editor, _, cx| {
16050        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16051    });
16052    executor.run_until_parked();
16053
16054    cx.assert_state_with_diff(
16055        r#"
16056        - ˇa
16057        + A
16058          b
16059        "#
16060        .unindent(),
16061    );
16062}
16063
16064#[gpui::test]
16065async fn test_toggle_deletion_hunk_at_start_of_file(
16066    executor: BackgroundExecutor,
16067    cx: &mut TestAppContext,
16068) {
16069    init_test(cx, |_| {});
16070    let mut cx = EditorTestContext::new(cx).await;
16071
16072    let diff_base = r#"
16073        a
16074        b
16075        c
16076        "#
16077    .unindent();
16078
16079    cx.set_state(
16080        &r#"
16081        ˇb
16082        c
16083        "#
16084        .unindent(),
16085    );
16086    cx.set_head_text(&diff_base);
16087    cx.update_editor(|editor, window, cx| {
16088        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16089    });
16090    executor.run_until_parked();
16091
16092    let hunk_expanded = r#"
16093        - a
16094          ˇb
16095          c
16096        "#
16097    .unindent();
16098
16099    cx.assert_state_with_diff(hunk_expanded.clone());
16100
16101    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16102        let snapshot = editor.snapshot(window, cx);
16103        let hunks = editor
16104            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16105            .collect::<Vec<_>>();
16106        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16107        let buffer_id = hunks[0].buffer_id;
16108        hunks
16109            .into_iter()
16110            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16111            .collect::<Vec<_>>()
16112    });
16113    assert_eq!(hunk_ranges.len(), 1);
16114
16115    cx.update_editor(|editor, _, cx| {
16116        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16117    });
16118    executor.run_until_parked();
16119
16120    let hunk_collapsed = r#"
16121          ˇb
16122          c
16123        "#
16124    .unindent();
16125
16126    cx.assert_state_with_diff(hunk_collapsed);
16127
16128    cx.update_editor(|editor, _, cx| {
16129        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16130    });
16131    executor.run_until_parked();
16132
16133    cx.assert_state_with_diff(hunk_expanded.clone());
16134}
16135
16136#[gpui::test]
16137async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16138    init_test(cx, |_| {});
16139
16140    let fs = FakeFs::new(cx.executor());
16141    fs.insert_tree(
16142        path!("/test"),
16143        json!({
16144            ".git": {},
16145            "file-1": "ONE\n",
16146            "file-2": "TWO\n",
16147            "file-3": "THREE\n",
16148        }),
16149    )
16150    .await;
16151
16152    fs.set_head_for_repo(
16153        path!("/test/.git").as_ref(),
16154        &[
16155            ("file-1".into(), "one\n".into()),
16156            ("file-2".into(), "two\n".into()),
16157            ("file-3".into(), "three\n".into()),
16158        ],
16159    );
16160
16161    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16162    let mut buffers = vec![];
16163    for i in 1..=3 {
16164        let buffer = project
16165            .update(cx, |project, cx| {
16166                let path = format!(path!("/test/file-{}"), i);
16167                project.open_local_buffer(path, cx)
16168            })
16169            .await
16170            .unwrap();
16171        buffers.push(buffer);
16172    }
16173
16174    let multibuffer = cx.new(|cx| {
16175        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16176        multibuffer.set_all_diff_hunks_expanded(cx);
16177        for buffer in &buffers {
16178            let snapshot = buffer.read(cx).snapshot();
16179            multibuffer.set_excerpts_for_path(
16180                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16181                buffer.clone(),
16182                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16183                DEFAULT_MULTIBUFFER_CONTEXT,
16184                cx,
16185            );
16186        }
16187        multibuffer
16188    });
16189
16190    let editor = cx.add_window(|window, cx| {
16191        Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx)
16192    });
16193    cx.run_until_parked();
16194
16195    let snapshot = editor
16196        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16197        .unwrap();
16198    let hunks = snapshot
16199        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16200        .map(|hunk| match hunk {
16201            DisplayDiffHunk::Unfolded {
16202                display_row_range, ..
16203            } => display_row_range,
16204            DisplayDiffHunk::Folded { .. } => unreachable!(),
16205        })
16206        .collect::<Vec<_>>();
16207    assert_eq!(
16208        hunks,
16209        [
16210            DisplayRow(2)..DisplayRow(4),
16211            DisplayRow(7)..DisplayRow(9),
16212            DisplayRow(12)..DisplayRow(14),
16213        ]
16214    );
16215}
16216
16217#[gpui::test]
16218async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16219    init_test(cx, |_| {});
16220
16221    let mut cx = EditorTestContext::new(cx).await;
16222    cx.set_head_text(indoc! { "
16223        one
16224        two
16225        three
16226        four
16227        five
16228        "
16229    });
16230    cx.set_index_text(indoc! { "
16231        one
16232        two
16233        three
16234        four
16235        five
16236        "
16237    });
16238    cx.set_state(indoc! {"
16239        one
16240        TWO
16241        ˇTHREE
16242        FOUR
16243        five
16244    "});
16245    cx.run_until_parked();
16246    cx.update_editor(|editor, window, cx| {
16247        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16248    });
16249    cx.run_until_parked();
16250    cx.assert_index_text(Some(indoc! {"
16251        one
16252        TWO
16253        THREE
16254        FOUR
16255        five
16256    "}));
16257    cx.set_state(indoc! { "
16258        one
16259        TWO
16260        ˇTHREE-HUNDRED
16261        FOUR
16262        five
16263    "});
16264    cx.run_until_parked();
16265    cx.update_editor(|editor, window, cx| {
16266        let snapshot = editor.snapshot(window, cx);
16267        let hunks = editor
16268            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16269            .collect::<Vec<_>>();
16270        assert_eq!(hunks.len(), 1);
16271        assert_eq!(
16272            hunks[0].status(),
16273            DiffHunkStatus {
16274                kind: DiffHunkStatusKind::Modified,
16275                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16276            }
16277        );
16278
16279        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16280    });
16281    cx.run_until_parked();
16282    cx.assert_index_text(Some(indoc! {"
16283        one
16284        TWO
16285        THREE-HUNDRED
16286        FOUR
16287        five
16288    "}));
16289}
16290
16291#[gpui::test]
16292fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16293    init_test(cx, |_| {});
16294
16295    let editor = cx.add_window(|window, cx| {
16296        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16297        build_editor(buffer, window, cx)
16298    });
16299
16300    let render_args = Arc::new(Mutex::new(None));
16301    let snapshot = editor
16302        .update(cx, |editor, window, cx| {
16303            let snapshot = editor.buffer().read(cx).snapshot(cx);
16304            let range =
16305                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16306
16307            struct RenderArgs {
16308                row: MultiBufferRow,
16309                folded: bool,
16310                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16311            }
16312
16313            let crease = Crease::inline(
16314                range,
16315                FoldPlaceholder::test(),
16316                {
16317                    let toggle_callback = render_args.clone();
16318                    move |row, folded, callback, _window, _cx| {
16319                        *toggle_callback.lock() = Some(RenderArgs {
16320                            row,
16321                            folded,
16322                            callback,
16323                        });
16324                        div()
16325                    }
16326                },
16327                |_row, _folded, _window, _cx| div(),
16328            );
16329
16330            editor.insert_creases(Some(crease), cx);
16331            let snapshot = editor.snapshot(window, cx);
16332            let _div = snapshot.render_crease_toggle(
16333                MultiBufferRow(1),
16334                false,
16335                cx.entity().clone(),
16336                window,
16337                cx,
16338            );
16339            snapshot
16340        })
16341        .unwrap();
16342
16343    let render_args = render_args.lock().take().unwrap();
16344    assert_eq!(render_args.row, MultiBufferRow(1));
16345    assert!(!render_args.folded);
16346    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16347
16348    cx.update_window(*editor, |_, window, cx| {
16349        (render_args.callback)(true, window, cx)
16350    })
16351    .unwrap();
16352    let snapshot = editor
16353        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16354        .unwrap();
16355    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16356
16357    cx.update_window(*editor, |_, window, cx| {
16358        (render_args.callback)(false, window, cx)
16359    })
16360    .unwrap();
16361    let snapshot = editor
16362        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16363        .unwrap();
16364    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16365}
16366
16367#[gpui::test]
16368async fn test_input_text(cx: &mut TestAppContext) {
16369    init_test(cx, |_| {});
16370    let mut cx = EditorTestContext::new(cx).await;
16371
16372    cx.set_state(
16373        &r#"ˇone
16374        two
16375
16376        three
16377        fourˇ
16378        five
16379
16380        siˇx"#
16381            .unindent(),
16382    );
16383
16384    cx.dispatch_action(HandleInput(String::new()));
16385    cx.assert_editor_state(
16386        &r#"ˇone
16387        two
16388
16389        three
16390        fourˇ
16391        five
16392
16393        siˇx"#
16394            .unindent(),
16395    );
16396
16397    cx.dispatch_action(HandleInput("AAAA".to_string()));
16398    cx.assert_editor_state(
16399        &r#"AAAAˇone
16400        two
16401
16402        three
16403        fourAAAAˇ
16404        five
16405
16406        siAAAAˇx"#
16407            .unindent(),
16408    );
16409}
16410
16411#[gpui::test]
16412async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16413    init_test(cx, |_| {});
16414
16415    let mut cx = EditorTestContext::new(cx).await;
16416    cx.set_state(
16417        r#"let foo = 1;
16418let foo = 2;
16419let foo = 3;
16420let fooˇ = 4;
16421let foo = 5;
16422let foo = 6;
16423let foo = 7;
16424let foo = 8;
16425let foo = 9;
16426let foo = 10;
16427let foo = 11;
16428let foo = 12;
16429let foo = 13;
16430let foo = 14;
16431let foo = 15;"#,
16432    );
16433
16434    cx.update_editor(|e, window, cx| {
16435        assert_eq!(
16436            e.next_scroll_position,
16437            NextScrollCursorCenterTopBottom::Center,
16438            "Default next scroll direction is center",
16439        );
16440
16441        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16442        assert_eq!(
16443            e.next_scroll_position,
16444            NextScrollCursorCenterTopBottom::Top,
16445            "After center, next scroll direction should be top",
16446        );
16447
16448        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16449        assert_eq!(
16450            e.next_scroll_position,
16451            NextScrollCursorCenterTopBottom::Bottom,
16452            "After top, next scroll direction should be bottom",
16453        );
16454
16455        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16456        assert_eq!(
16457            e.next_scroll_position,
16458            NextScrollCursorCenterTopBottom::Center,
16459            "After bottom, scrolling should start over",
16460        );
16461
16462        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16463        assert_eq!(
16464            e.next_scroll_position,
16465            NextScrollCursorCenterTopBottom::Top,
16466            "Scrolling continues if retriggered fast enough"
16467        );
16468    });
16469
16470    cx.executor()
16471        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
16472    cx.executor().run_until_parked();
16473    cx.update_editor(|e, _, _| {
16474        assert_eq!(
16475            e.next_scroll_position,
16476            NextScrollCursorCenterTopBottom::Center,
16477            "If scrolling is not triggered fast enough, it should reset"
16478        );
16479    });
16480}
16481
16482#[gpui::test]
16483async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
16484    init_test(cx, |_| {});
16485    let mut cx = EditorLspTestContext::new_rust(
16486        lsp::ServerCapabilities {
16487            definition_provider: Some(lsp::OneOf::Left(true)),
16488            references_provider: Some(lsp::OneOf::Left(true)),
16489            ..lsp::ServerCapabilities::default()
16490        },
16491        cx,
16492    )
16493    .await;
16494
16495    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
16496        let go_to_definition = cx
16497            .lsp
16498            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16499                move |params, _| async move {
16500                    if empty_go_to_definition {
16501                        Ok(None)
16502                    } else {
16503                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
16504                            uri: params.text_document_position_params.text_document.uri,
16505                            range: lsp::Range::new(
16506                                lsp::Position::new(4, 3),
16507                                lsp::Position::new(4, 6),
16508                            ),
16509                        })))
16510                    }
16511                },
16512            );
16513        let references = cx
16514            .lsp
16515            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
16516                Ok(Some(vec![lsp::Location {
16517                    uri: params.text_document_position.text_document.uri,
16518                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
16519                }]))
16520            });
16521        (go_to_definition, references)
16522    };
16523
16524    cx.set_state(
16525        &r#"fn one() {
16526            let mut a = ˇtwo();
16527        }
16528
16529        fn two() {}"#
16530            .unindent(),
16531    );
16532    set_up_lsp_handlers(false, &mut cx);
16533    let navigated = cx
16534        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16535        .await
16536        .expect("Failed to navigate to definition");
16537    assert_eq!(
16538        navigated,
16539        Navigated::Yes,
16540        "Should have navigated to definition from the GetDefinition response"
16541    );
16542    cx.assert_editor_state(
16543        &r#"fn one() {
16544            let mut a = two();
16545        }
16546
16547        fn «twoˇ»() {}"#
16548            .unindent(),
16549    );
16550
16551    let editors = cx.update_workspace(|workspace, _, cx| {
16552        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16553    });
16554    cx.update_editor(|_, _, test_editor_cx| {
16555        assert_eq!(
16556            editors.len(),
16557            1,
16558            "Initially, only one, test, editor should be open in the workspace"
16559        );
16560        assert_eq!(
16561            test_editor_cx.entity(),
16562            editors.last().expect("Asserted len is 1").clone()
16563        );
16564    });
16565
16566    set_up_lsp_handlers(true, &mut cx);
16567    let navigated = cx
16568        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16569        .await
16570        .expect("Failed to navigate to lookup references");
16571    assert_eq!(
16572        navigated,
16573        Navigated::Yes,
16574        "Should have navigated to references as a fallback after empty GoToDefinition response"
16575    );
16576    // We should not change the selections in the existing file,
16577    // if opening another milti buffer with the references
16578    cx.assert_editor_state(
16579        &r#"fn one() {
16580            let mut a = two();
16581        }
16582
16583        fn «twoˇ»() {}"#
16584            .unindent(),
16585    );
16586    let editors = cx.update_workspace(|workspace, _, cx| {
16587        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16588    });
16589    cx.update_editor(|_, _, test_editor_cx| {
16590        assert_eq!(
16591            editors.len(),
16592            2,
16593            "After falling back to references search, we open a new editor with the results"
16594        );
16595        let references_fallback_text = editors
16596            .into_iter()
16597            .find(|new_editor| *new_editor != test_editor_cx.entity())
16598            .expect("Should have one non-test editor now")
16599            .read(test_editor_cx)
16600            .text(test_editor_cx);
16601        assert_eq!(
16602            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
16603            "Should use the range from the references response and not the GoToDefinition one"
16604        );
16605    });
16606}
16607
16608#[gpui::test]
16609async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
16610    init_test(cx, |_| {});
16611    cx.update(|cx| {
16612        let mut editor_settings = EditorSettings::get_global(cx).clone();
16613        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
16614        EditorSettings::override_global(editor_settings, cx);
16615    });
16616    let mut cx = EditorLspTestContext::new_rust(
16617        lsp::ServerCapabilities {
16618            definition_provider: Some(lsp::OneOf::Left(true)),
16619            references_provider: Some(lsp::OneOf::Left(true)),
16620            ..lsp::ServerCapabilities::default()
16621        },
16622        cx,
16623    )
16624    .await;
16625    let original_state = r#"fn one() {
16626        let mut a = ˇtwo();
16627    }
16628
16629    fn two() {}"#
16630        .unindent();
16631    cx.set_state(&original_state);
16632
16633    let mut go_to_definition = cx
16634        .lsp
16635        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
16636            move |_, _| async move { Ok(None) },
16637        );
16638    let _references = cx
16639        .lsp
16640        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
16641            panic!("Should not call for references with no go to definition fallback")
16642        });
16643
16644    let navigated = cx
16645        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
16646        .await
16647        .expect("Failed to navigate to lookup references");
16648    go_to_definition
16649        .next()
16650        .await
16651        .expect("Should have called the go_to_definition handler");
16652
16653    assert_eq!(
16654        navigated,
16655        Navigated::No,
16656        "Should have navigated to references as a fallback after empty GoToDefinition response"
16657    );
16658    cx.assert_editor_state(&original_state);
16659    let editors = cx.update_workspace(|workspace, _, cx| {
16660        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
16661    });
16662    cx.update_editor(|_, _, _| {
16663        assert_eq!(
16664            editors.len(),
16665            1,
16666            "After unsuccessful fallback, no other editor should have been opened"
16667        );
16668    });
16669}
16670
16671#[gpui::test]
16672async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
16673    init_test(cx, |_| {});
16674
16675    let language = Arc::new(Language::new(
16676        LanguageConfig::default(),
16677        Some(tree_sitter_rust::LANGUAGE.into()),
16678    ));
16679
16680    let text = r#"
16681        #[cfg(test)]
16682        mod tests() {
16683            #[test]
16684            fn runnable_1() {
16685                let a = 1;
16686            }
16687
16688            #[test]
16689            fn runnable_2() {
16690                let a = 1;
16691                let b = 2;
16692            }
16693        }
16694    "#
16695    .unindent();
16696
16697    let fs = FakeFs::new(cx.executor());
16698    fs.insert_file("/file.rs", Default::default()).await;
16699
16700    let project = Project::test(fs, ["/a".as_ref()], cx).await;
16701    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16702    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16703    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
16704    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
16705
16706    let editor = cx.new_window_entity(|window, cx| {
16707        Editor::new(
16708            EditorMode::Full,
16709            multi_buffer,
16710            Some(project.clone()),
16711            window,
16712            cx,
16713        )
16714    });
16715
16716    editor.update_in(cx, |editor, window, cx| {
16717        let snapshot = editor.buffer().read(cx).snapshot(cx);
16718        editor.tasks.insert(
16719            (buffer.read(cx).remote_id(), 3),
16720            RunnableTasks {
16721                templates: vec![],
16722                offset: snapshot.anchor_before(43),
16723                column: 0,
16724                extra_variables: HashMap::default(),
16725                context_range: BufferOffset(43)..BufferOffset(85),
16726            },
16727        );
16728        editor.tasks.insert(
16729            (buffer.read(cx).remote_id(), 8),
16730            RunnableTasks {
16731                templates: vec![],
16732                offset: snapshot.anchor_before(86),
16733                column: 0,
16734                extra_variables: HashMap::default(),
16735                context_range: BufferOffset(86)..BufferOffset(191),
16736            },
16737        );
16738
16739        // Test finding task when cursor is inside function body
16740        editor.change_selections(None, window, cx, |s| {
16741            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
16742        });
16743        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16744        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
16745
16746        // Test finding task when cursor is on function name
16747        editor.change_selections(None, window, cx, |s| {
16748            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
16749        });
16750        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
16751        assert_eq!(row, 8, "Should find task when cursor is on function name");
16752    });
16753}
16754
16755#[gpui::test]
16756async fn test_folding_buffers(cx: &mut TestAppContext) {
16757    init_test(cx, |_| {});
16758
16759    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
16760    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
16761    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
16762
16763    let fs = FakeFs::new(cx.executor());
16764    fs.insert_tree(
16765        path!("/a"),
16766        json!({
16767            "first.rs": sample_text_1,
16768            "second.rs": sample_text_2,
16769            "third.rs": sample_text_3,
16770        }),
16771    )
16772    .await;
16773    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16774    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16775    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16776    let worktree = project.update(cx, |project, cx| {
16777        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16778        assert_eq!(worktrees.len(), 1);
16779        worktrees.pop().unwrap()
16780    });
16781    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16782
16783    let buffer_1 = project
16784        .update(cx, |project, cx| {
16785            project.open_buffer((worktree_id, "first.rs"), cx)
16786        })
16787        .await
16788        .unwrap();
16789    let buffer_2 = project
16790        .update(cx, |project, cx| {
16791            project.open_buffer((worktree_id, "second.rs"), cx)
16792        })
16793        .await
16794        .unwrap();
16795    let buffer_3 = project
16796        .update(cx, |project, cx| {
16797            project.open_buffer((worktree_id, "third.rs"), cx)
16798        })
16799        .await
16800        .unwrap();
16801
16802    let multi_buffer = cx.new(|cx| {
16803        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16804        multi_buffer.push_excerpts(
16805            buffer_1.clone(),
16806            [
16807                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16808                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16809                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16810            ],
16811            cx,
16812        );
16813        multi_buffer.push_excerpts(
16814            buffer_2.clone(),
16815            [
16816                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16817                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16818                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16819            ],
16820            cx,
16821        );
16822        multi_buffer.push_excerpts(
16823            buffer_3.clone(),
16824            [
16825                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
16826                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
16827                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
16828            ],
16829            cx,
16830        );
16831        multi_buffer
16832    });
16833    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16834        Editor::new(
16835            EditorMode::Full,
16836            multi_buffer.clone(),
16837            Some(project.clone()),
16838            window,
16839            cx,
16840        )
16841    });
16842
16843    assert_eq!(
16844        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16845        "\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",
16846    );
16847
16848    multi_buffer_editor.update(cx, |editor, cx| {
16849        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
16850    });
16851    assert_eq!(
16852        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16853        "\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",
16854        "After folding the first buffer, its text should not be displayed"
16855    );
16856
16857    multi_buffer_editor.update(cx, |editor, cx| {
16858        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
16859    });
16860    assert_eq!(
16861        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16862        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
16863        "After folding the second buffer, its text should not be displayed"
16864    );
16865
16866    multi_buffer_editor.update(cx, |editor, cx| {
16867        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
16868    });
16869    assert_eq!(
16870        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16871        "\n\n\n\n\n",
16872        "After folding the third buffer, its text should not be displayed"
16873    );
16874
16875    // Emulate selection inside the fold logic, that should work
16876    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16877        editor
16878            .snapshot(window, cx)
16879            .next_line_boundary(Point::new(0, 4));
16880    });
16881
16882    multi_buffer_editor.update(cx, |editor, cx| {
16883        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
16884    });
16885    assert_eq!(
16886        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16887        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16888        "After unfolding the second buffer, its text should be displayed"
16889    );
16890
16891    // Typing inside of buffer 1 causes that buffer to be unfolded.
16892    multi_buffer_editor.update_in(cx, |editor, window, cx| {
16893        assert_eq!(
16894            multi_buffer
16895                .read(cx)
16896                .snapshot(cx)
16897                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
16898                .collect::<String>(),
16899            "bbbb"
16900        );
16901        editor.change_selections(None, window, cx, |selections| {
16902            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
16903        });
16904        editor.handle_input("B", window, cx);
16905    });
16906
16907    assert_eq!(
16908        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16909        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
16910        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
16911    );
16912
16913    multi_buffer_editor.update(cx, |editor, cx| {
16914        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
16915    });
16916    assert_eq!(
16917        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
16918        "\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",
16919        "After unfolding the all buffers, all original text should be displayed"
16920    );
16921}
16922
16923#[gpui::test]
16924async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
16925    init_test(cx, |_| {});
16926
16927    let sample_text_1 = "1111\n2222\n3333".to_string();
16928    let sample_text_2 = "4444\n5555\n6666".to_string();
16929    let sample_text_3 = "7777\n8888\n9999".to_string();
16930
16931    let fs = FakeFs::new(cx.executor());
16932    fs.insert_tree(
16933        path!("/a"),
16934        json!({
16935            "first.rs": sample_text_1,
16936            "second.rs": sample_text_2,
16937            "third.rs": sample_text_3,
16938        }),
16939    )
16940    .await;
16941    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
16942    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
16943    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
16944    let worktree = project.update(cx, |project, cx| {
16945        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
16946        assert_eq!(worktrees.len(), 1);
16947        worktrees.pop().unwrap()
16948    });
16949    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
16950
16951    let buffer_1 = project
16952        .update(cx, |project, cx| {
16953            project.open_buffer((worktree_id, "first.rs"), cx)
16954        })
16955        .await
16956        .unwrap();
16957    let buffer_2 = project
16958        .update(cx, |project, cx| {
16959            project.open_buffer((worktree_id, "second.rs"), cx)
16960        })
16961        .await
16962        .unwrap();
16963    let buffer_3 = project
16964        .update(cx, |project, cx| {
16965            project.open_buffer((worktree_id, "third.rs"), cx)
16966        })
16967        .await
16968        .unwrap();
16969
16970    let multi_buffer = cx.new(|cx| {
16971        let mut multi_buffer = MultiBuffer::new(ReadWrite);
16972        multi_buffer.push_excerpts(
16973            buffer_1.clone(),
16974            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16975            cx,
16976        );
16977        multi_buffer.push_excerpts(
16978            buffer_2.clone(),
16979            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16980            cx,
16981        );
16982        multi_buffer.push_excerpts(
16983            buffer_3.clone(),
16984            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
16985            cx,
16986        );
16987        multi_buffer
16988    });
16989
16990    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
16991        Editor::new(
16992            EditorMode::Full,
16993            multi_buffer,
16994            Some(project.clone()),
16995            window,
16996            cx,
16997        )
16998    });
16999
17000    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17001    assert_eq!(
17002        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17003        full_text,
17004    );
17005
17006    multi_buffer_editor.update(cx, |editor, cx| {
17007        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17008    });
17009    assert_eq!(
17010        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17011        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17012        "After folding the first buffer, its text should not be displayed"
17013    );
17014
17015    multi_buffer_editor.update(cx, |editor, cx| {
17016        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17017    });
17018
17019    assert_eq!(
17020        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17021        "\n\n\n\n\n\n7777\n8888\n9999",
17022        "After folding the second buffer, its text should not be displayed"
17023    );
17024
17025    multi_buffer_editor.update(cx, |editor, cx| {
17026        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17027    });
17028    assert_eq!(
17029        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17030        "\n\n\n\n\n",
17031        "After folding the third buffer, its text should not be displayed"
17032    );
17033
17034    multi_buffer_editor.update(cx, |editor, cx| {
17035        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17036    });
17037    assert_eq!(
17038        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17039        "\n\n\n\n4444\n5555\n6666\n\n",
17040        "After unfolding the second buffer, its text should be displayed"
17041    );
17042
17043    multi_buffer_editor.update(cx, |editor, cx| {
17044        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17045    });
17046    assert_eq!(
17047        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17048        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17049        "After unfolding the first buffer, its text should be displayed"
17050    );
17051
17052    multi_buffer_editor.update(cx, |editor, cx| {
17053        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17054    });
17055    assert_eq!(
17056        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17057        full_text,
17058        "After unfolding all buffers, all original text should be displayed"
17059    );
17060}
17061
17062#[gpui::test]
17063async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17064    init_test(cx, |_| {});
17065
17066    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17067
17068    let fs = FakeFs::new(cx.executor());
17069    fs.insert_tree(
17070        path!("/a"),
17071        json!({
17072            "main.rs": sample_text,
17073        }),
17074    )
17075    .await;
17076    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17077    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17078    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17079    let worktree = project.update(cx, |project, cx| {
17080        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17081        assert_eq!(worktrees.len(), 1);
17082        worktrees.pop().unwrap()
17083    });
17084    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17085
17086    let buffer_1 = project
17087        .update(cx, |project, cx| {
17088            project.open_buffer((worktree_id, "main.rs"), cx)
17089        })
17090        .await
17091        .unwrap();
17092
17093    let multi_buffer = cx.new(|cx| {
17094        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17095        multi_buffer.push_excerpts(
17096            buffer_1.clone(),
17097            [ExcerptRange::new(
17098                Point::new(0, 0)
17099                    ..Point::new(
17100                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17101                        0,
17102                    ),
17103            )],
17104            cx,
17105        );
17106        multi_buffer
17107    });
17108    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17109        Editor::new(
17110            EditorMode::Full,
17111            multi_buffer,
17112            Some(project.clone()),
17113            window,
17114            cx,
17115        )
17116    });
17117
17118    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17119    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17120        enum TestHighlight {}
17121        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17122        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17123        editor.highlight_text::<TestHighlight>(
17124            vec![highlight_range.clone()],
17125            HighlightStyle::color(Hsla::green()),
17126            cx,
17127        );
17128        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17129    });
17130
17131    let full_text = format!("\n\n{sample_text}");
17132    assert_eq!(
17133        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17134        full_text,
17135    );
17136}
17137
17138#[gpui::test]
17139async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17140    init_test(cx, |_| {});
17141    cx.update(|cx| {
17142        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17143            "keymaps/default-linux.json",
17144            cx,
17145        )
17146        .unwrap();
17147        cx.bind_keys(default_key_bindings);
17148    });
17149
17150    let (editor, cx) = cx.add_window_view(|window, cx| {
17151        let multi_buffer = MultiBuffer::build_multi(
17152            [
17153                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17154                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17155                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17156                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17157            ],
17158            cx,
17159        );
17160        let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx);
17161
17162        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17163        // fold all but the second buffer, so that we test navigating between two
17164        // adjacent folded buffers, as well as folded buffers at the start and
17165        // end the multibuffer
17166        editor.fold_buffer(buffer_ids[0], cx);
17167        editor.fold_buffer(buffer_ids[2], cx);
17168        editor.fold_buffer(buffer_ids[3], cx);
17169
17170        editor
17171    });
17172    cx.simulate_resize(size(px(1000.), px(1000.)));
17173
17174    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17175    cx.assert_excerpts_with_selections(indoc! {"
17176        [EXCERPT]
17177        ˇ[FOLDED]
17178        [EXCERPT]
17179        a1
17180        b1
17181        [EXCERPT]
17182        [FOLDED]
17183        [EXCERPT]
17184        [FOLDED]
17185        "
17186    });
17187    cx.simulate_keystroke("down");
17188    cx.assert_excerpts_with_selections(indoc! {"
17189        [EXCERPT]
17190        [FOLDED]
17191        [EXCERPT]
17192        ˇa1
17193        b1
17194        [EXCERPT]
17195        [FOLDED]
17196        [EXCERPT]
17197        [FOLDED]
17198        "
17199    });
17200    cx.simulate_keystroke("down");
17201    cx.assert_excerpts_with_selections(indoc! {"
17202        [EXCERPT]
17203        [FOLDED]
17204        [EXCERPT]
17205        a1
17206        ˇb1
17207        [EXCERPT]
17208        [FOLDED]
17209        [EXCERPT]
17210        [FOLDED]
17211        "
17212    });
17213    cx.simulate_keystroke("down");
17214    cx.assert_excerpts_with_selections(indoc! {"
17215        [EXCERPT]
17216        [FOLDED]
17217        [EXCERPT]
17218        a1
17219        b1
17220        ˇ[EXCERPT]
17221        [FOLDED]
17222        [EXCERPT]
17223        [FOLDED]
17224        "
17225    });
17226    cx.simulate_keystroke("down");
17227    cx.assert_excerpts_with_selections(indoc! {"
17228        [EXCERPT]
17229        [FOLDED]
17230        [EXCERPT]
17231        a1
17232        b1
17233        [EXCERPT]
17234        ˇ[FOLDED]
17235        [EXCERPT]
17236        [FOLDED]
17237        "
17238    });
17239    for _ in 0..5 {
17240        cx.simulate_keystroke("down");
17241        cx.assert_excerpts_with_selections(indoc! {"
17242            [EXCERPT]
17243            [FOLDED]
17244            [EXCERPT]
17245            a1
17246            b1
17247            [EXCERPT]
17248            [FOLDED]
17249            [EXCERPT]
17250            ˇ[FOLDED]
17251            "
17252        });
17253    }
17254
17255    cx.simulate_keystroke("up");
17256    cx.assert_excerpts_with_selections(indoc! {"
17257        [EXCERPT]
17258        [FOLDED]
17259        [EXCERPT]
17260        a1
17261        b1
17262        [EXCERPT]
17263        ˇ[FOLDED]
17264        [EXCERPT]
17265        [FOLDED]
17266        "
17267    });
17268    cx.simulate_keystroke("up");
17269    cx.assert_excerpts_with_selections(indoc! {"
17270        [EXCERPT]
17271        [FOLDED]
17272        [EXCERPT]
17273        a1
17274        b1
17275        ˇ[EXCERPT]
17276        [FOLDED]
17277        [EXCERPT]
17278        [FOLDED]
17279        "
17280    });
17281    cx.simulate_keystroke("up");
17282    cx.assert_excerpts_with_selections(indoc! {"
17283        [EXCERPT]
17284        [FOLDED]
17285        [EXCERPT]
17286        a1
17287        ˇb1
17288        [EXCERPT]
17289        [FOLDED]
17290        [EXCERPT]
17291        [FOLDED]
17292        "
17293    });
17294    cx.simulate_keystroke("up");
17295    cx.assert_excerpts_with_selections(indoc! {"
17296        [EXCERPT]
17297        [FOLDED]
17298        [EXCERPT]
17299        ˇa1
17300        b1
17301        [EXCERPT]
17302        [FOLDED]
17303        [EXCERPT]
17304        [FOLDED]
17305        "
17306    });
17307    for _ in 0..5 {
17308        cx.simulate_keystroke("up");
17309        cx.assert_excerpts_with_selections(indoc! {"
17310            [EXCERPT]
17311            ˇ[FOLDED]
17312            [EXCERPT]
17313            a1
17314            b1
17315            [EXCERPT]
17316            [FOLDED]
17317            [EXCERPT]
17318            [FOLDED]
17319            "
17320        });
17321    }
17322}
17323
17324#[gpui::test]
17325async fn test_inline_completion_text(cx: &mut TestAppContext) {
17326    init_test(cx, |_| {});
17327
17328    // Simple insertion
17329    assert_highlighted_edits(
17330        "Hello, world!",
17331        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17332        true,
17333        cx,
17334        |highlighted_edits, cx| {
17335            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17336            assert_eq!(highlighted_edits.highlights.len(), 1);
17337            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17338            assert_eq!(
17339                highlighted_edits.highlights[0].1.background_color,
17340                Some(cx.theme().status().created_background)
17341            );
17342        },
17343    )
17344    .await;
17345
17346    // Replacement
17347    assert_highlighted_edits(
17348        "This is a test.",
17349        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17350        false,
17351        cx,
17352        |highlighted_edits, cx| {
17353            assert_eq!(highlighted_edits.text, "That is a test.");
17354            assert_eq!(highlighted_edits.highlights.len(), 1);
17355            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17356            assert_eq!(
17357                highlighted_edits.highlights[0].1.background_color,
17358                Some(cx.theme().status().created_background)
17359            );
17360        },
17361    )
17362    .await;
17363
17364    // Multiple edits
17365    assert_highlighted_edits(
17366        "Hello, world!",
17367        vec![
17368            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17369            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17370        ],
17371        false,
17372        cx,
17373        |highlighted_edits, cx| {
17374            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17375            assert_eq!(highlighted_edits.highlights.len(), 2);
17376            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17377            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17378            assert_eq!(
17379                highlighted_edits.highlights[0].1.background_color,
17380                Some(cx.theme().status().created_background)
17381            );
17382            assert_eq!(
17383                highlighted_edits.highlights[1].1.background_color,
17384                Some(cx.theme().status().created_background)
17385            );
17386        },
17387    )
17388    .await;
17389
17390    // Multiple lines with edits
17391    assert_highlighted_edits(
17392        "First line\nSecond line\nThird line\nFourth line",
17393        vec![
17394            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17395            (
17396                Point::new(2, 0)..Point::new(2, 10),
17397                "New third line".to_string(),
17398            ),
17399            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17400        ],
17401        false,
17402        cx,
17403        |highlighted_edits, cx| {
17404            assert_eq!(
17405                highlighted_edits.text,
17406                "Second modified\nNew third line\nFourth updated line"
17407            );
17408            assert_eq!(highlighted_edits.highlights.len(), 3);
17409            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17410            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17411            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17412            for highlight in &highlighted_edits.highlights {
17413                assert_eq!(
17414                    highlight.1.background_color,
17415                    Some(cx.theme().status().created_background)
17416                );
17417            }
17418        },
17419    )
17420    .await;
17421}
17422
17423#[gpui::test]
17424async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17425    init_test(cx, |_| {});
17426
17427    // Deletion
17428    assert_highlighted_edits(
17429        "Hello, world!",
17430        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17431        true,
17432        cx,
17433        |highlighted_edits, cx| {
17434            assert_eq!(highlighted_edits.text, "Hello, world!");
17435            assert_eq!(highlighted_edits.highlights.len(), 1);
17436            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17437            assert_eq!(
17438                highlighted_edits.highlights[0].1.background_color,
17439                Some(cx.theme().status().deleted_background)
17440            );
17441        },
17442    )
17443    .await;
17444
17445    // Insertion
17446    assert_highlighted_edits(
17447        "Hello, world!",
17448        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17449        true,
17450        cx,
17451        |highlighted_edits, cx| {
17452            assert_eq!(highlighted_edits.highlights.len(), 1);
17453            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17454            assert_eq!(
17455                highlighted_edits.highlights[0].1.background_color,
17456                Some(cx.theme().status().created_background)
17457            );
17458        },
17459    )
17460    .await;
17461}
17462
17463async fn assert_highlighted_edits(
17464    text: &str,
17465    edits: Vec<(Range<Point>, String)>,
17466    include_deletions: bool,
17467    cx: &mut TestAppContext,
17468    assertion_fn: impl Fn(HighlightedText, &App),
17469) {
17470    let window = cx.add_window(|window, cx| {
17471        let buffer = MultiBuffer::build_simple(text, cx);
17472        Editor::new(EditorMode::Full, buffer, None, window, cx)
17473    });
17474    let cx = &mut VisualTestContext::from_window(*window, cx);
17475
17476    let (buffer, snapshot) = window
17477        .update(cx, |editor, _window, cx| {
17478            (
17479                editor.buffer().clone(),
17480                editor.buffer().read(cx).snapshot(cx),
17481            )
17482        })
17483        .unwrap();
17484
17485    let edits = edits
17486        .into_iter()
17487        .map(|(range, edit)| {
17488            (
17489                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
17490                edit,
17491            )
17492        })
17493        .collect::<Vec<_>>();
17494
17495    let text_anchor_edits = edits
17496        .clone()
17497        .into_iter()
17498        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
17499        .collect::<Vec<_>>();
17500
17501    let edit_preview = window
17502        .update(cx, |_, _window, cx| {
17503            buffer
17504                .read(cx)
17505                .as_singleton()
17506                .unwrap()
17507                .read(cx)
17508                .preview_edits(text_anchor_edits.into(), cx)
17509        })
17510        .unwrap()
17511        .await;
17512
17513    cx.update(|_window, cx| {
17514        let highlighted_edits = inline_completion_edit_text(
17515            &snapshot.as_singleton().unwrap().2,
17516            &edits,
17517            &edit_preview,
17518            include_deletions,
17519            cx,
17520        );
17521        assertion_fn(highlighted_edits, cx)
17522    });
17523}
17524
17525#[track_caller]
17526fn assert_breakpoint(
17527    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
17528    path: &Arc<Path>,
17529    expected: Vec<(u32, Breakpoint)>,
17530) {
17531    if expected.len() == 0usize {
17532        assert!(!breakpoints.contains_key(path), "{}", path.display());
17533    } else {
17534        let mut breakpoint = breakpoints
17535            .get(path)
17536            .unwrap()
17537            .into_iter()
17538            .map(|breakpoint| {
17539                (
17540                    breakpoint.row,
17541                    Breakpoint {
17542                        message: breakpoint.message.clone(),
17543                        state: breakpoint.state,
17544                        condition: breakpoint.condition.clone(),
17545                        hit_condition: breakpoint.hit_condition.clone(),
17546                    },
17547                )
17548            })
17549            .collect::<Vec<_>>();
17550
17551        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
17552
17553        assert_eq!(expected, breakpoint);
17554    }
17555}
17556
17557fn add_log_breakpoint_at_cursor(
17558    editor: &mut Editor,
17559    log_message: &str,
17560    window: &mut Window,
17561    cx: &mut Context<Editor>,
17562) {
17563    let (anchor, bp) = editor
17564        .breakpoint_at_cursor_head(window, cx)
17565        .unwrap_or_else(|| {
17566            let cursor_position: Point = editor.selections.newest(cx).head();
17567
17568            let breakpoint_position = editor
17569                .snapshot(window, cx)
17570                .display_snapshot
17571                .buffer_snapshot
17572                .anchor_before(Point::new(cursor_position.row, 0));
17573
17574            (breakpoint_position, Breakpoint::new_log(&log_message))
17575        });
17576
17577    editor.edit_breakpoint_at_anchor(
17578        anchor,
17579        bp,
17580        BreakpointEditAction::EditLogMessage(log_message.into()),
17581        cx,
17582    );
17583}
17584
17585#[gpui::test]
17586async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
17587    init_test(cx, |_| {});
17588
17589    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17590    let fs = FakeFs::new(cx.executor());
17591    fs.insert_tree(
17592        path!("/a"),
17593        json!({
17594            "main.rs": sample_text,
17595        }),
17596    )
17597    .await;
17598    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17599    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17600    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17601
17602    let fs = FakeFs::new(cx.executor());
17603    fs.insert_tree(
17604        path!("/a"),
17605        json!({
17606            "main.rs": sample_text,
17607        }),
17608    )
17609    .await;
17610    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17611    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17612    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17613    let worktree_id = workspace
17614        .update(cx, |workspace, _window, cx| {
17615            workspace.project().update(cx, |project, cx| {
17616                project.worktrees(cx).next().unwrap().read(cx).id()
17617            })
17618        })
17619        .unwrap();
17620
17621    let buffer = project
17622        .update(cx, |project, cx| {
17623            project.open_buffer((worktree_id, "main.rs"), cx)
17624        })
17625        .await
17626        .unwrap();
17627
17628    let (editor, cx) = cx.add_window_view(|window, cx| {
17629        Editor::new(
17630            EditorMode::Full,
17631            MultiBuffer::build_from_buffer(buffer, cx),
17632            Some(project.clone()),
17633            window,
17634            cx,
17635        )
17636    });
17637
17638    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17639    let abs_path = project.read_with(cx, |project, cx| {
17640        project
17641            .absolute_path(&project_path, cx)
17642            .map(|path_buf| Arc::from(path_buf.to_owned()))
17643            .unwrap()
17644    });
17645
17646    // assert we can add breakpoint on the first line
17647    editor.update_in(cx, |editor, window, cx| {
17648        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17649        editor.move_to_end(&MoveToEnd, window, cx);
17650        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17651    });
17652
17653    let breakpoints = editor.update(cx, |editor, cx| {
17654        editor
17655            .breakpoint_store()
17656            .as_ref()
17657            .unwrap()
17658            .read(cx)
17659            .all_breakpoints(cx)
17660            .clone()
17661    });
17662
17663    assert_eq!(1, breakpoints.len());
17664    assert_breakpoint(
17665        &breakpoints,
17666        &abs_path,
17667        vec![
17668            (0, Breakpoint::new_standard()),
17669            (3, Breakpoint::new_standard()),
17670        ],
17671    );
17672
17673    editor.update_in(cx, |editor, window, cx| {
17674        editor.move_to_beginning(&MoveToBeginning, window, cx);
17675        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17676    });
17677
17678    let breakpoints = editor.update(cx, |editor, cx| {
17679        editor
17680            .breakpoint_store()
17681            .as_ref()
17682            .unwrap()
17683            .read(cx)
17684            .all_breakpoints(cx)
17685            .clone()
17686    });
17687
17688    assert_eq!(1, breakpoints.len());
17689    assert_breakpoint(
17690        &breakpoints,
17691        &abs_path,
17692        vec![(3, Breakpoint::new_standard())],
17693    );
17694
17695    editor.update_in(cx, |editor, window, cx| {
17696        editor.move_to_end(&MoveToEnd, window, cx);
17697        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17698    });
17699
17700    let breakpoints = editor.update(cx, |editor, cx| {
17701        editor
17702            .breakpoint_store()
17703            .as_ref()
17704            .unwrap()
17705            .read(cx)
17706            .all_breakpoints(cx)
17707            .clone()
17708    });
17709
17710    assert_eq!(0, breakpoints.len());
17711    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17712}
17713
17714#[gpui::test]
17715async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
17716    init_test(cx, |_| {});
17717
17718    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17719
17720    let fs = FakeFs::new(cx.executor());
17721    fs.insert_tree(
17722        path!("/a"),
17723        json!({
17724            "main.rs": sample_text,
17725        }),
17726    )
17727    .await;
17728    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17729    let (workspace, cx) =
17730        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
17731
17732    let worktree_id = workspace.update(cx, |workspace, cx| {
17733        workspace.project().update(cx, |project, cx| {
17734            project.worktrees(cx).next().unwrap().read(cx).id()
17735        })
17736    });
17737
17738    let buffer = project
17739        .update(cx, |project, cx| {
17740            project.open_buffer((worktree_id, "main.rs"), cx)
17741        })
17742        .await
17743        .unwrap();
17744
17745    let (editor, cx) = cx.add_window_view(|window, cx| {
17746        Editor::new(
17747            EditorMode::Full,
17748            MultiBuffer::build_from_buffer(buffer, cx),
17749            Some(project.clone()),
17750            window,
17751            cx,
17752        )
17753    });
17754
17755    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17756    let abs_path = project.read_with(cx, |project, cx| {
17757        project
17758            .absolute_path(&project_path, cx)
17759            .map(|path_buf| Arc::from(path_buf.to_owned()))
17760            .unwrap()
17761    });
17762
17763    editor.update_in(cx, |editor, window, cx| {
17764        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17765    });
17766
17767    let breakpoints = editor.update(cx, |editor, cx| {
17768        editor
17769            .breakpoint_store()
17770            .as_ref()
17771            .unwrap()
17772            .read(cx)
17773            .all_breakpoints(cx)
17774            .clone()
17775    });
17776
17777    assert_breakpoint(
17778        &breakpoints,
17779        &abs_path,
17780        vec![(0, Breakpoint::new_log("hello world"))],
17781    );
17782
17783    // Removing a log message from a log breakpoint should remove it
17784    editor.update_in(cx, |editor, window, cx| {
17785        add_log_breakpoint_at_cursor(editor, "", window, cx);
17786    });
17787
17788    let breakpoints = editor.update(cx, |editor, cx| {
17789        editor
17790            .breakpoint_store()
17791            .as_ref()
17792            .unwrap()
17793            .read(cx)
17794            .all_breakpoints(cx)
17795            .clone()
17796    });
17797
17798    assert_breakpoint(&breakpoints, &abs_path, vec![]);
17799
17800    editor.update_in(cx, |editor, window, cx| {
17801        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17802        editor.move_to_end(&MoveToEnd, window, cx);
17803        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17804        // Not adding a log message to a standard breakpoint shouldn't remove it
17805        add_log_breakpoint_at_cursor(editor, "", window, cx);
17806    });
17807
17808    let breakpoints = editor.update(cx, |editor, cx| {
17809        editor
17810            .breakpoint_store()
17811            .as_ref()
17812            .unwrap()
17813            .read(cx)
17814            .all_breakpoints(cx)
17815            .clone()
17816    });
17817
17818    assert_breakpoint(
17819        &breakpoints,
17820        &abs_path,
17821        vec![
17822            (0, Breakpoint::new_standard()),
17823            (3, Breakpoint::new_standard()),
17824        ],
17825    );
17826
17827    editor.update_in(cx, |editor, window, cx| {
17828        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
17829    });
17830
17831    let breakpoints = editor.update(cx, |editor, cx| {
17832        editor
17833            .breakpoint_store()
17834            .as_ref()
17835            .unwrap()
17836            .read(cx)
17837            .all_breakpoints(cx)
17838            .clone()
17839    });
17840
17841    assert_breakpoint(
17842        &breakpoints,
17843        &abs_path,
17844        vec![
17845            (0, Breakpoint::new_standard()),
17846            (3, Breakpoint::new_log("hello world")),
17847        ],
17848    );
17849
17850    editor.update_in(cx, |editor, window, cx| {
17851        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
17852    });
17853
17854    let breakpoints = editor.update(cx, |editor, cx| {
17855        editor
17856            .breakpoint_store()
17857            .as_ref()
17858            .unwrap()
17859            .read(cx)
17860            .all_breakpoints(cx)
17861            .clone()
17862    });
17863
17864    assert_breakpoint(
17865        &breakpoints,
17866        &abs_path,
17867        vec![
17868            (0, Breakpoint::new_standard()),
17869            (3, Breakpoint::new_log("hello Earth!!")),
17870        ],
17871    );
17872}
17873
17874/// This also tests that Editor::breakpoint_at_cursor_head is working properly
17875/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
17876/// or when breakpoints were placed out of order. This tests for a regression too
17877#[gpui::test]
17878async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
17879    init_test(cx, |_| {});
17880
17881    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
17882    let fs = FakeFs::new(cx.executor());
17883    fs.insert_tree(
17884        path!("/a"),
17885        json!({
17886            "main.rs": sample_text,
17887        }),
17888    )
17889    .await;
17890    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17891    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17892    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17893
17894    let fs = FakeFs::new(cx.executor());
17895    fs.insert_tree(
17896        path!("/a"),
17897        json!({
17898            "main.rs": sample_text,
17899        }),
17900    )
17901    .await;
17902    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17903    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17904    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17905    let worktree_id = workspace
17906        .update(cx, |workspace, _window, cx| {
17907            workspace.project().update(cx, |project, cx| {
17908                project.worktrees(cx).next().unwrap().read(cx).id()
17909            })
17910        })
17911        .unwrap();
17912
17913    let buffer = project
17914        .update(cx, |project, cx| {
17915            project.open_buffer((worktree_id, "main.rs"), cx)
17916        })
17917        .await
17918        .unwrap();
17919
17920    let (editor, cx) = cx.add_window_view(|window, cx| {
17921        Editor::new(
17922            EditorMode::Full,
17923            MultiBuffer::build_from_buffer(buffer, cx),
17924            Some(project.clone()),
17925            window,
17926            cx,
17927        )
17928    });
17929
17930    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
17931    let abs_path = project.read_with(cx, |project, cx| {
17932        project
17933            .absolute_path(&project_path, cx)
17934            .map(|path_buf| Arc::from(path_buf.to_owned()))
17935            .unwrap()
17936    });
17937
17938    // assert we can add breakpoint on the first line
17939    editor.update_in(cx, |editor, window, cx| {
17940        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17941        editor.move_to_end(&MoveToEnd, window, cx);
17942        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17943        editor.move_up(&MoveUp, window, cx);
17944        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
17945    });
17946
17947    let breakpoints = editor.update(cx, |editor, cx| {
17948        editor
17949            .breakpoint_store()
17950            .as_ref()
17951            .unwrap()
17952            .read(cx)
17953            .all_breakpoints(cx)
17954            .clone()
17955    });
17956
17957    assert_eq!(1, breakpoints.len());
17958    assert_breakpoint(
17959        &breakpoints,
17960        &abs_path,
17961        vec![
17962            (0, Breakpoint::new_standard()),
17963            (2, Breakpoint::new_standard()),
17964            (3, Breakpoint::new_standard()),
17965        ],
17966    );
17967
17968    editor.update_in(cx, |editor, window, cx| {
17969        editor.move_to_beginning(&MoveToBeginning, window, cx);
17970        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17971        editor.move_to_end(&MoveToEnd, window, cx);
17972        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17973        // Disabling a breakpoint that doesn't exist should do nothing
17974        editor.move_up(&MoveUp, window, cx);
17975        editor.move_up(&MoveUp, window, cx);
17976        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
17977    });
17978
17979    let breakpoints = editor.update(cx, |editor, cx| {
17980        editor
17981            .breakpoint_store()
17982            .as_ref()
17983            .unwrap()
17984            .read(cx)
17985            .all_breakpoints(cx)
17986            .clone()
17987    });
17988
17989    let disable_breakpoint = {
17990        let mut bp = Breakpoint::new_standard();
17991        bp.state = BreakpointState::Disabled;
17992        bp
17993    };
17994
17995    assert_eq!(1, breakpoints.len());
17996    assert_breakpoint(
17997        &breakpoints,
17998        &abs_path,
17999        vec![
18000            (0, disable_breakpoint.clone()),
18001            (2, Breakpoint::new_standard()),
18002            (3, disable_breakpoint.clone()),
18003        ],
18004    );
18005
18006    editor.update_in(cx, |editor, window, cx| {
18007        editor.move_to_beginning(&MoveToBeginning, window, cx);
18008        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18009        editor.move_to_end(&MoveToEnd, window, cx);
18010        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18011        editor.move_up(&MoveUp, window, cx);
18012        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18013    });
18014
18015    let breakpoints = editor.update(cx, |editor, cx| {
18016        editor
18017            .breakpoint_store()
18018            .as_ref()
18019            .unwrap()
18020            .read(cx)
18021            .all_breakpoints(cx)
18022            .clone()
18023    });
18024
18025    assert_eq!(1, breakpoints.len());
18026    assert_breakpoint(
18027        &breakpoints,
18028        &abs_path,
18029        vec![
18030            (0, Breakpoint::new_standard()),
18031            (2, disable_breakpoint),
18032            (3, Breakpoint::new_standard()),
18033        ],
18034    );
18035}
18036
18037#[gpui::test]
18038async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18039    init_test(cx, |_| {});
18040    let capabilities = lsp::ServerCapabilities {
18041        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18042            prepare_provider: Some(true),
18043            work_done_progress_options: Default::default(),
18044        })),
18045        ..Default::default()
18046    };
18047    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18048
18049    cx.set_state(indoc! {"
18050        struct Fˇoo {}
18051    "});
18052
18053    cx.update_editor(|editor, _, cx| {
18054        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18055        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18056        editor.highlight_background::<DocumentHighlightRead>(
18057            &[highlight_range],
18058            |c| c.editor_document_highlight_read_background,
18059            cx,
18060        );
18061    });
18062
18063    let mut prepare_rename_handler = cx
18064        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18065            move |_, _, _| async move {
18066                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18067                    start: lsp::Position {
18068                        line: 0,
18069                        character: 7,
18070                    },
18071                    end: lsp::Position {
18072                        line: 0,
18073                        character: 10,
18074                    },
18075                })))
18076            },
18077        );
18078    let prepare_rename_task = cx
18079        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18080        .expect("Prepare rename was not started");
18081    prepare_rename_handler.next().await.unwrap();
18082    prepare_rename_task.await.expect("Prepare rename failed");
18083
18084    let mut rename_handler =
18085        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18086            let edit = lsp::TextEdit {
18087                range: lsp::Range {
18088                    start: lsp::Position {
18089                        line: 0,
18090                        character: 7,
18091                    },
18092                    end: lsp::Position {
18093                        line: 0,
18094                        character: 10,
18095                    },
18096                },
18097                new_text: "FooRenamed".to_string(),
18098            };
18099            Ok(Some(lsp::WorkspaceEdit::new(
18100                // Specify the same edit twice
18101                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18102            )))
18103        });
18104    let rename_task = cx
18105        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18106        .expect("Confirm rename was not started");
18107    rename_handler.next().await.unwrap();
18108    rename_task.await.expect("Confirm rename failed");
18109    cx.run_until_parked();
18110
18111    // Despite two edits, only one is actually applied as those are identical
18112    cx.assert_editor_state(indoc! {"
18113        struct FooRenamedˇ {}
18114    "});
18115}
18116
18117#[gpui::test]
18118async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18119    init_test(cx, |_| {});
18120    // These capabilities indicate that the server does not support prepare rename.
18121    let capabilities = lsp::ServerCapabilities {
18122        rename_provider: Some(lsp::OneOf::Left(true)),
18123        ..Default::default()
18124    };
18125    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18126
18127    cx.set_state(indoc! {"
18128        struct Fˇoo {}
18129    "});
18130
18131    cx.update_editor(|editor, _window, cx| {
18132        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18133        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18134        editor.highlight_background::<DocumentHighlightRead>(
18135            &[highlight_range],
18136            |c| c.editor_document_highlight_read_background,
18137            cx,
18138        );
18139    });
18140
18141    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18142        .expect("Prepare rename was not started")
18143        .await
18144        .expect("Prepare rename failed");
18145
18146    let mut rename_handler =
18147        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18148            let edit = lsp::TextEdit {
18149                range: lsp::Range {
18150                    start: lsp::Position {
18151                        line: 0,
18152                        character: 7,
18153                    },
18154                    end: lsp::Position {
18155                        line: 0,
18156                        character: 10,
18157                    },
18158                },
18159                new_text: "FooRenamed".to_string(),
18160            };
18161            Ok(Some(lsp::WorkspaceEdit::new(
18162                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18163            )))
18164        });
18165    let rename_task = cx
18166        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18167        .expect("Confirm rename was not started");
18168    rename_handler.next().await.unwrap();
18169    rename_task.await.expect("Confirm rename failed");
18170    cx.run_until_parked();
18171
18172    // Correct range is renamed, as `surrounding_word` is used to find it.
18173    cx.assert_editor_state(indoc! {"
18174        struct FooRenamedˇ {}
18175    "});
18176}
18177
18178#[gpui::test]
18179async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18180    init_test(cx, |_| {});
18181    let mut cx = EditorTestContext::new(cx).await;
18182
18183    let language = Arc::new(
18184        Language::new(
18185            LanguageConfig::default(),
18186            Some(tree_sitter_html::LANGUAGE.into()),
18187        )
18188        .with_brackets_query(
18189            r#"
18190            ("<" @open "/>" @close)
18191            ("</" @open ">" @close)
18192            ("<" @open ">" @close)
18193            ("\"" @open "\"" @close)
18194            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18195        "#,
18196        )
18197        .unwrap(),
18198    );
18199    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18200
18201    cx.set_state(indoc! {"
18202        <span>ˇ</span>
18203    "});
18204    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18205    cx.assert_editor_state(indoc! {"
18206        <span>
18207        ˇ
18208        </span>
18209    "});
18210
18211    cx.set_state(indoc! {"
18212        <span><span></span>ˇ</span>
18213    "});
18214    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18215    cx.assert_editor_state(indoc! {"
18216        <span><span></span>
18217        ˇ</span>
18218    "});
18219
18220    cx.set_state(indoc! {"
18221        <span>ˇ
18222        </span>
18223    "});
18224    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18225    cx.assert_editor_state(indoc! {"
18226        <span>
18227        ˇ
18228        </span>
18229    "});
18230}
18231
18232#[gpui::test(iterations = 10)]
18233async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18234    init_test(cx, |_| {});
18235
18236    let fs = FakeFs::new(cx.executor());
18237    fs.insert_tree(
18238        path!("/dir"),
18239        json!({
18240            "a.ts": "a",
18241        }),
18242    )
18243    .await;
18244
18245    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18246    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18247    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18248
18249    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18250    language_registry.add(Arc::new(Language::new(
18251        LanguageConfig {
18252            name: "TypeScript".into(),
18253            matcher: LanguageMatcher {
18254                path_suffixes: vec!["ts".to_string()],
18255                ..Default::default()
18256            },
18257            ..Default::default()
18258        },
18259        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18260    )));
18261    let mut fake_language_servers = language_registry.register_fake_lsp(
18262        "TypeScript",
18263        FakeLspAdapter {
18264            capabilities: lsp::ServerCapabilities {
18265                code_lens_provider: Some(lsp::CodeLensOptions {
18266                    resolve_provider: Some(true),
18267                }),
18268                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18269                    commands: vec!["_the/command".to_string()],
18270                    ..lsp::ExecuteCommandOptions::default()
18271                }),
18272                ..lsp::ServerCapabilities::default()
18273            },
18274            ..FakeLspAdapter::default()
18275        },
18276    );
18277
18278    let (buffer, _handle) = project
18279        .update(cx, |p, cx| {
18280            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18281        })
18282        .await
18283        .unwrap();
18284    cx.executor().run_until_parked();
18285
18286    let fake_server = fake_language_servers.next().await.unwrap();
18287
18288    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18289    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18290    drop(buffer_snapshot);
18291    let actions = cx
18292        .update_window(*workspace, |_, window, cx| {
18293            project.code_actions(&buffer, anchor..anchor, window, cx)
18294        })
18295        .unwrap();
18296
18297    fake_server
18298        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18299            Ok(Some(vec![
18300                lsp::CodeLens {
18301                    range: lsp::Range::default(),
18302                    command: Some(lsp::Command {
18303                        title: "Code lens command".to_owned(),
18304                        command: "_the/command".to_owned(),
18305                        arguments: None,
18306                    }),
18307                    data: None,
18308                },
18309                lsp::CodeLens {
18310                    range: lsp::Range::default(),
18311                    command: Some(lsp::Command {
18312                        title: "Command not in capabilities".to_owned(),
18313                        command: "not in capabilities".to_owned(),
18314                        arguments: None,
18315                    }),
18316                    data: None,
18317                },
18318                lsp::CodeLens {
18319                    range: lsp::Range {
18320                        start: lsp::Position {
18321                            line: 1,
18322                            character: 1,
18323                        },
18324                        end: lsp::Position {
18325                            line: 1,
18326                            character: 1,
18327                        },
18328                    },
18329                    command: Some(lsp::Command {
18330                        title: "Command not in range".to_owned(),
18331                        command: "_the/command".to_owned(),
18332                        arguments: None,
18333                    }),
18334                    data: None,
18335                },
18336            ]))
18337        })
18338        .next()
18339        .await;
18340
18341    let actions = actions.await.unwrap();
18342    assert_eq!(
18343        actions.len(),
18344        1,
18345        "Should have only one valid action for the 0..0 range"
18346    );
18347    let action = actions[0].clone();
18348    let apply = project.update(cx, |project, cx| {
18349        project.apply_code_action(buffer.clone(), action, true, cx)
18350    });
18351
18352    // Resolving the code action does not populate its edits. In absence of
18353    // edits, we must execute the given command.
18354    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18355        |mut lens, _| async move {
18356            let lens_command = lens.command.as_mut().expect("should have a command");
18357            assert_eq!(lens_command.title, "Code lens command");
18358            lens_command.arguments = Some(vec![json!("the-argument")]);
18359            Ok(lens)
18360        },
18361    );
18362
18363    // While executing the command, the language server sends the editor
18364    // a `workspaceEdit` request.
18365    fake_server
18366        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18367            let fake = fake_server.clone();
18368            move |params, _| {
18369                assert_eq!(params.command, "_the/command");
18370                let fake = fake.clone();
18371                async move {
18372                    fake.server
18373                        .request::<lsp::request::ApplyWorkspaceEdit>(
18374                            lsp::ApplyWorkspaceEditParams {
18375                                label: None,
18376                                edit: lsp::WorkspaceEdit {
18377                                    changes: Some(
18378                                        [(
18379                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18380                                            vec![lsp::TextEdit {
18381                                                range: lsp::Range::new(
18382                                                    lsp::Position::new(0, 0),
18383                                                    lsp::Position::new(0, 0),
18384                                                ),
18385                                                new_text: "X".into(),
18386                                            }],
18387                                        )]
18388                                        .into_iter()
18389                                        .collect(),
18390                                    ),
18391                                    ..Default::default()
18392                                },
18393                            },
18394                        )
18395                        .await
18396                        .unwrap();
18397                    Ok(Some(json!(null)))
18398                }
18399            }
18400        })
18401        .next()
18402        .await;
18403
18404    // Applying the code lens command returns a project transaction containing the edits
18405    // sent by the language server in its `workspaceEdit` request.
18406    let transaction = apply.await.unwrap();
18407    assert!(transaction.0.contains_key(&buffer));
18408    buffer.update(cx, |buffer, cx| {
18409        assert_eq!(buffer.text(), "Xa");
18410        buffer.undo(cx);
18411        assert_eq!(buffer.text(), "a");
18412    });
18413}
18414
18415#[gpui::test]
18416async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18417    init_test(cx, |_| {});
18418
18419    let fs = FakeFs::new(cx.executor());
18420    let main_text = r#"fn main() {
18421println!("1");
18422println!("2");
18423println!("3");
18424println!("4");
18425println!("5");
18426}"#;
18427    let lib_text = "mod foo {}";
18428    fs.insert_tree(
18429        path!("/a"),
18430        json!({
18431            "lib.rs": lib_text,
18432            "main.rs": main_text,
18433        }),
18434    )
18435    .await;
18436
18437    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18438    let (workspace, cx) =
18439        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18440    let worktree_id = workspace.update(cx, |workspace, cx| {
18441        workspace.project().update(cx, |project, cx| {
18442            project.worktrees(cx).next().unwrap().read(cx).id()
18443        })
18444    });
18445
18446    let expected_ranges = vec![
18447        Point::new(0, 0)..Point::new(0, 0),
18448        Point::new(1, 0)..Point::new(1, 1),
18449        Point::new(2, 0)..Point::new(2, 2),
18450        Point::new(3, 0)..Point::new(3, 3),
18451    ];
18452
18453    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18454    let editor_1 = workspace
18455        .update_in(cx, |workspace, window, cx| {
18456            workspace.open_path(
18457                (worktree_id, "main.rs"),
18458                Some(pane_1.downgrade()),
18459                true,
18460                window,
18461                cx,
18462            )
18463        })
18464        .unwrap()
18465        .await
18466        .downcast::<Editor>()
18467        .unwrap();
18468    pane_1.update(cx, |pane, cx| {
18469        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18470        open_editor.update(cx, |editor, cx| {
18471            assert_eq!(
18472                editor.display_text(cx),
18473                main_text,
18474                "Original main.rs text on initial open",
18475            );
18476            assert_eq!(
18477                editor
18478                    .selections
18479                    .all::<Point>(cx)
18480                    .into_iter()
18481                    .map(|s| s.range())
18482                    .collect::<Vec<_>>(),
18483                vec![Point::zero()..Point::zero()],
18484                "Default selections on initial open",
18485            );
18486        })
18487    });
18488    editor_1.update_in(cx, |editor, window, cx| {
18489        editor.change_selections(None, window, cx, |s| {
18490            s.select_ranges(expected_ranges.clone());
18491        });
18492    });
18493
18494    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
18495        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
18496    });
18497    let editor_2 = workspace
18498        .update_in(cx, |workspace, window, cx| {
18499            workspace.open_path(
18500                (worktree_id, "main.rs"),
18501                Some(pane_2.downgrade()),
18502                true,
18503                window,
18504                cx,
18505            )
18506        })
18507        .unwrap()
18508        .await
18509        .downcast::<Editor>()
18510        .unwrap();
18511    pane_2.update(cx, |pane, cx| {
18512        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18513        open_editor.update(cx, |editor, cx| {
18514            assert_eq!(
18515                editor.display_text(cx),
18516                main_text,
18517                "Original main.rs text on initial open in another panel",
18518            );
18519            assert_eq!(
18520                editor
18521                    .selections
18522                    .all::<Point>(cx)
18523                    .into_iter()
18524                    .map(|s| s.range())
18525                    .collect::<Vec<_>>(),
18526                vec![Point::zero()..Point::zero()],
18527                "Default selections on initial open in another panel",
18528            );
18529        })
18530    });
18531
18532    editor_2.update_in(cx, |editor, window, cx| {
18533        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
18534    });
18535
18536    let _other_editor_1 = workspace
18537        .update_in(cx, |workspace, window, cx| {
18538            workspace.open_path(
18539                (worktree_id, "lib.rs"),
18540                Some(pane_1.downgrade()),
18541                true,
18542                window,
18543                cx,
18544            )
18545        })
18546        .unwrap()
18547        .await
18548        .downcast::<Editor>()
18549        .unwrap();
18550    pane_1
18551        .update_in(cx, |pane, window, cx| {
18552            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18553                .unwrap()
18554        })
18555        .await
18556        .unwrap();
18557    drop(editor_1);
18558    pane_1.update(cx, |pane, cx| {
18559        pane.active_item()
18560            .unwrap()
18561            .downcast::<Editor>()
18562            .unwrap()
18563            .update(cx, |editor, cx| {
18564                assert_eq!(
18565                    editor.display_text(cx),
18566                    lib_text,
18567                    "Other file should be open and active",
18568                );
18569            });
18570        assert_eq!(pane.items().count(), 1, "No other editors should be open");
18571    });
18572
18573    let _other_editor_2 = workspace
18574        .update_in(cx, |workspace, window, cx| {
18575            workspace.open_path(
18576                (worktree_id, "lib.rs"),
18577                Some(pane_2.downgrade()),
18578                true,
18579                window,
18580                cx,
18581            )
18582        })
18583        .unwrap()
18584        .await
18585        .downcast::<Editor>()
18586        .unwrap();
18587    pane_2
18588        .update_in(cx, |pane, window, cx| {
18589            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
18590                .unwrap()
18591        })
18592        .await
18593        .unwrap();
18594    drop(editor_2);
18595    pane_2.update(cx, |pane, cx| {
18596        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18597        open_editor.update(cx, |editor, cx| {
18598            assert_eq!(
18599                editor.display_text(cx),
18600                lib_text,
18601                "Other file should be open and active in another panel too",
18602            );
18603        });
18604        assert_eq!(
18605            pane.items().count(),
18606            1,
18607            "No other editors should be open in another pane",
18608        );
18609    });
18610
18611    let _editor_1_reopened = workspace
18612        .update_in(cx, |workspace, window, cx| {
18613            workspace.open_path(
18614                (worktree_id, "main.rs"),
18615                Some(pane_1.downgrade()),
18616                true,
18617                window,
18618                cx,
18619            )
18620        })
18621        .unwrap()
18622        .await
18623        .downcast::<Editor>()
18624        .unwrap();
18625    let _editor_2_reopened = workspace
18626        .update_in(cx, |workspace, window, cx| {
18627            workspace.open_path(
18628                (worktree_id, "main.rs"),
18629                Some(pane_2.downgrade()),
18630                true,
18631                window,
18632                cx,
18633            )
18634        })
18635        .unwrap()
18636        .await
18637        .downcast::<Editor>()
18638        .unwrap();
18639    pane_1.update(cx, |pane, cx| {
18640        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18641        open_editor.update(cx, |editor, cx| {
18642            assert_eq!(
18643                editor.display_text(cx),
18644                main_text,
18645                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
18646            );
18647            assert_eq!(
18648                editor
18649                    .selections
18650                    .all::<Point>(cx)
18651                    .into_iter()
18652                    .map(|s| s.range())
18653                    .collect::<Vec<_>>(),
18654                expected_ranges,
18655                "Previous editor in the 1st panel had selections and should get them restored on reopen",
18656            );
18657        })
18658    });
18659    pane_2.update(cx, |pane, cx| {
18660        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18661        open_editor.update(cx, |editor, cx| {
18662            assert_eq!(
18663                editor.display_text(cx),
18664                r#"fn main() {
18665⋯rintln!("1");
18666⋯intln!("2");
18667⋯ntln!("3");
18668println!("4");
18669println!("5");
18670}"#,
18671                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
18672            );
18673            assert_eq!(
18674                editor
18675                    .selections
18676                    .all::<Point>(cx)
18677                    .into_iter()
18678                    .map(|s| s.range())
18679                    .collect::<Vec<_>>(),
18680                vec![Point::zero()..Point::zero()],
18681                "Previous editor in the 2nd pane had no selections changed hence should restore none",
18682            );
18683        })
18684    });
18685}
18686
18687#[gpui::test]
18688async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
18689    init_test(cx, |_| {});
18690
18691    let fs = FakeFs::new(cx.executor());
18692    let main_text = r#"fn main() {
18693println!("1");
18694println!("2");
18695println!("3");
18696println!("4");
18697println!("5");
18698}"#;
18699    let lib_text = "mod foo {}";
18700    fs.insert_tree(
18701        path!("/a"),
18702        json!({
18703            "lib.rs": lib_text,
18704            "main.rs": main_text,
18705        }),
18706    )
18707    .await;
18708
18709    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18710    let (workspace, cx) =
18711        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18712    let worktree_id = workspace.update(cx, |workspace, cx| {
18713        workspace.project().update(cx, |project, cx| {
18714            project.worktrees(cx).next().unwrap().read(cx).id()
18715        })
18716    });
18717
18718    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18719    let editor = workspace
18720        .update_in(cx, |workspace, window, cx| {
18721            workspace.open_path(
18722                (worktree_id, "main.rs"),
18723                Some(pane.downgrade()),
18724                true,
18725                window,
18726                cx,
18727            )
18728        })
18729        .unwrap()
18730        .await
18731        .downcast::<Editor>()
18732        .unwrap();
18733    pane.update(cx, |pane, cx| {
18734        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18735        open_editor.update(cx, |editor, cx| {
18736            assert_eq!(
18737                editor.display_text(cx),
18738                main_text,
18739                "Original main.rs text on initial open",
18740            );
18741        })
18742    });
18743    editor.update_in(cx, |editor, window, cx| {
18744        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
18745    });
18746
18747    cx.update_global(|store: &mut SettingsStore, cx| {
18748        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18749            s.restore_on_file_reopen = Some(false);
18750        });
18751    });
18752    editor.update_in(cx, |editor, window, cx| {
18753        editor.fold_ranges(
18754            vec![
18755                Point::new(1, 0)..Point::new(1, 1),
18756                Point::new(2, 0)..Point::new(2, 2),
18757                Point::new(3, 0)..Point::new(3, 3),
18758            ],
18759            false,
18760            window,
18761            cx,
18762        );
18763    });
18764    pane.update_in(cx, |pane, window, cx| {
18765        pane.close_all_items(&CloseAllItems::default(), window, cx)
18766            .unwrap()
18767    })
18768    .await
18769    .unwrap();
18770    pane.update(cx, |pane, _| {
18771        assert!(pane.active_item().is_none());
18772    });
18773    cx.update_global(|store: &mut SettingsStore, cx| {
18774        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
18775            s.restore_on_file_reopen = Some(true);
18776        });
18777    });
18778
18779    let _editor_reopened = workspace
18780        .update_in(cx, |workspace, window, cx| {
18781            workspace.open_path(
18782                (worktree_id, "main.rs"),
18783                Some(pane.downgrade()),
18784                true,
18785                window,
18786                cx,
18787            )
18788        })
18789        .unwrap()
18790        .await
18791        .downcast::<Editor>()
18792        .unwrap();
18793    pane.update(cx, |pane, cx| {
18794        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
18795        open_editor.update(cx, |editor, cx| {
18796            assert_eq!(
18797                editor.display_text(cx),
18798                main_text,
18799                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
18800            );
18801        })
18802    });
18803}
18804
18805fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
18806    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
18807    point..point
18808}
18809
18810fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
18811    let (text, ranges) = marked_text_ranges(marked_text, true);
18812    assert_eq!(editor.text(cx), text);
18813    assert_eq!(
18814        editor.selections.ranges(cx),
18815        ranges,
18816        "Assert selections are {}",
18817        marked_text
18818    );
18819}
18820
18821pub fn handle_signature_help_request(
18822    cx: &mut EditorLspTestContext,
18823    mocked_response: lsp::SignatureHelp,
18824) -> impl Future<Output = ()> + use<> {
18825    let mut request =
18826        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
18827            let mocked_response = mocked_response.clone();
18828            async move { Ok(Some(mocked_response)) }
18829        });
18830
18831    async move {
18832        request.next().await;
18833    }
18834}
18835
18836/// Handle completion request passing a marked string specifying where the completion
18837/// should be triggered from using '|' character, what range should be replaced, and what completions
18838/// should be returned using '<' and '>' to delimit the range.
18839///
18840/// Also see `handle_completion_request_with_insert_and_replace`.
18841#[track_caller]
18842pub fn handle_completion_request(
18843    cx: &mut EditorLspTestContext,
18844    marked_string: &str,
18845    completions: Vec<&'static str>,
18846    counter: Arc<AtomicUsize>,
18847) -> impl Future<Output = ()> {
18848    let complete_from_marker: TextRangeMarker = '|'.into();
18849    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18850    let (_, mut marked_ranges) = marked_text_ranges_by(
18851        marked_string,
18852        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18853    );
18854
18855    let complete_from_position =
18856        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18857    let replace_range =
18858        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18859
18860    let mut request =
18861        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18862            let completions = completions.clone();
18863            counter.fetch_add(1, atomic::Ordering::Release);
18864            async move {
18865                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18866                assert_eq!(
18867                    params.text_document_position.position,
18868                    complete_from_position
18869                );
18870                Ok(Some(lsp::CompletionResponse::Array(
18871                    completions
18872                        .iter()
18873                        .map(|completion_text| lsp::CompletionItem {
18874                            label: completion_text.to_string(),
18875                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
18876                                range: replace_range,
18877                                new_text: completion_text.to_string(),
18878                            })),
18879                            ..Default::default()
18880                        })
18881                        .collect(),
18882                )))
18883            }
18884        });
18885
18886    async move {
18887        request.next().await;
18888    }
18889}
18890
18891/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
18892/// given instead, which also contains an `insert` range.
18893///
18894/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
18895/// that is, `replace_range.start..cursor_pos`.
18896pub fn handle_completion_request_with_insert_and_replace(
18897    cx: &mut EditorLspTestContext,
18898    marked_string: &str,
18899    completions: Vec<&'static str>,
18900    counter: Arc<AtomicUsize>,
18901) -> impl Future<Output = ()> {
18902    let complete_from_marker: TextRangeMarker = '|'.into();
18903    let replace_range_marker: TextRangeMarker = ('<', '>').into();
18904    let (_, mut marked_ranges) = marked_text_ranges_by(
18905        marked_string,
18906        vec![complete_from_marker.clone(), replace_range_marker.clone()],
18907    );
18908
18909    let complete_from_position =
18910        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
18911    let replace_range =
18912        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
18913
18914    let mut request =
18915        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
18916            let completions = completions.clone();
18917            counter.fetch_add(1, atomic::Ordering::Release);
18918            async move {
18919                assert_eq!(params.text_document_position.text_document.uri, url.clone());
18920                assert_eq!(
18921                    params.text_document_position.position, complete_from_position,
18922                    "marker `|` position doesn't match",
18923                );
18924                Ok(Some(lsp::CompletionResponse::Array(
18925                    completions
18926                        .iter()
18927                        .map(|completion_text| lsp::CompletionItem {
18928                            label: completion_text.to_string(),
18929                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
18930                                lsp::InsertReplaceEdit {
18931                                    insert: lsp::Range {
18932                                        start: replace_range.start,
18933                                        end: complete_from_position,
18934                                    },
18935                                    replace: replace_range,
18936                                    new_text: completion_text.to_string(),
18937                                },
18938                            )),
18939                            ..Default::default()
18940                        })
18941                        .collect(),
18942                )))
18943            }
18944        });
18945
18946    async move {
18947        request.next().await;
18948    }
18949}
18950
18951fn handle_resolve_completion_request(
18952    cx: &mut EditorLspTestContext,
18953    edits: Option<Vec<(&'static str, &'static str)>>,
18954) -> impl Future<Output = ()> {
18955    let edits = edits.map(|edits| {
18956        edits
18957            .iter()
18958            .map(|(marked_string, new_text)| {
18959                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
18960                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
18961                lsp::TextEdit::new(replace_range, new_text.to_string())
18962            })
18963            .collect::<Vec<_>>()
18964    });
18965
18966    let mut request =
18967        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
18968            let edits = edits.clone();
18969            async move {
18970                Ok(lsp::CompletionItem {
18971                    additional_text_edits: edits,
18972                    ..Default::default()
18973                })
18974            }
18975        });
18976
18977    async move {
18978        request.next().await;
18979    }
18980}
18981
18982pub(crate) fn update_test_language_settings(
18983    cx: &mut TestAppContext,
18984    f: impl Fn(&mut AllLanguageSettingsContent),
18985) {
18986    cx.update(|cx| {
18987        SettingsStore::update_global(cx, |store, cx| {
18988            store.update_user_settings::<AllLanguageSettings>(cx, f);
18989        });
18990    });
18991}
18992
18993pub(crate) fn update_test_project_settings(
18994    cx: &mut TestAppContext,
18995    f: impl Fn(&mut ProjectSettings),
18996) {
18997    cx.update(|cx| {
18998        SettingsStore::update_global(cx, |store, cx| {
18999            store.update_user_settings::<ProjectSettings>(cx, f);
19000        });
19001    });
19002}
19003
19004pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19005    cx.update(|cx| {
19006        assets::Assets.load_test_fonts(cx);
19007        let store = SettingsStore::test(cx);
19008        cx.set_global(store);
19009        theme::init(theme::LoadThemes::JustBase, cx);
19010        release_channel::init(SemanticVersion::default(), cx);
19011        client::init_settings(cx);
19012        language::init(cx);
19013        Project::init_settings(cx);
19014        workspace::init_settings(cx);
19015        crate::init(cx);
19016    });
19017
19018    update_test_language_settings(cx, f);
19019}
19020
19021#[track_caller]
19022fn assert_hunk_revert(
19023    not_reverted_text_with_selections: &str,
19024    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19025    expected_reverted_text_with_selections: &str,
19026    base_text: &str,
19027    cx: &mut EditorLspTestContext,
19028) {
19029    cx.set_state(not_reverted_text_with_selections);
19030    cx.set_head_text(base_text);
19031    cx.executor().run_until_parked();
19032
19033    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19034        let snapshot = editor.snapshot(window, cx);
19035        let reverted_hunk_statuses = snapshot
19036            .buffer_snapshot
19037            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19038            .map(|hunk| hunk.status().kind)
19039            .collect::<Vec<_>>();
19040
19041        editor.git_restore(&Default::default(), window, cx);
19042        reverted_hunk_statuses
19043    });
19044    cx.executor().run_until_parked();
19045    cx.assert_editor_state(expected_reverted_text_with_selections);
19046    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19047}