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#[gpui::test]
 1320fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
 1321    init_test(cx, |_| {});
 1322
 1323    let editor = cx.add_window(|window, cx| {
 1324        let buffer = MultiBuffer::build_simple("🟥🟧🟨🟩🟦🟪\nabcde\nαβγδε", cx);
 1325        build_editor(buffer.clone(), window, cx)
 1326    });
 1327
 1328    assert_eq!('🟥'.len_utf8(), 4);
 1329    assert_eq!('α'.len_utf8(), 2);
 1330
 1331    _ = editor.update(cx, |editor, window, cx| {
 1332        editor.fold_creases(
 1333            vec![
 1334                Crease::simple(Point::new(0, 8)..Point::new(0, 16), FoldPlaceholder::test()),
 1335                Crease::simple(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
 1336                Crease::simple(Point::new(2, 4)..Point::new(2, 8), FoldPlaceholder::test()),
 1337            ],
 1338            true,
 1339            window,
 1340            cx,
 1341        );
 1342        assert_eq!(editor.display_text(cx), "🟥🟧⋯🟦🟪\nab⋯e\nαβ⋯ε");
 1343
 1344        editor.move_right(&MoveRight, window, cx);
 1345        assert_eq!(
 1346            editor.selections.display_ranges(cx),
 1347            &[empty_range(0, "🟥".len())]
 1348        );
 1349        editor.move_right(&MoveRight, window, cx);
 1350        assert_eq!(
 1351            editor.selections.display_ranges(cx),
 1352            &[empty_range(0, "🟥🟧".len())]
 1353        );
 1354        editor.move_right(&MoveRight, window, cx);
 1355        assert_eq!(
 1356            editor.selections.display_ranges(cx),
 1357            &[empty_range(0, "🟥🟧⋯".len())]
 1358        );
 1359
 1360        editor.move_down(&MoveDown, window, cx);
 1361        assert_eq!(
 1362            editor.selections.display_ranges(cx),
 1363            &[empty_range(1, "ab⋯e".len())]
 1364        );
 1365        editor.move_left(&MoveLeft, window, cx);
 1366        assert_eq!(
 1367            editor.selections.display_ranges(cx),
 1368            &[empty_range(1, "ab⋯".len())]
 1369        );
 1370        editor.move_left(&MoveLeft, window, cx);
 1371        assert_eq!(
 1372            editor.selections.display_ranges(cx),
 1373            &[empty_range(1, "ab".len())]
 1374        );
 1375        editor.move_left(&MoveLeft, window, cx);
 1376        assert_eq!(
 1377            editor.selections.display_ranges(cx),
 1378            &[empty_range(1, "a".len())]
 1379        );
 1380
 1381        editor.move_down(&MoveDown, window, cx);
 1382        assert_eq!(
 1383            editor.selections.display_ranges(cx),
 1384            &[empty_range(2, "α".len())]
 1385        );
 1386        editor.move_right(&MoveRight, window, cx);
 1387        assert_eq!(
 1388            editor.selections.display_ranges(cx),
 1389            &[empty_range(2, "αβ".len())]
 1390        );
 1391        editor.move_right(&MoveRight, window, cx);
 1392        assert_eq!(
 1393            editor.selections.display_ranges(cx),
 1394            &[empty_range(2, "αβ⋯".len())]
 1395        );
 1396        editor.move_right(&MoveRight, window, cx);
 1397        assert_eq!(
 1398            editor.selections.display_ranges(cx),
 1399            &[empty_range(2, "αβ⋯ε".len())]
 1400        );
 1401
 1402        editor.move_up(&MoveUp, window, cx);
 1403        assert_eq!(
 1404            editor.selections.display_ranges(cx),
 1405            &[empty_range(1, "ab⋯e".len())]
 1406        );
 1407        editor.move_down(&MoveDown, window, cx);
 1408        assert_eq!(
 1409            editor.selections.display_ranges(cx),
 1410            &[empty_range(2, "αβ⋯ε".len())]
 1411        );
 1412        editor.move_up(&MoveUp, window, cx);
 1413        assert_eq!(
 1414            editor.selections.display_ranges(cx),
 1415            &[empty_range(1, "ab⋯e".len())]
 1416        );
 1417
 1418        editor.move_up(&MoveUp, window, cx);
 1419        assert_eq!(
 1420            editor.selections.display_ranges(cx),
 1421            &[empty_range(0, "🟥🟧".len())]
 1422        );
 1423        editor.move_left(&MoveLeft, window, cx);
 1424        assert_eq!(
 1425            editor.selections.display_ranges(cx),
 1426            &[empty_range(0, "🟥".len())]
 1427        );
 1428        editor.move_left(&MoveLeft, window, cx);
 1429        assert_eq!(
 1430            editor.selections.display_ranges(cx),
 1431            &[empty_range(0, "".len())]
 1432        );
 1433    });
 1434}
 1435
 1436#[gpui::test]
 1437fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
 1438    init_test(cx, |_| {});
 1439
 1440    let editor = cx.add_window(|window, cx| {
 1441        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
 1442        build_editor(buffer.clone(), window, cx)
 1443    });
 1444    _ = editor.update(cx, |editor, window, cx| {
 1445        editor.change_selections(None, window, cx, |s| {
 1446            s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
 1447        });
 1448
 1449        // moving above start of document should move selection to start of document,
 1450        // but the next move down should still be at the original goal_x
 1451        editor.move_up(&MoveUp, window, cx);
 1452        assert_eq!(
 1453            editor.selections.display_ranges(cx),
 1454            &[empty_range(0, "".len())]
 1455        );
 1456
 1457        editor.move_down(&MoveDown, window, cx);
 1458        assert_eq!(
 1459            editor.selections.display_ranges(cx),
 1460            &[empty_range(1, "abcd".len())]
 1461        );
 1462
 1463        editor.move_down(&MoveDown, window, cx);
 1464        assert_eq!(
 1465            editor.selections.display_ranges(cx),
 1466            &[empty_range(2, "αβγ".len())]
 1467        );
 1468
 1469        editor.move_down(&MoveDown, window, cx);
 1470        assert_eq!(
 1471            editor.selections.display_ranges(cx),
 1472            &[empty_range(3, "abcd".len())]
 1473        );
 1474
 1475        editor.move_down(&MoveDown, window, cx);
 1476        assert_eq!(
 1477            editor.selections.display_ranges(cx),
 1478            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1479        );
 1480
 1481        // moving past end of document should not change goal_x
 1482        editor.move_down(&MoveDown, window, cx);
 1483        assert_eq!(
 1484            editor.selections.display_ranges(cx),
 1485            &[empty_range(5, "".len())]
 1486        );
 1487
 1488        editor.move_down(&MoveDown, window, cx);
 1489        assert_eq!(
 1490            editor.selections.display_ranges(cx),
 1491            &[empty_range(5, "".len())]
 1492        );
 1493
 1494        editor.move_up(&MoveUp, window, cx);
 1495        assert_eq!(
 1496            editor.selections.display_ranges(cx),
 1497            &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
 1498        );
 1499
 1500        editor.move_up(&MoveUp, window, cx);
 1501        assert_eq!(
 1502            editor.selections.display_ranges(cx),
 1503            &[empty_range(3, "abcd".len())]
 1504        );
 1505
 1506        editor.move_up(&MoveUp, window, cx);
 1507        assert_eq!(
 1508            editor.selections.display_ranges(cx),
 1509            &[empty_range(2, "αβγ".len())]
 1510        );
 1511    });
 1512}
 1513
 1514#[gpui::test]
 1515fn test_beginning_end_of_line(cx: &mut TestAppContext) {
 1516    init_test(cx, |_| {});
 1517    let move_to_beg = MoveToBeginningOfLine {
 1518        stop_at_soft_wraps: true,
 1519        stop_at_indent: true,
 1520    };
 1521
 1522    let delete_to_beg = DeleteToBeginningOfLine {
 1523        stop_at_indent: false,
 1524    };
 1525
 1526    let move_to_end = MoveToEndOfLine {
 1527        stop_at_soft_wraps: true,
 1528    };
 1529
 1530    let editor = cx.add_window(|window, cx| {
 1531        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1532        build_editor(buffer, window, cx)
 1533    });
 1534    _ = editor.update(cx, |editor, window, cx| {
 1535        editor.change_selections(None, window, cx, |s| {
 1536            s.select_display_ranges([
 1537                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1538                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1539            ]);
 1540        });
 1541    });
 1542
 1543    _ = editor.update(cx, |editor, window, cx| {
 1544        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1545        assert_eq!(
 1546            editor.selections.display_ranges(cx),
 1547            &[
 1548                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1549                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1550            ]
 1551        );
 1552    });
 1553
 1554    _ = editor.update(cx, |editor, window, cx| {
 1555        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1556        assert_eq!(
 1557            editor.selections.display_ranges(cx),
 1558            &[
 1559                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1560                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1561            ]
 1562        );
 1563    });
 1564
 1565    _ = editor.update(cx, |editor, window, cx| {
 1566        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1567        assert_eq!(
 1568            editor.selections.display_ranges(cx),
 1569            &[
 1570                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1571                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1572            ]
 1573        );
 1574    });
 1575
 1576    _ = editor.update(cx, |editor, window, cx| {
 1577        editor.move_to_end_of_line(&move_to_end, window, cx);
 1578        assert_eq!(
 1579            editor.selections.display_ranges(cx),
 1580            &[
 1581                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1582                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1583            ]
 1584        );
 1585    });
 1586
 1587    // Moving to the end of line again is a no-op.
 1588    _ = editor.update(cx, |editor, window, cx| {
 1589        editor.move_to_end_of_line(&move_to_end, window, cx);
 1590        assert_eq!(
 1591            editor.selections.display_ranges(cx),
 1592            &[
 1593                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 1594                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 1595            ]
 1596        );
 1597    });
 1598
 1599    _ = editor.update(cx, |editor, window, cx| {
 1600        editor.move_left(&MoveLeft, window, cx);
 1601        editor.select_to_beginning_of_line(
 1602            &SelectToBeginningOfLine {
 1603                stop_at_soft_wraps: true,
 1604                stop_at_indent: true,
 1605            },
 1606            window,
 1607            cx,
 1608        );
 1609        assert_eq!(
 1610            editor.selections.display_ranges(cx),
 1611            &[
 1612                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1613                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1614            ]
 1615        );
 1616    });
 1617
 1618    _ = editor.update(cx, |editor, window, cx| {
 1619        editor.select_to_beginning_of_line(
 1620            &SelectToBeginningOfLine {
 1621                stop_at_soft_wraps: true,
 1622                stop_at_indent: true,
 1623            },
 1624            window,
 1625            cx,
 1626        );
 1627        assert_eq!(
 1628            editor.selections.display_ranges(cx),
 1629            &[
 1630                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1631                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1632            ]
 1633        );
 1634    });
 1635
 1636    _ = editor.update(cx, |editor, window, cx| {
 1637        editor.select_to_beginning_of_line(
 1638            &SelectToBeginningOfLine {
 1639                stop_at_soft_wraps: true,
 1640                stop_at_indent: true,
 1641            },
 1642            window,
 1643            cx,
 1644        );
 1645        assert_eq!(
 1646            editor.selections.display_ranges(cx),
 1647            &[
 1648                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1649                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1650            ]
 1651        );
 1652    });
 1653
 1654    _ = editor.update(cx, |editor, window, cx| {
 1655        editor.select_to_end_of_line(
 1656            &SelectToEndOfLine {
 1657                stop_at_soft_wraps: true,
 1658            },
 1659            window,
 1660            cx,
 1661        );
 1662        assert_eq!(
 1663            editor.selections.display_ranges(cx),
 1664            &[
 1665                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
 1666                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 5),
 1667            ]
 1668        );
 1669    });
 1670
 1671    _ = editor.update(cx, |editor, window, cx| {
 1672        editor.delete_to_end_of_line(&DeleteToEndOfLine, window, cx);
 1673        assert_eq!(editor.display_text(cx), "ab\n  de");
 1674        assert_eq!(
 1675            editor.selections.display_ranges(cx),
 1676            &[
 1677                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 1678                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1679            ]
 1680        );
 1681    });
 1682
 1683    _ = editor.update(cx, |editor, window, cx| {
 1684        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1685        assert_eq!(editor.display_text(cx), "\n");
 1686        assert_eq!(
 1687            editor.selections.display_ranges(cx),
 1688            &[
 1689                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1690                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1691            ]
 1692        );
 1693    });
 1694}
 1695
 1696#[gpui::test]
 1697fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
 1698    init_test(cx, |_| {});
 1699    let move_to_beg = MoveToBeginningOfLine {
 1700        stop_at_soft_wraps: false,
 1701        stop_at_indent: false,
 1702    };
 1703
 1704    let move_to_end = MoveToEndOfLine {
 1705        stop_at_soft_wraps: false,
 1706    };
 1707
 1708    let editor = cx.add_window(|window, cx| {
 1709        let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
 1710        build_editor(buffer, window, cx)
 1711    });
 1712
 1713    _ = editor.update(cx, |editor, window, cx| {
 1714        editor.set_wrap_width(Some(140.0.into()), cx);
 1715
 1716        // We expect the following lines after wrapping
 1717        // ```
 1718        // thequickbrownfox
 1719        // jumpedoverthelazydo
 1720        // gs
 1721        // ```
 1722        // The final `gs` was soft-wrapped onto a new line.
 1723        assert_eq!(
 1724            "thequickbrownfox\njumpedoverthelaz\nydogs",
 1725            editor.display_text(cx),
 1726        );
 1727
 1728        // First, let's assert behavior on the first line, that was not soft-wrapped.
 1729        // Start the cursor at the `k` on the first line
 1730        editor.change_selections(None, window, cx, |s| {
 1731            s.select_display_ranges([
 1732                DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 7)
 1733            ]);
 1734        });
 1735
 1736        // Moving to the beginning of the line should put us at the beginning of the line.
 1737        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1738        assert_eq!(
 1739            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),],
 1740            editor.selections.display_ranges(cx)
 1741        );
 1742
 1743        // Moving to the end of the line should put us at the end of the line.
 1744        editor.move_to_end_of_line(&move_to_end, window, cx);
 1745        assert_eq!(
 1746            vec![DisplayPoint::new(DisplayRow(0), 16)..DisplayPoint::new(DisplayRow(0), 16),],
 1747            editor.selections.display_ranges(cx)
 1748        );
 1749
 1750        // Now, let's assert behavior on the second line, that ended up being soft-wrapped.
 1751        // Start the cursor at the last line (`y` that was wrapped to a new line)
 1752        editor.change_selections(None, window, cx, |s| {
 1753            s.select_display_ranges([
 1754                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0)
 1755            ]);
 1756        });
 1757
 1758        // Moving to the beginning of the line should put us at the start of the second line of
 1759        // display text, i.e., the `j`.
 1760        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1761        assert_eq!(
 1762            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1763            editor.selections.display_ranges(cx)
 1764        );
 1765
 1766        // Moving to the beginning of the line again should be a no-op.
 1767        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1768        assert_eq!(
 1769            vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),],
 1770            editor.selections.display_ranges(cx)
 1771        );
 1772
 1773        // Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
 1774        // next display line.
 1775        editor.move_to_end_of_line(&move_to_end, window, cx);
 1776        assert_eq!(
 1777            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1778            editor.selections.display_ranges(cx)
 1779        );
 1780
 1781        // Moving to the end of the line again should be a no-op.
 1782        editor.move_to_end_of_line(&move_to_end, window, cx);
 1783        assert_eq!(
 1784            vec![DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),],
 1785            editor.selections.display_ranges(cx)
 1786        );
 1787    });
 1788}
 1789
 1790#[gpui::test]
 1791fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
 1792    init_test(cx, |_| {});
 1793
 1794    let move_to_beg = MoveToBeginningOfLine {
 1795        stop_at_soft_wraps: true,
 1796        stop_at_indent: true,
 1797    };
 1798
 1799    let select_to_beg = SelectToBeginningOfLine {
 1800        stop_at_soft_wraps: true,
 1801        stop_at_indent: true,
 1802    };
 1803
 1804    let delete_to_beg = DeleteToBeginningOfLine {
 1805        stop_at_indent: true,
 1806    };
 1807
 1808    let move_to_end = MoveToEndOfLine {
 1809        stop_at_soft_wraps: false,
 1810    };
 1811
 1812    let editor = cx.add_window(|window, cx| {
 1813        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
 1814        build_editor(buffer, window, cx)
 1815    });
 1816
 1817    _ = editor.update(cx, |editor, window, cx| {
 1818        editor.change_selections(None, window, cx, |s| {
 1819            s.select_display_ranges([
 1820                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 1821                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4),
 1822            ]);
 1823        });
 1824
 1825        // Moving to the beginning of the line should put the first cursor at the beginning of the line,
 1826        // and the second cursor at the first non-whitespace character in the line.
 1827        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1828        assert_eq!(
 1829            editor.selections.display_ranges(cx),
 1830            &[
 1831                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1832                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1833            ]
 1834        );
 1835
 1836        // Moving to the beginning of the line again should be a no-op for the first cursor,
 1837        // and should move the second cursor to the beginning of the line.
 1838        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1839        assert_eq!(
 1840            editor.selections.display_ranges(cx),
 1841            &[
 1842                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1843                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 1844            ]
 1845        );
 1846
 1847        // Moving to the beginning of the line again should still be a no-op for the first cursor,
 1848        // and should move the second cursor back to the first non-whitespace character in the line.
 1849        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
 1850        assert_eq!(
 1851            editor.selections.display_ranges(cx),
 1852            &[
 1853                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 1854                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 1855            ]
 1856        );
 1857
 1858        // Selecting to the beginning of the line should select to the beginning of the line for the first cursor,
 1859        // and to the first non-whitespace character in the line for the second cursor.
 1860        editor.move_to_end_of_line(&move_to_end, window, cx);
 1861        editor.move_left(&MoveLeft, window, cx);
 1862        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1863        assert_eq!(
 1864            editor.selections.display_ranges(cx),
 1865            &[
 1866                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1867                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2),
 1868            ]
 1869        );
 1870
 1871        // Selecting to the beginning of the line again should be a no-op for the first cursor,
 1872        // and should select to the beginning of the line for the second cursor.
 1873        editor.select_to_beginning_of_line(&select_to_beg, window, cx);
 1874        assert_eq!(
 1875            editor.selections.display_ranges(cx),
 1876            &[
 1877                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0),
 1878                DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0),
 1879            ]
 1880        );
 1881
 1882        // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor,
 1883        // and should delete to the first non-whitespace character in the line for the second cursor.
 1884        editor.move_to_end_of_line(&move_to_end, window, cx);
 1885        editor.move_left(&MoveLeft, window, cx);
 1886        editor.delete_to_beginning_of_line(&delete_to_beg, window, cx);
 1887        assert_eq!(editor.text(cx), "c\n  f");
 1888    });
 1889}
 1890
 1891#[gpui::test]
 1892fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
 1893    init_test(cx, |_| {});
 1894
 1895    let editor = cx.add_window(|window, cx| {
 1896        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
 1897        build_editor(buffer, window, cx)
 1898    });
 1899    _ = editor.update(cx, |editor, window, cx| {
 1900        editor.change_selections(None, window, cx, |s| {
 1901            s.select_display_ranges([
 1902                DisplayPoint::new(DisplayRow(0), 11)..DisplayPoint::new(DisplayRow(0), 11),
 1903                DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4),
 1904            ])
 1905        });
 1906
 1907        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1908        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1909
 1910        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1911        assert_selection_ranges("use stdˇ::str::{foo, bar}\n\n  ˇ{baz.qux()}", editor, cx);
 1912
 1913        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1914        assert_selection_ranges("use ˇstd::str::{foo, bar}\n\nˇ  {baz.qux()}", editor, cx);
 1915
 1916        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1917        assert_selection_ranges("ˇuse std::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1918
 1919        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 1920        assert_selection_ranges("ˇuse std::str::{foo, barˇ}\n\n  {baz.qux()}", editor, cx);
 1921
 1922        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1923        assert_selection_ranges("useˇ std::str::{foo, bar}ˇ\n\n  {baz.qux()}", editor, cx);
 1924
 1925        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1926        assert_selection_ranges("use stdˇ::str::{foo, bar}\nˇ\n  {baz.qux()}", editor, cx);
 1927
 1928        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1929        assert_selection_ranges("use std::ˇstr::{foo, bar}\n\n  {ˇbaz.qux()}", editor, cx);
 1930
 1931        editor.move_right(&MoveRight, window, cx);
 1932        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1933        assert_selection_ranges(
 1934            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1935            editor,
 1936            cx,
 1937        );
 1938
 1939        editor.select_to_previous_word_start(&SelectToPreviousWordStart, window, cx);
 1940        assert_selection_ranges(
 1941            "use std«ˇ::s»tr::{foo, bar}\n\n  «ˇ{b»az.qux()}",
 1942            editor,
 1943            cx,
 1944        );
 1945
 1946        editor.select_to_next_word_end(&SelectToNextWordEnd, window, cx);
 1947        assert_selection_ranges(
 1948            "use std::«ˇs»tr::{foo, bar}\n\n  {«ˇb»az.qux()}",
 1949            editor,
 1950            cx,
 1951        );
 1952    });
 1953}
 1954
 1955#[gpui::test]
 1956fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
 1957    init_test(cx, |_| {});
 1958
 1959    let editor = cx.add_window(|window, cx| {
 1960        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
 1961        build_editor(buffer, window, cx)
 1962    });
 1963
 1964    _ = editor.update(cx, |editor, window, cx| {
 1965        editor.set_wrap_width(Some(140.0.into()), cx);
 1966        assert_eq!(
 1967            editor.display_text(cx),
 1968            "use one::{\n    two::three::\n    four::five\n};"
 1969        );
 1970
 1971        editor.change_selections(None, window, cx, |s| {
 1972            s.select_display_ranges([
 1973                DisplayPoint::new(DisplayRow(1), 7)..DisplayPoint::new(DisplayRow(1), 7)
 1974            ]);
 1975        });
 1976
 1977        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1978        assert_eq!(
 1979            editor.selections.display_ranges(cx),
 1980            &[DisplayPoint::new(DisplayRow(1), 9)..DisplayPoint::new(DisplayRow(1), 9)]
 1981        );
 1982
 1983        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1984        assert_eq!(
 1985            editor.selections.display_ranges(cx),
 1986            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 1987        );
 1988
 1989        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1990        assert_eq!(
 1991            editor.selections.display_ranges(cx),
 1992            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 1993        );
 1994
 1995        editor.move_to_next_word_end(&MoveToNextWordEnd, window, cx);
 1996        assert_eq!(
 1997            editor.selections.display_ranges(cx),
 1998            &[DisplayPoint::new(DisplayRow(2), 8)..DisplayPoint::new(DisplayRow(2), 8)]
 1999        );
 2000
 2001        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2002        assert_eq!(
 2003            editor.selections.display_ranges(cx),
 2004            &[DisplayPoint::new(DisplayRow(2), 4)..DisplayPoint::new(DisplayRow(2), 4)]
 2005        );
 2006
 2007        editor.move_to_previous_word_start(&MoveToPreviousWordStart, window, cx);
 2008        assert_eq!(
 2009            editor.selections.display_ranges(cx),
 2010            &[DisplayPoint::new(DisplayRow(1), 14)..DisplayPoint::new(DisplayRow(1), 14)]
 2011        );
 2012    });
 2013}
 2014
 2015#[gpui::test]
 2016async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut TestAppContext) {
 2017    init_test(cx, |_| {});
 2018    let mut cx = EditorTestContext::new(cx).await;
 2019
 2020    let line_height = cx.editor(|editor, window, _| {
 2021        editor
 2022            .style()
 2023            .unwrap()
 2024            .text
 2025            .line_height_in_pixels(window.rem_size())
 2026    });
 2027    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
 2028
 2029    cx.set_state(
 2030        &r#"ˇone
 2031        two
 2032
 2033        three
 2034        fourˇ
 2035        five
 2036
 2037        six"#
 2038            .unindent(),
 2039    );
 2040
 2041    cx.update_editor(|editor, window, cx| {
 2042        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2043    });
 2044    cx.assert_editor_state(
 2045        &r#"one
 2046        two
 2047        ˇ
 2048        three
 2049        four
 2050        five
 2051        ˇ
 2052        six"#
 2053            .unindent(),
 2054    );
 2055
 2056    cx.update_editor(|editor, window, cx| {
 2057        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2058    });
 2059    cx.assert_editor_state(
 2060        &r#"one
 2061        two
 2062
 2063        three
 2064        four
 2065        five
 2066        ˇ
 2067        sixˇ"#
 2068            .unindent(),
 2069    );
 2070
 2071    cx.update_editor(|editor, window, cx| {
 2072        editor.move_to_end_of_paragraph(&MoveToEndOfParagraph, window, cx)
 2073    });
 2074    cx.assert_editor_state(
 2075        &r#"one
 2076        two
 2077
 2078        three
 2079        four
 2080        five
 2081
 2082        sixˇ"#
 2083            .unindent(),
 2084    );
 2085
 2086    cx.update_editor(|editor, window, cx| {
 2087        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2088    });
 2089    cx.assert_editor_state(
 2090        &r#"one
 2091        two
 2092
 2093        three
 2094        four
 2095        five
 2096        ˇ
 2097        six"#
 2098            .unindent(),
 2099    );
 2100
 2101    cx.update_editor(|editor, window, cx| {
 2102        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2103    });
 2104    cx.assert_editor_state(
 2105        &r#"one
 2106        two
 2107        ˇ
 2108        three
 2109        four
 2110        five
 2111
 2112        six"#
 2113            .unindent(),
 2114    );
 2115
 2116    cx.update_editor(|editor, window, cx| {
 2117        editor.move_to_start_of_paragraph(&MoveToStartOfParagraph, window, cx)
 2118    });
 2119    cx.assert_editor_state(
 2120        &r#"ˇone
 2121        two
 2122
 2123        three
 2124        four
 2125        five
 2126
 2127        six"#
 2128            .unindent(),
 2129    );
 2130}
 2131
 2132#[gpui::test]
 2133async fn test_scroll_page_up_page_down(cx: &mut TestAppContext) {
 2134    init_test(cx, |_| {});
 2135    let mut cx = EditorTestContext::new(cx).await;
 2136    let line_height = cx.editor(|editor, window, _| {
 2137        editor
 2138            .style()
 2139            .unwrap()
 2140            .text
 2141            .line_height_in_pixels(window.rem_size())
 2142    });
 2143    let window = cx.window;
 2144    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
 2145
 2146    cx.set_state(
 2147        r#"ˇone
 2148        two
 2149        three
 2150        four
 2151        five
 2152        six
 2153        seven
 2154        eight
 2155        nine
 2156        ten
 2157        "#,
 2158    );
 2159
 2160    cx.update_editor(|editor, window, cx| {
 2161        assert_eq!(
 2162            editor.snapshot(window, cx).scroll_position(),
 2163            gpui::Point::new(0., 0.)
 2164        );
 2165        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2166        assert_eq!(
 2167            editor.snapshot(window, cx).scroll_position(),
 2168            gpui::Point::new(0., 3.)
 2169        );
 2170        editor.scroll_screen(&ScrollAmount::Page(1.), window, cx);
 2171        assert_eq!(
 2172            editor.snapshot(window, cx).scroll_position(),
 2173            gpui::Point::new(0., 6.)
 2174        );
 2175        editor.scroll_screen(&ScrollAmount::Page(-1.), window, cx);
 2176        assert_eq!(
 2177            editor.snapshot(window, cx).scroll_position(),
 2178            gpui::Point::new(0., 3.)
 2179        );
 2180
 2181        editor.scroll_screen(&ScrollAmount::Page(-0.5), window, cx);
 2182        assert_eq!(
 2183            editor.snapshot(window, cx).scroll_position(),
 2184            gpui::Point::new(0., 1.)
 2185        );
 2186        editor.scroll_screen(&ScrollAmount::Page(0.5), window, cx);
 2187        assert_eq!(
 2188            editor.snapshot(window, cx).scroll_position(),
 2189            gpui::Point::new(0., 3.)
 2190        );
 2191    });
 2192}
 2193
 2194#[gpui::test]
 2195async fn test_autoscroll(cx: &mut TestAppContext) {
 2196    init_test(cx, |_| {});
 2197    let mut cx = EditorTestContext::new(cx).await;
 2198
 2199    let line_height = cx.update_editor(|editor, window, cx| {
 2200        editor.set_vertical_scroll_margin(2, cx);
 2201        editor
 2202            .style()
 2203            .unwrap()
 2204            .text
 2205            .line_height_in_pixels(window.rem_size())
 2206    });
 2207    let window = cx.window;
 2208    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
 2209
 2210    cx.set_state(
 2211        r#"ˇone
 2212            two
 2213            three
 2214            four
 2215            five
 2216            six
 2217            seven
 2218            eight
 2219            nine
 2220            ten
 2221        "#,
 2222    );
 2223    cx.update_editor(|editor, window, cx| {
 2224        assert_eq!(
 2225            editor.snapshot(window, cx).scroll_position(),
 2226            gpui::Point::new(0., 0.0)
 2227        );
 2228    });
 2229
 2230    // Add a cursor below the visible area. Since both cursors cannot fit
 2231    // on screen, the editor autoscrolls to reveal the newest cursor, and
 2232    // allows the vertical scroll margin below that cursor.
 2233    cx.update_editor(|editor, window, cx| {
 2234        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2235            selections.select_ranges([
 2236                Point::new(0, 0)..Point::new(0, 0),
 2237                Point::new(6, 0)..Point::new(6, 0),
 2238            ]);
 2239        })
 2240    });
 2241    cx.update_editor(|editor, window, cx| {
 2242        assert_eq!(
 2243            editor.snapshot(window, cx).scroll_position(),
 2244            gpui::Point::new(0., 3.0)
 2245        );
 2246    });
 2247
 2248    // Move down. The editor cursor scrolls down to track the newest cursor.
 2249    cx.update_editor(|editor, window, cx| {
 2250        editor.move_down(&Default::default(), window, cx);
 2251    });
 2252    cx.update_editor(|editor, window, cx| {
 2253        assert_eq!(
 2254            editor.snapshot(window, cx).scroll_position(),
 2255            gpui::Point::new(0., 4.0)
 2256        );
 2257    });
 2258
 2259    // Add a cursor above the visible area. Since both cursors fit on screen,
 2260    // the editor scrolls to show both.
 2261    cx.update_editor(|editor, window, cx| {
 2262        editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
 2263            selections.select_ranges([
 2264                Point::new(1, 0)..Point::new(1, 0),
 2265                Point::new(6, 0)..Point::new(6, 0),
 2266            ]);
 2267        })
 2268    });
 2269    cx.update_editor(|editor, window, cx| {
 2270        assert_eq!(
 2271            editor.snapshot(window, cx).scroll_position(),
 2272            gpui::Point::new(0., 1.0)
 2273        );
 2274    });
 2275}
 2276
 2277#[gpui::test]
 2278async fn test_move_page_up_page_down(cx: &mut TestAppContext) {
 2279    init_test(cx, |_| {});
 2280    let mut cx = EditorTestContext::new(cx).await;
 2281
 2282    let line_height = cx.editor(|editor, window, _cx| {
 2283        editor
 2284            .style()
 2285            .unwrap()
 2286            .text
 2287            .line_height_in_pixels(window.rem_size())
 2288    });
 2289    let window = cx.window;
 2290    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
 2291    cx.set_state(
 2292        &r#"
 2293        ˇone
 2294        two
 2295        threeˇ
 2296        four
 2297        five
 2298        six
 2299        seven
 2300        eight
 2301        nine
 2302        ten
 2303        "#
 2304        .unindent(),
 2305    );
 2306
 2307    cx.update_editor(|editor, window, cx| {
 2308        editor.move_page_down(&MovePageDown::default(), window, cx)
 2309    });
 2310    cx.assert_editor_state(
 2311        &r#"
 2312        one
 2313        two
 2314        three
 2315        ˇfour
 2316        five
 2317        sixˇ
 2318        seven
 2319        eight
 2320        nine
 2321        ten
 2322        "#
 2323        .unindent(),
 2324    );
 2325
 2326    cx.update_editor(|editor, window, cx| {
 2327        editor.move_page_down(&MovePageDown::default(), window, cx)
 2328    });
 2329    cx.assert_editor_state(
 2330        &r#"
 2331        one
 2332        two
 2333        three
 2334        four
 2335        five
 2336        six
 2337        ˇseven
 2338        eight
 2339        nineˇ
 2340        ten
 2341        "#
 2342        .unindent(),
 2343    );
 2344
 2345    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2346    cx.assert_editor_state(
 2347        &r#"
 2348        one
 2349        two
 2350        three
 2351        ˇfour
 2352        five
 2353        sixˇ
 2354        seven
 2355        eight
 2356        nine
 2357        ten
 2358        "#
 2359        .unindent(),
 2360    );
 2361
 2362    cx.update_editor(|editor, window, cx| editor.move_page_up(&MovePageUp::default(), window, cx));
 2363    cx.assert_editor_state(
 2364        &r#"
 2365        ˇone
 2366        two
 2367        threeˇ
 2368        four
 2369        five
 2370        six
 2371        seven
 2372        eight
 2373        nine
 2374        ten
 2375        "#
 2376        .unindent(),
 2377    );
 2378
 2379    // Test select collapsing
 2380    cx.update_editor(|editor, window, cx| {
 2381        editor.move_page_down(&MovePageDown::default(), window, cx);
 2382        editor.move_page_down(&MovePageDown::default(), window, cx);
 2383        editor.move_page_down(&MovePageDown::default(), window, cx);
 2384    });
 2385    cx.assert_editor_state(
 2386        &r#"
 2387        one
 2388        two
 2389        three
 2390        four
 2391        five
 2392        six
 2393        seven
 2394        eight
 2395        nine
 2396        ˇten
 2397        ˇ"#
 2398        .unindent(),
 2399    );
 2400}
 2401
 2402#[gpui::test]
 2403async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) {
 2404    init_test(cx, |_| {});
 2405    let mut cx = EditorTestContext::new(cx).await;
 2406    cx.set_state("one «two threeˇ» four");
 2407    cx.update_editor(|editor, window, cx| {
 2408        editor.delete_to_beginning_of_line(
 2409            &DeleteToBeginningOfLine {
 2410                stop_at_indent: false,
 2411            },
 2412            window,
 2413            cx,
 2414        );
 2415        assert_eq!(editor.text(cx), " four");
 2416    });
 2417}
 2418
 2419#[gpui::test]
 2420fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
 2421    init_test(cx, |_| {});
 2422
 2423    let editor = cx.add_window(|window, cx| {
 2424        let buffer = MultiBuffer::build_simple("one two three four", cx);
 2425        build_editor(buffer.clone(), window, cx)
 2426    });
 2427
 2428    _ = editor.update(cx, |editor, window, cx| {
 2429        editor.change_selections(None, window, cx, |s| {
 2430            s.select_display_ranges([
 2431                // an empty selection - the preceding word fragment is deleted
 2432                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2433                // characters selected - they are deleted
 2434                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 12),
 2435            ])
 2436        });
 2437        editor.delete_to_previous_word_start(
 2438            &DeleteToPreviousWordStart {
 2439                ignore_newlines: false,
 2440            },
 2441            window,
 2442            cx,
 2443        );
 2444        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e two te four");
 2445    });
 2446
 2447    _ = editor.update(cx, |editor, window, cx| {
 2448        editor.change_selections(None, window, cx, |s| {
 2449            s.select_display_ranges([
 2450                // an empty selection - the following word fragment is deleted
 2451                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3),
 2452                // characters selected - they are deleted
 2453                DisplayPoint::new(DisplayRow(0), 9)..DisplayPoint::new(DisplayRow(0), 10),
 2454            ])
 2455        });
 2456        editor.delete_to_next_word_end(
 2457            &DeleteToNextWordEnd {
 2458                ignore_newlines: false,
 2459            },
 2460            window,
 2461            cx,
 2462        );
 2463        assert_eq!(editor.buffer.read(cx).read(cx).text(), "e t te our");
 2464    });
 2465}
 2466
 2467#[gpui::test]
 2468fn test_delete_to_previous_word_start_or_newline(cx: &mut TestAppContext) {
 2469    init_test(cx, |_| {});
 2470
 2471    let editor = cx.add_window(|window, cx| {
 2472        let buffer = MultiBuffer::build_simple("one\n2\nthree\n4", cx);
 2473        build_editor(buffer.clone(), window, cx)
 2474    });
 2475    let del_to_prev_word_start = DeleteToPreviousWordStart {
 2476        ignore_newlines: false,
 2477    };
 2478    let del_to_prev_word_start_ignore_newlines = DeleteToPreviousWordStart {
 2479        ignore_newlines: true,
 2480    };
 2481
 2482    _ = editor.update(cx, |editor, window, cx| {
 2483        editor.change_selections(None, window, cx, |s| {
 2484            s.select_display_ranges([
 2485                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1)
 2486            ])
 2487        });
 2488        editor.delete_to_previous_word_start(&del_to_prev_word_start, window, cx);
 2489        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n2\nthree\n");
 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");
 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\n");
 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");
 2496        editor.delete_to_previous_word_start(&del_to_prev_word_start_ignore_newlines, window, cx);
 2497        assert_eq!(editor.buffer.read(cx).read(cx).text(), "one\n");
 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(), "");
 2500    });
 2501}
 2502
 2503#[gpui::test]
 2504fn test_delete_to_next_word_end_or_newline(cx: &mut TestAppContext) {
 2505    init_test(cx, |_| {});
 2506
 2507    let editor = cx.add_window(|window, cx| {
 2508        let buffer = MultiBuffer::build_simple("\none\n   two\nthree\n   four", cx);
 2509        build_editor(buffer.clone(), window, cx)
 2510    });
 2511    let del_to_next_word_end = DeleteToNextWordEnd {
 2512        ignore_newlines: false,
 2513    };
 2514    let del_to_next_word_end_ignore_newlines = DeleteToNextWordEnd {
 2515        ignore_newlines: true,
 2516    };
 2517
 2518    _ = editor.update(cx, |editor, window, cx| {
 2519        editor.change_selections(None, window, cx, |s| {
 2520            s.select_display_ranges([
 2521                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)
 2522            ])
 2523        });
 2524        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2525        assert_eq!(
 2526            editor.buffer.read(cx).read(cx).text(),
 2527            "one\n   two\nthree\n   four"
 2528        );
 2529        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2530        assert_eq!(
 2531            editor.buffer.read(cx).read(cx).text(),
 2532            "\n   two\nthree\n   four"
 2533        );
 2534        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2535        assert_eq!(
 2536            editor.buffer.read(cx).read(cx).text(),
 2537            "two\nthree\n   four"
 2538        );
 2539        editor.delete_to_next_word_end(&del_to_next_word_end, window, cx);
 2540        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\nthree\n   four");
 2541        editor.delete_to_next_word_end(&del_to_next_word_end_ignore_newlines, window, cx);
 2542        assert_eq!(editor.buffer.read(cx).read(cx).text(), "\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(), "");
 2545    });
 2546}
 2547
 2548#[gpui::test]
 2549fn test_newline(cx: &mut TestAppContext) {
 2550    init_test(cx, |_| {});
 2551
 2552    let editor = cx.add_window(|window, cx| {
 2553        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
 2554        build_editor(buffer.clone(), window, cx)
 2555    });
 2556
 2557    _ = editor.update(cx, |editor, window, cx| {
 2558        editor.change_selections(None, window, cx, |s| {
 2559            s.select_display_ranges([
 2560                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 2561                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 2562                DisplayPoint::new(DisplayRow(1), 6)..DisplayPoint::new(DisplayRow(1), 6),
 2563            ])
 2564        });
 2565
 2566        editor.newline(&Newline, window, cx);
 2567        assert_eq!(editor.text(cx), "aa\naa\n  \n    bb\n    bb\n");
 2568    });
 2569}
 2570
 2571#[gpui::test]
 2572fn test_newline_with_old_selections(cx: &mut TestAppContext) {
 2573    init_test(cx, |_| {});
 2574
 2575    let editor = cx.add_window(|window, cx| {
 2576        let buffer = MultiBuffer::build_simple(
 2577            "
 2578                a
 2579                b(
 2580                    X
 2581                )
 2582                c(
 2583                    X
 2584                )
 2585            "
 2586            .unindent()
 2587            .as_str(),
 2588            cx,
 2589        );
 2590        let mut editor = build_editor(buffer.clone(), window, cx);
 2591        editor.change_selections(None, window, cx, |s| {
 2592            s.select_ranges([
 2593                Point::new(2, 4)..Point::new(2, 5),
 2594                Point::new(5, 4)..Point::new(5, 5),
 2595            ])
 2596        });
 2597        editor
 2598    });
 2599
 2600    _ = editor.update(cx, |editor, window, cx| {
 2601        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2602        editor.buffer.update(cx, |buffer, cx| {
 2603            buffer.edit(
 2604                [
 2605                    (Point::new(1, 2)..Point::new(3, 0), ""),
 2606                    (Point::new(4, 2)..Point::new(6, 0), ""),
 2607                ],
 2608                None,
 2609                cx,
 2610            );
 2611            assert_eq!(
 2612                buffer.read(cx).text(),
 2613                "
 2614                    a
 2615                    b()
 2616                    c()
 2617                "
 2618                .unindent()
 2619            );
 2620        });
 2621        assert_eq!(
 2622            editor.selections.ranges(cx),
 2623            &[
 2624                Point::new(1, 2)..Point::new(1, 2),
 2625                Point::new(2, 2)..Point::new(2, 2),
 2626            ],
 2627        );
 2628
 2629        editor.newline(&Newline, window, cx);
 2630        assert_eq!(
 2631            editor.text(cx),
 2632            "
 2633                a
 2634                b(
 2635                )
 2636                c(
 2637                )
 2638            "
 2639            .unindent()
 2640        );
 2641
 2642        // The selections are moved after the inserted newlines
 2643        assert_eq!(
 2644            editor.selections.ranges(cx),
 2645            &[
 2646                Point::new(2, 0)..Point::new(2, 0),
 2647                Point::new(4, 0)..Point::new(4, 0),
 2648            ],
 2649        );
 2650    });
 2651}
 2652
 2653#[gpui::test]
 2654async fn test_newline_above(cx: &mut TestAppContext) {
 2655    init_test(cx, |settings| {
 2656        settings.defaults.tab_size = NonZeroU32::new(4)
 2657    });
 2658
 2659    let language = Arc::new(
 2660        Language::new(
 2661            LanguageConfig::default(),
 2662            Some(tree_sitter_rust::LANGUAGE.into()),
 2663        )
 2664        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2665        .unwrap(),
 2666    );
 2667
 2668    let mut cx = EditorTestContext::new(cx).await;
 2669    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2670    cx.set_state(indoc! {"
 2671        const a: ˇA = (
 2672 2673                «const_functionˇ»(ˇ),
 2674                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2675 2676        ˇ);ˇ
 2677    "});
 2678
 2679    cx.update_editor(|e, window, cx| e.newline_above(&NewlineAbove, window, cx));
 2680    cx.assert_editor_state(indoc! {"
 2681        ˇ
 2682        const a: A = (
 2683            ˇ
 2684            (
 2685                ˇ
 2686                ˇ
 2687                const_function(),
 2688                ˇ
 2689                ˇ
 2690                ˇ
 2691                ˇ
 2692                something_else,
 2693                ˇ
 2694            )
 2695            ˇ
 2696            ˇ
 2697        );
 2698    "});
 2699}
 2700
 2701#[gpui::test]
 2702async fn test_newline_below(cx: &mut TestAppContext) {
 2703    init_test(cx, |settings| {
 2704        settings.defaults.tab_size = NonZeroU32::new(4)
 2705    });
 2706
 2707    let language = Arc::new(
 2708        Language::new(
 2709            LanguageConfig::default(),
 2710            Some(tree_sitter_rust::LANGUAGE.into()),
 2711        )
 2712        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2713        .unwrap(),
 2714    );
 2715
 2716    let mut cx = EditorTestContext::new(cx).await;
 2717    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2718    cx.set_state(indoc! {"
 2719        const a: ˇA = (
 2720 2721                «const_functionˇ»(ˇ),
 2722                so«mˇ»et«hˇ»ing_ˇelse,ˇ
 2723 2724        ˇ);ˇ
 2725    "});
 2726
 2727    cx.update_editor(|e, window, cx| e.newline_below(&NewlineBelow, window, cx));
 2728    cx.assert_editor_state(indoc! {"
 2729        const a: A = (
 2730            ˇ
 2731            (
 2732                ˇ
 2733                const_function(),
 2734                ˇ
 2735                ˇ
 2736                something_else,
 2737                ˇ
 2738                ˇ
 2739                ˇ
 2740                ˇ
 2741            )
 2742            ˇ
 2743        );
 2744        ˇ
 2745        ˇ
 2746    "});
 2747}
 2748
 2749#[gpui::test]
 2750async fn test_newline_comments(cx: &mut TestAppContext) {
 2751    init_test(cx, |settings| {
 2752        settings.defaults.tab_size = NonZeroU32::new(4)
 2753    });
 2754
 2755    let language = Arc::new(Language::new(
 2756        LanguageConfig {
 2757            line_comments: vec!["//".into()],
 2758            ..LanguageConfig::default()
 2759        },
 2760        None,
 2761    ));
 2762    {
 2763        let mut cx = EditorTestContext::new(cx).await;
 2764        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2765        cx.set_state(indoc! {"
 2766        // Fooˇ
 2767    "});
 2768
 2769        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2770        cx.assert_editor_state(indoc! {"
 2771        // Foo
 2772        //ˇ
 2773    "});
 2774        // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
 2775        cx.set_state(indoc! {"
 2776        ˇ// Foo
 2777    "});
 2778        cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2779        cx.assert_editor_state(indoc! {"
 2780
 2781        ˇ// Foo
 2782    "});
 2783    }
 2784    // Ensure that comment continuations can be disabled.
 2785    update_test_language_settings(cx, |settings| {
 2786        settings.defaults.extend_comment_on_newline = Some(false);
 2787    });
 2788    let mut cx = EditorTestContext::new(cx).await;
 2789    cx.set_state(indoc! {"
 2790        // Fooˇ
 2791    "});
 2792    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
 2793    cx.assert_editor_state(indoc! {"
 2794        // Foo
 2795        ˇ
 2796    "});
 2797}
 2798
 2799#[gpui::test]
 2800fn test_insert_with_old_selections(cx: &mut TestAppContext) {
 2801    init_test(cx, |_| {});
 2802
 2803    let editor = cx.add_window(|window, cx| {
 2804        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
 2805        let mut editor = build_editor(buffer.clone(), window, cx);
 2806        editor.change_selections(None, window, cx, |s| {
 2807            s.select_ranges([3..4, 11..12, 19..20])
 2808        });
 2809        editor
 2810    });
 2811
 2812    _ = editor.update(cx, |editor, window, cx| {
 2813        // Edit the buffer directly, deleting ranges surrounding the editor's selections
 2814        editor.buffer.update(cx, |buffer, cx| {
 2815            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
 2816            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
 2817        });
 2818        assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 2819
 2820        editor.insert("Z", window, cx);
 2821        assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
 2822
 2823        // The selections are moved after the inserted characters
 2824        assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
 2825    });
 2826}
 2827
 2828#[gpui::test]
 2829async fn test_tab(cx: &mut TestAppContext) {
 2830    init_test(cx, |settings| {
 2831        settings.defaults.tab_size = NonZeroU32::new(3)
 2832    });
 2833
 2834    let mut cx = EditorTestContext::new(cx).await;
 2835    cx.set_state(indoc! {"
 2836        ˇabˇc
 2837        ˇ🏀ˇ🏀ˇefg
 2838 2839    "});
 2840    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2841    cx.assert_editor_state(indoc! {"
 2842           ˇab ˇc
 2843           ˇ🏀  ˇ🏀  ˇefg
 2844        d  ˇ
 2845    "});
 2846
 2847    cx.set_state(indoc! {"
 2848        a
 2849        «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2850    "});
 2851    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2852    cx.assert_editor_state(indoc! {"
 2853        a
 2854           «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
 2855    "});
 2856}
 2857
 2858#[gpui::test]
 2859async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut TestAppContext) {
 2860    init_test(cx, |_| {});
 2861
 2862    let mut cx = EditorTestContext::new(cx).await;
 2863    let language = Arc::new(
 2864        Language::new(
 2865            LanguageConfig::default(),
 2866            Some(tree_sitter_rust::LANGUAGE.into()),
 2867        )
 2868        .with_indents_query(r#"(_ "(" ")" @end) @indent"#)
 2869        .unwrap(),
 2870    );
 2871    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2872
 2873    // cursors that are already at the suggested indent level insert
 2874    // a soft tab. cursors that are to the left of the suggested indent
 2875    // auto-indent their line.
 2876    cx.set_state(indoc! {"
 2877        ˇ
 2878        const a: B = (
 2879            c(
 2880                d(
 2881        ˇ
 2882                )
 2883        ˇ
 2884        ˇ    )
 2885        );
 2886    "});
 2887    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2888    cx.assert_editor_state(indoc! {"
 2889            ˇ
 2890        const a: B = (
 2891            c(
 2892                d(
 2893                    ˇ
 2894                )
 2895                ˇ
 2896            ˇ)
 2897        );
 2898    "});
 2899
 2900    // handle auto-indent when there are multiple cursors on the same line
 2901    cx.set_state(indoc! {"
 2902        const a: B = (
 2903            c(
 2904        ˇ    ˇ
 2905        ˇ    )
 2906        );
 2907    "});
 2908    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2909    cx.assert_editor_state(indoc! {"
 2910        const a: B = (
 2911            c(
 2912                ˇ
 2913            ˇ)
 2914        );
 2915    "});
 2916}
 2917
 2918#[gpui::test]
 2919async fn test_tab_with_mixed_whitespace_txt(cx: &mut TestAppContext) {
 2920    init_test(cx, |settings| {
 2921        settings.defaults.tab_size = NonZeroU32::new(3)
 2922    });
 2923
 2924    let mut cx = EditorTestContext::new(cx).await;
 2925    cx.set_state(indoc! {"
 2926         ˇ
 2927        \t ˇ
 2928        \t  ˇ
 2929        \t   ˇ
 2930         \t  \t\t \t      \t\t   \t\t    \t \t ˇ
 2931    "});
 2932
 2933    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2934    cx.assert_editor_state(indoc! {"
 2935           ˇ
 2936        \t   ˇ
 2937        \t   ˇ
 2938        \t      ˇ
 2939         \t  \t\t \t      \t\t   \t\t    \t \t   ˇ
 2940    "});
 2941}
 2942
 2943#[gpui::test]
 2944async fn test_tab_with_mixed_whitespace_rust(cx: &mut TestAppContext) {
 2945    init_test(cx, |settings| {
 2946        settings.defaults.tab_size = NonZeroU32::new(4)
 2947    });
 2948
 2949    let language = Arc::new(
 2950        Language::new(
 2951            LanguageConfig::default(),
 2952            Some(tree_sitter_rust::LANGUAGE.into()),
 2953        )
 2954        .with_indents_query(r#"(_ "{" "}" @end) @indent"#)
 2955        .unwrap(),
 2956    );
 2957
 2958    let mut cx = EditorTestContext::new(cx).await;
 2959    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 2960    cx.set_state(indoc! {"
 2961        fn a() {
 2962            if b {
 2963        \t ˇc
 2964            }
 2965        }
 2966    "});
 2967
 2968    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2969    cx.assert_editor_state(indoc! {"
 2970        fn a() {
 2971            if b {
 2972                ˇc
 2973            }
 2974        }
 2975    "});
 2976}
 2977
 2978#[gpui::test]
 2979async fn test_indent_outdent(cx: &mut TestAppContext) {
 2980    init_test(cx, |settings| {
 2981        settings.defaults.tab_size = NonZeroU32::new(4);
 2982    });
 2983
 2984    let mut cx = EditorTestContext::new(cx).await;
 2985
 2986    cx.set_state(indoc! {"
 2987          «oneˇ» «twoˇ»
 2988        three
 2989         four
 2990    "});
 2991    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 2992    cx.assert_editor_state(indoc! {"
 2993            «oneˇ» «twoˇ»
 2994        three
 2995         four
 2996    "});
 2997
 2998    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 2999    cx.assert_editor_state(indoc! {"
 3000        «oneˇ» «twoˇ»
 3001        three
 3002         four
 3003    "});
 3004
 3005    // select across line ending
 3006    cx.set_state(indoc! {"
 3007        one two
 3008        t«hree
 3009        ˇ» four
 3010    "});
 3011    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3012    cx.assert_editor_state(indoc! {"
 3013        one two
 3014            t«hree
 3015        ˇ» four
 3016    "});
 3017
 3018    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3019    cx.assert_editor_state(indoc! {"
 3020        one two
 3021        t«hree
 3022        ˇ» four
 3023    "});
 3024
 3025    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3026    cx.set_state(indoc! {"
 3027        one two
 3028        ˇthree
 3029            four
 3030    "});
 3031    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3032    cx.assert_editor_state(indoc! {"
 3033        one two
 3034            ˇthree
 3035            four
 3036    "});
 3037
 3038    cx.set_state(indoc! {"
 3039        one two
 3040        ˇ    three
 3041            four
 3042    "});
 3043    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3044    cx.assert_editor_state(indoc! {"
 3045        one two
 3046        ˇthree
 3047            four
 3048    "});
 3049}
 3050
 3051#[gpui::test]
 3052async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
 3053    init_test(cx, |settings| {
 3054        settings.defaults.hard_tabs = Some(true);
 3055    });
 3056
 3057    let mut cx = EditorTestContext::new(cx).await;
 3058
 3059    // select two ranges on one line
 3060    cx.set_state(indoc! {"
 3061        «oneˇ» «twoˇ»
 3062        three
 3063        four
 3064    "});
 3065    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3066    cx.assert_editor_state(indoc! {"
 3067        \t«oneˇ» «twoˇ»
 3068        three
 3069        four
 3070    "});
 3071    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3072    cx.assert_editor_state(indoc! {"
 3073        \t\t«oneˇ» «twoˇ»
 3074        three
 3075        four
 3076    "});
 3077    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3078    cx.assert_editor_state(indoc! {"
 3079        \t«oneˇ» «twoˇ»
 3080        three
 3081        four
 3082    "});
 3083    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3084    cx.assert_editor_state(indoc! {"
 3085        «oneˇ» «twoˇ»
 3086        three
 3087        four
 3088    "});
 3089
 3090    // select across a line ending
 3091    cx.set_state(indoc! {"
 3092        one two
 3093        t«hree
 3094        ˇ»four
 3095    "});
 3096    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3097    cx.assert_editor_state(indoc! {"
 3098        one two
 3099        \tt«hree
 3100        ˇ»four
 3101    "});
 3102    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3103    cx.assert_editor_state(indoc! {"
 3104        one two
 3105        \t\tt«hree
 3106        ˇ»four
 3107    "});
 3108    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3109    cx.assert_editor_state(indoc! {"
 3110        one two
 3111        \tt«hree
 3112        ˇ»four
 3113    "});
 3114    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3115    cx.assert_editor_state(indoc! {"
 3116        one two
 3117        t«hree
 3118        ˇ»four
 3119    "});
 3120
 3121    // Ensure that indenting/outdenting works when the cursor is at column 0.
 3122    cx.set_state(indoc! {"
 3123        one two
 3124        ˇthree
 3125        four
 3126    "});
 3127    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3128    cx.assert_editor_state(indoc! {"
 3129        one two
 3130        ˇthree
 3131        four
 3132    "});
 3133    cx.update_editor(|e, window, cx| e.tab(&Tab, window, cx));
 3134    cx.assert_editor_state(indoc! {"
 3135        one two
 3136        \tˇthree
 3137        four
 3138    "});
 3139    cx.update_editor(|e, window, cx| e.backtab(&Backtab, window, cx));
 3140    cx.assert_editor_state(indoc! {"
 3141        one two
 3142        ˇthree
 3143        four
 3144    "});
 3145}
 3146
 3147#[gpui::test]
 3148fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
 3149    init_test(cx, |settings| {
 3150        settings.languages.extend([
 3151            (
 3152                "TOML".into(),
 3153                LanguageSettingsContent {
 3154                    tab_size: NonZeroU32::new(2),
 3155                    ..Default::default()
 3156                },
 3157            ),
 3158            (
 3159                "Rust".into(),
 3160                LanguageSettingsContent {
 3161                    tab_size: NonZeroU32::new(4),
 3162                    ..Default::default()
 3163                },
 3164            ),
 3165        ]);
 3166    });
 3167
 3168    let toml_language = Arc::new(Language::new(
 3169        LanguageConfig {
 3170            name: "TOML".into(),
 3171            ..Default::default()
 3172        },
 3173        None,
 3174    ));
 3175    let rust_language = Arc::new(Language::new(
 3176        LanguageConfig {
 3177            name: "Rust".into(),
 3178            ..Default::default()
 3179        },
 3180        None,
 3181    ));
 3182
 3183    let toml_buffer =
 3184        cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx).with_language(toml_language, cx));
 3185    let rust_buffer =
 3186        cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
 3187    let multibuffer = cx.new(|cx| {
 3188        let mut multibuffer = MultiBuffer::new(ReadWrite);
 3189        multibuffer.push_excerpts(
 3190            toml_buffer.clone(),
 3191            [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
 3192            cx,
 3193        );
 3194        multibuffer.push_excerpts(
 3195            rust_buffer.clone(),
 3196            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
 3197            cx,
 3198        );
 3199        multibuffer
 3200    });
 3201
 3202    cx.add_window(|window, cx| {
 3203        let mut editor = build_editor(multibuffer, window, cx);
 3204
 3205        assert_eq!(
 3206            editor.text(cx),
 3207            indoc! {"
 3208                a = 1
 3209                b = 2
 3210
 3211                const c: usize = 3;
 3212            "}
 3213        );
 3214
 3215        select_ranges(
 3216            &mut editor,
 3217            indoc! {"
 3218                «aˇ» = 1
 3219                b = 2
 3220
 3221                «const c:ˇ» usize = 3;
 3222            "},
 3223            window,
 3224            cx,
 3225        );
 3226
 3227        editor.tab(&Tab, window, cx);
 3228        assert_text_with_selections(
 3229            &mut editor,
 3230            indoc! {"
 3231                  «aˇ» = 1
 3232                b = 2
 3233
 3234                    «const c:ˇ» usize = 3;
 3235            "},
 3236            cx,
 3237        );
 3238        editor.backtab(&Backtab, window, cx);
 3239        assert_text_with_selections(
 3240            &mut editor,
 3241            indoc! {"
 3242                «aˇ» = 1
 3243                b = 2
 3244
 3245                «const c:ˇ» usize = 3;
 3246            "},
 3247            cx,
 3248        );
 3249
 3250        editor
 3251    });
 3252}
 3253
 3254#[gpui::test]
 3255async fn test_backspace(cx: &mut TestAppContext) {
 3256    init_test(cx, |_| {});
 3257
 3258    let mut cx = EditorTestContext::new(cx).await;
 3259
 3260    // Basic backspace
 3261    cx.set_state(indoc! {"
 3262        onˇe two three
 3263        fou«rˇ» five six
 3264        seven «ˇeight nine
 3265        »ten
 3266    "});
 3267    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3268    cx.assert_editor_state(indoc! {"
 3269        oˇe two three
 3270        fouˇ five six
 3271        seven ˇten
 3272    "});
 3273
 3274    // Test backspace inside and around indents
 3275    cx.set_state(indoc! {"
 3276        zero
 3277            ˇone
 3278                ˇtwo
 3279            ˇ ˇ ˇ  three
 3280        ˇ  ˇ  four
 3281    "});
 3282    cx.update_editor(|e, window, cx| e.backspace(&Backspace, window, cx));
 3283    cx.assert_editor_state(indoc! {"
 3284        zero
 3285        ˇone
 3286            ˇtwo
 3287        ˇ  threeˇ  four
 3288    "});
 3289}
 3290
 3291#[gpui::test]
 3292async fn test_delete(cx: &mut TestAppContext) {
 3293    init_test(cx, |_| {});
 3294
 3295    let mut cx = EditorTestContext::new(cx).await;
 3296    cx.set_state(indoc! {"
 3297        onˇe two three
 3298        fou«rˇ» five six
 3299        seven «ˇeight nine
 3300        »ten
 3301    "});
 3302    cx.update_editor(|e, window, cx| e.delete(&Delete, window, cx));
 3303    cx.assert_editor_state(indoc! {"
 3304        onˇ two three
 3305        fouˇ five six
 3306        seven ˇten
 3307    "});
 3308}
 3309
 3310#[gpui::test]
 3311fn test_delete_line(cx: &mut TestAppContext) {
 3312    init_test(cx, |_| {});
 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(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 3322                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 3323                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 3324            ])
 3325        });
 3326        editor.delete_line(&DeleteLine, window, cx);
 3327        assert_eq!(editor.display_text(cx), "ghi");
 3328        assert_eq!(
 3329            editor.selections.display_ranges(cx),
 3330            vec![
 3331                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
 3332                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)
 3333            ]
 3334        );
 3335    });
 3336
 3337    let editor = cx.add_window(|window, cx| {
 3338        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 3339        build_editor(buffer, window, cx)
 3340    });
 3341    _ = editor.update(cx, |editor, window, cx| {
 3342        editor.change_selections(None, window, cx, |s| {
 3343            s.select_display_ranges([
 3344                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(0), 1)
 3345            ])
 3346        });
 3347        editor.delete_line(&DeleteLine, window, cx);
 3348        assert_eq!(editor.display_text(cx), "ghi\n");
 3349        assert_eq!(
 3350            editor.selections.display_ranges(cx),
 3351            vec![DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1)]
 3352        );
 3353    });
 3354}
 3355
 3356#[gpui::test]
 3357fn test_join_lines_with_single_selection(cx: &mut TestAppContext) {
 3358    init_test(cx, |_| {});
 3359
 3360    cx.add_window(|window, cx| {
 3361        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3362        let mut editor = build_editor(buffer.clone(), window, cx);
 3363        let buffer = buffer.read(cx).as_singleton().unwrap();
 3364
 3365        assert_eq!(
 3366            editor.selections.ranges::<Point>(cx),
 3367            &[Point::new(0, 0)..Point::new(0, 0)]
 3368        );
 3369
 3370        // When on single line, replace newline at end by space
 3371        editor.join_lines(&JoinLines, window, cx);
 3372        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3373        assert_eq!(
 3374            editor.selections.ranges::<Point>(cx),
 3375            &[Point::new(0, 3)..Point::new(0, 3)]
 3376        );
 3377
 3378        // When multiple lines are selected, remove newlines that are spanned by the selection
 3379        editor.change_selections(None, window, cx, |s| {
 3380            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
 3381        });
 3382        editor.join_lines(&JoinLines, window, cx);
 3383        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
 3384        assert_eq!(
 3385            editor.selections.ranges::<Point>(cx),
 3386            &[Point::new(0, 11)..Point::new(0, 11)]
 3387        );
 3388
 3389        // Undo should be transactional
 3390        editor.undo(&Undo, window, cx);
 3391        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
 3392        assert_eq!(
 3393            editor.selections.ranges::<Point>(cx),
 3394            &[Point::new(0, 5)..Point::new(2, 2)]
 3395        );
 3396
 3397        // When joining an empty line don't insert a space
 3398        editor.change_selections(None, window, cx, |s| {
 3399            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
 3400        });
 3401        editor.join_lines(&JoinLines, window, cx);
 3402        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
 3403        assert_eq!(
 3404            editor.selections.ranges::<Point>(cx),
 3405            [Point::new(2, 3)..Point::new(2, 3)]
 3406        );
 3407
 3408        // We can remove trailing newlines
 3409        editor.join_lines(&JoinLines, window, cx);
 3410        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3411        assert_eq!(
 3412            editor.selections.ranges::<Point>(cx),
 3413            [Point::new(2, 3)..Point::new(2, 3)]
 3414        );
 3415
 3416        // We don't blow up on the last line
 3417        editor.join_lines(&JoinLines, window, cx);
 3418        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd");
 3419        assert_eq!(
 3420            editor.selections.ranges::<Point>(cx),
 3421            [Point::new(2, 3)..Point::new(2, 3)]
 3422        );
 3423
 3424        // reset to test indentation
 3425        editor.buffer.update(cx, |buffer, cx| {
 3426            buffer.edit(
 3427                [
 3428                    (Point::new(1, 0)..Point::new(1, 2), "  "),
 3429                    (Point::new(2, 0)..Point::new(2, 3), "  \n\td"),
 3430                ],
 3431                None,
 3432                cx,
 3433            )
 3434        });
 3435
 3436        // We remove any leading spaces
 3437        assert_eq!(buffer.read(cx).text(), "aaa bbb\n  c\n  \n\td");
 3438        editor.change_selections(None, window, cx, |s| {
 3439            s.select_ranges([Point::new(0, 1)..Point::new(0, 1)])
 3440        });
 3441        editor.join_lines(&JoinLines, window, cx);
 3442        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n  \n\td");
 3443
 3444        // We don't insert a space for a line containing only spaces
 3445        editor.join_lines(&JoinLines, window, cx);
 3446        assert_eq!(buffer.read(cx).text(), "aaa bbb c\n\td");
 3447
 3448        // We ignore any leading tabs
 3449        editor.join_lines(&JoinLines, window, cx);
 3450        assert_eq!(buffer.read(cx).text(), "aaa bbb c d");
 3451
 3452        editor
 3453    });
 3454}
 3455
 3456#[gpui::test]
 3457fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) {
 3458    init_test(cx, |_| {});
 3459
 3460    cx.add_window(|window, cx| {
 3461        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
 3462        let mut editor = build_editor(buffer.clone(), window, cx);
 3463        let buffer = buffer.read(cx).as_singleton().unwrap();
 3464
 3465        editor.change_selections(None, window, cx, |s| {
 3466            s.select_ranges([
 3467                Point::new(0, 2)..Point::new(1, 1),
 3468                Point::new(1, 2)..Point::new(1, 2),
 3469                Point::new(3, 1)..Point::new(3, 2),
 3470            ])
 3471        });
 3472
 3473        editor.join_lines(&JoinLines, window, cx);
 3474        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc\nddd\n");
 3475
 3476        assert_eq!(
 3477            editor.selections.ranges::<Point>(cx),
 3478            [
 3479                Point::new(0, 7)..Point::new(0, 7),
 3480                Point::new(1, 3)..Point::new(1, 3)
 3481            ]
 3482        );
 3483        editor
 3484    });
 3485}
 3486
 3487#[gpui::test]
 3488async fn test_join_lines_with_git_diff_base(executor: BackgroundExecutor, cx: &mut TestAppContext) {
 3489    init_test(cx, |_| {});
 3490
 3491    let mut cx = EditorTestContext::new(cx).await;
 3492
 3493    let diff_base = r#"
 3494        Line 0
 3495        Line 1
 3496        Line 2
 3497        Line 3
 3498        "#
 3499    .unindent();
 3500
 3501    cx.set_state(
 3502        &r#"
 3503        ˇLine 0
 3504        Line 1
 3505        Line 2
 3506        Line 3
 3507        "#
 3508        .unindent(),
 3509    );
 3510
 3511    cx.set_head_text(&diff_base);
 3512    executor.run_until_parked();
 3513
 3514    // Join lines
 3515    cx.update_editor(|editor, window, cx| {
 3516        editor.join_lines(&JoinLines, window, cx);
 3517    });
 3518    executor.run_until_parked();
 3519
 3520    cx.assert_editor_state(
 3521        &r#"
 3522        Line 0ˇ Line 1
 3523        Line 2
 3524        Line 3
 3525        "#
 3526        .unindent(),
 3527    );
 3528    // Join again
 3529    cx.update_editor(|editor, window, cx| {
 3530        editor.join_lines(&JoinLines, window, cx);
 3531    });
 3532    executor.run_until_parked();
 3533
 3534    cx.assert_editor_state(
 3535        &r#"
 3536        Line 0 Line 1ˇ Line 2
 3537        Line 3
 3538        "#
 3539        .unindent(),
 3540    );
 3541}
 3542
 3543#[gpui::test]
 3544async fn test_custom_newlines_cause_no_false_positive_diffs(
 3545    executor: BackgroundExecutor,
 3546    cx: &mut TestAppContext,
 3547) {
 3548    init_test(cx, |_| {});
 3549    let mut cx = EditorTestContext::new(cx).await;
 3550    cx.set_state("Line 0\r\nLine 1\rˇ\nLine 2\r\nLine 3");
 3551    cx.set_head_text("Line 0\r\nLine 1\r\nLine 2\r\nLine 3");
 3552    executor.run_until_parked();
 3553
 3554    cx.update_editor(|editor, window, cx| {
 3555        let snapshot = editor.snapshot(window, cx);
 3556        assert_eq!(
 3557            snapshot
 3558                .buffer_snapshot
 3559                .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
 3560                .collect::<Vec<_>>(),
 3561            Vec::new(),
 3562            "Should not have any diffs for files with custom newlines"
 3563        );
 3564    });
 3565}
 3566
 3567#[gpui::test]
 3568async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) {
 3569    init_test(cx, |_| {});
 3570
 3571    let mut cx = EditorTestContext::new(cx).await;
 3572
 3573    // Test sort_lines_case_insensitive()
 3574    cx.set_state(indoc! {"
 3575        «z
 3576        y
 3577        x
 3578        Z
 3579        Y
 3580        Xˇ»
 3581    "});
 3582    cx.update_editor(|e, window, cx| {
 3583        e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, window, cx)
 3584    });
 3585    cx.assert_editor_state(indoc! {"
 3586        «x
 3587        X
 3588        y
 3589        Y
 3590        z
 3591        Zˇ»
 3592    "});
 3593
 3594    // Test reverse_lines()
 3595    cx.set_state(indoc! {"
 3596        «5
 3597        4
 3598        3
 3599        2
 3600        1ˇ»
 3601    "});
 3602    cx.update_editor(|e, window, cx| e.reverse_lines(&ReverseLines, window, cx));
 3603    cx.assert_editor_state(indoc! {"
 3604        «1
 3605        2
 3606        3
 3607        4
 3608        5ˇ»
 3609    "});
 3610
 3611    // Skip testing shuffle_line()
 3612
 3613    // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive()
 3614    // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines)
 3615
 3616    // Don't manipulate when cursor is on single line, but expand the selection
 3617    cx.set_state(indoc! {"
 3618        ddˇdd
 3619        ccc
 3620        bb
 3621        a
 3622    "});
 3623    cx.update_editor(|e, window, cx| {
 3624        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3625    });
 3626    cx.assert_editor_state(indoc! {"
 3627        «ddddˇ»
 3628        ccc
 3629        bb
 3630        a
 3631    "});
 3632
 3633    // Basic manipulate case
 3634    // Start selection moves to column 0
 3635    // End of selection shrinks to fit shorter line
 3636    cx.set_state(indoc! {"
 3637        dd«d
 3638        ccc
 3639        bb
 3640        aaaaaˇ»
 3641    "});
 3642    cx.update_editor(|e, window, cx| {
 3643        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3644    });
 3645    cx.assert_editor_state(indoc! {"
 3646        «aaaaa
 3647        bb
 3648        ccc
 3649        dddˇ»
 3650    "});
 3651
 3652    // Manipulate case with newlines
 3653    cx.set_state(indoc! {"
 3654        dd«d
 3655        ccc
 3656
 3657        bb
 3658        aaaaa
 3659
 3660        ˇ»
 3661    "});
 3662    cx.update_editor(|e, window, cx| {
 3663        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3664    });
 3665    cx.assert_editor_state(indoc! {"
 3666        «
 3667
 3668        aaaaa
 3669        bb
 3670        ccc
 3671        dddˇ»
 3672
 3673    "});
 3674
 3675    // Adding new line
 3676    cx.set_state(indoc! {"
 3677        aa«a
 3678        bbˇ»b
 3679    "});
 3680    cx.update_editor(|e, window, cx| {
 3681        e.manipulate_lines(window, cx, |lines| lines.push("added_line"))
 3682    });
 3683    cx.assert_editor_state(indoc! {"
 3684        «aaa
 3685        bbb
 3686        added_lineˇ»
 3687    "});
 3688
 3689    // Removing line
 3690    cx.set_state(indoc! {"
 3691        aa«a
 3692        bbbˇ»
 3693    "});
 3694    cx.update_editor(|e, window, cx| {
 3695        e.manipulate_lines(window, cx, |lines| {
 3696            lines.pop();
 3697        })
 3698    });
 3699    cx.assert_editor_state(indoc! {"
 3700        «aaaˇ»
 3701    "});
 3702
 3703    // Removing all lines
 3704    cx.set_state(indoc! {"
 3705        aa«a
 3706        bbbˇ»
 3707    "});
 3708    cx.update_editor(|e, window, cx| {
 3709        e.manipulate_lines(window, cx, |lines| {
 3710            lines.drain(..);
 3711        })
 3712    });
 3713    cx.assert_editor_state(indoc! {"
 3714        ˇ
 3715    "});
 3716}
 3717
 3718#[gpui::test]
 3719async fn test_unique_lines_multi_selection(cx: &mut TestAppContext) {
 3720    init_test(cx, |_| {});
 3721
 3722    let mut cx = EditorTestContext::new(cx).await;
 3723
 3724    // Consider continuous selection as single selection
 3725    cx.set_state(indoc! {"
 3726        Aaa«aa
 3727        cˇ»c«c
 3728        bb
 3729        aaaˇ»aa
 3730    "});
 3731    cx.update_editor(|e, window, cx| {
 3732        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3733    });
 3734    cx.assert_editor_state(indoc! {"
 3735        «Aaaaa
 3736        ccc
 3737        bb
 3738        aaaaaˇ»
 3739    "});
 3740
 3741    cx.set_state(indoc! {"
 3742        Aaa«aa
 3743        cˇ»c«c
 3744        bb
 3745        aaaˇ»aa
 3746    "});
 3747    cx.update_editor(|e, window, cx| {
 3748        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3749    });
 3750    cx.assert_editor_state(indoc! {"
 3751        «Aaaaa
 3752        ccc
 3753        bbˇ»
 3754    "});
 3755
 3756    // Consider non continuous selection as distinct dedup operations
 3757    cx.set_state(indoc! {"
 3758        «aaaaa
 3759        bb
 3760        aaaaa
 3761        aaaaaˇ»
 3762
 3763        aaa«aaˇ»
 3764    "});
 3765    cx.update_editor(|e, window, cx| {
 3766        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3767    });
 3768    cx.assert_editor_state(indoc! {"
 3769        «aaaaa
 3770        bbˇ»
 3771
 3772        «aaaaaˇ»
 3773    "});
 3774}
 3775
 3776#[gpui::test]
 3777async fn test_unique_lines_single_selection(cx: &mut TestAppContext) {
 3778    init_test(cx, |_| {});
 3779
 3780    let mut cx = EditorTestContext::new(cx).await;
 3781
 3782    cx.set_state(indoc! {"
 3783        «Aaa
 3784        aAa
 3785        Aaaˇ»
 3786    "});
 3787    cx.update_editor(|e, window, cx| {
 3788        e.unique_lines_case_sensitive(&UniqueLinesCaseSensitive, window, cx)
 3789    });
 3790    cx.assert_editor_state(indoc! {"
 3791        «Aaa
 3792        aAaˇ»
 3793    "});
 3794
 3795    cx.set_state(indoc! {"
 3796        «Aaa
 3797        aAa
 3798        aaAˇ»
 3799    "});
 3800    cx.update_editor(|e, window, cx| {
 3801        e.unique_lines_case_insensitive(&UniqueLinesCaseInsensitive, window, cx)
 3802    });
 3803    cx.assert_editor_state(indoc! {"
 3804        «Aaaˇ»
 3805    "});
 3806}
 3807
 3808#[gpui::test]
 3809async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
 3810    init_test(cx, |_| {});
 3811
 3812    let mut cx = EditorTestContext::new(cx).await;
 3813
 3814    // Manipulate with multiple selections on a single line
 3815    cx.set_state(indoc! {"
 3816        dd«dd
 3817        cˇ»c«c
 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        «aaaaa
 3826        bb
 3827        ccc
 3828        ddddˇ»
 3829    "});
 3830
 3831    // Manipulate with multiple disjoin selections
 3832    cx.set_state(indoc! {"
 3833 3834        4
 3835        3
 3836        2
 3837        1ˇ»
 3838
 3839        dd«dd
 3840        ccc
 3841        bb
 3842        aaaˇ»aa
 3843    "});
 3844    cx.update_editor(|e, window, cx| {
 3845        e.sort_lines_case_sensitive(&SortLinesCaseSensitive, window, cx)
 3846    });
 3847    cx.assert_editor_state(indoc! {"
 3848        «1
 3849        2
 3850        3
 3851        4
 3852        5ˇ»
 3853
 3854        «aaaaa
 3855        bb
 3856        ccc
 3857        ddddˇ»
 3858    "});
 3859
 3860    // Adding lines on each selection
 3861    cx.set_state(indoc! {"
 3862 3863        1ˇ»
 3864
 3865        bb«bb
 3866        aaaˇ»aa
 3867    "});
 3868    cx.update_editor(|e, window, cx| {
 3869        e.manipulate_lines(window, cx, |lines| lines.push("added line"))
 3870    });
 3871    cx.assert_editor_state(indoc! {"
 3872        «2
 3873        1
 3874        added lineˇ»
 3875
 3876        «bbbb
 3877        aaaaa
 3878        added lineˇ»
 3879    "});
 3880
 3881    // Removing lines on each selection
 3882    cx.set_state(indoc! {"
 3883 3884        1ˇ»
 3885
 3886        bb«bb
 3887        aaaˇ»aa
 3888    "});
 3889    cx.update_editor(|e, window, cx| {
 3890        e.manipulate_lines(window, cx, |lines| {
 3891            lines.pop();
 3892        })
 3893    });
 3894    cx.assert_editor_state(indoc! {"
 3895        «2ˇ»
 3896
 3897        «bbbbˇ»
 3898    "});
 3899}
 3900
 3901#[gpui::test]
 3902async fn test_toggle_case(cx: &mut TestAppContext) {
 3903    init_test(cx, |_| {});
 3904
 3905    let mut cx = EditorTestContext::new(cx).await;
 3906
 3907    // If all lower case -> upper case
 3908    cx.set_state(indoc! {"
 3909        «hello worldˇ»
 3910    "});
 3911    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3912    cx.assert_editor_state(indoc! {"
 3913        «HELLO WORLDˇ»
 3914    "});
 3915
 3916    // If all upper case -> lower case
 3917    cx.set_state(indoc! {"
 3918        «HELLO WORLDˇ»
 3919    "});
 3920    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3921    cx.assert_editor_state(indoc! {"
 3922        «hello worldˇ»
 3923    "});
 3924
 3925    // If any upper case characters are identified -> lower case
 3926    // This matches JetBrains IDEs
 3927    cx.set_state(indoc! {"
 3928        «hEllo worldˇ»
 3929    "});
 3930    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
 3931    cx.assert_editor_state(indoc! {"
 3932        «hello worldˇ»
 3933    "});
 3934}
 3935
 3936#[gpui::test]
 3937async fn test_manipulate_text(cx: &mut TestAppContext) {
 3938    init_test(cx, |_| {});
 3939
 3940    let mut cx = EditorTestContext::new(cx).await;
 3941
 3942    // Test convert_to_upper_case()
 3943    cx.set_state(indoc! {"
 3944        «hello worldˇ»
 3945    "});
 3946    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3947    cx.assert_editor_state(indoc! {"
 3948        «HELLO WORLDˇ»
 3949    "});
 3950
 3951    // Test convert_to_lower_case()
 3952    cx.set_state(indoc! {"
 3953        «HELLO WORLDˇ»
 3954    "});
 3955    cx.update_editor(|e, window, cx| e.convert_to_lower_case(&ConvertToLowerCase, window, cx));
 3956    cx.assert_editor_state(indoc! {"
 3957        «hello worldˇ»
 3958    "});
 3959
 3960    // Test multiple line, single selection case
 3961    cx.set_state(indoc! {"
 3962        «The quick brown
 3963        fox jumps over
 3964        the lazy dogˇ»
 3965    "});
 3966    cx.update_editor(|e, window, cx| e.convert_to_title_case(&ConvertToTitleCase, window, cx));
 3967    cx.assert_editor_state(indoc! {"
 3968        «The Quick Brown
 3969        Fox Jumps Over
 3970        The Lazy Dogˇ»
 3971    "});
 3972
 3973    // Test multiple line, single selection case
 3974    cx.set_state(indoc! {"
 3975        «The quick brown
 3976        fox jumps over
 3977        the lazy dogˇ»
 3978    "});
 3979    cx.update_editor(|e, window, cx| {
 3980        e.convert_to_upper_camel_case(&ConvertToUpperCamelCase, window, cx)
 3981    });
 3982    cx.assert_editor_state(indoc! {"
 3983        «TheQuickBrown
 3984        FoxJumpsOver
 3985        TheLazyDogˇ»
 3986    "});
 3987
 3988    // From here on out, test more complex cases of manipulate_text()
 3989
 3990    // Test no selection case - should affect words cursors are in
 3991    // Cursor at beginning, middle, and end of word
 3992    cx.set_state(indoc! {"
 3993        ˇhello big beauˇtiful worldˇ
 3994    "});
 3995    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 3996    cx.assert_editor_state(indoc! {"
 3997        «HELLOˇ» big «BEAUTIFULˇ» «WORLDˇ»
 3998    "});
 3999
 4000    // Test multiple selections on a single line and across multiple lines
 4001    cx.set_state(indoc! {"
 4002        «Theˇ» quick «brown
 4003        foxˇ» jumps «overˇ»
 4004        the «lazyˇ» dog
 4005    "});
 4006    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4007    cx.assert_editor_state(indoc! {"
 4008        «THEˇ» quick «BROWN
 4009        FOXˇ» jumps «OVERˇ»
 4010        the «LAZYˇ» dog
 4011    "});
 4012
 4013    // Test case where text length grows
 4014    cx.set_state(indoc! {"
 4015        «tschüߡ»
 4016    "});
 4017    cx.update_editor(|e, window, cx| e.convert_to_upper_case(&ConvertToUpperCase, window, cx));
 4018    cx.assert_editor_state(indoc! {"
 4019        «TSCHÜSSˇ»
 4020    "});
 4021
 4022    // Test to make sure we don't crash when text shrinks
 4023    cx.set_state(indoc! {"
 4024        aaa_bbbˇ
 4025    "});
 4026    cx.update_editor(|e, window, cx| {
 4027        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4028    });
 4029    cx.assert_editor_state(indoc! {"
 4030        «aaaBbbˇ»
 4031    "});
 4032
 4033    // Test to make sure we all aware of the fact that each word can grow and shrink
 4034    // Final selections should be aware of this fact
 4035    cx.set_state(indoc! {"
 4036        aaa_bˇbb bbˇb_ccc ˇccc_ddd
 4037    "});
 4038    cx.update_editor(|e, window, cx| {
 4039        e.convert_to_lower_camel_case(&ConvertToLowerCamelCase, window, cx)
 4040    });
 4041    cx.assert_editor_state(indoc! {"
 4042        «aaaBbbˇ» «bbbCccˇ» «cccDddˇ»
 4043    "});
 4044
 4045    cx.set_state(indoc! {"
 4046        «hElLo, WoRld!ˇ»
 4047    "});
 4048    cx.update_editor(|e, window, cx| {
 4049        e.convert_to_opposite_case(&ConvertToOppositeCase, window, cx)
 4050    });
 4051    cx.assert_editor_state(indoc! {"
 4052        «HeLlO, wOrLD!ˇ»
 4053    "});
 4054}
 4055
 4056#[gpui::test]
 4057fn test_duplicate_line(cx: &mut TestAppContext) {
 4058    init_test(cx, |_| {});
 4059
 4060    let editor = cx.add_window(|window, cx| {
 4061        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4062        build_editor(buffer, window, cx)
 4063    });
 4064    _ = editor.update(cx, |editor, window, cx| {
 4065        editor.change_selections(None, window, cx, |s| {
 4066            s.select_display_ranges([
 4067                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4068                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4069                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4070                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4071            ])
 4072        });
 4073        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4074        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4075        assert_eq!(
 4076            editor.selections.display_ranges(cx),
 4077            vec![
 4078                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 4079                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2),
 4080                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4081                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4082            ]
 4083        );
 4084    });
 4085
 4086    let editor = cx.add_window(|window, cx| {
 4087        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4088        build_editor(buffer, window, cx)
 4089    });
 4090    _ = editor.update(cx, |editor, window, cx| {
 4091        editor.change_selections(None, window, cx, |s| {
 4092            s.select_display_ranges([
 4093                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4094                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4095            ])
 4096        });
 4097        editor.duplicate_line_down(&DuplicateLineDown, window, cx);
 4098        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4099        assert_eq!(
 4100            editor.selections.display_ranges(cx),
 4101            vec![
 4102                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(4), 1),
 4103                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(5), 1),
 4104            ]
 4105        );
 4106    });
 4107
 4108    // With `move_upwards` the selections stay in place, except for
 4109    // the lines inserted above them
 4110    let editor = cx.add_window(|window, cx| {
 4111        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4112        build_editor(buffer, window, cx)
 4113    });
 4114    _ = editor.update(cx, |editor, window, cx| {
 4115        editor.change_selections(None, window, cx, |s| {
 4116            s.select_display_ranges([
 4117                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4118                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4119                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 4120                DisplayPoint::new(DisplayRow(3), 0)..DisplayPoint::new(DisplayRow(3), 0),
 4121            ])
 4122        });
 4123        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4124        assert_eq!(editor.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
 4125        assert_eq!(
 4126            editor.selections.display_ranges(cx),
 4127            vec![
 4128                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 4129                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 4130                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
 4131                DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
 4132            ]
 4133        );
 4134    });
 4135
 4136    let editor = cx.add_window(|window, cx| {
 4137        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4138        build_editor(buffer, window, cx)
 4139    });
 4140    _ = editor.update(cx, |editor, window, cx| {
 4141        editor.change_selections(None, window, cx, |s| {
 4142            s.select_display_ranges([
 4143                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4144                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4145            ])
 4146        });
 4147        editor.duplicate_line_up(&DuplicateLineUp, window, cx);
 4148        assert_eq!(editor.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
 4149        assert_eq!(
 4150            editor.selections.display_ranges(cx),
 4151            vec![
 4152                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4153                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4154            ]
 4155        );
 4156    });
 4157
 4158    let editor = cx.add_window(|window, cx| {
 4159        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
 4160        build_editor(buffer, window, cx)
 4161    });
 4162    _ = editor.update(cx, |editor, window, cx| {
 4163        editor.change_selections(None, window, cx, |s| {
 4164            s.select_display_ranges([
 4165                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4166                DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(2), 1),
 4167            ])
 4168        });
 4169        editor.duplicate_selection(&DuplicateSelection, window, cx);
 4170        assert_eq!(editor.display_text(cx), "abc\ndbc\ndef\ngf\nghi\n");
 4171        assert_eq!(
 4172            editor.selections.display_ranges(cx),
 4173            vec![
 4174                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4175                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 1),
 4176            ]
 4177        );
 4178    });
 4179}
 4180
 4181#[gpui::test]
 4182fn test_move_line_up_down(cx: &mut TestAppContext) {
 4183    init_test(cx, |_| {});
 4184
 4185    let editor = cx.add_window(|window, cx| {
 4186        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4187        build_editor(buffer, window, cx)
 4188    });
 4189    _ = editor.update(cx, |editor, window, cx| {
 4190        editor.fold_creases(
 4191            vec![
 4192                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 4193                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 4194                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 4195            ],
 4196            true,
 4197            window,
 4198            cx,
 4199        );
 4200        editor.change_selections(None, window, cx, |s| {
 4201            s.select_display_ranges([
 4202                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4203                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4204                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4205                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2),
 4206            ])
 4207        });
 4208        assert_eq!(
 4209            editor.display_text(cx),
 4210            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i\njjjjj"
 4211        );
 4212
 4213        editor.move_line_up(&MoveLineUp, window, cx);
 4214        assert_eq!(
 4215            editor.display_text(cx),
 4216            "aa⋯bbb\nccc⋯eeee\nggggg\n⋯i\njjjjj\nfffff"
 4217        );
 4218        assert_eq!(
 4219            editor.selections.display_ranges(cx),
 4220            vec![
 4221                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 4222                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4223                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4224                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4225            ]
 4226        );
 4227    });
 4228
 4229    _ = editor.update(cx, |editor, window, cx| {
 4230        editor.move_line_down(&MoveLineDown, window, cx);
 4231        assert_eq!(
 4232            editor.display_text(cx),
 4233            "ccc⋯eeee\naa⋯bbb\nfffff\nggggg\n⋯i\njjjjj"
 4234        );
 4235        assert_eq!(
 4236            editor.selections.display_ranges(cx),
 4237            vec![
 4238                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4239                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4240                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4241                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4242            ]
 4243        );
 4244    });
 4245
 4246    _ = editor.update(cx, |editor, window, cx| {
 4247        editor.move_line_down(&MoveLineDown, window, cx);
 4248        assert_eq!(
 4249            editor.display_text(cx),
 4250            "ccc⋯eeee\nfffff\naa⋯bbb\nggggg\n⋯i\njjjjj"
 4251        );
 4252        assert_eq!(
 4253            editor.selections.display_ranges(cx),
 4254            vec![
 4255                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4256                DisplayPoint::new(DisplayRow(3), 1)..DisplayPoint::new(DisplayRow(3), 1),
 4257                DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(4), 3),
 4258                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 2)
 4259            ]
 4260        );
 4261    });
 4262
 4263    _ = editor.update(cx, |editor, window, cx| {
 4264        editor.move_line_up(&MoveLineUp, window, cx);
 4265        assert_eq!(
 4266            editor.display_text(cx),
 4267            "ccc⋯eeee\naa⋯bbb\nggggg\n⋯i\njjjjj\nfffff"
 4268        );
 4269        assert_eq!(
 4270            editor.selections.display_ranges(cx),
 4271            vec![
 4272                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 4273                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1),
 4274                DisplayPoint::new(DisplayRow(2), 2)..DisplayPoint::new(DisplayRow(3), 3),
 4275                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(4), 2)
 4276            ]
 4277        );
 4278    });
 4279}
 4280
 4281#[gpui::test]
 4282fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
 4283    init_test(cx, |_| {});
 4284
 4285    let editor = cx.add_window(|window, cx| {
 4286        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
 4287        build_editor(buffer, window, cx)
 4288    });
 4289    _ = editor.update(cx, |editor, window, cx| {
 4290        let snapshot = editor.buffer.read(cx).snapshot(cx);
 4291        editor.insert_blocks(
 4292            [BlockProperties {
 4293                style: BlockStyle::Fixed,
 4294                placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
 4295                height: Some(1),
 4296                render: Arc::new(|_| div().into_any()),
 4297                priority: 0,
 4298            }],
 4299            Some(Autoscroll::fit()),
 4300            cx,
 4301        );
 4302        editor.change_selections(None, window, cx, |s| {
 4303            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
 4304        });
 4305        editor.move_line_down(&MoveLineDown, window, cx);
 4306    });
 4307}
 4308
 4309#[gpui::test]
 4310async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
 4311    init_test(cx, |_| {});
 4312
 4313    let mut cx = EditorTestContext::new(cx).await;
 4314    cx.set_state(
 4315        &"
 4316            ˇzero
 4317            one
 4318            two
 4319            three
 4320            four
 4321            five
 4322        "
 4323        .unindent(),
 4324    );
 4325
 4326    // Create a four-line block that replaces three lines of text.
 4327    cx.update_editor(|editor, window, cx| {
 4328        let snapshot = editor.snapshot(window, cx);
 4329        let snapshot = &snapshot.buffer_snapshot;
 4330        let placement = BlockPlacement::Replace(
 4331            snapshot.anchor_after(Point::new(1, 0))..=snapshot.anchor_after(Point::new(3, 0)),
 4332        );
 4333        editor.insert_blocks(
 4334            [BlockProperties {
 4335                placement,
 4336                height: Some(4),
 4337                style: BlockStyle::Sticky,
 4338                render: Arc::new(|_| gpui::div().into_any_element()),
 4339                priority: 0,
 4340            }],
 4341            None,
 4342            cx,
 4343        );
 4344    });
 4345
 4346    // Move down so that the cursor touches the block.
 4347    cx.update_editor(|editor, window, cx| {
 4348        editor.move_down(&Default::default(), window, cx);
 4349    });
 4350    cx.assert_editor_state(
 4351        &"
 4352            zero
 4353            «one
 4354            two
 4355            threeˇ»
 4356            four
 4357            five
 4358        "
 4359        .unindent(),
 4360    );
 4361
 4362    // Move down past the block.
 4363    cx.update_editor(|editor, window, cx| {
 4364        editor.move_down(&Default::default(), window, cx);
 4365    });
 4366    cx.assert_editor_state(
 4367        &"
 4368            zero
 4369            one
 4370            two
 4371            three
 4372            ˇfour
 4373            five
 4374        "
 4375        .unindent(),
 4376    );
 4377}
 4378
 4379#[gpui::test]
 4380fn test_transpose(cx: &mut TestAppContext) {
 4381    init_test(cx, |_| {});
 4382
 4383    _ = cx.add_window(|window, cx| {
 4384        let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx);
 4385        editor.set_style(EditorStyle::default(), window, cx);
 4386        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
 4387        editor.transpose(&Default::default(), window, cx);
 4388        assert_eq!(editor.text(cx), "bac");
 4389        assert_eq!(editor.selections.ranges(cx), [2..2]);
 4390
 4391        editor.transpose(&Default::default(), window, cx);
 4392        assert_eq!(editor.text(cx), "bca");
 4393        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4394
 4395        editor.transpose(&Default::default(), window, cx);
 4396        assert_eq!(editor.text(cx), "bac");
 4397        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4398
 4399        editor
 4400    });
 4401
 4402    _ = cx.add_window(|window, cx| {
 4403        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4404        editor.set_style(EditorStyle::default(), window, cx);
 4405        editor.change_selections(None, window, cx, |s| s.select_ranges([3..3]));
 4406        editor.transpose(&Default::default(), window, cx);
 4407        assert_eq!(editor.text(cx), "acb\nde");
 4408        assert_eq!(editor.selections.ranges(cx), [3..3]);
 4409
 4410        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4411        editor.transpose(&Default::default(), window, cx);
 4412        assert_eq!(editor.text(cx), "acbd\ne");
 4413        assert_eq!(editor.selections.ranges(cx), [5..5]);
 4414
 4415        editor.transpose(&Default::default(), window, cx);
 4416        assert_eq!(editor.text(cx), "acbde\n");
 4417        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4418
 4419        editor.transpose(&Default::default(), window, cx);
 4420        assert_eq!(editor.text(cx), "acbd\ne");
 4421        assert_eq!(editor.selections.ranges(cx), [6..6]);
 4422
 4423        editor
 4424    });
 4425
 4426    _ = cx.add_window(|window, cx| {
 4427        let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx);
 4428        editor.set_style(EditorStyle::default(), window, cx);
 4429        editor.change_selections(None, window, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
 4430        editor.transpose(&Default::default(), window, cx);
 4431        assert_eq!(editor.text(cx), "bacd\ne");
 4432        assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
 4433
 4434        editor.transpose(&Default::default(), window, cx);
 4435        assert_eq!(editor.text(cx), "bcade\n");
 4436        assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
 4437
 4438        editor.transpose(&Default::default(), window, cx);
 4439        assert_eq!(editor.text(cx), "bcda\ne");
 4440        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4441
 4442        editor.transpose(&Default::default(), window, cx);
 4443        assert_eq!(editor.text(cx), "bcade\n");
 4444        assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
 4445
 4446        editor.transpose(&Default::default(), window, cx);
 4447        assert_eq!(editor.text(cx), "bcaed\n");
 4448        assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
 4449
 4450        editor
 4451    });
 4452
 4453    _ = cx.add_window(|window, cx| {
 4454        let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx);
 4455        editor.set_style(EditorStyle::default(), window, cx);
 4456        editor.change_selections(None, window, cx, |s| s.select_ranges([4..4]));
 4457        editor.transpose(&Default::default(), window, cx);
 4458        assert_eq!(editor.text(cx), "🏀🍐✋");
 4459        assert_eq!(editor.selections.ranges(cx), [8..8]);
 4460
 4461        editor.transpose(&Default::default(), window, cx);
 4462        assert_eq!(editor.text(cx), "🏀✋🍐");
 4463        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4464
 4465        editor.transpose(&Default::default(), window, cx);
 4466        assert_eq!(editor.text(cx), "🏀🍐✋");
 4467        assert_eq!(editor.selections.ranges(cx), [11..11]);
 4468
 4469        editor
 4470    });
 4471}
 4472
 4473#[gpui::test]
 4474async fn test_rewrap(cx: &mut TestAppContext) {
 4475    init_test(cx, |settings| {
 4476        settings.languages.extend([
 4477            (
 4478                "Markdown".into(),
 4479                LanguageSettingsContent {
 4480                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4481                    ..Default::default()
 4482                },
 4483            ),
 4484            (
 4485                "Plain Text".into(),
 4486                LanguageSettingsContent {
 4487                    allow_rewrap: Some(language_settings::RewrapBehavior::Anywhere),
 4488                    ..Default::default()
 4489                },
 4490            ),
 4491        ])
 4492    });
 4493
 4494    let mut cx = EditorTestContext::new(cx).await;
 4495
 4496    let language_with_c_comments = Arc::new(Language::new(
 4497        LanguageConfig {
 4498            line_comments: vec!["// ".into()],
 4499            ..LanguageConfig::default()
 4500        },
 4501        None,
 4502    ));
 4503    let language_with_pound_comments = Arc::new(Language::new(
 4504        LanguageConfig {
 4505            line_comments: vec!["# ".into()],
 4506            ..LanguageConfig::default()
 4507        },
 4508        None,
 4509    ));
 4510    let markdown_language = Arc::new(Language::new(
 4511        LanguageConfig {
 4512            name: "Markdown".into(),
 4513            ..LanguageConfig::default()
 4514        },
 4515        None,
 4516    ));
 4517    let language_with_doc_comments = Arc::new(Language::new(
 4518        LanguageConfig {
 4519            line_comments: vec!["// ".into(), "/// ".into()],
 4520            ..LanguageConfig::default()
 4521        },
 4522        Some(tree_sitter_rust::LANGUAGE.into()),
 4523    ));
 4524
 4525    let plaintext_language = Arc::new(Language::new(
 4526        LanguageConfig {
 4527            name: "Plain Text".into(),
 4528            ..LanguageConfig::default()
 4529        },
 4530        None,
 4531    ));
 4532
 4533    assert_rewrap(
 4534        indoc! {"
 4535            // ˇ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.
 4536        "},
 4537        indoc! {"
 4538            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4539            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4540            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4541            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4542            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4543            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4544            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4545            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4546            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4547            // porttitor id. Aliquam id accumsan eros.
 4548        "},
 4549        language_with_c_comments.clone(),
 4550        &mut cx,
 4551    );
 4552
 4553    // Test that rewrapping works inside of a selection
 4554    assert_rewrap(
 4555        indoc! {"
 4556            «// 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.ˇ»
 4557        "},
 4558        indoc! {"
 4559            «// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4560            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4561            // auctor, eu lacinia sapien scelerisque. Vivamus sit amet neque et quam
 4562            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4563            // Pellentesque odio lectus, iaculis ac volutpat et, blandit quis urna. Sed
 4564            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4565            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4566            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4567            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4568            // porttitor id. Aliquam id accumsan eros.ˇ»
 4569        "},
 4570        language_with_c_comments.clone(),
 4571        &mut cx,
 4572    );
 4573
 4574    // Test that cursors that expand to the same region are collapsed.
 4575    assert_rewrap(
 4576        indoc! {"
 4577            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4578            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4579            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4580            // ˇ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.
 4581        "},
 4582        indoc! {"
 4583            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4584            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4585            // auctor, eu lacinia sapien scelerisque. ˇVivamus sit amet neque et quam
 4586            // tincidunt hendrerit. Praesent semper egestas tellus id dignissim.
 4587            // Pellentesque odio lectus, iaculis ac volutpat et, ˇblandit quis urna. Sed
 4588            // vestibulum nisi sit amet nisl venenatis tempus. Donec molestie blandit quam,
 4589            // et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
 4590            // dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
 4591            // viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
 4592            // porttitor id. Aliquam id accumsan eros.
 4593        "},
 4594        language_with_c_comments.clone(),
 4595        &mut cx,
 4596    );
 4597
 4598    // Test that non-contiguous selections are treated separately.
 4599    assert_rewrap(
 4600        indoc! {"
 4601            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit.
 4602            // ˇVivamus mollis elit purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor, eu lacinia sapien scelerisque.
 4603            //
 4604            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4605            // ˇ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.
 4606        "},
 4607        indoc! {"
 4608            // ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. ˇVivamus mollis elit
 4609            // purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
 4610            // auctor, eu lacinia sapien scelerisque.
 4611            //
 4612            // ˇVivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
 4613            // tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
 4614            // ˇblandit quis urna. Sed vestibulum nisi sit amet nisl venenatis tempus. Donec
 4615            // molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
 4616            // nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
 4617            // porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
 4618            // vulputate turpis porttitor id. Aliquam id accumsan eros.
 4619        "},
 4620        language_with_c_comments.clone(),
 4621        &mut cx,
 4622    );
 4623
 4624    // Test that different comment prefixes are supported.
 4625    assert_rewrap(
 4626        indoc! {"
 4627            # ˇ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.
 4628        "},
 4629        indoc! {"
 4630            # ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
 4631            # purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4632            # eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4633            # hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4634            # lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit
 4635            # amet nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet
 4636            # in. Integer sit amet scelerisque nisi. Lorem ipsum dolor sit amet, consectetur
 4637            # adipiscing elit. Cras egestas porta metus, eu viverra ipsum efficitur quis.
 4638            # Donec luctus eros turpis, id vulputate turpis porttitor id. Aliquam id
 4639            # accumsan eros.
 4640        "},
 4641        language_with_pound_comments.clone(),
 4642        &mut cx,
 4643    );
 4644
 4645    // Test that rewrapping is ignored outside of comments in most languages.
 4646    assert_rewrap(
 4647        indoc! {"
 4648            /// Adds two numbers.
 4649            /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4650            fn add(a: u32, b: u32) -> u32 {
 4651                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ˇ
 4652            }
 4653        "},
 4654        indoc! {"
 4655            /// Adds two numbers. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 4656            /// Vivamus mollis elit purus, a ornare lacus gravida vitae.ˇ
 4657            fn add(a: u32, b: u32) -> u32 {
 4658                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ˇ
 4659            }
 4660        "},
 4661        language_with_doc_comments.clone(),
 4662        &mut cx,
 4663    );
 4664
 4665    // Test that rewrapping works in Markdown and Plain Text languages.
 4666    assert_rewrap(
 4667        indoc! {"
 4668            # Hello
 4669
 4670            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.
 4671        "},
 4672        indoc! {"
 4673            # Hello
 4674
 4675            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4676            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4677            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4678            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4679            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4680            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4681            Integer sit amet scelerisque nisi.
 4682        "},
 4683        markdown_language,
 4684        &mut cx,
 4685    );
 4686
 4687    assert_rewrap(
 4688        indoc! {"
 4689            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.
 4690        "},
 4691        indoc! {"
 4692            Lorem ipsum dolor sit amet, ˇconsectetur adipiscing elit. Vivamus mollis elit
 4693            purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus auctor,
 4694            eu lacinia sapien scelerisque. Vivamus sit amet neque et quam tincidunt
 4695            hendrerit. Praesent semper egestas tellus id dignissim. Pellentesque odio
 4696            lectus, iaculis ac volutpat et, blandit quis urna. Sed vestibulum nisi sit amet
 4697            nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
 4698            Integer sit amet scelerisque nisi.
 4699        "},
 4700        plaintext_language,
 4701        &mut cx,
 4702    );
 4703
 4704    // Test rewrapping unaligned comments in a selection.
 4705    assert_rewrap(
 4706        indoc! {"
 4707            fn foo() {
 4708                if true {
 4709            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4710            // Praesent semper egestas tellus id dignissim.ˇ»
 4711                    do_something();
 4712                } else {
 4713                    //
 4714                }
 4715            }
 4716        "},
 4717        indoc! {"
 4718            fn foo() {
 4719                if true {
 4720            «        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4721                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4722                    // egestas tellus id dignissim.ˇ»
 4723                    do_something();
 4724                } else {
 4725                    //
 4726                }
 4727            }
 4728        "},
 4729        language_with_doc_comments.clone(),
 4730        &mut cx,
 4731    );
 4732
 4733    assert_rewrap(
 4734        indoc! {"
 4735            fn foo() {
 4736                if true {
 4737            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
 4738            // Praesent semper egestas tellus id dignissim.»
 4739                    do_something();
 4740                } else {
 4741                    //
 4742                }
 4743
 4744            }
 4745        "},
 4746        indoc! {"
 4747            fn foo() {
 4748                if true {
 4749            «ˇ        // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
 4750                    // mollis elit purus, a ornare lacus gravida vitae. Praesent semper
 4751                    // egestas tellus id dignissim.»
 4752                    do_something();
 4753                } else {
 4754                    //
 4755                }
 4756
 4757            }
 4758        "},
 4759        language_with_doc_comments.clone(),
 4760        &mut cx,
 4761    );
 4762
 4763    #[track_caller]
 4764    fn assert_rewrap(
 4765        unwrapped_text: &str,
 4766        wrapped_text: &str,
 4767        language: Arc<Language>,
 4768        cx: &mut EditorTestContext,
 4769    ) {
 4770        cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
 4771        cx.set_state(unwrapped_text);
 4772        cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx));
 4773        cx.assert_editor_state(wrapped_text);
 4774    }
 4775}
 4776
 4777#[gpui::test]
 4778async fn test_hard_wrap(cx: &mut TestAppContext) {
 4779    init_test(cx, |_| {});
 4780    let mut cx = EditorTestContext::new(cx).await;
 4781
 4782    cx.update_buffer(|buffer, cx| buffer.set_language(Some(git_commit_lang()), cx));
 4783    cx.update_editor(|editor, _, cx| {
 4784        editor.set_hard_wrap(Some(14), cx);
 4785    });
 4786
 4787    cx.set_state(indoc!(
 4788        "
 4789        one two three ˇ
 4790        "
 4791    ));
 4792    cx.simulate_input("four");
 4793    cx.run_until_parked();
 4794
 4795    cx.assert_editor_state(indoc!(
 4796        "
 4797        one two three
 4798        fourˇ
 4799        "
 4800    ));
 4801
 4802    cx.update_editor(|editor, window, cx| {
 4803        editor.newline(&Default::default(), window, cx);
 4804    });
 4805    cx.run_until_parked();
 4806    cx.assert_editor_state(indoc!(
 4807        "
 4808        one two three
 4809        four
 4810        ˇ
 4811        "
 4812    ));
 4813
 4814    cx.simulate_input("five");
 4815    cx.run_until_parked();
 4816    cx.assert_editor_state(indoc!(
 4817        "
 4818        one two three
 4819        four
 4820        fiveˇ
 4821        "
 4822    ));
 4823
 4824    cx.update_editor(|editor, window, cx| {
 4825        editor.newline(&Default::default(), window, cx);
 4826    });
 4827    cx.run_until_parked();
 4828    cx.simulate_input("# ");
 4829    cx.run_until_parked();
 4830    cx.assert_editor_state(indoc!(
 4831        "
 4832        one two three
 4833        four
 4834        five
 4835        # ˇ
 4836        "
 4837    ));
 4838
 4839    cx.update_editor(|editor, window, cx| {
 4840        editor.newline(&Default::default(), window, cx);
 4841    });
 4842    cx.run_until_parked();
 4843    cx.assert_editor_state(indoc!(
 4844        "
 4845        one two three
 4846        four
 4847        five
 4848        #\x20
 4849 4850        "
 4851    ));
 4852
 4853    cx.simulate_input(" 6");
 4854    cx.run_until_parked();
 4855    cx.assert_editor_state(indoc!(
 4856        "
 4857        one two three
 4858        four
 4859        five
 4860        #
 4861        # 6ˇ
 4862        "
 4863    ));
 4864}
 4865
 4866#[gpui::test]
 4867async fn test_clipboard(cx: &mut TestAppContext) {
 4868    init_test(cx, |_| {});
 4869
 4870    let mut cx = EditorTestContext::new(cx).await;
 4871
 4872    cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
 4873    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4874    cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
 4875
 4876    // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
 4877    cx.set_state("two ˇfour ˇsix ˇ");
 4878    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4879    cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
 4880
 4881    // Paste again but with only two cursors. Since the number of cursors doesn't
 4882    // match the number of slices in the clipboard, the entire clipboard text
 4883    // is pasted at each cursor.
 4884    cx.set_state("ˇtwo one✅ four three six five ˇ");
 4885    cx.update_editor(|e, window, cx| {
 4886        e.handle_input("( ", window, cx);
 4887        e.paste(&Paste, window, cx);
 4888        e.handle_input(") ", window, cx);
 4889    });
 4890    cx.assert_editor_state(
 4891        &([
 4892            "( one✅ ",
 4893            "three ",
 4894            "five ) ˇtwo one✅ four three six five ( one✅ ",
 4895            "three ",
 4896            "five ) ˇ",
 4897        ]
 4898        .join("\n")),
 4899    );
 4900
 4901    // Cut with three selections, one of which is full-line.
 4902    cx.set_state(indoc! {"
 4903        1«2ˇ»3
 4904        4ˇ567
 4905        «8ˇ»9"});
 4906    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 4907    cx.assert_editor_state(indoc! {"
 4908        1ˇ3
 4909        ˇ9"});
 4910
 4911    // Paste with three selections, noticing how the copied selection that was full-line
 4912    // gets inserted before the second cursor.
 4913    cx.set_state(indoc! {"
 4914        1ˇ3
 4915 4916        «oˇ»ne"});
 4917    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4918    cx.assert_editor_state(indoc! {"
 4919        12ˇ3
 4920        4567
 4921 4922        8ˇne"});
 4923
 4924    // Copy with a single cursor only, which writes the whole line into the clipboard.
 4925    cx.set_state(indoc! {"
 4926        The quick brown
 4927        fox juˇmps over
 4928        the lazy dog"});
 4929    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4930    assert_eq!(
 4931        cx.read_from_clipboard()
 4932            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4933        Some("fox jumps over\n".to_string())
 4934    );
 4935
 4936    // Paste with three selections, noticing how the copied full-line selection is inserted
 4937    // before the empty selections but replaces the selection that is non-empty.
 4938    cx.set_state(indoc! {"
 4939        Tˇhe quick brown
 4940        «foˇ»x jumps over
 4941        tˇhe lazy dog"});
 4942    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 4943    cx.assert_editor_state(indoc! {"
 4944        fox jumps over
 4945        Tˇhe quick brown
 4946        fox jumps over
 4947        ˇx jumps over
 4948        fox jumps over
 4949        tˇhe lazy dog"});
 4950}
 4951
 4952#[gpui::test]
 4953async fn test_copy_trim(cx: &mut TestAppContext) {
 4954    init_test(cx, |_| {});
 4955
 4956    let mut cx = EditorTestContext::new(cx).await;
 4957    cx.set_state(
 4958        r#"            «for selection in selections.iter() {
 4959            let mut start = selection.start;
 4960            let mut end = selection.end;
 4961            let is_entire_line = selection.is_empty();
 4962            if is_entire_line {
 4963                start = Point::new(start.row, 0);ˇ»
 4964                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 4965            }
 4966        "#,
 4967    );
 4968    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 4969    assert_eq!(
 4970        cx.read_from_clipboard()
 4971            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4972        Some(
 4973            "for selection in selections.iter() {
 4974            let mut start = selection.start;
 4975            let mut end = selection.end;
 4976            let is_entire_line = selection.is_empty();
 4977            if is_entire_line {
 4978                start = Point::new(start.row, 0);"
 4979                .to_string()
 4980        ),
 4981        "Regular copying preserves all indentation selected",
 4982    );
 4983    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 4984    assert_eq!(
 4985        cx.read_from_clipboard()
 4986            .and_then(|item| item.text().as_deref().map(str::to_string)),
 4987        Some(
 4988            "for selection in selections.iter() {
 4989let mut start = selection.start;
 4990let mut end = selection.end;
 4991let is_entire_line = selection.is_empty();
 4992if is_entire_line {
 4993    start = Point::new(start.row, 0);"
 4994                .to_string()
 4995        ),
 4996        "Copying with stripping should strip all leading whitespaces"
 4997    );
 4998
 4999    cx.set_state(
 5000        r#"       «     for selection in selections.iter() {
 5001            let mut start = selection.start;
 5002            let mut end = selection.end;
 5003            let is_entire_line = selection.is_empty();
 5004            if is_entire_line {
 5005                start = Point::new(start.row, 0);ˇ»
 5006                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5007            }
 5008        "#,
 5009    );
 5010    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5011    assert_eq!(
 5012        cx.read_from_clipboard()
 5013            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5014        Some(
 5015            "     for selection in selections.iter() {
 5016            let mut start = selection.start;
 5017            let mut end = selection.end;
 5018            let is_entire_line = selection.is_empty();
 5019            if is_entire_line {
 5020                start = Point::new(start.row, 0);"
 5021                .to_string()
 5022        ),
 5023        "Regular copying preserves all indentation selected",
 5024    );
 5025    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5026    assert_eq!(
 5027        cx.read_from_clipboard()
 5028            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5029        Some(
 5030            "for selection in selections.iter() {
 5031let mut start = selection.start;
 5032let mut end = selection.end;
 5033let is_entire_line = selection.is_empty();
 5034if is_entire_line {
 5035    start = Point::new(start.row, 0);"
 5036                .to_string()
 5037        ),
 5038        "Copying with stripping should strip all leading whitespaces, even if some of it was selected"
 5039    );
 5040
 5041    cx.set_state(
 5042        r#"       «ˇ     for selection in selections.iter() {
 5043            let mut start = selection.start;
 5044            let mut end = selection.end;
 5045            let is_entire_line = selection.is_empty();
 5046            if is_entire_line {
 5047                start = Point::new(start.row, 0);»
 5048                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5049            }
 5050        "#,
 5051    );
 5052    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5053    assert_eq!(
 5054        cx.read_from_clipboard()
 5055            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5056        Some(
 5057            "     for selection in selections.iter() {
 5058            let mut start = selection.start;
 5059            let mut end = selection.end;
 5060            let is_entire_line = selection.is_empty();
 5061            if is_entire_line {
 5062                start = Point::new(start.row, 0);"
 5063                .to_string()
 5064        ),
 5065        "Regular copying for reverse selection works the same",
 5066    );
 5067    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5068    assert_eq!(
 5069        cx.read_from_clipboard()
 5070            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5071        Some(
 5072            "for selection in selections.iter() {
 5073let mut start = selection.start;
 5074let mut end = selection.end;
 5075let is_entire_line = selection.is_empty();
 5076if is_entire_line {
 5077    start = Point::new(start.row, 0);"
 5078                .to_string()
 5079        ),
 5080        "Copying with stripping for reverse selection works the same"
 5081    );
 5082
 5083    cx.set_state(
 5084        r#"            for selection «in selections.iter() {
 5085            let mut start = selection.start;
 5086            let mut end = selection.end;
 5087            let is_entire_line = selection.is_empty();
 5088            if is_entire_line {
 5089                start = Point::new(start.row, 0);ˇ»
 5090                end = cmp::min(max_point, Point::new(end.row + 1, 0));
 5091            }
 5092        "#,
 5093    );
 5094    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5095    assert_eq!(
 5096        cx.read_from_clipboard()
 5097            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5098        Some(
 5099            "in selections.iter() {
 5100            let mut start = selection.start;
 5101            let mut end = selection.end;
 5102            let is_entire_line = selection.is_empty();
 5103            if is_entire_line {
 5104                start = Point::new(start.row, 0);"
 5105                .to_string()
 5106        ),
 5107        "When selecting past the indent, the copying works as usual",
 5108    );
 5109    cx.update_editor(|e, window, cx| e.copy_and_trim(&CopyAndTrim, window, cx));
 5110    assert_eq!(
 5111        cx.read_from_clipboard()
 5112            .and_then(|item| item.text().as_deref().map(str::to_string)),
 5113        Some(
 5114            "in selections.iter() {
 5115            let mut start = selection.start;
 5116            let mut end = selection.end;
 5117            let is_entire_line = selection.is_empty();
 5118            if is_entire_line {
 5119                start = Point::new(start.row, 0);"
 5120                .to_string()
 5121        ),
 5122        "When selecting past the indent, nothing is trimmed"
 5123    );
 5124}
 5125
 5126#[gpui::test]
 5127async fn test_paste_multiline(cx: &mut TestAppContext) {
 5128    init_test(cx, |_| {});
 5129
 5130    let mut cx = EditorTestContext::new(cx).await;
 5131    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5132
 5133    // Cut an indented block, without the leading whitespace.
 5134    cx.set_state(indoc! {"
 5135        const a: B = (
 5136            c(),
 5137            «d(
 5138                e,
 5139                f
 5140            )ˇ»
 5141        );
 5142    "});
 5143    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5144    cx.assert_editor_state(indoc! {"
 5145        const a: B = (
 5146            c(),
 5147            ˇ
 5148        );
 5149    "});
 5150
 5151    // Paste it at the same position.
 5152    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5153    cx.assert_editor_state(indoc! {"
 5154        const a: B = (
 5155            c(),
 5156            d(
 5157                e,
 5158                f
 5159 5160        );
 5161    "});
 5162
 5163    // Paste it at a line with a lower indent level.
 5164    cx.set_state(indoc! {"
 5165        ˇ
 5166        const a: B = (
 5167            c(),
 5168        );
 5169    "});
 5170    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5171    cx.assert_editor_state(indoc! {"
 5172        d(
 5173            e,
 5174            f
 5175 5176        const a: B = (
 5177            c(),
 5178        );
 5179    "});
 5180
 5181    // Cut an indented block, with the leading whitespace.
 5182    cx.set_state(indoc! {"
 5183        const a: B = (
 5184            c(),
 5185        «    d(
 5186                e,
 5187                f
 5188            )
 5189        ˇ»);
 5190    "});
 5191    cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
 5192    cx.assert_editor_state(indoc! {"
 5193        const a: B = (
 5194            c(),
 5195        ˇ);
 5196    "});
 5197
 5198    // Paste it at the same position.
 5199    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5200    cx.assert_editor_state(indoc! {"
 5201        const a: B = (
 5202            c(),
 5203            d(
 5204                e,
 5205                f
 5206            )
 5207        ˇ);
 5208    "});
 5209
 5210    // Paste it at a line with a higher indent level.
 5211    cx.set_state(indoc! {"
 5212        const a: B = (
 5213            c(),
 5214            d(
 5215                e,
 5216 5217            )
 5218        );
 5219    "});
 5220    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5221    cx.assert_editor_state(indoc! {"
 5222        const a: B = (
 5223            c(),
 5224            d(
 5225                e,
 5226                f    d(
 5227                    e,
 5228                    f
 5229                )
 5230        ˇ
 5231            )
 5232        );
 5233    "});
 5234
 5235    // Copy an indented block, starting mid-line
 5236    cx.set_state(indoc! {"
 5237        const a: B = (
 5238            c(),
 5239            somethin«g(
 5240                e,
 5241                f
 5242            )ˇ»
 5243        );
 5244    "});
 5245    cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
 5246
 5247    // Paste it on a line with a lower indent level
 5248    cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx));
 5249    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5250    cx.assert_editor_state(indoc! {"
 5251        const a: B = (
 5252            c(),
 5253            something(
 5254                e,
 5255                f
 5256            )
 5257        );
 5258        g(
 5259            e,
 5260            f
 5261"});
 5262}
 5263
 5264#[gpui::test]
 5265async fn test_paste_content_from_other_app(cx: &mut TestAppContext) {
 5266    init_test(cx, |_| {});
 5267
 5268    cx.write_to_clipboard(ClipboardItem::new_string(
 5269        "    d(\n        e\n    );\n".into(),
 5270    ));
 5271
 5272    let mut cx = EditorTestContext::new(cx).await;
 5273    cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 5274
 5275    cx.set_state(indoc! {"
 5276        fn a() {
 5277            b();
 5278            if c() {
 5279                ˇ
 5280            }
 5281        }
 5282    "});
 5283
 5284    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5285    cx.assert_editor_state(indoc! {"
 5286        fn a() {
 5287            b();
 5288            if c() {
 5289                d(
 5290                    e
 5291                );
 5292        ˇ
 5293            }
 5294        }
 5295    "});
 5296
 5297    cx.set_state(indoc! {"
 5298        fn a() {
 5299            b();
 5300            ˇ
 5301        }
 5302    "});
 5303
 5304    cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
 5305    cx.assert_editor_state(indoc! {"
 5306        fn a() {
 5307            b();
 5308            d(
 5309                e
 5310            );
 5311        ˇ
 5312        }
 5313    "});
 5314}
 5315
 5316#[gpui::test]
 5317fn test_select_all(cx: &mut TestAppContext) {
 5318    init_test(cx, |_| {});
 5319
 5320    let editor = cx.add_window(|window, cx| {
 5321        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
 5322        build_editor(buffer, window, cx)
 5323    });
 5324    _ = editor.update(cx, |editor, window, cx| {
 5325        editor.select_all(&SelectAll, window, cx);
 5326        assert_eq!(
 5327            editor.selections.display_ranges(cx),
 5328            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 3)]
 5329        );
 5330    });
 5331}
 5332
 5333#[gpui::test]
 5334fn test_select_line(cx: &mut TestAppContext) {
 5335    init_test(cx, |_| {});
 5336
 5337    let editor = cx.add_window(|window, cx| {
 5338        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
 5339        build_editor(buffer, window, cx)
 5340    });
 5341    _ = editor.update(cx, |editor, window, cx| {
 5342        editor.change_selections(None, window, cx, |s| {
 5343            s.select_display_ranges([
 5344                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5345                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5346                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5347                DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 2),
 5348            ])
 5349        });
 5350        editor.select_line(&SelectLine, window, cx);
 5351        assert_eq!(
 5352            editor.selections.display_ranges(cx),
 5353            vec![
 5354                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(2), 0),
 5355                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 0),
 5356            ]
 5357        );
 5358    });
 5359
 5360    _ = editor.update(cx, |editor, window, cx| {
 5361        editor.select_line(&SelectLine, window, cx);
 5362        assert_eq!(
 5363            editor.selections.display_ranges(cx),
 5364            vec![
 5365                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(3), 0),
 5366                DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(5), 5),
 5367            ]
 5368        );
 5369    });
 5370
 5371    _ = editor.update(cx, |editor, window, cx| {
 5372        editor.select_line(&SelectLine, window, cx);
 5373        assert_eq!(
 5374            editor.selections.display_ranges(cx),
 5375            vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(5), 5)]
 5376        );
 5377    });
 5378}
 5379
 5380#[gpui::test]
 5381async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
 5382    init_test(cx, |_| {});
 5383    let mut cx = EditorTestContext::new(cx).await;
 5384
 5385    #[track_caller]
 5386    fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
 5387        cx.set_state(initial_state);
 5388        cx.update_editor(|e, window, cx| {
 5389            e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
 5390        });
 5391        cx.assert_editor_state(expected_state);
 5392    }
 5393
 5394    // Selection starts and ends at the middle of lines, left-to-right
 5395    test(
 5396        &mut cx,
 5397        "aa\nb«ˇb\ncc\ndd\ne»e\nff",
 5398        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5399    );
 5400    // Same thing, right-to-left
 5401    test(
 5402        &mut cx,
 5403        "aa\nb«b\ncc\ndd\neˇ»e\nff",
 5404        "aa\nbbˇ\nccˇ\nddˇ\neˇe\nff",
 5405    );
 5406
 5407    // Whole buffer, left-to-right, last line *doesn't* end with newline
 5408    test(
 5409        &mut cx,
 5410        "«ˇaa\nbb\ncc\ndd\nee\nff»",
 5411        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5412    );
 5413    // Same thing, right-to-left
 5414    test(
 5415        &mut cx,
 5416        "«aa\nbb\ncc\ndd\nee\nffˇ»",
 5417        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ",
 5418    );
 5419
 5420    // Whole buffer, left-to-right, last line ends with newline
 5421    test(
 5422        &mut cx,
 5423        "«ˇaa\nbb\ncc\ndd\nee\nff\n»",
 5424        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5425    );
 5426    // Same thing, right-to-left
 5427    test(
 5428        &mut cx,
 5429        "«aa\nbb\ncc\ndd\nee\nff\nˇ»",
 5430        "aaˇ\nbbˇ\nccˇ\nddˇ\neeˇ\nffˇ\n",
 5431    );
 5432
 5433    // Starts at the end of a line, ends at the start of another
 5434    test(
 5435        &mut cx,
 5436        "aa\nbb«ˇ\ncc\ndd\nee\n»ff\n",
 5437        "aa\nbbˇ\nccˇ\nddˇ\neeˇ\nff\n",
 5438    );
 5439}
 5440
 5441#[gpui::test]
 5442async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestAppContext) {
 5443    init_test(cx, |_| {});
 5444
 5445    let editor = cx.add_window(|window, cx| {
 5446        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
 5447        build_editor(buffer, window, cx)
 5448    });
 5449
 5450    // setup
 5451    _ = editor.update(cx, |editor, window, cx| {
 5452        editor.fold_creases(
 5453            vec![
 5454                Crease::simple(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
 5455                Crease::simple(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
 5456                Crease::simple(Point::new(7, 0)..Point::new(8, 4), FoldPlaceholder::test()),
 5457            ],
 5458            true,
 5459            window,
 5460            cx,
 5461        );
 5462        assert_eq!(
 5463            editor.display_text(cx),
 5464            "aa⋯bbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5465        );
 5466    });
 5467
 5468    _ = editor.update(cx, |editor, window, cx| {
 5469        editor.change_selections(None, window, cx, |s| {
 5470            s.select_display_ranges([
 5471                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 5472                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
 5473                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
 5474                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
 5475            ])
 5476        });
 5477        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5478        assert_eq!(
 5479            editor.display_text(cx),
 5480            "aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
 5481        );
 5482    });
 5483    EditorTestContext::for_editor(editor, cx)
 5484        .await
 5485        .assert_editor_state("aˇaˇaaa\nbbbbb\nˇccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiiiˇ");
 5486
 5487    _ = editor.update(cx, |editor, window, cx| {
 5488        editor.change_selections(None, window, cx, |s| {
 5489            s.select_display_ranges([
 5490                DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
 5491            ])
 5492        });
 5493        editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
 5494        assert_eq!(
 5495            editor.display_text(cx),
 5496            "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
 5497        );
 5498        assert_eq!(
 5499            editor.selections.display_ranges(cx),
 5500            [
 5501                DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5),
 5502                DisplayPoint::new(DisplayRow(1), 5)..DisplayPoint::new(DisplayRow(1), 5),
 5503                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
 5504                DisplayPoint::new(DisplayRow(3), 5)..DisplayPoint::new(DisplayRow(3), 5),
 5505                DisplayPoint::new(DisplayRow(4), 5)..DisplayPoint::new(DisplayRow(4), 5),
 5506                DisplayPoint::new(DisplayRow(5), 5)..DisplayPoint::new(DisplayRow(5), 5),
 5507                DisplayPoint::new(DisplayRow(6), 5)..DisplayPoint::new(DisplayRow(6), 5)
 5508            ]
 5509        );
 5510    });
 5511    EditorTestContext::for_editor(editor, cx)
 5512        .await
 5513        .assert_editor_state(
 5514            "aaaaaˇ\nbbbbbˇ\ncccccˇ\ndddddˇ\neeeeeˇ\nfffffˇ\ngggggˇ\nhhhhh\niiiii",
 5515        );
 5516}
 5517
 5518#[gpui::test]
 5519async fn test_add_selection_above_below(cx: &mut TestAppContext) {
 5520    init_test(cx, |_| {});
 5521
 5522    let mut cx = EditorTestContext::new(cx).await;
 5523
 5524    cx.set_state(indoc!(
 5525        r#"abc
 5526           defˇghi
 5527
 5528           jk
 5529           nlmo
 5530           "#
 5531    ));
 5532
 5533    cx.update_editor(|editor, window, cx| {
 5534        editor.add_selection_above(&Default::default(), window, cx);
 5535    });
 5536
 5537    cx.assert_editor_state(indoc!(
 5538        r#"abcˇ
 5539           defˇghi
 5540
 5541           jk
 5542           nlmo
 5543           "#
 5544    ));
 5545
 5546    cx.update_editor(|editor, window, cx| {
 5547        editor.add_selection_above(&Default::default(), window, cx);
 5548    });
 5549
 5550    cx.assert_editor_state(indoc!(
 5551        r#"abcˇ
 5552            defˇghi
 5553
 5554            jk
 5555            nlmo
 5556            "#
 5557    ));
 5558
 5559    cx.update_editor(|editor, window, cx| {
 5560        editor.add_selection_below(&Default::default(), window, cx);
 5561    });
 5562
 5563    cx.assert_editor_state(indoc!(
 5564        r#"abc
 5565           defˇghi
 5566
 5567           jk
 5568           nlmo
 5569           "#
 5570    ));
 5571
 5572    cx.update_editor(|editor, window, cx| {
 5573        editor.undo_selection(&Default::default(), window, cx);
 5574    });
 5575
 5576    cx.assert_editor_state(indoc!(
 5577        r#"abcˇ
 5578           defˇghi
 5579
 5580           jk
 5581           nlmo
 5582           "#
 5583    ));
 5584
 5585    cx.update_editor(|editor, window, cx| {
 5586        editor.redo_selection(&Default::default(), window, cx);
 5587    });
 5588
 5589    cx.assert_editor_state(indoc!(
 5590        r#"abc
 5591           defˇghi
 5592
 5593           jk
 5594           nlmo
 5595           "#
 5596    ));
 5597
 5598    cx.update_editor(|editor, window, cx| {
 5599        editor.add_selection_below(&Default::default(), window, cx);
 5600    });
 5601
 5602    cx.assert_editor_state(indoc!(
 5603        r#"abc
 5604           defˇghi
 5605
 5606           jk
 5607           nlmˇo
 5608           "#
 5609    ));
 5610
 5611    cx.update_editor(|editor, window, cx| {
 5612        editor.add_selection_below(&Default::default(), window, cx);
 5613    });
 5614
 5615    cx.assert_editor_state(indoc!(
 5616        r#"abc
 5617           defˇghi
 5618
 5619           jk
 5620           nlmˇo
 5621           "#
 5622    ));
 5623
 5624    // change selections
 5625    cx.set_state(indoc!(
 5626        r#"abc
 5627           def«ˇg»hi
 5628
 5629           jk
 5630           nlmo
 5631           "#
 5632    ));
 5633
 5634    cx.update_editor(|editor, window, cx| {
 5635        editor.add_selection_below(&Default::default(), window, cx);
 5636    });
 5637
 5638    cx.assert_editor_state(indoc!(
 5639        r#"abc
 5640           def«ˇg»hi
 5641
 5642           jk
 5643           nlm«ˇo»
 5644           "#
 5645    ));
 5646
 5647    cx.update_editor(|editor, window, cx| {
 5648        editor.add_selection_below(&Default::default(), window, cx);
 5649    });
 5650
 5651    cx.assert_editor_state(indoc!(
 5652        r#"abc
 5653           def«ˇg»hi
 5654
 5655           jk
 5656           nlm«ˇo»
 5657           "#
 5658    ));
 5659
 5660    cx.update_editor(|editor, window, cx| {
 5661        editor.add_selection_above(&Default::default(), window, cx);
 5662    });
 5663
 5664    cx.assert_editor_state(indoc!(
 5665        r#"abc
 5666           def«ˇg»hi
 5667
 5668           jk
 5669           nlmo
 5670           "#
 5671    ));
 5672
 5673    cx.update_editor(|editor, window, cx| {
 5674        editor.add_selection_above(&Default::default(), window, cx);
 5675    });
 5676
 5677    cx.assert_editor_state(indoc!(
 5678        r#"abc
 5679           def«ˇg»hi
 5680
 5681           jk
 5682           nlmo
 5683           "#
 5684    ));
 5685
 5686    // Change selections again
 5687    cx.set_state(indoc!(
 5688        r#"a«bc
 5689           defgˇ»hi
 5690
 5691           jk
 5692           nlmo
 5693           "#
 5694    ));
 5695
 5696    cx.update_editor(|editor, window, cx| {
 5697        editor.add_selection_below(&Default::default(), window, cx);
 5698    });
 5699
 5700    cx.assert_editor_state(indoc!(
 5701        r#"a«bcˇ»
 5702           d«efgˇ»hi
 5703
 5704           j«kˇ»
 5705           nlmo
 5706           "#
 5707    ));
 5708
 5709    cx.update_editor(|editor, window, cx| {
 5710        editor.add_selection_below(&Default::default(), window, cx);
 5711    });
 5712    cx.assert_editor_state(indoc!(
 5713        r#"a«bcˇ»
 5714           d«efgˇ»hi
 5715
 5716           j«kˇ»
 5717           n«lmoˇ»
 5718           "#
 5719    ));
 5720    cx.update_editor(|editor, window, cx| {
 5721        editor.add_selection_above(&Default::default(), window, cx);
 5722    });
 5723
 5724    cx.assert_editor_state(indoc!(
 5725        r#"a«bcˇ»
 5726           d«efgˇ»hi
 5727
 5728           j«kˇ»
 5729           nlmo
 5730           "#
 5731    ));
 5732
 5733    // Change selections again
 5734    cx.set_state(indoc!(
 5735        r#"abc
 5736           d«ˇefghi
 5737
 5738           jk
 5739           nlm»o
 5740           "#
 5741    ));
 5742
 5743    cx.update_editor(|editor, window, cx| {
 5744        editor.add_selection_above(&Default::default(), window, cx);
 5745    });
 5746
 5747    cx.assert_editor_state(indoc!(
 5748        r#"a«ˇbc»
 5749           d«ˇef»ghi
 5750
 5751           j«ˇk»
 5752           n«ˇlm»o
 5753           "#
 5754    ));
 5755
 5756    cx.update_editor(|editor, window, cx| {
 5757        editor.add_selection_below(&Default::default(), window, cx);
 5758    });
 5759
 5760    cx.assert_editor_state(indoc!(
 5761        r#"abc
 5762           d«ˇef»ghi
 5763
 5764           j«ˇk»
 5765           n«ˇlm»o
 5766           "#
 5767    ));
 5768}
 5769
 5770#[gpui::test]
 5771async fn test_select_next(cx: &mut TestAppContext) {
 5772    init_test(cx, |_| {});
 5773
 5774    let mut cx = EditorTestContext::new(cx).await;
 5775    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5776
 5777    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5778        .unwrap();
 5779    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5780
 5781    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5782        .unwrap();
 5783    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5784
 5785    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 5786    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 5787
 5788    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 5789    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\nabc");
 5790
 5791    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5792        .unwrap();
 5793    cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5794
 5795    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5796        .unwrap();
 5797    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5798}
 5799
 5800#[gpui::test]
 5801async fn test_select_all_matches(cx: &mut TestAppContext) {
 5802    init_test(cx, |_| {});
 5803
 5804    let mut cx = EditorTestContext::new(cx).await;
 5805
 5806    // Test caret-only selections
 5807    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 5808    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5809        .unwrap();
 5810    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»");
 5811
 5812    // Test left-to-right selections
 5813    cx.set_state("abc\n«abcˇ»\nabc");
 5814    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5815        .unwrap();
 5816    cx.assert_editor_state("«abcˇ»\n«abcˇ»\n«abcˇ»");
 5817
 5818    // Test right-to-left selections
 5819    cx.set_state("abc\n«ˇabc»\nabc");
 5820    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5821        .unwrap();
 5822    cx.assert_editor_state("«ˇabc»\n«ˇabc»\n«ˇabc»");
 5823
 5824    // Test selecting whitespace with caret selection
 5825    cx.set_state("abc\nˇ   abc\nabc");
 5826    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5827        .unwrap();
 5828    cx.assert_editor_state("abc\n«   ˇ»abc\nabc");
 5829
 5830    // Test selecting whitespace with left-to-right selection
 5831    cx.set_state("abc\n«ˇ  »abc\nabc");
 5832    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5833        .unwrap();
 5834    cx.assert_editor_state("abc\n«ˇ  »abc\nabc");
 5835
 5836    // Test no matches with right-to-left selection
 5837    cx.set_state("abc\n«  ˇ»abc\nabc");
 5838    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5839        .unwrap();
 5840    cx.assert_editor_state("abc\n«  ˇ»abc\nabc");
 5841}
 5842
 5843#[gpui::test]
 5844async fn test_select_all_matches_does_not_scroll(cx: &mut TestAppContext) {
 5845    init_test(cx, |_| {});
 5846
 5847    let mut cx = EditorTestContext::new(cx).await;
 5848
 5849    let large_body_1 = "\nd".repeat(200);
 5850    let large_body_2 = "\ne".repeat(200);
 5851
 5852    cx.set_state(&format!(
 5853        "abc\nabc{large_body_1} «ˇa»bc{large_body_2}\nefabc\nabc"
 5854    ));
 5855    let initial_scroll_position = cx.update_editor(|editor, _, cx| {
 5856        let scroll_position = editor.scroll_position(cx);
 5857        assert!(scroll_position.y > 0.0, "Initial selection is between two large bodies and should have the editor scrolled to it");
 5858        scroll_position
 5859    });
 5860
 5861    cx.update_editor(|e, window, cx| e.select_all_matches(&SelectAllMatches, window, cx))
 5862        .unwrap();
 5863    cx.assert_editor_state(&format!(
 5864        "«ˇa»bc\n«ˇa»bc{large_body_1} «ˇa»bc{large_body_2}\nef«ˇa»bc\n«ˇa»bc"
 5865    ));
 5866    let scroll_position_after_selection =
 5867        cx.update_editor(|editor, _, cx| editor.scroll_position(cx));
 5868    assert_eq!(
 5869        initial_scroll_position, scroll_position_after_selection,
 5870        "Scroll position should not change after selecting all matches"
 5871    );
 5872}
 5873
 5874#[gpui::test]
 5875async fn test_undo_format_scrolls_to_last_edit_pos(cx: &mut TestAppContext) {
 5876    init_test(cx, |_| {});
 5877
 5878    let mut cx = EditorLspTestContext::new_rust(
 5879        lsp::ServerCapabilities {
 5880            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 5881            ..Default::default()
 5882        },
 5883        cx,
 5884    )
 5885    .await;
 5886
 5887    cx.set_state(indoc! {"
 5888        line 1
 5889        line 2
 5890        linˇe 3
 5891        line 4
 5892        line 5
 5893    "});
 5894
 5895    // Make an edit
 5896    cx.update_editor(|editor, window, cx| {
 5897        editor.handle_input("X", window, cx);
 5898    });
 5899
 5900    // Move cursor to a different position
 5901    cx.update_editor(|editor, window, cx| {
 5902        editor.change_selections(None, window, cx, |s| {
 5903            s.select_ranges([Point::new(4, 2)..Point::new(4, 2)]);
 5904        });
 5905    });
 5906
 5907    cx.assert_editor_state(indoc! {"
 5908        line 1
 5909        line 2
 5910        linXe 3
 5911        line 4
 5912        liˇne 5
 5913    "});
 5914
 5915    cx.lsp
 5916        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 5917            Ok(Some(vec![lsp::TextEdit::new(
 5918                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 5919                "PREFIX ".to_string(),
 5920            )]))
 5921        });
 5922
 5923    cx.update_editor(|editor, window, cx| editor.format(&Default::default(), window, cx))
 5924        .unwrap()
 5925        .await
 5926        .unwrap();
 5927
 5928    cx.assert_editor_state(indoc! {"
 5929        PREFIX line 1
 5930        line 2
 5931        linXe 3
 5932        line 4
 5933        liˇne 5
 5934    "});
 5935
 5936    // Undo formatting
 5937    cx.update_editor(|editor, window, cx| {
 5938        editor.undo(&Default::default(), window, cx);
 5939    });
 5940
 5941    // Verify cursor moved back to position after edit
 5942    cx.assert_editor_state(indoc! {"
 5943        line 1
 5944        line 2
 5945        linXˇe 3
 5946        line 4
 5947        line 5
 5948    "});
 5949}
 5950
 5951#[gpui::test]
 5952async fn test_select_next_with_multiple_carets(cx: &mut TestAppContext) {
 5953    init_test(cx, |_| {});
 5954
 5955    let mut cx = EditorTestContext::new(cx).await;
 5956    cx.set_state(
 5957        r#"let foo = 2;
 5958lˇet foo = 2;
 5959let fooˇ = 2;
 5960let foo = 2;
 5961let foo = ˇ2;"#,
 5962    );
 5963
 5964    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5965        .unwrap();
 5966    cx.assert_editor_state(
 5967        r#"let foo = 2;
 5968«letˇ» foo = 2;
 5969let «fooˇ» = 2;
 5970let foo = 2;
 5971let foo = «2ˇ»;"#,
 5972    );
 5973
 5974    // noop for multiple selections with different contents
 5975    cx.update_editor(|e, window, cx| e.select_next(&SelectNext::default(), window, cx))
 5976        .unwrap();
 5977    cx.assert_editor_state(
 5978        r#"let foo = 2;
 5979«letˇ» foo = 2;
 5980let «fooˇ» = 2;
 5981let foo = 2;
 5982let foo = «2ˇ»;"#,
 5983    );
 5984}
 5985
 5986#[gpui::test]
 5987async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
 5988    init_test(cx, |_| {});
 5989
 5990    let mut cx =
 5991        EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
 5992
 5993    cx.assert_editor_state(indoc! {"
 5994        ˇbbb
 5995        ccc
 5996
 5997        bbb
 5998        ccc
 5999        "});
 6000    cx.dispatch_action(SelectPrevious::default());
 6001    cx.assert_editor_state(indoc! {"
 6002                «bbbˇ»
 6003                ccc
 6004
 6005                bbb
 6006                ccc
 6007                "});
 6008    cx.dispatch_action(SelectPrevious::default());
 6009    cx.assert_editor_state(indoc! {"
 6010                «bbbˇ»
 6011                ccc
 6012
 6013                «bbbˇ»
 6014                ccc
 6015                "});
 6016}
 6017
 6018#[gpui::test]
 6019async fn test_select_previous_with_single_caret(cx: &mut TestAppContext) {
 6020    init_test(cx, |_| {});
 6021
 6022    let mut cx = EditorTestContext::new(cx).await;
 6023    cx.set_state("abc\nˇabc abc\ndefabc\nabc");
 6024
 6025    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6026        .unwrap();
 6027    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6028
 6029    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6030        .unwrap();
 6031    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6032
 6033    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6034    cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc");
 6035
 6036    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6037    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\nabc");
 6038
 6039    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6040        .unwrap();
 6041    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndefabc\n«abcˇ»");
 6042
 6043    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6044        .unwrap();
 6045    cx.assert_editor_state("«abcˇ»\n«abcˇ» abc\ndef«abcˇ»\n«abcˇ»");
 6046
 6047    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6048        .unwrap();
 6049    cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6050}
 6051
 6052#[gpui::test]
 6053async fn test_select_previous_empty_buffer(cx: &mut TestAppContext) {
 6054    init_test(cx, |_| {});
 6055
 6056    let mut cx = EditorTestContext::new(cx).await;
 6057    cx.set_state("");
 6058
 6059    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6060        .unwrap();
 6061    cx.assert_editor_state("«aˇ»");
 6062    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6063        .unwrap();
 6064    cx.assert_editor_state("«aˇ»");
 6065}
 6066
 6067#[gpui::test]
 6068async fn test_select_previous_with_multiple_carets(cx: &mut TestAppContext) {
 6069    init_test(cx, |_| {});
 6070
 6071    let mut cx = EditorTestContext::new(cx).await;
 6072    cx.set_state(
 6073        r#"let foo = 2;
 6074lˇet foo = 2;
 6075let fooˇ = 2;
 6076let foo = 2;
 6077let foo = ˇ2;"#,
 6078    );
 6079
 6080    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6081        .unwrap();
 6082    cx.assert_editor_state(
 6083        r#"let foo = 2;
 6084«letˇ» foo = 2;
 6085let «fooˇ» = 2;
 6086let foo = 2;
 6087let foo = «2ˇ»;"#,
 6088    );
 6089
 6090    // noop for multiple selections with different contents
 6091    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6092        .unwrap();
 6093    cx.assert_editor_state(
 6094        r#"let foo = 2;
 6095«letˇ» foo = 2;
 6096let «fooˇ» = 2;
 6097let foo = 2;
 6098let foo = «2ˇ»;"#,
 6099    );
 6100}
 6101
 6102#[gpui::test]
 6103async fn test_select_previous_with_single_selection(cx: &mut TestAppContext) {
 6104    init_test(cx, |_| {});
 6105
 6106    let mut cx = EditorTestContext::new(cx).await;
 6107    cx.set_state("abc\n«ˇabc» abc\ndefabc\nabc");
 6108
 6109    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6110        .unwrap();
 6111    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6112
 6113    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6114        .unwrap();
 6115    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6116
 6117    cx.update_editor(|editor, window, cx| editor.undo_selection(&UndoSelection, window, cx));
 6118    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\nabc");
 6119
 6120    cx.update_editor(|editor, window, cx| editor.redo_selection(&RedoSelection, window, cx));
 6121    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndefabc\n«abcˇ»");
 6122
 6123    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6124        .unwrap();
 6125    cx.assert_editor_state("«abcˇ»\n«ˇabc» abc\ndef«abcˇ»\n«abcˇ»");
 6126
 6127    cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx))
 6128        .unwrap();
 6129    cx.assert_editor_state("«abcˇ»\n«ˇabc» «abcˇ»\ndef«abcˇ»\n«abcˇ»");
 6130}
 6131
 6132#[gpui::test]
 6133async fn test_select_larger_smaller_syntax_node(cx: &mut TestAppContext) {
 6134    init_test(cx, |_| {});
 6135
 6136    let language = Arc::new(Language::new(
 6137        LanguageConfig::default(),
 6138        Some(tree_sitter_rust::LANGUAGE.into()),
 6139    ));
 6140
 6141    let text = r#"
 6142        use mod1::mod2::{mod3, mod4};
 6143
 6144        fn fn_1(param1: bool, param2: &str) {
 6145            let var1 = "text";
 6146        }
 6147    "#
 6148    .unindent();
 6149
 6150    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6151    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6152    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6153
 6154    editor
 6155        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6156        .await;
 6157
 6158    editor.update_in(cx, |editor, window, cx| {
 6159        editor.change_selections(None, window, cx, |s| {
 6160            s.select_display_ranges([
 6161                DisplayPoint::new(DisplayRow(0), 25)..DisplayPoint::new(DisplayRow(0), 25),
 6162                DisplayPoint::new(DisplayRow(2), 24)..DisplayPoint::new(DisplayRow(2), 12),
 6163                DisplayPoint::new(DisplayRow(3), 18)..DisplayPoint::new(DisplayRow(3), 18),
 6164            ]);
 6165        });
 6166        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6167    });
 6168    editor.update(cx, |editor, cx| {
 6169        assert_text_with_selections(
 6170            editor,
 6171            indoc! {r#"
 6172                use mod1::mod2::{mod3, «mod4ˇ»};
 6173
 6174                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6175                    let var1 = "«ˇtext»";
 6176                }
 6177            "#},
 6178            cx,
 6179        );
 6180    });
 6181
 6182    editor.update_in(cx, |editor, window, cx| {
 6183        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6184    });
 6185    editor.update(cx, |editor, cx| {
 6186        assert_text_with_selections(
 6187            editor,
 6188            indoc! {r#"
 6189                use mod1::mod2::«{mod3, mod4}ˇ»;
 6190
 6191                «ˇfn fn_1(param1: bool, param2: &str) {
 6192                    let var1 = "text";
 6193 6194            "#},
 6195            cx,
 6196        );
 6197    });
 6198
 6199    editor.update_in(cx, |editor, window, cx| {
 6200        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6201    });
 6202    assert_eq!(
 6203        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6204        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6205    );
 6206
 6207    // Trying to expand the selected syntax node one more time has no effect.
 6208    editor.update_in(cx, |editor, window, cx| {
 6209        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6210    });
 6211    assert_eq!(
 6212        editor.update(cx, |editor, cx| editor.selections.display_ranges(cx)),
 6213        &[DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 0)]
 6214    );
 6215
 6216    editor.update_in(cx, |editor, window, cx| {
 6217        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6218    });
 6219    editor.update(cx, |editor, cx| {
 6220        assert_text_with_selections(
 6221            editor,
 6222            indoc! {r#"
 6223                use mod1::mod2::«{mod3, mod4}ˇ»;
 6224
 6225                «ˇfn fn_1(param1: bool, param2: &str) {
 6226                    let var1 = "text";
 6227 6228            "#},
 6229            cx,
 6230        );
 6231    });
 6232
 6233    editor.update_in(cx, |editor, window, cx| {
 6234        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6235    });
 6236    editor.update(cx, |editor, cx| {
 6237        assert_text_with_selections(
 6238            editor,
 6239            indoc! {r#"
 6240                use mod1::mod2::{mod3, «mod4ˇ»};
 6241
 6242                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6243                    let var1 = "«ˇtext»";
 6244                }
 6245            "#},
 6246            cx,
 6247        );
 6248    });
 6249
 6250    editor.update_in(cx, |editor, window, cx| {
 6251        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6252    });
 6253    editor.update(cx, |editor, cx| {
 6254        assert_text_with_selections(
 6255            editor,
 6256            indoc! {r#"
 6257                use mod1::mod2::{mod3, mo«ˇ»d4};
 6258
 6259                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6260                    let var1 = "te«ˇ»xt";
 6261                }
 6262            "#},
 6263            cx,
 6264        );
 6265    });
 6266
 6267    // Trying to shrink the selected syntax node one more time has no effect.
 6268    editor.update_in(cx, |editor, window, cx| {
 6269        editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
 6270    });
 6271    editor.update_in(cx, |editor, _, cx| {
 6272        assert_text_with_selections(
 6273            editor,
 6274            indoc! {r#"
 6275                use mod1::mod2::{mod3, mo«ˇ»d4};
 6276
 6277                fn fn_1(para«ˇm1: bool, pa»ram2: &str) {
 6278                    let var1 = "te«ˇ»xt";
 6279                }
 6280            "#},
 6281            cx,
 6282        );
 6283    });
 6284
 6285    // Ensure that we keep expanding the selection if the larger selection starts or ends within
 6286    // a fold.
 6287    editor.update_in(cx, |editor, window, cx| {
 6288        editor.fold_creases(
 6289            vec![
 6290                Crease::simple(
 6291                    Point::new(0, 21)..Point::new(0, 24),
 6292                    FoldPlaceholder::test(),
 6293                ),
 6294                Crease::simple(
 6295                    Point::new(3, 20)..Point::new(3, 22),
 6296                    FoldPlaceholder::test(),
 6297                ),
 6298            ],
 6299            true,
 6300            window,
 6301            cx,
 6302        );
 6303        editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
 6304    });
 6305    editor.update(cx, |editor, cx| {
 6306        assert_text_with_selections(
 6307            editor,
 6308            indoc! {r#"
 6309                use mod1::mod2::«{mod3, mod4}ˇ»;
 6310
 6311                fn fn_1«ˇ(param1: bool, param2: &str)» {
 6312                    «ˇlet var1 = "text";»
 6313                }
 6314            "#},
 6315            cx,
 6316        );
 6317    });
 6318}
 6319
 6320#[gpui::test]
 6321async fn test_fold_function_bodies(cx: &mut TestAppContext) {
 6322    init_test(cx, |_| {});
 6323
 6324    let base_text = r#"
 6325        impl A {
 6326            // this is an uncommitted comment
 6327
 6328            fn b() {
 6329                c();
 6330            }
 6331
 6332            // this is another uncommitted comment
 6333
 6334            fn d() {
 6335                // e
 6336                // f
 6337            }
 6338        }
 6339
 6340        fn g() {
 6341            // h
 6342        }
 6343    "#
 6344    .unindent();
 6345
 6346    let text = r#"
 6347        ˇimpl A {
 6348
 6349            fn b() {
 6350                c();
 6351            }
 6352
 6353            fn d() {
 6354                // e
 6355                // f
 6356            }
 6357        }
 6358
 6359        fn g() {
 6360            // h
 6361        }
 6362    "#
 6363    .unindent();
 6364
 6365    let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6366    cx.set_state(&text);
 6367    cx.set_head_text(&base_text);
 6368    cx.update_editor(|editor, window, cx| {
 6369        editor.expand_all_diff_hunks(&Default::default(), window, cx);
 6370    });
 6371
 6372    cx.assert_state_with_diff(
 6373        "
 6374        ˇimpl A {
 6375      -     // this is an uncommitted comment
 6376
 6377            fn b() {
 6378                c();
 6379            }
 6380
 6381      -     // this is another uncommitted comment
 6382      -
 6383            fn d() {
 6384                // e
 6385                // f
 6386            }
 6387        }
 6388
 6389        fn g() {
 6390            // h
 6391        }
 6392    "
 6393        .unindent(),
 6394    );
 6395
 6396    let expected_display_text = "
 6397        impl A {
 6398            // this is an uncommitted comment
 6399
 6400            fn b() {
 6401 6402            }
 6403
 6404            // this is another uncommitted comment
 6405
 6406            fn d() {
 6407 6408            }
 6409        }
 6410
 6411        fn g() {
 6412 6413        }
 6414        "
 6415    .unindent();
 6416
 6417    cx.update_editor(|editor, window, cx| {
 6418        editor.fold_function_bodies(&FoldFunctionBodies, window, cx);
 6419        assert_eq!(editor.display_text(cx), expected_display_text);
 6420    });
 6421}
 6422
 6423#[gpui::test]
 6424async fn test_autoindent(cx: &mut TestAppContext) {
 6425    init_test(cx, |_| {});
 6426
 6427    let language = Arc::new(
 6428        Language::new(
 6429            LanguageConfig {
 6430                brackets: BracketPairConfig {
 6431                    pairs: vec![
 6432                        BracketPair {
 6433                            start: "{".to_string(),
 6434                            end: "}".to_string(),
 6435                            close: false,
 6436                            surround: false,
 6437                            newline: true,
 6438                        },
 6439                        BracketPair {
 6440                            start: "(".to_string(),
 6441                            end: ")".to_string(),
 6442                            close: false,
 6443                            surround: false,
 6444                            newline: true,
 6445                        },
 6446                    ],
 6447                    ..Default::default()
 6448                },
 6449                ..Default::default()
 6450            },
 6451            Some(tree_sitter_rust::LANGUAGE.into()),
 6452        )
 6453        .with_indents_query(
 6454            r#"
 6455                (_ "(" ")" @end) @indent
 6456                (_ "{" "}" @end) @indent
 6457            "#,
 6458        )
 6459        .unwrap(),
 6460    );
 6461
 6462    let text = "fn a() {}";
 6463
 6464    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 6465    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 6466    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 6467    editor
 6468        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 6469        .await;
 6470
 6471    editor.update_in(cx, |editor, window, cx| {
 6472        editor.change_selections(None, window, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
 6473        editor.newline(&Newline, window, cx);
 6474        assert_eq!(editor.text(cx), "fn a(\n    \n) {\n    \n}\n");
 6475        assert_eq!(
 6476            editor.selections.ranges(cx),
 6477            &[
 6478                Point::new(1, 4)..Point::new(1, 4),
 6479                Point::new(3, 4)..Point::new(3, 4),
 6480                Point::new(5, 0)..Point::new(5, 0)
 6481            ]
 6482        );
 6483    });
 6484}
 6485
 6486#[gpui::test]
 6487async fn test_autoindent_selections(cx: &mut TestAppContext) {
 6488    init_test(cx, |_| {});
 6489
 6490    {
 6491        let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
 6492        cx.set_state(indoc! {"
 6493            impl A {
 6494
 6495                fn b() {}
 6496
 6497            «fn c() {
 6498
 6499            }ˇ»
 6500            }
 6501        "});
 6502
 6503        cx.update_editor(|editor, window, cx| {
 6504            editor.autoindent(&Default::default(), window, cx);
 6505        });
 6506
 6507        cx.assert_editor_state(indoc! {"
 6508            impl A {
 6509
 6510                fn b() {}
 6511
 6512                «fn c() {
 6513
 6514                }ˇ»
 6515            }
 6516        "});
 6517    }
 6518
 6519    {
 6520        let mut cx = EditorTestContext::new_multibuffer(
 6521            cx,
 6522            [indoc! { "
 6523                impl A {
 6524                «
 6525                // a
 6526                fn b(){}
 6527                »
 6528                «
 6529                    }
 6530                    fn c(){}
 6531                »
 6532            "}],
 6533        );
 6534
 6535        let buffer = cx.update_editor(|editor, _, cx| {
 6536            let buffer = editor.buffer().update(cx, |buffer, _| {
 6537                buffer.all_buffers().iter().next().unwrap().clone()
 6538            });
 6539            buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
 6540            buffer
 6541        });
 6542
 6543        cx.run_until_parked();
 6544        cx.update_editor(|editor, window, cx| {
 6545            editor.select_all(&Default::default(), window, cx);
 6546            editor.autoindent(&Default::default(), window, cx)
 6547        });
 6548        cx.run_until_parked();
 6549
 6550        cx.update(|_, cx| {
 6551            assert_eq!(
 6552                buffer.read(cx).text(),
 6553                indoc! { "
 6554                    impl A {
 6555
 6556                        // a
 6557                        fn b(){}
 6558
 6559
 6560                    }
 6561                    fn c(){}
 6562
 6563                " }
 6564            )
 6565        });
 6566    }
 6567}
 6568
 6569#[gpui::test]
 6570async fn test_autoclose_and_auto_surround_pairs(cx: &mut TestAppContext) {
 6571    init_test(cx, |_| {});
 6572
 6573    let mut cx = EditorTestContext::new(cx).await;
 6574
 6575    let language = Arc::new(Language::new(
 6576        LanguageConfig {
 6577            brackets: BracketPairConfig {
 6578                pairs: vec![
 6579                    BracketPair {
 6580                        start: "{".to_string(),
 6581                        end: "}".to_string(),
 6582                        close: true,
 6583                        surround: true,
 6584                        newline: true,
 6585                    },
 6586                    BracketPair {
 6587                        start: "(".to_string(),
 6588                        end: ")".to_string(),
 6589                        close: true,
 6590                        surround: true,
 6591                        newline: true,
 6592                    },
 6593                    BracketPair {
 6594                        start: "/*".to_string(),
 6595                        end: " */".to_string(),
 6596                        close: true,
 6597                        surround: true,
 6598                        newline: true,
 6599                    },
 6600                    BracketPair {
 6601                        start: "[".to_string(),
 6602                        end: "]".to_string(),
 6603                        close: false,
 6604                        surround: false,
 6605                        newline: true,
 6606                    },
 6607                    BracketPair {
 6608                        start: "\"".to_string(),
 6609                        end: "\"".to_string(),
 6610                        close: true,
 6611                        surround: true,
 6612                        newline: false,
 6613                    },
 6614                    BracketPair {
 6615                        start: "<".to_string(),
 6616                        end: ">".to_string(),
 6617                        close: false,
 6618                        surround: true,
 6619                        newline: true,
 6620                    },
 6621                ],
 6622                ..Default::default()
 6623            },
 6624            autoclose_before: "})]".to_string(),
 6625            ..Default::default()
 6626        },
 6627        Some(tree_sitter_rust::LANGUAGE.into()),
 6628    ));
 6629
 6630    cx.language_registry().add(language.clone());
 6631    cx.update_buffer(|buffer, cx| {
 6632        buffer.set_language(Some(language), cx);
 6633    });
 6634
 6635    cx.set_state(
 6636        &r#"
 6637            🏀ˇ
 6638            εˇ
 6639            ❤️ˇ
 6640        "#
 6641        .unindent(),
 6642    );
 6643
 6644    // autoclose multiple nested brackets at multiple cursors
 6645    cx.update_editor(|editor, window, cx| {
 6646        editor.handle_input("{", window, cx);
 6647        editor.handle_input("{", window, cx);
 6648        editor.handle_input("{", window, cx);
 6649    });
 6650    cx.assert_editor_state(
 6651        &"
 6652            🏀{{{ˇ}}}
 6653            ε{{{ˇ}}}
 6654            ❤️{{{ˇ}}}
 6655        "
 6656        .unindent(),
 6657    );
 6658
 6659    // insert a different closing bracket
 6660    cx.update_editor(|editor, window, cx| {
 6661        editor.handle_input(")", window, cx);
 6662    });
 6663    cx.assert_editor_state(
 6664        &"
 6665            🏀{{{)ˇ}}}
 6666            ε{{{)ˇ}}}
 6667            ❤️{{{)ˇ}}}
 6668        "
 6669        .unindent(),
 6670    );
 6671
 6672    // skip over the auto-closed brackets when typing a closing bracket
 6673    cx.update_editor(|editor, window, cx| {
 6674        editor.move_right(&MoveRight, window, cx);
 6675        editor.handle_input("}", window, cx);
 6676        editor.handle_input("}", window, cx);
 6677        editor.handle_input("}", window, cx);
 6678    });
 6679    cx.assert_editor_state(
 6680        &"
 6681            🏀{{{)}}}}ˇ
 6682            ε{{{)}}}}ˇ
 6683            ❤️{{{)}}}}ˇ
 6684        "
 6685        .unindent(),
 6686    );
 6687
 6688    // autoclose multi-character pairs
 6689    cx.set_state(
 6690        &"
 6691            ˇ
 6692            ˇ
 6693        "
 6694        .unindent(),
 6695    );
 6696    cx.update_editor(|editor, window, cx| {
 6697        editor.handle_input("/", window, cx);
 6698        editor.handle_input("*", window, cx);
 6699    });
 6700    cx.assert_editor_state(
 6701        &"
 6702            /*ˇ */
 6703            /*ˇ */
 6704        "
 6705        .unindent(),
 6706    );
 6707
 6708    // one cursor autocloses a multi-character pair, one cursor
 6709    // does not autoclose.
 6710    cx.set_state(
 6711        &"
 6712 6713            ˇ
 6714        "
 6715        .unindent(),
 6716    );
 6717    cx.update_editor(|editor, window, cx| editor.handle_input("*", window, cx));
 6718    cx.assert_editor_state(
 6719        &"
 6720            /*ˇ */
 6721 6722        "
 6723        .unindent(),
 6724    );
 6725
 6726    // Don't autoclose if the next character isn't whitespace and isn't
 6727    // listed in the language's "autoclose_before" section.
 6728    cx.set_state("ˇa b");
 6729    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6730    cx.assert_editor_state("{ˇa b");
 6731
 6732    // Don't autoclose if `close` is false for the bracket pair
 6733    cx.set_state("ˇ");
 6734    cx.update_editor(|editor, window, cx| editor.handle_input("[", window, cx));
 6735    cx.assert_editor_state("");
 6736
 6737    // Surround with brackets if text is selected
 6738    cx.set_state("«aˇ» b");
 6739    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6740    cx.assert_editor_state("{«aˇ»} b");
 6741
 6742    // Autoclose when not immediately after a word character
 6743    cx.set_state("a ˇ");
 6744    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6745    cx.assert_editor_state("a \"ˇ\"");
 6746
 6747    // Autoclose pair where the start and end characters are the same
 6748    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6749    cx.assert_editor_state("a \"\"ˇ");
 6750
 6751    // Don't autoclose when immediately after a word character
 6752    cx.set_state("");
 6753    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6754    cx.assert_editor_state("a\"ˇ");
 6755
 6756    // Do autoclose when after a non-word character
 6757    cx.set_state("");
 6758    cx.update_editor(|editor, window, cx| editor.handle_input("\"", window, cx));
 6759    cx.assert_editor_state("{\"ˇ\"");
 6760
 6761    // Non identical pairs autoclose regardless of preceding character
 6762    cx.set_state("");
 6763    cx.update_editor(|editor, window, cx| editor.handle_input("{", window, cx));
 6764    cx.assert_editor_state("a{ˇ}");
 6765
 6766    // Don't autoclose pair if autoclose is disabled
 6767    cx.set_state("ˇ");
 6768    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6769    cx.assert_editor_state("");
 6770
 6771    // Surround with brackets if text is selected and auto_surround is enabled, even if autoclose is disabled
 6772    cx.set_state("«aˇ» b");
 6773    cx.update_editor(|editor, window, cx| editor.handle_input("<", window, cx));
 6774    cx.assert_editor_state("<«aˇ»> b");
 6775}
 6776
 6777#[gpui::test]
 6778async fn test_always_treat_brackets_as_autoclosed_skip_over(cx: &mut TestAppContext) {
 6779    init_test(cx, |settings| {
 6780        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 6781    });
 6782
 6783    let mut cx = EditorTestContext::new(cx).await;
 6784
 6785    let language = Arc::new(Language::new(
 6786        LanguageConfig {
 6787            brackets: BracketPairConfig {
 6788                pairs: vec![
 6789                    BracketPair {
 6790                        start: "{".to_string(),
 6791                        end: "}".to_string(),
 6792                        close: true,
 6793                        surround: true,
 6794                        newline: true,
 6795                    },
 6796                    BracketPair {
 6797                        start: "(".to_string(),
 6798                        end: ")".to_string(),
 6799                        close: true,
 6800                        surround: true,
 6801                        newline: true,
 6802                    },
 6803                    BracketPair {
 6804                        start: "[".to_string(),
 6805                        end: "]".to_string(),
 6806                        close: false,
 6807                        surround: false,
 6808                        newline: true,
 6809                    },
 6810                ],
 6811                ..Default::default()
 6812            },
 6813            autoclose_before: "})]".to_string(),
 6814            ..Default::default()
 6815        },
 6816        Some(tree_sitter_rust::LANGUAGE.into()),
 6817    ));
 6818
 6819    cx.language_registry().add(language.clone());
 6820    cx.update_buffer(|buffer, cx| {
 6821        buffer.set_language(Some(language), cx);
 6822    });
 6823
 6824    cx.set_state(
 6825        &"
 6826            ˇ
 6827            ˇ
 6828            ˇ
 6829        "
 6830        .unindent(),
 6831    );
 6832
 6833    // ensure only matching closing brackets are skipped over
 6834    cx.update_editor(|editor, window, cx| {
 6835        editor.handle_input("}", window, cx);
 6836        editor.move_left(&MoveLeft, window, cx);
 6837        editor.handle_input(")", window, cx);
 6838        editor.move_left(&MoveLeft, window, cx);
 6839    });
 6840    cx.assert_editor_state(
 6841        &"
 6842            ˇ)}
 6843            ˇ)}
 6844            ˇ)}
 6845        "
 6846        .unindent(),
 6847    );
 6848
 6849    // skip-over closing brackets at multiple cursors
 6850    cx.update_editor(|editor, window, cx| {
 6851        editor.handle_input(")", window, cx);
 6852        editor.handle_input("}", window, cx);
 6853    });
 6854    cx.assert_editor_state(
 6855        &"
 6856            )}ˇ
 6857            )}ˇ
 6858            )}ˇ
 6859        "
 6860        .unindent(),
 6861    );
 6862
 6863    // ignore non-close brackets
 6864    cx.update_editor(|editor, window, cx| {
 6865        editor.handle_input("]", window, cx);
 6866        editor.move_left(&MoveLeft, window, cx);
 6867        editor.handle_input("]", window, cx);
 6868    });
 6869    cx.assert_editor_state(
 6870        &"
 6871            )}]ˇ]
 6872            )}]ˇ]
 6873            )}]ˇ]
 6874        "
 6875        .unindent(),
 6876    );
 6877}
 6878
 6879#[gpui::test]
 6880async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) {
 6881    init_test(cx, |_| {});
 6882
 6883    let mut cx = EditorTestContext::new(cx).await;
 6884
 6885    let html_language = Arc::new(
 6886        Language::new(
 6887            LanguageConfig {
 6888                name: "HTML".into(),
 6889                brackets: BracketPairConfig {
 6890                    pairs: vec![
 6891                        BracketPair {
 6892                            start: "<".into(),
 6893                            end: ">".into(),
 6894                            close: true,
 6895                            ..Default::default()
 6896                        },
 6897                        BracketPair {
 6898                            start: "{".into(),
 6899                            end: "}".into(),
 6900                            close: true,
 6901                            ..Default::default()
 6902                        },
 6903                        BracketPair {
 6904                            start: "(".into(),
 6905                            end: ")".into(),
 6906                            close: true,
 6907                            ..Default::default()
 6908                        },
 6909                    ],
 6910                    ..Default::default()
 6911                },
 6912                autoclose_before: "})]>".into(),
 6913                ..Default::default()
 6914            },
 6915            Some(tree_sitter_html::LANGUAGE.into()),
 6916        )
 6917        .with_injection_query(
 6918            r#"
 6919            (script_element
 6920                (raw_text) @injection.content
 6921                (#set! injection.language "javascript"))
 6922            "#,
 6923        )
 6924        .unwrap(),
 6925    );
 6926
 6927    let javascript_language = Arc::new(Language::new(
 6928        LanguageConfig {
 6929            name: "JavaScript".into(),
 6930            brackets: BracketPairConfig {
 6931                pairs: vec![
 6932                    BracketPair {
 6933                        start: "/*".into(),
 6934                        end: " */".into(),
 6935                        close: true,
 6936                        ..Default::default()
 6937                    },
 6938                    BracketPair {
 6939                        start: "{".into(),
 6940                        end: "}".into(),
 6941                        close: true,
 6942                        ..Default::default()
 6943                    },
 6944                    BracketPair {
 6945                        start: "(".into(),
 6946                        end: ")".into(),
 6947                        close: true,
 6948                        ..Default::default()
 6949                    },
 6950                ],
 6951                ..Default::default()
 6952            },
 6953            autoclose_before: "})]>".into(),
 6954            ..Default::default()
 6955        },
 6956        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
 6957    ));
 6958
 6959    cx.language_registry().add(html_language.clone());
 6960    cx.language_registry().add(javascript_language.clone());
 6961
 6962    cx.update_buffer(|buffer, cx| {
 6963        buffer.set_language(Some(html_language), cx);
 6964    });
 6965
 6966    cx.set_state(
 6967        &r#"
 6968            <body>ˇ
 6969                <script>
 6970                    var x = 1;ˇ
 6971                </script>
 6972            </body>ˇ
 6973        "#
 6974        .unindent(),
 6975    );
 6976
 6977    // Precondition: different languages are active at different locations.
 6978    cx.update_editor(|editor, window, cx| {
 6979        let snapshot = editor.snapshot(window, cx);
 6980        let cursors = editor.selections.ranges::<usize>(cx);
 6981        let languages = cursors
 6982            .iter()
 6983            .map(|c| snapshot.language_at(c.start).unwrap().name())
 6984            .collect::<Vec<_>>();
 6985        assert_eq!(
 6986            languages,
 6987            &["HTML".into(), "JavaScript".into(), "HTML".into()]
 6988        );
 6989    });
 6990
 6991    // Angle brackets autoclose in HTML, but not JavaScript.
 6992    cx.update_editor(|editor, window, cx| {
 6993        editor.handle_input("<", window, cx);
 6994        editor.handle_input("a", window, cx);
 6995    });
 6996    cx.assert_editor_state(
 6997        &r#"
 6998            <body><aˇ>
 6999                <script>
 7000                    var x = 1;<aˇ
 7001                </script>
 7002            </body><aˇ>
 7003        "#
 7004        .unindent(),
 7005    );
 7006
 7007    // Curly braces and parens autoclose in both HTML and JavaScript.
 7008    cx.update_editor(|editor, window, cx| {
 7009        editor.handle_input(" b=", window, cx);
 7010        editor.handle_input("{", window, cx);
 7011        editor.handle_input("c", window, cx);
 7012        editor.handle_input("(", window, cx);
 7013    });
 7014    cx.assert_editor_state(
 7015        &r#"
 7016            <body><a b={c(ˇ)}>
 7017                <script>
 7018                    var x = 1;<a b={c(ˇ)}
 7019                </script>
 7020            </body><a b={c(ˇ)}>
 7021        "#
 7022        .unindent(),
 7023    );
 7024
 7025    // Brackets that were already autoclosed are skipped.
 7026    cx.update_editor(|editor, window, cx| {
 7027        editor.handle_input(")", window, cx);
 7028        editor.handle_input("d", window, cx);
 7029        editor.handle_input("}", window, cx);
 7030    });
 7031    cx.assert_editor_state(
 7032        &r#"
 7033            <body><a b={c()d}ˇ>
 7034                <script>
 7035                    var x = 1;<a b={c()d}ˇ
 7036                </script>
 7037            </body><a b={c()d}ˇ>
 7038        "#
 7039        .unindent(),
 7040    );
 7041    cx.update_editor(|editor, window, cx| {
 7042        editor.handle_input(">", window, cx);
 7043    });
 7044    cx.assert_editor_state(
 7045        &r#"
 7046            <body><a b={c()d}>ˇ
 7047                <script>
 7048                    var x = 1;<a b={c()d}>ˇ
 7049                </script>
 7050            </body><a b={c()d}>ˇ
 7051        "#
 7052        .unindent(),
 7053    );
 7054
 7055    // Reset
 7056    cx.set_state(
 7057        &r#"
 7058            <body>ˇ
 7059                <script>
 7060                    var x = 1;ˇ
 7061                </script>
 7062            </body>ˇ
 7063        "#
 7064        .unindent(),
 7065    );
 7066
 7067    cx.update_editor(|editor, window, cx| {
 7068        editor.handle_input("<", window, cx);
 7069    });
 7070    cx.assert_editor_state(
 7071        &r#"
 7072            <body><ˇ>
 7073                <script>
 7074                    var x = 1;<ˇ
 7075                </script>
 7076            </body><ˇ>
 7077        "#
 7078        .unindent(),
 7079    );
 7080
 7081    // When backspacing, the closing angle brackets are removed.
 7082    cx.update_editor(|editor, window, cx| {
 7083        editor.backspace(&Backspace, window, cx);
 7084    });
 7085    cx.assert_editor_state(
 7086        &r#"
 7087            <body>ˇ
 7088                <script>
 7089                    var x = 1;ˇ
 7090                </script>
 7091            </body>ˇ
 7092        "#
 7093        .unindent(),
 7094    );
 7095
 7096    // Block comments autoclose in JavaScript, but not HTML.
 7097    cx.update_editor(|editor, window, cx| {
 7098        editor.handle_input("/", window, cx);
 7099        editor.handle_input("*", window, cx);
 7100    });
 7101    cx.assert_editor_state(
 7102        &r#"
 7103            <body>/*ˇ
 7104                <script>
 7105                    var x = 1;/*ˇ */
 7106                </script>
 7107            </body>/*ˇ
 7108        "#
 7109        .unindent(),
 7110    );
 7111}
 7112
 7113#[gpui::test]
 7114async fn test_autoclose_with_overrides(cx: &mut TestAppContext) {
 7115    init_test(cx, |_| {});
 7116
 7117    let mut cx = EditorTestContext::new(cx).await;
 7118
 7119    let rust_language = Arc::new(
 7120        Language::new(
 7121            LanguageConfig {
 7122                name: "Rust".into(),
 7123                brackets: serde_json::from_value(json!([
 7124                    { "start": "{", "end": "}", "close": true, "newline": true },
 7125                    { "start": "\"", "end": "\"", "close": true, "newline": false, "not_in": ["string"] },
 7126                ]))
 7127                .unwrap(),
 7128                autoclose_before: "})]>".into(),
 7129                ..Default::default()
 7130            },
 7131            Some(tree_sitter_rust::LANGUAGE.into()),
 7132        )
 7133        .with_override_query("(string_literal) @string")
 7134        .unwrap(),
 7135    );
 7136
 7137    cx.language_registry().add(rust_language.clone());
 7138    cx.update_buffer(|buffer, cx| {
 7139        buffer.set_language(Some(rust_language), cx);
 7140    });
 7141
 7142    cx.set_state(
 7143        &r#"
 7144            let x = ˇ
 7145        "#
 7146        .unindent(),
 7147    );
 7148
 7149    // Inserting a quotation mark. A closing quotation mark is automatically inserted.
 7150    cx.update_editor(|editor, window, cx| {
 7151        editor.handle_input("\"", window, cx);
 7152    });
 7153    cx.assert_editor_state(
 7154        &r#"
 7155            let x = "ˇ"
 7156        "#
 7157        .unindent(),
 7158    );
 7159
 7160    // Inserting another quotation mark. The cursor moves across the existing
 7161    // automatically-inserted quotation mark.
 7162    cx.update_editor(|editor, window, cx| {
 7163        editor.handle_input("\"", window, cx);
 7164    });
 7165    cx.assert_editor_state(
 7166        &r#"
 7167            let x = ""ˇ
 7168        "#
 7169        .unindent(),
 7170    );
 7171
 7172    // Reset
 7173    cx.set_state(
 7174        &r#"
 7175            let x = ˇ
 7176        "#
 7177        .unindent(),
 7178    );
 7179
 7180    // Inserting a quotation mark inside of a string. A second quotation mark is not inserted.
 7181    cx.update_editor(|editor, window, cx| {
 7182        editor.handle_input("\"", window, cx);
 7183        editor.handle_input(" ", window, cx);
 7184        editor.move_left(&Default::default(), window, cx);
 7185        editor.handle_input("\\", window, cx);
 7186        editor.handle_input("\"", window, cx);
 7187    });
 7188    cx.assert_editor_state(
 7189        &r#"
 7190            let x = "\"ˇ "
 7191        "#
 7192        .unindent(),
 7193    );
 7194
 7195    // Inserting a closing quotation mark at the position of an automatically-inserted quotation
 7196    // mark. Nothing is inserted.
 7197    cx.update_editor(|editor, window, cx| {
 7198        editor.move_right(&Default::default(), window, cx);
 7199        editor.handle_input("\"", window, cx);
 7200    });
 7201    cx.assert_editor_state(
 7202        &r#"
 7203            let x = "\" "ˇ
 7204        "#
 7205        .unindent(),
 7206    );
 7207}
 7208
 7209#[gpui::test]
 7210async fn test_surround_with_pair(cx: &mut TestAppContext) {
 7211    init_test(cx, |_| {});
 7212
 7213    let language = Arc::new(Language::new(
 7214        LanguageConfig {
 7215            brackets: BracketPairConfig {
 7216                pairs: vec![
 7217                    BracketPair {
 7218                        start: "{".to_string(),
 7219                        end: "}".to_string(),
 7220                        close: true,
 7221                        surround: true,
 7222                        newline: true,
 7223                    },
 7224                    BracketPair {
 7225                        start: "/* ".to_string(),
 7226                        end: "*/".to_string(),
 7227                        close: true,
 7228                        surround: true,
 7229                        ..Default::default()
 7230                    },
 7231                ],
 7232                ..Default::default()
 7233            },
 7234            ..Default::default()
 7235        },
 7236        Some(tree_sitter_rust::LANGUAGE.into()),
 7237    ));
 7238
 7239    let text = r#"
 7240        a
 7241        b
 7242        c
 7243    "#
 7244    .unindent();
 7245
 7246    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7247    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7248    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7249    editor
 7250        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7251        .await;
 7252
 7253    editor.update_in(cx, |editor, window, cx| {
 7254        editor.change_selections(None, window, cx, |s| {
 7255            s.select_display_ranges([
 7256                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7257                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7258                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1),
 7259            ])
 7260        });
 7261
 7262        editor.handle_input("{", window, cx);
 7263        editor.handle_input("{", window, cx);
 7264        editor.handle_input("{", window, cx);
 7265        assert_eq!(
 7266            editor.text(cx),
 7267            "
 7268                {{{a}}}
 7269                {{{b}}}
 7270                {{{c}}}
 7271            "
 7272            .unindent()
 7273        );
 7274        assert_eq!(
 7275            editor.selections.display_ranges(cx),
 7276            [
 7277                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 4),
 7278                DisplayPoint::new(DisplayRow(1), 3)..DisplayPoint::new(DisplayRow(1), 4),
 7279                DisplayPoint::new(DisplayRow(2), 3)..DisplayPoint::new(DisplayRow(2), 4)
 7280            ]
 7281        );
 7282
 7283        editor.undo(&Undo, window, cx);
 7284        editor.undo(&Undo, window, cx);
 7285        editor.undo(&Undo, window, cx);
 7286        assert_eq!(
 7287            editor.text(cx),
 7288            "
 7289                a
 7290                b
 7291                c
 7292            "
 7293            .unindent()
 7294        );
 7295        assert_eq!(
 7296            editor.selections.display_ranges(cx),
 7297            [
 7298                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7299                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7300                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7301            ]
 7302        );
 7303
 7304        // Ensure inserting the first character of a multi-byte bracket pair
 7305        // doesn't surround the selections with the bracket.
 7306        editor.handle_input("/", window, cx);
 7307        assert_eq!(
 7308            editor.text(cx),
 7309            "
 7310                /
 7311                /
 7312                /
 7313            "
 7314            .unindent()
 7315        );
 7316        assert_eq!(
 7317            editor.selections.display_ranges(cx),
 7318            [
 7319                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7320                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7321                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7322            ]
 7323        );
 7324
 7325        editor.undo(&Undo, window, cx);
 7326        assert_eq!(
 7327            editor.text(cx),
 7328            "
 7329                a
 7330                b
 7331                c
 7332            "
 7333            .unindent()
 7334        );
 7335        assert_eq!(
 7336            editor.selections.display_ranges(cx),
 7337            [
 7338                DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
 7339                DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 1),
 7340                DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 1)
 7341            ]
 7342        );
 7343
 7344        // Ensure inserting the last character of a multi-byte bracket pair
 7345        // doesn't surround the selections with the bracket.
 7346        editor.handle_input("*", window, cx);
 7347        assert_eq!(
 7348            editor.text(cx),
 7349            "
 7350                *
 7351                *
 7352                *
 7353            "
 7354            .unindent()
 7355        );
 7356        assert_eq!(
 7357            editor.selections.display_ranges(cx),
 7358            [
 7359                DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1),
 7360                DisplayPoint::new(DisplayRow(1), 1)..DisplayPoint::new(DisplayRow(1), 1),
 7361                DisplayPoint::new(DisplayRow(2), 1)..DisplayPoint::new(DisplayRow(2), 1)
 7362            ]
 7363        );
 7364    });
 7365}
 7366
 7367#[gpui::test]
 7368async fn test_delete_autoclose_pair(cx: &mut TestAppContext) {
 7369    init_test(cx, |_| {});
 7370
 7371    let language = Arc::new(Language::new(
 7372        LanguageConfig {
 7373            brackets: BracketPairConfig {
 7374                pairs: vec![BracketPair {
 7375                    start: "{".to_string(),
 7376                    end: "}".to_string(),
 7377                    close: true,
 7378                    surround: true,
 7379                    newline: true,
 7380                }],
 7381                ..Default::default()
 7382            },
 7383            autoclose_before: "}".to_string(),
 7384            ..Default::default()
 7385        },
 7386        Some(tree_sitter_rust::LANGUAGE.into()),
 7387    ));
 7388
 7389    let text = r#"
 7390        a
 7391        b
 7392        c
 7393    "#
 7394    .unindent();
 7395
 7396    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
 7397    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7398    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7399    editor
 7400        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7401        .await;
 7402
 7403    editor.update_in(cx, |editor, window, cx| {
 7404        editor.change_selections(None, window, cx, |s| {
 7405            s.select_ranges([
 7406                Point::new(0, 1)..Point::new(0, 1),
 7407                Point::new(1, 1)..Point::new(1, 1),
 7408                Point::new(2, 1)..Point::new(2, 1),
 7409            ])
 7410        });
 7411
 7412        editor.handle_input("{", window, cx);
 7413        editor.handle_input("{", window, cx);
 7414        editor.handle_input("_", window, cx);
 7415        assert_eq!(
 7416            editor.text(cx),
 7417            "
 7418                a{{_}}
 7419                b{{_}}
 7420                c{{_}}
 7421            "
 7422            .unindent()
 7423        );
 7424        assert_eq!(
 7425            editor.selections.ranges::<Point>(cx),
 7426            [
 7427                Point::new(0, 4)..Point::new(0, 4),
 7428                Point::new(1, 4)..Point::new(1, 4),
 7429                Point::new(2, 4)..Point::new(2, 4)
 7430            ]
 7431        );
 7432
 7433        editor.backspace(&Default::default(), window, cx);
 7434        editor.backspace(&Default::default(), window, cx);
 7435        assert_eq!(
 7436            editor.text(cx),
 7437            "
 7438                a{}
 7439                b{}
 7440                c{}
 7441            "
 7442            .unindent()
 7443        );
 7444        assert_eq!(
 7445            editor.selections.ranges::<Point>(cx),
 7446            [
 7447                Point::new(0, 2)..Point::new(0, 2),
 7448                Point::new(1, 2)..Point::new(1, 2),
 7449                Point::new(2, 2)..Point::new(2, 2)
 7450            ]
 7451        );
 7452
 7453        editor.delete_to_previous_word_start(&Default::default(), window, cx);
 7454        assert_eq!(
 7455            editor.text(cx),
 7456            "
 7457                a
 7458                b
 7459                c
 7460            "
 7461            .unindent()
 7462        );
 7463        assert_eq!(
 7464            editor.selections.ranges::<Point>(cx),
 7465            [
 7466                Point::new(0, 1)..Point::new(0, 1),
 7467                Point::new(1, 1)..Point::new(1, 1),
 7468                Point::new(2, 1)..Point::new(2, 1)
 7469            ]
 7470        );
 7471    });
 7472}
 7473
 7474#[gpui::test]
 7475async fn test_always_treat_brackets_as_autoclosed_delete(cx: &mut TestAppContext) {
 7476    init_test(cx, |settings| {
 7477        settings.defaults.always_treat_brackets_as_autoclosed = Some(true);
 7478    });
 7479
 7480    let mut cx = EditorTestContext::new(cx).await;
 7481
 7482    let language = Arc::new(Language::new(
 7483        LanguageConfig {
 7484            brackets: BracketPairConfig {
 7485                pairs: vec![
 7486                    BracketPair {
 7487                        start: "{".to_string(),
 7488                        end: "}".to_string(),
 7489                        close: true,
 7490                        surround: true,
 7491                        newline: true,
 7492                    },
 7493                    BracketPair {
 7494                        start: "(".to_string(),
 7495                        end: ")".to_string(),
 7496                        close: true,
 7497                        surround: true,
 7498                        newline: true,
 7499                    },
 7500                    BracketPair {
 7501                        start: "[".to_string(),
 7502                        end: "]".to_string(),
 7503                        close: false,
 7504                        surround: true,
 7505                        newline: true,
 7506                    },
 7507                ],
 7508                ..Default::default()
 7509            },
 7510            autoclose_before: "})]".to_string(),
 7511            ..Default::default()
 7512        },
 7513        Some(tree_sitter_rust::LANGUAGE.into()),
 7514    ));
 7515
 7516    cx.language_registry().add(language.clone());
 7517    cx.update_buffer(|buffer, cx| {
 7518        buffer.set_language(Some(language), cx);
 7519    });
 7520
 7521    cx.set_state(
 7522        &"
 7523            {(ˇ)}
 7524            [[ˇ]]
 7525            {(ˇ)}
 7526        "
 7527        .unindent(),
 7528    );
 7529
 7530    cx.update_editor(|editor, window, cx| {
 7531        editor.backspace(&Default::default(), window, cx);
 7532        editor.backspace(&Default::default(), window, cx);
 7533    });
 7534
 7535    cx.assert_editor_state(
 7536        &"
 7537            ˇ
 7538            ˇ]]
 7539            ˇ
 7540        "
 7541        .unindent(),
 7542    );
 7543
 7544    cx.update_editor(|editor, window, cx| {
 7545        editor.handle_input("{", window, cx);
 7546        editor.handle_input("{", window, cx);
 7547        editor.move_right(&MoveRight, window, cx);
 7548        editor.move_right(&MoveRight, window, cx);
 7549        editor.move_left(&MoveLeft, window, cx);
 7550        editor.move_left(&MoveLeft, window, cx);
 7551        editor.backspace(&Default::default(), window, cx);
 7552    });
 7553
 7554    cx.assert_editor_state(
 7555        &"
 7556            {ˇ}
 7557            {ˇ}]]
 7558            {ˇ}
 7559        "
 7560        .unindent(),
 7561    );
 7562
 7563    cx.update_editor(|editor, window, cx| {
 7564        editor.backspace(&Default::default(), window, cx);
 7565    });
 7566
 7567    cx.assert_editor_state(
 7568        &"
 7569            ˇ
 7570            ˇ]]
 7571            ˇ
 7572        "
 7573        .unindent(),
 7574    );
 7575}
 7576
 7577#[gpui::test]
 7578async fn test_auto_replace_emoji_shortcode(cx: &mut TestAppContext) {
 7579    init_test(cx, |_| {});
 7580
 7581    let language = Arc::new(Language::new(
 7582        LanguageConfig::default(),
 7583        Some(tree_sitter_rust::LANGUAGE.into()),
 7584    ));
 7585
 7586    let buffer = cx.new(|cx| Buffer::local("", cx).with_language(language, cx));
 7587    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7588    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7589    editor
 7590        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
 7591        .await;
 7592
 7593    editor.update_in(cx, |editor, window, cx| {
 7594        editor.set_auto_replace_emoji_shortcode(true);
 7595
 7596        editor.handle_input("Hello ", window, cx);
 7597        editor.handle_input(":wave", window, cx);
 7598        assert_eq!(editor.text(cx), "Hello :wave".unindent());
 7599
 7600        editor.handle_input(":", window, cx);
 7601        assert_eq!(editor.text(cx), "Hello 👋".unindent());
 7602
 7603        editor.handle_input(" :smile", window, cx);
 7604        assert_eq!(editor.text(cx), "Hello 👋 :smile".unindent());
 7605
 7606        editor.handle_input(":", window, cx);
 7607        assert_eq!(editor.text(cx), "Hello 👋 😄".unindent());
 7608
 7609        // Ensure shortcode gets replaced when it is part of a word that only consists of emojis
 7610        editor.handle_input(":wave", window, cx);
 7611        assert_eq!(editor.text(cx), "Hello 👋 😄:wave".unindent());
 7612
 7613        editor.handle_input(":", window, cx);
 7614        assert_eq!(editor.text(cx), "Hello 👋 😄👋".unindent());
 7615
 7616        editor.handle_input(":1", window, cx);
 7617        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1".unindent());
 7618
 7619        editor.handle_input(":", window, cx);
 7620        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1:".unindent());
 7621
 7622        // Ensure shortcode does not get replaced when it is part of a word
 7623        editor.handle_input(" Test:wave", window, cx);
 7624        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave".unindent());
 7625
 7626        editor.handle_input(":", window, cx);
 7627        assert_eq!(editor.text(cx), "Hello 👋 😄👋:1: Test:wave:".unindent());
 7628
 7629        editor.set_auto_replace_emoji_shortcode(false);
 7630
 7631        // Ensure shortcode does not get replaced when auto replace is off
 7632        editor.handle_input(" :wave", window, cx);
 7633        assert_eq!(
 7634            editor.text(cx),
 7635            "Hello 👋 😄👋:1: Test:wave: :wave".unindent()
 7636        );
 7637
 7638        editor.handle_input(":", window, cx);
 7639        assert_eq!(
 7640            editor.text(cx),
 7641            "Hello 👋 😄👋:1: Test:wave: :wave:".unindent()
 7642        );
 7643    });
 7644}
 7645
 7646#[gpui::test]
 7647async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) {
 7648    init_test(cx, |_| {});
 7649
 7650    let (text, insertion_ranges) = marked_text_ranges(
 7651        indoc! {"
 7652            ˇ
 7653        "},
 7654        false,
 7655    );
 7656
 7657    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7658    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7659
 7660    _ = editor.update_in(cx, |editor, window, cx| {
 7661        let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap();
 7662
 7663        editor
 7664            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7665            .unwrap();
 7666
 7667        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7668            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7669            assert_eq!(editor.text(cx), expected_text);
 7670            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7671        }
 7672
 7673        assert(
 7674            editor,
 7675            cx,
 7676            indoc! {"
 7677            type «» =•
 7678            "},
 7679        );
 7680
 7681        assert!(editor.context_menu_visible(), "There should be a matches");
 7682    });
 7683}
 7684
 7685#[gpui::test]
 7686async fn test_snippets(cx: &mut TestAppContext) {
 7687    init_test(cx, |_| {});
 7688
 7689    let (text, insertion_ranges) = marked_text_ranges(
 7690        indoc! {"
 7691            a.ˇ b
 7692            a.ˇ b
 7693            a.ˇ b
 7694        "},
 7695        false,
 7696    );
 7697
 7698    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
 7699    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
 7700
 7701    editor.update_in(cx, |editor, window, cx| {
 7702        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 7703
 7704        editor
 7705            .insert_snippet(&insertion_ranges, snippet, window, cx)
 7706            .unwrap();
 7707
 7708        fn assert(editor: &mut Editor, cx: &mut Context<Editor>, marked_text: &str) {
 7709            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
 7710            assert_eq!(editor.text(cx), expected_text);
 7711            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
 7712        }
 7713
 7714        assert(
 7715            editor,
 7716            cx,
 7717            indoc! {"
 7718                a.f(«one», two, «three») b
 7719                a.f(«one», two, «three») b
 7720                a.f(«one», two, «three») b
 7721            "},
 7722        );
 7723
 7724        // Can't move earlier than the first tab stop
 7725        assert!(!editor.move_to_prev_snippet_tabstop(window, cx));
 7726        assert(
 7727            editor,
 7728            cx,
 7729            indoc! {"
 7730                a.f(«one», two, «three») b
 7731                a.f(«one», two, «three») b
 7732                a.f(«one», two, «three») b
 7733            "},
 7734        );
 7735
 7736        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7737        assert(
 7738            editor,
 7739            cx,
 7740            indoc! {"
 7741                a.f(one, «two», three) b
 7742                a.f(one, «two», three) b
 7743                a.f(one, «two», three) b
 7744            "},
 7745        );
 7746
 7747        editor.move_to_prev_snippet_tabstop(window, cx);
 7748        assert(
 7749            editor,
 7750            cx,
 7751            indoc! {"
 7752                a.f(«one», two, «three») b
 7753                a.f(«one», two, «three») b
 7754                a.f(«one», two, «three») b
 7755            "},
 7756        );
 7757
 7758        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7759        assert(
 7760            editor,
 7761            cx,
 7762            indoc! {"
 7763                a.f(one, «two», three) b
 7764                a.f(one, «two», three) b
 7765                a.f(one, «two», three) b
 7766            "},
 7767        );
 7768        assert!(editor.move_to_next_snippet_tabstop(window, cx));
 7769        assert(
 7770            editor,
 7771            cx,
 7772            indoc! {"
 7773                a.f(one, two, three)ˇ b
 7774                a.f(one, two, three)ˇ b
 7775                a.f(one, two, three)ˇ b
 7776            "},
 7777        );
 7778
 7779        // As soon as the last tab stop is reached, snippet state is gone
 7780        editor.move_to_prev_snippet_tabstop(window, cx);
 7781        assert(
 7782            editor,
 7783            cx,
 7784            indoc! {"
 7785                a.f(one, two, three)ˇ b
 7786                a.f(one, two, three)ˇ b
 7787                a.f(one, two, three)ˇ b
 7788            "},
 7789        );
 7790    });
 7791}
 7792
 7793#[gpui::test]
 7794async fn test_document_format_during_save(cx: &mut TestAppContext) {
 7795    init_test(cx, |_| {});
 7796
 7797    let fs = FakeFs::new(cx.executor());
 7798    fs.insert_file(path!("/file.rs"), Default::default()).await;
 7799
 7800    let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await;
 7801
 7802    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7803    language_registry.add(rust_lang());
 7804    let mut fake_servers = language_registry.register_fake_lsp(
 7805        "Rust",
 7806        FakeLspAdapter {
 7807            capabilities: lsp::ServerCapabilities {
 7808                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7809                ..Default::default()
 7810            },
 7811            ..Default::default()
 7812        },
 7813    );
 7814
 7815    let buffer = project
 7816        .update(cx, |project, cx| {
 7817            project.open_local_buffer(path!("/file.rs"), cx)
 7818        })
 7819        .await
 7820        .unwrap();
 7821
 7822    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 7823    let (editor, cx) = cx.add_window_view(|window, cx| {
 7824        build_editor_with_project(project.clone(), buffer, window, cx)
 7825    });
 7826    editor.update_in(cx, |editor, window, cx| {
 7827        editor.set_text("one\ntwo\nthree\n", window, cx)
 7828    });
 7829    assert!(cx.read(|cx| editor.is_dirty(cx)));
 7830
 7831    cx.executor().start_waiting();
 7832    let fake_server = fake_servers.next().await.unwrap();
 7833
 7834    {
 7835        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7836            move |params, _| async move {
 7837                assert_eq!(
 7838                    params.text_document.uri,
 7839                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7840                );
 7841                assert_eq!(params.options.tab_size, 4);
 7842                Ok(Some(vec![lsp::TextEdit::new(
 7843                    lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 7844                    ", ".to_string(),
 7845                )]))
 7846            },
 7847        );
 7848        let save = editor
 7849            .update_in(cx, |editor, window, cx| {
 7850                editor.save(true, project.clone(), window, cx)
 7851            })
 7852            .unwrap();
 7853        cx.executor().start_waiting();
 7854        save.await;
 7855
 7856        assert_eq!(
 7857            editor.update(cx, |editor, cx| editor.text(cx)),
 7858            "one, two\nthree\n"
 7859        );
 7860        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7861    }
 7862
 7863    {
 7864        editor.update_in(cx, |editor, window, cx| {
 7865            editor.set_text("one\ntwo\nthree\n", window, cx)
 7866        });
 7867        assert!(cx.read(|cx| editor.is_dirty(cx)));
 7868
 7869        // Ensure we can still save even if formatting hangs.
 7870        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 7871            move |params, _| async move {
 7872                assert_eq!(
 7873                    params.text_document.uri,
 7874                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7875                );
 7876                futures::future::pending::<()>().await;
 7877                unreachable!()
 7878            },
 7879        );
 7880        let save = editor
 7881            .update_in(cx, |editor, window, cx| {
 7882                editor.save(true, project.clone(), window, cx)
 7883            })
 7884            .unwrap();
 7885        cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 7886        cx.executor().start_waiting();
 7887        save.await;
 7888        assert_eq!(
 7889            editor.update(cx, |editor, cx| editor.text(cx)),
 7890            "one\ntwo\nthree\n"
 7891        );
 7892    }
 7893
 7894    // For non-dirty buffer, no formatting request should be sent
 7895    {
 7896        assert!(!cx.read(|cx| editor.is_dirty(cx)));
 7897
 7898        fake_server.set_request_handler::<lsp::request::Formatting, _, _>(move |_, _| async move {
 7899            panic!("Should not be invoked on non-dirty buffer");
 7900        });
 7901        let save = editor
 7902            .update_in(cx, |editor, window, cx| {
 7903                editor.save(true, project.clone(), window, cx)
 7904            })
 7905            .unwrap();
 7906        cx.executor().start_waiting();
 7907        save.await;
 7908    }
 7909
 7910    // Set rust language override and assert overridden tabsize is sent to language server
 7911    update_test_language_settings(cx, |settings| {
 7912        settings.languages.insert(
 7913            "Rust".into(),
 7914            LanguageSettingsContent {
 7915                tab_size: NonZeroU32::new(8),
 7916                ..Default::default()
 7917            },
 7918        );
 7919    });
 7920
 7921    {
 7922        editor.update_in(cx, |editor, window, cx| {
 7923            editor.set_text("somehting_new\n", window, cx)
 7924        });
 7925        assert!(cx.read(|cx| editor.is_dirty(cx)));
 7926        let _formatting_request_signal = fake_server
 7927            .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 7928                assert_eq!(
 7929                    params.text_document.uri,
 7930                    lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 7931                );
 7932                assert_eq!(params.options.tab_size, 8);
 7933                Ok(Some(vec![]))
 7934            });
 7935        let save = editor
 7936            .update_in(cx, |editor, window, cx| {
 7937                editor.save(true, project.clone(), window, cx)
 7938            })
 7939            .unwrap();
 7940        cx.executor().start_waiting();
 7941        save.await;
 7942    }
 7943}
 7944
 7945#[gpui::test]
 7946async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
 7947    init_test(cx, |_| {});
 7948
 7949    let cols = 4;
 7950    let rows = 10;
 7951    let sample_text_1 = sample_text(rows, cols, 'a');
 7952    assert_eq!(
 7953        sample_text_1,
 7954        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
 7955    );
 7956    let sample_text_2 = sample_text(rows, cols, 'l');
 7957    assert_eq!(
 7958        sample_text_2,
 7959        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
 7960    );
 7961    let sample_text_3 = sample_text(rows, cols, 'v');
 7962    assert_eq!(
 7963        sample_text_3,
 7964        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
 7965    );
 7966
 7967    let fs = FakeFs::new(cx.executor());
 7968    fs.insert_tree(
 7969        path!("/a"),
 7970        json!({
 7971            "main.rs": sample_text_1,
 7972            "other.rs": sample_text_2,
 7973            "lib.rs": sample_text_3,
 7974        }),
 7975    )
 7976    .await;
 7977
 7978    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
 7979    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
 7980    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
 7981
 7982    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 7983    language_registry.add(rust_lang());
 7984    let mut fake_servers = language_registry.register_fake_lsp(
 7985        "Rust",
 7986        FakeLspAdapter {
 7987            capabilities: lsp::ServerCapabilities {
 7988                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 7989                ..Default::default()
 7990            },
 7991            ..Default::default()
 7992        },
 7993    );
 7994
 7995    let worktree = project.update(cx, |project, cx| {
 7996        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
 7997        assert_eq!(worktrees.len(), 1);
 7998        worktrees.pop().unwrap()
 7999    });
 8000    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
 8001
 8002    let buffer_1 = project
 8003        .update(cx, |project, cx| {
 8004            project.open_buffer((worktree_id, "main.rs"), cx)
 8005        })
 8006        .await
 8007        .unwrap();
 8008    let buffer_2 = project
 8009        .update(cx, |project, cx| {
 8010            project.open_buffer((worktree_id, "other.rs"), cx)
 8011        })
 8012        .await
 8013        .unwrap();
 8014    let buffer_3 = project
 8015        .update(cx, |project, cx| {
 8016            project.open_buffer((worktree_id, "lib.rs"), cx)
 8017        })
 8018        .await
 8019        .unwrap();
 8020
 8021    let multi_buffer = cx.new(|cx| {
 8022        let mut multi_buffer = MultiBuffer::new(ReadWrite);
 8023        multi_buffer.push_excerpts(
 8024            buffer_1.clone(),
 8025            [
 8026                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8027                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8028                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8029            ],
 8030            cx,
 8031        );
 8032        multi_buffer.push_excerpts(
 8033            buffer_2.clone(),
 8034            [
 8035                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8036                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8037                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8038            ],
 8039            cx,
 8040        );
 8041        multi_buffer.push_excerpts(
 8042            buffer_3.clone(),
 8043            [
 8044                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
 8045                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
 8046                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
 8047            ],
 8048            cx,
 8049        );
 8050        multi_buffer
 8051    });
 8052    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
 8053        Editor::new(
 8054            EditorMode::full(),
 8055            multi_buffer,
 8056            Some(project.clone()),
 8057            window,
 8058            cx,
 8059        )
 8060    });
 8061
 8062    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8063        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8064            s.select_ranges(Some(1..2))
 8065        });
 8066        editor.insert("|one|two|three|", window, cx);
 8067    });
 8068    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8069    multi_buffer_editor.update_in(cx, |editor, window, cx| {
 8070        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
 8071            s.select_ranges(Some(60..70))
 8072        });
 8073        editor.insert("|four|five|six|", window, cx);
 8074    });
 8075    assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
 8076
 8077    // First two buffers should be edited, but not the third one.
 8078    assert_eq!(
 8079        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8080        "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}",
 8081    );
 8082    buffer_1.update(cx, |buffer, _| {
 8083        assert!(buffer.is_dirty());
 8084        assert_eq!(
 8085            buffer.text(),
 8086            "a|one|two|three|aa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj",
 8087        )
 8088    });
 8089    buffer_2.update(cx, |buffer, _| {
 8090        assert!(buffer.is_dirty());
 8091        assert_eq!(
 8092            buffer.text(),
 8093            "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
 8094        )
 8095    });
 8096    buffer_3.update(cx, |buffer, _| {
 8097        assert!(!buffer.is_dirty());
 8098        assert_eq!(buffer.text(), sample_text_3,)
 8099    });
 8100    cx.executor().run_until_parked();
 8101
 8102    cx.executor().start_waiting();
 8103    let save = multi_buffer_editor
 8104        .update_in(cx, |editor, window, cx| {
 8105            editor.save(true, project.clone(), window, cx)
 8106        })
 8107        .unwrap();
 8108
 8109    let fake_server = fake_servers.next().await.unwrap();
 8110    fake_server
 8111        .server
 8112        .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8113            Ok(Some(vec![lsp::TextEdit::new(
 8114                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8115                format!("[{} formatted]", params.text_document.uri),
 8116            )]))
 8117        })
 8118        .detach();
 8119    save.await;
 8120
 8121    // After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
 8122    assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
 8123    assert_eq!(
 8124        multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
 8125        uri!(
 8126            "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}"
 8127        ),
 8128    );
 8129    buffer_1.update(cx, |buffer, _| {
 8130        assert!(!buffer.is_dirty());
 8131        assert_eq!(
 8132            buffer.text(),
 8133            uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
 8134        )
 8135    });
 8136    buffer_2.update(cx, |buffer, _| {
 8137        assert!(!buffer.is_dirty());
 8138        assert_eq!(
 8139            buffer.text(),
 8140            uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
 8141        )
 8142    });
 8143    buffer_3.update(cx, |buffer, _| {
 8144        assert!(!buffer.is_dirty());
 8145        assert_eq!(buffer.text(), sample_text_3,)
 8146    });
 8147}
 8148
 8149#[gpui::test]
 8150async fn test_range_format_during_save(cx: &mut TestAppContext) {
 8151    init_test(cx, |_| {});
 8152
 8153    let fs = FakeFs::new(cx.executor());
 8154    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8155
 8156    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8157
 8158    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8159    language_registry.add(rust_lang());
 8160    let mut fake_servers = language_registry.register_fake_lsp(
 8161        "Rust",
 8162        FakeLspAdapter {
 8163            capabilities: lsp::ServerCapabilities {
 8164                document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
 8165                ..Default::default()
 8166            },
 8167            ..Default::default()
 8168        },
 8169    );
 8170
 8171    let buffer = project
 8172        .update(cx, |project, cx| {
 8173            project.open_local_buffer(path!("/file.rs"), cx)
 8174        })
 8175        .await
 8176        .unwrap();
 8177
 8178    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8179    let (editor, cx) = cx.add_window_view(|window, cx| {
 8180        build_editor_with_project(project.clone(), buffer, window, cx)
 8181    });
 8182    editor.update_in(cx, |editor, window, cx| {
 8183        editor.set_text("one\ntwo\nthree\n", window, cx)
 8184    });
 8185    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8186
 8187    cx.executor().start_waiting();
 8188    let fake_server = fake_servers.next().await.unwrap();
 8189
 8190    let save = editor
 8191        .update_in(cx, |editor, window, cx| {
 8192            editor.save(true, project.clone(), window, cx)
 8193        })
 8194        .unwrap();
 8195    fake_server
 8196        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8197            assert_eq!(
 8198                params.text_document.uri,
 8199                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8200            );
 8201            assert_eq!(params.options.tab_size, 4);
 8202            Ok(Some(vec![lsp::TextEdit::new(
 8203                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8204                ", ".to_string(),
 8205            )]))
 8206        })
 8207        .next()
 8208        .await;
 8209    cx.executor().start_waiting();
 8210    save.await;
 8211    assert_eq!(
 8212        editor.update(cx, |editor, cx| editor.text(cx)),
 8213        "one, two\nthree\n"
 8214    );
 8215    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8216
 8217    editor.update_in(cx, |editor, window, cx| {
 8218        editor.set_text("one\ntwo\nthree\n", window, cx)
 8219    });
 8220    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8221
 8222    // Ensure we can still save even if formatting hangs.
 8223    fake_server.set_request_handler::<lsp::request::RangeFormatting, _, _>(
 8224        move |params, _| async move {
 8225            assert_eq!(
 8226                params.text_document.uri,
 8227                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8228            );
 8229            futures::future::pending::<()>().await;
 8230            unreachable!()
 8231        },
 8232    );
 8233    let save = editor
 8234        .update_in(cx, |editor, window, cx| {
 8235            editor.save(true, project.clone(), window, cx)
 8236        })
 8237        .unwrap();
 8238    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8239    cx.executor().start_waiting();
 8240    save.await;
 8241    assert_eq!(
 8242        editor.update(cx, |editor, cx| editor.text(cx)),
 8243        "one\ntwo\nthree\n"
 8244    );
 8245    assert!(!cx.read(|cx| editor.is_dirty(cx)));
 8246
 8247    // For non-dirty buffer, no formatting request should be sent
 8248    let save = editor
 8249        .update_in(cx, |editor, window, cx| {
 8250            editor.save(true, project.clone(), window, cx)
 8251        })
 8252        .unwrap();
 8253    let _pending_format_request = fake_server
 8254        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |_, _| async move {
 8255            panic!("Should not be invoked on non-dirty buffer");
 8256        })
 8257        .next();
 8258    cx.executor().start_waiting();
 8259    save.await;
 8260
 8261    // Set Rust language override and assert overridden tabsize is sent to language server
 8262    update_test_language_settings(cx, |settings| {
 8263        settings.languages.insert(
 8264            "Rust".into(),
 8265            LanguageSettingsContent {
 8266                tab_size: NonZeroU32::new(8),
 8267                ..Default::default()
 8268            },
 8269        );
 8270    });
 8271
 8272    editor.update_in(cx, |editor, window, cx| {
 8273        editor.set_text("somehting_new\n", window, cx)
 8274    });
 8275    assert!(cx.read(|cx| editor.is_dirty(cx)));
 8276    let save = editor
 8277        .update_in(cx, |editor, window, cx| {
 8278            editor.save(true, project.clone(), window, cx)
 8279        })
 8280        .unwrap();
 8281    fake_server
 8282        .set_request_handler::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
 8283            assert_eq!(
 8284                params.text_document.uri,
 8285                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8286            );
 8287            assert_eq!(params.options.tab_size, 8);
 8288            Ok(Some(vec![]))
 8289        })
 8290        .next()
 8291        .await;
 8292    cx.executor().start_waiting();
 8293    save.await;
 8294}
 8295
 8296#[gpui::test]
 8297async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
 8298    init_test(cx, |settings| {
 8299        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8300            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8301        ))
 8302    });
 8303
 8304    let fs = FakeFs::new(cx.executor());
 8305    fs.insert_file(path!("/file.rs"), Default::default()).await;
 8306
 8307    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8308
 8309    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8310    language_registry.add(Arc::new(Language::new(
 8311        LanguageConfig {
 8312            name: "Rust".into(),
 8313            matcher: LanguageMatcher {
 8314                path_suffixes: vec!["rs".to_string()],
 8315                ..Default::default()
 8316            },
 8317            ..LanguageConfig::default()
 8318        },
 8319        Some(tree_sitter_rust::LANGUAGE.into()),
 8320    )));
 8321    update_test_language_settings(cx, |settings| {
 8322        // Enable Prettier formatting for the same buffer, and ensure
 8323        // LSP is called instead of Prettier.
 8324        settings.defaults.prettier = Some(PrettierSettings {
 8325            allowed: true,
 8326            ..PrettierSettings::default()
 8327        });
 8328    });
 8329    let mut fake_servers = language_registry.register_fake_lsp(
 8330        "Rust",
 8331        FakeLspAdapter {
 8332            capabilities: lsp::ServerCapabilities {
 8333                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8334                ..Default::default()
 8335            },
 8336            ..Default::default()
 8337        },
 8338    );
 8339
 8340    let buffer = project
 8341        .update(cx, |project, cx| {
 8342            project.open_local_buffer(path!("/file.rs"), cx)
 8343        })
 8344        .await
 8345        .unwrap();
 8346
 8347    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8348    let (editor, cx) = cx.add_window_view(|window, cx| {
 8349        build_editor_with_project(project.clone(), buffer, window, cx)
 8350    });
 8351    editor.update_in(cx, |editor, window, cx| {
 8352        editor.set_text("one\ntwo\nthree\n", window, cx)
 8353    });
 8354
 8355    cx.executor().start_waiting();
 8356    let fake_server = fake_servers.next().await.unwrap();
 8357
 8358    let format = editor
 8359        .update_in(cx, |editor, window, cx| {
 8360            editor.perform_format(
 8361                project.clone(),
 8362                FormatTrigger::Manual,
 8363                FormatTarget::Buffers,
 8364                window,
 8365                cx,
 8366            )
 8367        })
 8368        .unwrap();
 8369    fake_server
 8370        .set_request_handler::<lsp::request::Formatting, _, _>(move |params, _| async move {
 8371            assert_eq!(
 8372                params.text_document.uri,
 8373                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8374            );
 8375            assert_eq!(params.options.tab_size, 4);
 8376            Ok(Some(vec![lsp::TextEdit::new(
 8377                lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
 8378                ", ".to_string(),
 8379            )]))
 8380        })
 8381        .next()
 8382        .await;
 8383    cx.executor().start_waiting();
 8384    format.await;
 8385    assert_eq!(
 8386        editor.update(cx, |editor, cx| editor.text(cx)),
 8387        "one, two\nthree\n"
 8388    );
 8389
 8390    editor.update_in(cx, |editor, window, cx| {
 8391        editor.set_text("one\ntwo\nthree\n", window, cx)
 8392    });
 8393    // Ensure we don't lock if formatting hangs.
 8394    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8395        move |params, _| async move {
 8396            assert_eq!(
 8397                params.text_document.uri,
 8398                lsp::Url::from_file_path(path!("/file.rs")).unwrap()
 8399            );
 8400            futures::future::pending::<()>().await;
 8401            unreachable!()
 8402        },
 8403    );
 8404    let format = editor
 8405        .update_in(cx, |editor, window, cx| {
 8406            editor.perform_format(
 8407                project,
 8408                FormatTrigger::Manual,
 8409                FormatTarget::Buffers,
 8410                window,
 8411                cx,
 8412            )
 8413        })
 8414        .unwrap();
 8415    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
 8416    cx.executor().start_waiting();
 8417    format.await;
 8418    assert_eq!(
 8419        editor.update(cx, |editor, cx| editor.text(cx)),
 8420        "one\ntwo\nthree\n"
 8421    );
 8422}
 8423
 8424#[gpui::test]
 8425async fn test_multiple_formatters(cx: &mut TestAppContext) {
 8426    init_test(cx, |settings| {
 8427        settings.defaults.remove_trailing_whitespace_on_save = Some(true);
 8428        settings.defaults.formatter =
 8429            Some(language_settings::SelectedFormatter::List(FormatterList(
 8430                vec![
 8431                    Formatter::LanguageServer { name: None },
 8432                    Formatter::CodeActions(
 8433                        [
 8434                            ("code-action-1".into(), true),
 8435                            ("code-action-2".into(), true),
 8436                        ]
 8437                        .into_iter()
 8438                        .collect(),
 8439                    ),
 8440                ]
 8441                .into(),
 8442            )))
 8443    });
 8444
 8445    let fs = FakeFs::new(cx.executor());
 8446    fs.insert_file(path!("/file.rs"), "one  \ntwo   \nthree".into())
 8447        .await;
 8448
 8449    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8450    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8451    language_registry.add(rust_lang());
 8452
 8453    let mut fake_servers = language_registry.register_fake_lsp(
 8454        "Rust",
 8455        FakeLspAdapter {
 8456            capabilities: lsp::ServerCapabilities {
 8457                document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8458                execute_command_provider: Some(lsp::ExecuteCommandOptions {
 8459                    commands: vec!["the-command-for-code-action-1".into()],
 8460                    ..Default::default()
 8461                }),
 8462                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8463                ..Default::default()
 8464            },
 8465            ..Default::default()
 8466        },
 8467    );
 8468
 8469    let buffer = project
 8470        .update(cx, |project, cx| {
 8471            project.open_local_buffer(path!("/file.rs"), cx)
 8472        })
 8473        .await
 8474        .unwrap();
 8475
 8476    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8477    let (editor, cx) = cx.add_window_view(|window, cx| {
 8478        build_editor_with_project(project.clone(), buffer, window, cx)
 8479    });
 8480
 8481    cx.executor().start_waiting();
 8482
 8483    let fake_server = fake_servers.next().await.unwrap();
 8484    fake_server.set_request_handler::<lsp::request::Formatting, _, _>(
 8485        move |_params, _| async move {
 8486            Ok(Some(vec![lsp::TextEdit::new(
 8487                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8488                "applied-formatting\n".to_string(),
 8489            )]))
 8490        },
 8491    );
 8492    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8493        move |params, _| async move {
 8494            assert_eq!(
 8495                params.context.only,
 8496                Some(vec!["code-action-1".into(), "code-action-2".into()])
 8497            );
 8498            let uri = lsp::Url::from_file_path(path!("/file.rs")).unwrap();
 8499            Ok(Some(vec![
 8500                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8501                    kind: Some("code-action-1".into()),
 8502                    edit: Some(lsp::WorkspaceEdit::new(
 8503                        [(
 8504                            uri.clone(),
 8505                            vec![lsp::TextEdit::new(
 8506                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8507                                "applied-code-action-1-edit\n".to_string(),
 8508                            )],
 8509                        )]
 8510                        .into_iter()
 8511                        .collect(),
 8512                    )),
 8513                    command: Some(lsp::Command {
 8514                        command: "the-command-for-code-action-1".into(),
 8515                        ..Default::default()
 8516                    }),
 8517                    ..Default::default()
 8518                }),
 8519                lsp::CodeActionOrCommand::CodeAction(lsp::CodeAction {
 8520                    kind: Some("code-action-2".into()),
 8521                    edit: Some(lsp::WorkspaceEdit::new(
 8522                        [(
 8523                            uri.clone(),
 8524                            vec![lsp::TextEdit::new(
 8525                                lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
 8526                                "applied-code-action-2-edit\n".to_string(),
 8527                            )],
 8528                        )]
 8529                        .into_iter()
 8530                        .collect(),
 8531                    )),
 8532                    ..Default::default()
 8533                }),
 8534            ]))
 8535        },
 8536    );
 8537
 8538    fake_server.set_request_handler::<lsp::request::CodeActionResolveRequest, _, _>({
 8539        move |params, _| async move { Ok(params) }
 8540    });
 8541
 8542    let command_lock = Arc::new(futures::lock::Mutex::new(()));
 8543    fake_server.set_request_handler::<lsp::request::ExecuteCommand, _, _>({
 8544        let fake = fake_server.clone();
 8545        let lock = command_lock.clone();
 8546        move |params, _| {
 8547            assert_eq!(params.command, "the-command-for-code-action-1");
 8548            let fake = fake.clone();
 8549            let lock = lock.clone();
 8550            async move {
 8551                lock.lock().await;
 8552                fake.server
 8553                    .request::<lsp::request::ApplyWorkspaceEdit>(lsp::ApplyWorkspaceEditParams {
 8554                        label: None,
 8555                        edit: lsp::WorkspaceEdit {
 8556                            changes: Some(
 8557                                [(
 8558                                    lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
 8559                                    vec![lsp::TextEdit {
 8560                                        range: lsp::Range::new(
 8561                                            lsp::Position::new(0, 0),
 8562                                            lsp::Position::new(0, 0),
 8563                                        ),
 8564                                        new_text: "applied-code-action-1-command\n".into(),
 8565                                    }],
 8566                                )]
 8567                                .into_iter()
 8568                                .collect(),
 8569                            ),
 8570                            ..Default::default()
 8571                        },
 8572                    })
 8573                    .await
 8574                    .unwrap();
 8575                Ok(Some(json!(null)))
 8576            }
 8577        }
 8578    });
 8579
 8580    cx.executor().start_waiting();
 8581    editor
 8582        .update_in(cx, |editor, window, cx| {
 8583            editor.perform_format(
 8584                project.clone(),
 8585                FormatTrigger::Manual,
 8586                FormatTarget::Buffers,
 8587                window,
 8588                cx,
 8589            )
 8590        })
 8591        .unwrap()
 8592        .await;
 8593    editor.update(cx, |editor, cx| {
 8594        assert_eq!(
 8595            editor.text(cx),
 8596            r#"
 8597                applied-code-action-2-edit
 8598                applied-code-action-1-command
 8599                applied-code-action-1-edit
 8600                applied-formatting
 8601                one
 8602                two
 8603                three
 8604            "#
 8605            .unindent()
 8606        );
 8607    });
 8608
 8609    editor.update_in(cx, |editor, window, cx| {
 8610        editor.undo(&Default::default(), window, cx);
 8611        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8612    });
 8613
 8614    // Perform a manual edit while waiting for an LSP command
 8615    // that's being run as part of a formatting code action.
 8616    let lock_guard = command_lock.lock().await;
 8617    let format = editor
 8618        .update_in(cx, |editor, window, cx| {
 8619            editor.perform_format(
 8620                project.clone(),
 8621                FormatTrigger::Manual,
 8622                FormatTarget::Buffers,
 8623                window,
 8624                cx,
 8625            )
 8626        })
 8627        .unwrap();
 8628    cx.run_until_parked();
 8629    editor.update(cx, |editor, cx| {
 8630        assert_eq!(
 8631            editor.text(cx),
 8632            r#"
 8633                applied-code-action-1-edit
 8634                applied-formatting
 8635                one
 8636                two
 8637                three
 8638            "#
 8639            .unindent()
 8640        );
 8641
 8642        editor.buffer.update(cx, |buffer, cx| {
 8643            let ix = buffer.len(cx);
 8644            buffer.edit([(ix..ix, "edited\n")], None, cx);
 8645        });
 8646    });
 8647
 8648    // Allow the LSP command to proceed. Because the buffer was edited,
 8649    // the second code action will not be run.
 8650    drop(lock_guard);
 8651    format.await;
 8652    editor.update_in(cx, |editor, window, cx| {
 8653        assert_eq!(
 8654            editor.text(cx),
 8655            r#"
 8656                applied-code-action-1-command
 8657                applied-code-action-1-edit
 8658                applied-formatting
 8659                one
 8660                two
 8661                three
 8662                edited
 8663            "#
 8664            .unindent()
 8665        );
 8666
 8667        // The manual edit is undone first, because it is the last thing the user did
 8668        // (even though the command completed afterwards).
 8669        editor.undo(&Default::default(), window, cx);
 8670        assert_eq!(
 8671            editor.text(cx),
 8672            r#"
 8673                applied-code-action-1-command
 8674                applied-code-action-1-edit
 8675                applied-formatting
 8676                one
 8677                two
 8678                three
 8679            "#
 8680            .unindent()
 8681        );
 8682
 8683        // All the formatting (including the command, which completed after the manual edit)
 8684        // is undone together.
 8685        editor.undo(&Default::default(), window, cx);
 8686        assert_eq!(editor.text(cx), "one  \ntwo   \nthree");
 8687    });
 8688}
 8689
 8690#[gpui::test]
 8691async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
 8692    init_test(cx, |settings| {
 8693        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
 8694            FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
 8695        ))
 8696    });
 8697
 8698    let fs = FakeFs::new(cx.executor());
 8699    fs.insert_file(path!("/file.ts"), Default::default()).await;
 8700
 8701    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
 8702
 8703    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
 8704    language_registry.add(Arc::new(Language::new(
 8705        LanguageConfig {
 8706            name: "TypeScript".into(),
 8707            matcher: LanguageMatcher {
 8708                path_suffixes: vec!["ts".to_string()],
 8709                ..Default::default()
 8710            },
 8711            ..LanguageConfig::default()
 8712        },
 8713        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
 8714    )));
 8715    update_test_language_settings(cx, |settings| {
 8716        settings.defaults.prettier = Some(PrettierSettings {
 8717            allowed: true,
 8718            ..PrettierSettings::default()
 8719        });
 8720    });
 8721    let mut fake_servers = language_registry.register_fake_lsp(
 8722        "TypeScript",
 8723        FakeLspAdapter {
 8724            capabilities: lsp::ServerCapabilities {
 8725                code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
 8726                ..Default::default()
 8727            },
 8728            ..Default::default()
 8729        },
 8730    );
 8731
 8732    let buffer = project
 8733        .update(cx, |project, cx| {
 8734            project.open_local_buffer(path!("/file.ts"), cx)
 8735        })
 8736        .await
 8737        .unwrap();
 8738
 8739    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 8740    let (editor, cx) = cx.add_window_view(|window, cx| {
 8741        build_editor_with_project(project.clone(), buffer, window, cx)
 8742    });
 8743    editor.update_in(cx, |editor, window, cx| {
 8744        editor.set_text(
 8745            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8746            window,
 8747            cx,
 8748        )
 8749    });
 8750
 8751    cx.executor().start_waiting();
 8752    let fake_server = fake_servers.next().await.unwrap();
 8753
 8754    let format = editor
 8755        .update_in(cx, |editor, window, cx| {
 8756            editor.perform_code_action_kind(
 8757                project.clone(),
 8758                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8759                window,
 8760                cx,
 8761            )
 8762        })
 8763        .unwrap();
 8764    fake_server
 8765        .set_request_handler::<lsp::request::CodeActionRequest, _, _>(move |params, _| async move {
 8766            assert_eq!(
 8767                params.text_document.uri,
 8768                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8769            );
 8770            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
 8771                lsp::CodeAction {
 8772                    title: "Organize Imports".to_string(),
 8773                    kind: Some(lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS),
 8774                    edit: Some(lsp::WorkspaceEdit {
 8775                        changes: Some(
 8776                            [(
 8777                                params.text_document.uri.clone(),
 8778                                vec![lsp::TextEdit::new(
 8779                                    lsp::Range::new(
 8780                                        lsp::Position::new(1, 0),
 8781                                        lsp::Position::new(2, 0),
 8782                                    ),
 8783                                    "".to_string(),
 8784                                )],
 8785                            )]
 8786                            .into_iter()
 8787                            .collect(),
 8788                        ),
 8789                        ..Default::default()
 8790                    }),
 8791                    ..Default::default()
 8792                },
 8793            )]))
 8794        })
 8795        .next()
 8796        .await;
 8797    cx.executor().start_waiting();
 8798    format.await;
 8799    assert_eq!(
 8800        editor.update(cx, |editor, cx| editor.text(cx)),
 8801        "import { a } from 'module';\n\nconst x = a;\n"
 8802    );
 8803
 8804    editor.update_in(cx, |editor, window, cx| {
 8805        editor.set_text(
 8806            "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n",
 8807            window,
 8808            cx,
 8809        )
 8810    });
 8811    // Ensure we don't lock if code action hangs.
 8812    fake_server.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
 8813        move |params, _| async move {
 8814            assert_eq!(
 8815                params.text_document.uri,
 8816                lsp::Url::from_file_path(path!("/file.ts")).unwrap()
 8817            );
 8818            futures::future::pending::<()>().await;
 8819            unreachable!()
 8820        },
 8821    );
 8822    let format = editor
 8823        .update_in(cx, |editor, window, cx| {
 8824            editor.perform_code_action_kind(
 8825                project,
 8826                CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
 8827                window,
 8828                cx,
 8829            )
 8830        })
 8831        .unwrap();
 8832    cx.executor().advance_clock(super::CODE_ACTION_TIMEOUT);
 8833    cx.executor().start_waiting();
 8834    format.await;
 8835    assert_eq!(
 8836        editor.update(cx, |editor, cx| editor.text(cx)),
 8837        "import { a } from 'module';\nimport { b } from 'module';\n\nconst x = a;\n"
 8838    );
 8839}
 8840
 8841#[gpui::test]
 8842async fn test_concurrent_format_requests(cx: &mut TestAppContext) {
 8843    init_test(cx, |_| {});
 8844
 8845    let mut cx = EditorLspTestContext::new_rust(
 8846        lsp::ServerCapabilities {
 8847            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8848            ..Default::default()
 8849        },
 8850        cx,
 8851    )
 8852    .await;
 8853
 8854    cx.set_state(indoc! {"
 8855        one.twoˇ
 8856    "});
 8857
 8858    // The format request takes a long time. When it completes, it inserts
 8859    // a newline and an indent before the `.`
 8860    cx.lsp
 8861        .set_request_handler::<lsp::request::Formatting, _, _>(move |_, cx| {
 8862            let executor = cx.background_executor().clone();
 8863            async move {
 8864                executor.timer(Duration::from_millis(100)).await;
 8865                Ok(Some(vec![lsp::TextEdit {
 8866                    range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)),
 8867                    new_text: "\n    ".into(),
 8868                }]))
 8869            }
 8870        });
 8871
 8872    // Submit a format request.
 8873    let format_1 = cx
 8874        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8875        .unwrap();
 8876    cx.executor().run_until_parked();
 8877
 8878    // Submit a second format request.
 8879    let format_2 = cx
 8880        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8881        .unwrap();
 8882    cx.executor().run_until_parked();
 8883
 8884    // Wait for both format requests to complete
 8885    cx.executor().advance_clock(Duration::from_millis(200));
 8886    cx.executor().start_waiting();
 8887    format_1.await.unwrap();
 8888    cx.executor().start_waiting();
 8889    format_2.await.unwrap();
 8890
 8891    // The formatting edits only happens once.
 8892    cx.assert_editor_state(indoc! {"
 8893        one
 8894            .twoˇ
 8895    "});
 8896}
 8897
 8898#[gpui::test]
 8899async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
 8900    init_test(cx, |settings| {
 8901        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
 8902    });
 8903
 8904    let mut cx = EditorLspTestContext::new_rust(
 8905        lsp::ServerCapabilities {
 8906            document_formatting_provider: Some(lsp::OneOf::Left(true)),
 8907            ..Default::default()
 8908        },
 8909        cx,
 8910    )
 8911    .await;
 8912
 8913    // Set up a buffer white some trailing whitespace and no trailing newline.
 8914    cx.set_state(
 8915        &[
 8916            "one ",   //
 8917            "twoˇ",   //
 8918            "three ", //
 8919            "four",   //
 8920        ]
 8921        .join("\n"),
 8922    );
 8923
 8924    // Submit a format request.
 8925    let format = cx
 8926        .update_editor(|editor, window, cx| editor.format(&Format, window, cx))
 8927        .unwrap();
 8928
 8929    // Record which buffer changes have been sent to the language server
 8930    let buffer_changes = Arc::new(Mutex::new(Vec::new()));
 8931    cx.lsp
 8932        .handle_notification::<lsp::notification::DidChangeTextDocument, _>({
 8933            let buffer_changes = buffer_changes.clone();
 8934            move |params, _| {
 8935                buffer_changes.lock().extend(
 8936                    params
 8937                        .content_changes
 8938                        .into_iter()
 8939                        .map(|e| (e.range.unwrap(), e.text)),
 8940                );
 8941            }
 8942        });
 8943
 8944    // Handle formatting requests to the language server.
 8945    cx.lsp
 8946        .set_request_handler::<lsp::request::Formatting, _, _>({
 8947            let buffer_changes = buffer_changes.clone();
 8948            move |_, _| {
 8949                // When formatting is requested, trailing whitespace has already been stripped,
 8950                // and the trailing newline has already been added.
 8951                assert_eq!(
 8952                    &buffer_changes.lock()[1..],
 8953                    &[
 8954                        (
 8955                            lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 4)),
 8956                            "".into()
 8957                        ),
 8958                        (
 8959                            lsp::Range::new(lsp::Position::new(2, 5), lsp::Position::new(2, 6)),
 8960                            "".into()
 8961                        ),
 8962                        (
 8963                            lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
 8964                            "\n".into()
 8965                        ),
 8966                    ]
 8967                );
 8968
 8969                // Insert blank lines between each line of the buffer.
 8970                async move {
 8971                    Ok(Some(vec![
 8972                        lsp::TextEdit {
 8973                            range: lsp::Range::new(
 8974                                lsp::Position::new(1, 0),
 8975                                lsp::Position::new(1, 0),
 8976                            ),
 8977                            new_text: "\n".into(),
 8978                        },
 8979                        lsp::TextEdit {
 8980                            range: lsp::Range::new(
 8981                                lsp::Position::new(2, 0),
 8982                                lsp::Position::new(2, 0),
 8983                            ),
 8984                            new_text: "\n".into(),
 8985                        },
 8986                    ]))
 8987                }
 8988            }
 8989        });
 8990
 8991    // After formatting the buffer, the trailing whitespace is stripped,
 8992    // a newline is appended, and the edits provided by the language server
 8993    // have been applied.
 8994    format.await.unwrap();
 8995    cx.assert_editor_state(
 8996        &[
 8997            "one",   //
 8998            "",      //
 8999            "twoˇ",  //
 9000            "",      //
 9001            "three", //
 9002            "four",  //
 9003            "",      //
 9004        ]
 9005        .join("\n"),
 9006    );
 9007
 9008    // Undoing the formatting undoes the trailing whitespace removal, the
 9009    // trailing newline, and the LSP edits.
 9010    cx.update_buffer(|buffer, cx| buffer.undo(cx));
 9011    cx.assert_editor_state(
 9012        &[
 9013            "one ",   //
 9014            "twoˇ",   //
 9015            "three ", //
 9016            "four",   //
 9017        ]
 9018        .join("\n"),
 9019    );
 9020}
 9021
 9022#[gpui::test]
 9023async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
 9024    cx: &mut TestAppContext,
 9025) {
 9026    init_test(cx, |_| {});
 9027
 9028    cx.update(|cx| {
 9029        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9030            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9031                settings.auto_signature_help = Some(true);
 9032            });
 9033        });
 9034    });
 9035
 9036    let mut cx = EditorLspTestContext::new_rust(
 9037        lsp::ServerCapabilities {
 9038            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9039                ..Default::default()
 9040            }),
 9041            ..Default::default()
 9042        },
 9043        cx,
 9044    )
 9045    .await;
 9046
 9047    let language = Language::new(
 9048        LanguageConfig {
 9049            name: "Rust".into(),
 9050            brackets: BracketPairConfig {
 9051                pairs: vec![
 9052                    BracketPair {
 9053                        start: "{".to_string(),
 9054                        end: "}".to_string(),
 9055                        close: true,
 9056                        surround: true,
 9057                        newline: true,
 9058                    },
 9059                    BracketPair {
 9060                        start: "(".to_string(),
 9061                        end: ")".to_string(),
 9062                        close: true,
 9063                        surround: true,
 9064                        newline: true,
 9065                    },
 9066                    BracketPair {
 9067                        start: "/*".to_string(),
 9068                        end: " */".to_string(),
 9069                        close: true,
 9070                        surround: true,
 9071                        newline: true,
 9072                    },
 9073                    BracketPair {
 9074                        start: "[".to_string(),
 9075                        end: "]".to_string(),
 9076                        close: false,
 9077                        surround: false,
 9078                        newline: true,
 9079                    },
 9080                    BracketPair {
 9081                        start: "\"".to_string(),
 9082                        end: "\"".to_string(),
 9083                        close: true,
 9084                        surround: true,
 9085                        newline: false,
 9086                    },
 9087                    BracketPair {
 9088                        start: "<".to_string(),
 9089                        end: ">".to_string(),
 9090                        close: false,
 9091                        surround: true,
 9092                        newline: true,
 9093                    },
 9094                ],
 9095                ..Default::default()
 9096            },
 9097            autoclose_before: "})]".to_string(),
 9098            ..Default::default()
 9099        },
 9100        Some(tree_sitter_rust::LANGUAGE.into()),
 9101    );
 9102    let language = Arc::new(language);
 9103
 9104    cx.language_registry().add(language.clone());
 9105    cx.update_buffer(|buffer, cx| {
 9106        buffer.set_language(Some(language), cx);
 9107    });
 9108
 9109    cx.set_state(
 9110        &r#"
 9111            fn main() {
 9112                sampleˇ
 9113            }
 9114        "#
 9115        .unindent(),
 9116    );
 9117
 9118    cx.update_editor(|editor, window, cx| {
 9119        editor.handle_input("(", window, cx);
 9120    });
 9121    cx.assert_editor_state(
 9122        &"
 9123            fn main() {
 9124                sample(ˇ)
 9125            }
 9126        "
 9127        .unindent(),
 9128    );
 9129
 9130    let mocked_response = lsp::SignatureHelp {
 9131        signatures: vec![lsp::SignatureInformation {
 9132            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9133            documentation: None,
 9134            parameters: Some(vec![
 9135                lsp::ParameterInformation {
 9136                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9137                    documentation: None,
 9138                },
 9139                lsp::ParameterInformation {
 9140                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9141                    documentation: None,
 9142                },
 9143            ]),
 9144            active_parameter: None,
 9145        }],
 9146        active_signature: Some(0),
 9147        active_parameter: Some(0),
 9148    };
 9149    handle_signature_help_request(&mut cx, mocked_response).await;
 9150
 9151    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9152        .await;
 9153
 9154    cx.editor(|editor, _, _| {
 9155        let signature_help_state = editor.signature_help_state.popover().cloned();
 9156        assert_eq!(
 9157            signature_help_state.unwrap().label,
 9158            "param1: u8, param2: u8"
 9159        );
 9160    });
 9161}
 9162
 9163#[gpui::test]
 9164async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestAppContext) {
 9165    init_test(cx, |_| {});
 9166
 9167    cx.update(|cx| {
 9168        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9169            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9170                settings.auto_signature_help = Some(false);
 9171                settings.show_signature_help_after_edits = Some(false);
 9172            });
 9173        });
 9174    });
 9175
 9176    let mut cx = EditorLspTestContext::new_rust(
 9177        lsp::ServerCapabilities {
 9178            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9179                ..Default::default()
 9180            }),
 9181            ..Default::default()
 9182        },
 9183        cx,
 9184    )
 9185    .await;
 9186
 9187    let language = Language::new(
 9188        LanguageConfig {
 9189            name: "Rust".into(),
 9190            brackets: BracketPairConfig {
 9191                pairs: vec![
 9192                    BracketPair {
 9193                        start: "{".to_string(),
 9194                        end: "}".to_string(),
 9195                        close: true,
 9196                        surround: true,
 9197                        newline: true,
 9198                    },
 9199                    BracketPair {
 9200                        start: "(".to_string(),
 9201                        end: ")".to_string(),
 9202                        close: true,
 9203                        surround: true,
 9204                        newline: true,
 9205                    },
 9206                    BracketPair {
 9207                        start: "/*".to_string(),
 9208                        end: " */".to_string(),
 9209                        close: true,
 9210                        surround: true,
 9211                        newline: true,
 9212                    },
 9213                    BracketPair {
 9214                        start: "[".to_string(),
 9215                        end: "]".to_string(),
 9216                        close: false,
 9217                        surround: false,
 9218                        newline: true,
 9219                    },
 9220                    BracketPair {
 9221                        start: "\"".to_string(),
 9222                        end: "\"".to_string(),
 9223                        close: true,
 9224                        surround: true,
 9225                        newline: false,
 9226                    },
 9227                    BracketPair {
 9228                        start: "<".to_string(),
 9229                        end: ">".to_string(),
 9230                        close: false,
 9231                        surround: true,
 9232                        newline: true,
 9233                    },
 9234                ],
 9235                ..Default::default()
 9236            },
 9237            autoclose_before: "})]".to_string(),
 9238            ..Default::default()
 9239        },
 9240        Some(tree_sitter_rust::LANGUAGE.into()),
 9241    );
 9242    let language = Arc::new(language);
 9243
 9244    cx.language_registry().add(language.clone());
 9245    cx.update_buffer(|buffer, cx| {
 9246        buffer.set_language(Some(language), cx);
 9247    });
 9248
 9249    // Ensure that signature_help is not called when no signature help is enabled.
 9250    cx.set_state(
 9251        &r#"
 9252            fn main() {
 9253                sampleˇ
 9254            }
 9255        "#
 9256        .unindent(),
 9257    );
 9258    cx.update_editor(|editor, window, cx| {
 9259        editor.handle_input("(", window, cx);
 9260    });
 9261    cx.assert_editor_state(
 9262        &"
 9263            fn main() {
 9264                sample(ˇ)
 9265            }
 9266        "
 9267        .unindent(),
 9268    );
 9269    cx.editor(|editor, _, _| {
 9270        assert!(editor.signature_help_state.task().is_none());
 9271    });
 9272
 9273    let mocked_response = lsp::SignatureHelp {
 9274        signatures: vec![lsp::SignatureInformation {
 9275            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9276            documentation: None,
 9277            parameters: Some(vec![
 9278                lsp::ParameterInformation {
 9279                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9280                    documentation: None,
 9281                },
 9282                lsp::ParameterInformation {
 9283                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9284                    documentation: None,
 9285                },
 9286            ]),
 9287            active_parameter: None,
 9288        }],
 9289        active_signature: Some(0),
 9290        active_parameter: Some(0),
 9291    };
 9292
 9293    // Ensure that signature_help is called when enabled afte edits
 9294    cx.update(|_, cx| {
 9295        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9296            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9297                settings.auto_signature_help = Some(false);
 9298                settings.show_signature_help_after_edits = Some(true);
 9299            });
 9300        });
 9301    });
 9302    cx.set_state(
 9303        &r#"
 9304            fn main() {
 9305                sampleˇ
 9306            }
 9307        "#
 9308        .unindent(),
 9309    );
 9310    cx.update_editor(|editor, window, cx| {
 9311        editor.handle_input("(", window, cx);
 9312    });
 9313    cx.assert_editor_state(
 9314        &"
 9315            fn main() {
 9316                sample(ˇ)
 9317            }
 9318        "
 9319        .unindent(),
 9320    );
 9321    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9322    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9323        .await;
 9324    cx.update_editor(|editor, _, _| {
 9325        let signature_help_state = editor.signature_help_state.popover().cloned();
 9326        assert!(signature_help_state.is_some());
 9327        assert_eq!(
 9328            signature_help_state.unwrap().label,
 9329            "param1: u8, param2: u8"
 9330        );
 9331        editor.signature_help_state = SignatureHelpState::default();
 9332    });
 9333
 9334    // Ensure that signature_help is called when auto signature help override is enabled
 9335    cx.update(|_, cx| {
 9336        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9337            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9338                settings.auto_signature_help = Some(true);
 9339                settings.show_signature_help_after_edits = Some(false);
 9340            });
 9341        });
 9342    });
 9343    cx.set_state(
 9344        &r#"
 9345            fn main() {
 9346                sampleˇ
 9347            }
 9348        "#
 9349        .unindent(),
 9350    );
 9351    cx.update_editor(|editor, window, cx| {
 9352        editor.handle_input("(", window, cx);
 9353    });
 9354    cx.assert_editor_state(
 9355        &"
 9356            fn main() {
 9357                sample(ˇ)
 9358            }
 9359        "
 9360        .unindent(),
 9361    );
 9362    handle_signature_help_request(&mut cx, mocked_response).await;
 9363    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9364        .await;
 9365    cx.editor(|editor, _, _| {
 9366        let signature_help_state = editor.signature_help_state.popover().cloned();
 9367        assert!(signature_help_state.is_some());
 9368        assert_eq!(
 9369            signature_help_state.unwrap().label,
 9370            "param1: u8, param2: u8"
 9371        );
 9372    });
 9373}
 9374
 9375#[gpui::test]
 9376async fn test_signature_help(cx: &mut TestAppContext) {
 9377    init_test(cx, |_| {});
 9378    cx.update(|cx| {
 9379        cx.update_global::<SettingsStore, _>(|settings, cx| {
 9380            settings.update_user_settings::<EditorSettings>(cx, |settings| {
 9381                settings.auto_signature_help = Some(true);
 9382            });
 9383        });
 9384    });
 9385
 9386    let mut cx = EditorLspTestContext::new_rust(
 9387        lsp::ServerCapabilities {
 9388            signature_help_provider: Some(lsp::SignatureHelpOptions {
 9389                ..Default::default()
 9390            }),
 9391            ..Default::default()
 9392        },
 9393        cx,
 9394    )
 9395    .await;
 9396
 9397    // A test that directly calls `show_signature_help`
 9398    cx.update_editor(|editor, window, cx| {
 9399        editor.show_signature_help(&ShowSignatureHelp, window, cx);
 9400    });
 9401
 9402    let mocked_response = lsp::SignatureHelp {
 9403        signatures: vec![lsp::SignatureInformation {
 9404            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9405            documentation: None,
 9406            parameters: Some(vec![
 9407                lsp::ParameterInformation {
 9408                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9409                    documentation: None,
 9410                },
 9411                lsp::ParameterInformation {
 9412                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9413                    documentation: None,
 9414                },
 9415            ]),
 9416            active_parameter: None,
 9417        }],
 9418        active_signature: Some(0),
 9419        active_parameter: Some(0),
 9420    };
 9421    handle_signature_help_request(&mut cx, mocked_response).await;
 9422
 9423    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9424        .await;
 9425
 9426    cx.editor(|editor, _, _| {
 9427        let signature_help_state = editor.signature_help_state.popover().cloned();
 9428        assert!(signature_help_state.is_some());
 9429        assert_eq!(
 9430            signature_help_state.unwrap().label,
 9431            "param1: u8, param2: u8"
 9432        );
 9433    });
 9434
 9435    // When exiting outside from inside the brackets, `signature_help` is closed.
 9436    cx.set_state(indoc! {"
 9437        fn main() {
 9438            sample(ˇ);
 9439        }
 9440
 9441        fn sample(param1: u8, param2: u8) {}
 9442    "});
 9443
 9444    cx.update_editor(|editor, window, cx| {
 9445        editor.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
 9446    });
 9447
 9448    let mocked_response = lsp::SignatureHelp {
 9449        signatures: Vec::new(),
 9450        active_signature: None,
 9451        active_parameter: None,
 9452    };
 9453    handle_signature_help_request(&mut cx, mocked_response).await;
 9454
 9455    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9456        .await;
 9457
 9458    cx.editor(|editor, _, _| {
 9459        assert!(!editor.signature_help_state.is_shown());
 9460    });
 9461
 9462    // When entering inside the brackets from outside, `show_signature_help` is automatically called.
 9463    cx.set_state(indoc! {"
 9464        fn main() {
 9465            sample(ˇ);
 9466        }
 9467
 9468        fn sample(param1: u8, param2: u8) {}
 9469    "});
 9470
 9471    let mocked_response = lsp::SignatureHelp {
 9472        signatures: vec![lsp::SignatureInformation {
 9473            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9474            documentation: None,
 9475            parameters: Some(vec![
 9476                lsp::ParameterInformation {
 9477                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9478                    documentation: None,
 9479                },
 9480                lsp::ParameterInformation {
 9481                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9482                    documentation: None,
 9483                },
 9484            ]),
 9485            active_parameter: None,
 9486        }],
 9487        active_signature: Some(0),
 9488        active_parameter: Some(0),
 9489    };
 9490    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9491    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9492        .await;
 9493    cx.editor(|editor, _, _| {
 9494        assert!(editor.signature_help_state.is_shown());
 9495    });
 9496
 9497    // Restore the popover with more parameter input
 9498    cx.set_state(indoc! {"
 9499        fn main() {
 9500            sample(param1, param2ˇ);
 9501        }
 9502
 9503        fn sample(param1: u8, param2: u8) {}
 9504    "});
 9505
 9506    let mocked_response = lsp::SignatureHelp {
 9507        signatures: vec![lsp::SignatureInformation {
 9508            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9509            documentation: None,
 9510            parameters: Some(vec![
 9511                lsp::ParameterInformation {
 9512                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9513                    documentation: None,
 9514                },
 9515                lsp::ParameterInformation {
 9516                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9517                    documentation: None,
 9518                },
 9519            ]),
 9520            active_parameter: None,
 9521        }],
 9522        active_signature: Some(0),
 9523        active_parameter: Some(1),
 9524    };
 9525    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9526    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9527        .await;
 9528
 9529    // When selecting a range, the popover is gone.
 9530    // Avoid using `cx.set_state` to not actually edit the document, just change its selections.
 9531    cx.update_editor(|editor, window, cx| {
 9532        editor.change_selections(None, window, cx, |s| {
 9533            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9534        })
 9535    });
 9536    cx.assert_editor_state(indoc! {"
 9537        fn main() {
 9538            sample(param1, «ˇparam2»);
 9539        }
 9540
 9541        fn sample(param1: u8, param2: u8) {}
 9542    "});
 9543    cx.editor(|editor, _, _| {
 9544        assert!(!editor.signature_help_state.is_shown());
 9545    });
 9546
 9547    // When unselecting again, the popover is back if within the brackets.
 9548    cx.update_editor(|editor, window, cx| {
 9549        editor.change_selections(None, window, cx, |s| {
 9550            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9551        })
 9552    });
 9553    cx.assert_editor_state(indoc! {"
 9554        fn main() {
 9555            sample(param1, ˇparam2);
 9556        }
 9557
 9558        fn sample(param1: u8, param2: u8) {}
 9559    "});
 9560    handle_signature_help_request(&mut cx, mocked_response).await;
 9561    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9562        .await;
 9563    cx.editor(|editor, _, _| {
 9564        assert!(editor.signature_help_state.is_shown());
 9565    });
 9566
 9567    // Test to confirm that SignatureHelp does not appear after deselecting multiple ranges when it was hidden by pressing Escape.
 9568    cx.update_editor(|editor, window, cx| {
 9569        editor.change_selections(None, window, cx, |s| {
 9570            s.select_ranges(Some(Point::new(0, 0)..Point::new(0, 0)));
 9571            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9572        })
 9573    });
 9574    cx.assert_editor_state(indoc! {"
 9575        fn main() {
 9576            sample(param1, ˇparam2);
 9577        }
 9578
 9579        fn sample(param1: u8, param2: u8) {}
 9580    "});
 9581
 9582    let mocked_response = lsp::SignatureHelp {
 9583        signatures: vec![lsp::SignatureInformation {
 9584            label: "fn sample(param1: u8, param2: u8)".to_string(),
 9585            documentation: None,
 9586            parameters: Some(vec![
 9587                lsp::ParameterInformation {
 9588                    label: lsp::ParameterLabel::Simple("param1: u8".to_string()),
 9589                    documentation: None,
 9590                },
 9591                lsp::ParameterInformation {
 9592                    label: lsp::ParameterLabel::Simple("param2: u8".to_string()),
 9593                    documentation: None,
 9594                },
 9595            ]),
 9596            active_parameter: None,
 9597        }],
 9598        active_signature: Some(0),
 9599        active_parameter: Some(1),
 9600    };
 9601    handle_signature_help_request(&mut cx, mocked_response.clone()).await;
 9602    cx.condition(|editor, _| editor.signature_help_state.is_shown())
 9603        .await;
 9604    cx.update_editor(|editor, _, cx| {
 9605        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 9606    });
 9607    cx.condition(|editor, _| !editor.signature_help_state.is_shown())
 9608        .await;
 9609    cx.update_editor(|editor, window, cx| {
 9610        editor.change_selections(None, window, cx, |s| {
 9611            s.select_ranges(Some(Point::new(1, 25)..Point::new(1, 19)));
 9612        })
 9613    });
 9614    cx.assert_editor_state(indoc! {"
 9615        fn main() {
 9616            sample(param1, «ˇparam2»);
 9617        }
 9618
 9619        fn sample(param1: u8, param2: u8) {}
 9620    "});
 9621    cx.update_editor(|editor, window, cx| {
 9622        editor.change_selections(None, window, cx, |s| {
 9623            s.select_ranges(Some(Point::new(1, 19)..Point::new(1, 19)));
 9624        })
 9625    });
 9626    cx.assert_editor_state(indoc! {"
 9627        fn main() {
 9628            sample(param1, ˇparam2);
 9629        }
 9630
 9631        fn sample(param1: u8, param2: u8) {}
 9632    "});
 9633    cx.condition(|editor, _| !editor.signature_help_state.is_shown()) // because hidden by escape
 9634        .await;
 9635}
 9636
 9637#[gpui::test]
 9638async fn test_completion_mode(cx: &mut TestAppContext) {
 9639    init_test(cx, |_| {});
 9640    let mut cx = EditorLspTestContext::new_rust(
 9641        lsp::ServerCapabilities {
 9642            completion_provider: Some(lsp::CompletionOptions {
 9643                resolve_provider: Some(true),
 9644                ..Default::default()
 9645            }),
 9646            ..Default::default()
 9647        },
 9648        cx,
 9649    )
 9650    .await;
 9651
 9652    struct Run {
 9653        run_description: &'static str,
 9654        initial_state: String,
 9655        buffer_marked_text: String,
 9656        completion_text: &'static str,
 9657        expected_with_insert_mode: String,
 9658        expected_with_replace_mode: String,
 9659        expected_with_replace_subsequence_mode: String,
 9660        expected_with_replace_suffix_mode: String,
 9661    }
 9662
 9663    let runs = [
 9664        Run {
 9665            run_description: "Start of word matches completion text",
 9666            initial_state: "before ediˇ after".into(),
 9667            buffer_marked_text: "before <edi|> after".into(),
 9668            completion_text: "editor",
 9669            expected_with_insert_mode: "before editorˇ after".into(),
 9670            expected_with_replace_mode: "before editorˇ after".into(),
 9671            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9672            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9673        },
 9674        Run {
 9675            run_description: "Accept same text at the middle of the word",
 9676            initial_state: "before ediˇtor after".into(),
 9677            buffer_marked_text: "before <edi|tor> after".into(),
 9678            completion_text: "editor",
 9679            expected_with_insert_mode: "before editorˇtor after".into(),
 9680            expected_with_replace_mode: "before editorˇ after".into(),
 9681            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9682            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9683        },
 9684        Run {
 9685            run_description: "End of word matches completion text -- cursor at end",
 9686            initial_state: "before torˇ after".into(),
 9687            buffer_marked_text: "before <tor|> after".into(),
 9688            completion_text: "editor",
 9689            expected_with_insert_mode: "before editorˇ after".into(),
 9690            expected_with_replace_mode: "before editorˇ after".into(),
 9691            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9692            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9693        },
 9694        Run {
 9695            run_description: "End of word matches completion text -- cursor at start",
 9696            initial_state: "before ˇtor after".into(),
 9697            buffer_marked_text: "before <|tor> after".into(),
 9698            completion_text: "editor",
 9699            expected_with_insert_mode: "before editorˇtor after".into(),
 9700            expected_with_replace_mode: "before editorˇ after".into(),
 9701            expected_with_replace_subsequence_mode: "before editorˇ after".into(),
 9702            expected_with_replace_suffix_mode: "before editorˇ after".into(),
 9703        },
 9704        Run {
 9705            run_description: "Prepend text containing whitespace",
 9706            initial_state: "pˇfield: bool".into(),
 9707            buffer_marked_text: "<p|field>: bool".into(),
 9708            completion_text: "pub ",
 9709            expected_with_insert_mode: "pub ˇfield: bool".into(),
 9710            expected_with_replace_mode: "pub ˇ: bool".into(),
 9711            expected_with_replace_subsequence_mode: "pub ˇfield: bool".into(),
 9712            expected_with_replace_suffix_mode: "pub ˇfield: bool".into(),
 9713        },
 9714        Run {
 9715            run_description: "Add element to start of list",
 9716            initial_state: "[element_ˇelement_2]".into(),
 9717            buffer_marked_text: "[<element_|element_2>]".into(),
 9718            completion_text: "element_1",
 9719            expected_with_insert_mode: "[element_1ˇelement_2]".into(),
 9720            expected_with_replace_mode: "[element_1ˇ]".into(),
 9721            expected_with_replace_subsequence_mode: "[element_1ˇelement_2]".into(),
 9722            expected_with_replace_suffix_mode: "[element_1ˇelement_2]".into(),
 9723        },
 9724        Run {
 9725            run_description: "Add element to start of list -- first and second elements are equal",
 9726            initial_state: "[elˇelement]".into(),
 9727            buffer_marked_text: "[<el|element>]".into(),
 9728            completion_text: "element",
 9729            expected_with_insert_mode: "[elementˇelement]".into(),
 9730            expected_with_replace_mode: "[elementˇ]".into(),
 9731            expected_with_replace_subsequence_mode: "[elementˇelement]".into(),
 9732            expected_with_replace_suffix_mode: "[elementˇ]".into(),
 9733        },
 9734        Run {
 9735            run_description: "Ends with matching suffix",
 9736            initial_state: "SubˇError".into(),
 9737            buffer_marked_text: "<Sub|Error>".into(),
 9738            completion_text: "SubscriptionError",
 9739            expected_with_insert_mode: "SubscriptionErrorˇError".into(),
 9740            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9741            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9742            expected_with_replace_suffix_mode: "SubscriptionErrorˇ".into(),
 9743        },
 9744        Run {
 9745            run_description: "Suffix is a subsequence -- contiguous",
 9746            initial_state: "SubˇErr".into(),
 9747            buffer_marked_text: "<Sub|Err>".into(),
 9748            completion_text: "SubscriptionError",
 9749            expected_with_insert_mode: "SubscriptionErrorˇErr".into(),
 9750            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9751            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9752            expected_with_replace_suffix_mode: "SubscriptionErrorˇErr".into(),
 9753        },
 9754        Run {
 9755            run_description: "Suffix is a subsequence -- non-contiguous -- replace intended",
 9756            initial_state: "Suˇscrirr".into(),
 9757            buffer_marked_text: "<Su|scrirr>".into(),
 9758            completion_text: "SubscriptionError",
 9759            expected_with_insert_mode: "SubscriptionErrorˇscrirr".into(),
 9760            expected_with_replace_mode: "SubscriptionErrorˇ".into(),
 9761            expected_with_replace_subsequence_mode: "SubscriptionErrorˇ".into(),
 9762            expected_with_replace_suffix_mode: "SubscriptionErrorˇscrirr".into(),
 9763        },
 9764        Run {
 9765            run_description: "Suffix is a subsequence -- non-contiguous -- replace unintended",
 9766            initial_state: "foo(indˇix)".into(),
 9767            buffer_marked_text: "foo(<ind|ix>)".into(),
 9768            completion_text: "node_index",
 9769            expected_with_insert_mode: "foo(node_indexˇix)".into(),
 9770            expected_with_replace_mode: "foo(node_indexˇ)".into(),
 9771            expected_with_replace_subsequence_mode: "foo(node_indexˇix)".into(),
 9772            expected_with_replace_suffix_mode: "foo(node_indexˇix)".into(),
 9773        },
 9774    ];
 9775
 9776    for run in runs {
 9777        let run_variations = [
 9778            (LspInsertMode::Insert, run.expected_with_insert_mode),
 9779            (LspInsertMode::Replace, run.expected_with_replace_mode),
 9780            (
 9781                LspInsertMode::ReplaceSubsequence,
 9782                run.expected_with_replace_subsequence_mode,
 9783            ),
 9784            (
 9785                LspInsertMode::ReplaceSuffix,
 9786                run.expected_with_replace_suffix_mode,
 9787            ),
 9788        ];
 9789
 9790        for (lsp_insert_mode, expected_text) in run_variations {
 9791            eprintln!(
 9792                "run = {:?}, mode = {lsp_insert_mode:.?}",
 9793                run.run_description,
 9794            );
 9795
 9796            update_test_language_settings(&mut cx, |settings| {
 9797                settings.defaults.completions = Some(CompletionSettings {
 9798                    lsp_insert_mode,
 9799                    words: WordsCompletionMode::Disabled,
 9800                    lsp: true,
 9801                    lsp_fetch_timeout_ms: 0,
 9802                });
 9803            });
 9804
 9805            cx.set_state(&run.initial_state);
 9806            cx.update_editor(|editor, window, cx| {
 9807                editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9808            });
 9809
 9810            let counter = Arc::new(AtomicUsize::new(0));
 9811            handle_completion_request_with_insert_and_replace(
 9812                &mut cx,
 9813                &run.buffer_marked_text,
 9814                vec![run.completion_text],
 9815                counter.clone(),
 9816            )
 9817            .await;
 9818            cx.condition(|editor, _| editor.context_menu_visible())
 9819                .await;
 9820            assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9821
 9822            let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9823                editor
 9824                    .confirm_completion(&ConfirmCompletion::default(), window, cx)
 9825                    .unwrap()
 9826            });
 9827            cx.assert_editor_state(&expected_text);
 9828            handle_resolve_completion_request(&mut cx, None).await;
 9829            apply_additional_edits.await.unwrap();
 9830        }
 9831    }
 9832}
 9833
 9834#[gpui::test]
 9835async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext) {
 9836    init_test(cx, |_| {});
 9837    let mut cx = EditorLspTestContext::new_rust(
 9838        lsp::ServerCapabilities {
 9839            completion_provider: Some(lsp::CompletionOptions {
 9840                resolve_provider: Some(true),
 9841                ..Default::default()
 9842            }),
 9843            ..Default::default()
 9844        },
 9845        cx,
 9846    )
 9847    .await;
 9848
 9849    let initial_state = "SubˇError";
 9850    let buffer_marked_text = "<Sub|Error>";
 9851    let completion_text = "SubscriptionError";
 9852    let expected_with_insert_mode = "SubscriptionErrorˇError";
 9853    let expected_with_replace_mode = "SubscriptionErrorˇ";
 9854
 9855    update_test_language_settings(&mut cx, |settings| {
 9856        settings.defaults.completions = Some(CompletionSettings {
 9857            words: WordsCompletionMode::Disabled,
 9858            // set the opposite here to ensure that the action is overriding the default behavior
 9859            lsp_insert_mode: LspInsertMode::Insert,
 9860            lsp: true,
 9861            lsp_fetch_timeout_ms: 0,
 9862        });
 9863    });
 9864
 9865    cx.set_state(initial_state);
 9866    cx.update_editor(|editor, window, cx| {
 9867        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9868    });
 9869
 9870    let counter = Arc::new(AtomicUsize::new(0));
 9871    handle_completion_request_with_insert_and_replace(
 9872        &mut cx,
 9873        &buffer_marked_text,
 9874        vec![completion_text],
 9875        counter.clone(),
 9876    )
 9877    .await;
 9878    cx.condition(|editor, _| editor.context_menu_visible())
 9879        .await;
 9880    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
 9881
 9882    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9883        editor
 9884            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
 9885            .unwrap()
 9886    });
 9887    cx.assert_editor_state(&expected_with_replace_mode);
 9888    handle_resolve_completion_request(&mut cx, None).await;
 9889    apply_additional_edits.await.unwrap();
 9890
 9891    update_test_language_settings(&mut cx, |settings| {
 9892        settings.defaults.completions = Some(CompletionSettings {
 9893            words: WordsCompletionMode::Disabled,
 9894            // set the opposite here to ensure that the action is overriding the default behavior
 9895            lsp_insert_mode: LspInsertMode::Replace,
 9896            lsp: true,
 9897            lsp_fetch_timeout_ms: 0,
 9898        });
 9899    });
 9900
 9901    cx.set_state(initial_state);
 9902    cx.update_editor(|editor, window, cx| {
 9903        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9904    });
 9905    handle_completion_request_with_insert_and_replace(
 9906        &mut cx,
 9907        &buffer_marked_text,
 9908        vec![completion_text],
 9909        counter.clone(),
 9910    )
 9911    .await;
 9912    cx.condition(|editor, _| editor.context_menu_visible())
 9913        .await;
 9914    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
 9915
 9916    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
 9917        editor
 9918            .confirm_completion_insert(&ConfirmCompletionInsert, window, cx)
 9919            .unwrap()
 9920    });
 9921    cx.assert_editor_state(&expected_with_insert_mode);
 9922    handle_resolve_completion_request(&mut cx, None).await;
 9923    apply_additional_edits.await.unwrap();
 9924}
 9925
 9926#[gpui::test]
 9927async fn test_completion_replacing_surrounding_text_with_multicursors(cx: &mut TestAppContext) {
 9928    init_test(cx, |_| {});
 9929    let mut cx = EditorLspTestContext::new_rust(
 9930        lsp::ServerCapabilities {
 9931            completion_provider: Some(lsp::CompletionOptions {
 9932                resolve_provider: Some(true),
 9933                ..Default::default()
 9934            }),
 9935            ..Default::default()
 9936        },
 9937        cx,
 9938    )
 9939    .await;
 9940
 9941    // scenario: surrounding text matches completion text
 9942    let completion_text = "to_offset";
 9943    let initial_state = indoc! {"
 9944        1. buf.to_offˇsuffix
 9945        2. buf.to_offˇsuf
 9946        3. buf.to_offˇfix
 9947        4. buf.to_offˇ
 9948        5. into_offˇensive
 9949        6. ˇsuffix
 9950        7. let ˇ //
 9951        8. aaˇzz
 9952        9. buf.to_off«zzzzzˇ»suffix
 9953        10. buf.«ˇzzzzz»suffix
 9954        11. to_off«ˇzzzzz»
 9955
 9956        buf.to_offˇsuffix  // newest cursor
 9957    "};
 9958    let completion_marked_buffer = indoc! {"
 9959        1. buf.to_offsuffix
 9960        2. buf.to_offsuf
 9961        3. buf.to_offfix
 9962        4. buf.to_off
 9963        5. into_offensive
 9964        6. suffix
 9965        7. let  //
 9966        8. aazz
 9967        9. buf.to_offzzzzzsuffix
 9968        10. buf.zzzzzsuffix
 9969        11. to_offzzzzz
 9970
 9971        buf.<to_off|suffix>  // newest cursor
 9972    "};
 9973    let expected = indoc! {"
 9974        1. buf.to_offsetˇ
 9975        2. buf.to_offsetˇsuf
 9976        3. buf.to_offsetˇfix
 9977        4. buf.to_offsetˇ
 9978        5. into_offsetˇensive
 9979        6. to_offsetˇsuffix
 9980        7. let to_offsetˇ //
 9981        8. aato_offsetˇzz
 9982        9. buf.to_offsetˇ
 9983        10. buf.to_offsetˇsuffix
 9984        11. to_offsetˇ
 9985
 9986        buf.to_offsetˇ  // newest cursor
 9987    "};
 9988    cx.set_state(initial_state);
 9989    cx.update_editor(|editor, window, cx| {
 9990        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
 9991    });
 9992    handle_completion_request_with_insert_and_replace(
 9993        &mut cx,
 9994        completion_marked_buffer,
 9995        vec![completion_text],
 9996        Arc::new(AtomicUsize::new(0)),
 9997    )
 9998    .await;
 9999    cx.condition(|editor, _| editor.context_menu_visible())
10000        .await;
10001    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10002        editor
10003            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10004            .unwrap()
10005    });
10006    cx.assert_editor_state(expected);
10007    handle_resolve_completion_request(&mut cx, None).await;
10008    apply_additional_edits.await.unwrap();
10009
10010    // scenario: surrounding text matches surroundings of newest cursor, inserting at the end
10011    let completion_text = "foo_and_bar";
10012    let initial_state = indoc! {"
10013        1. ooanbˇ
10014        2. zooanbˇ
10015        3. ooanbˇz
10016        4. zooanbˇz
10017        5. ooanˇ
10018        6. oanbˇ
10019
10020        ooanbˇ
10021    "};
10022    let completion_marked_buffer = indoc! {"
10023        1. ooanb
10024        2. zooanb
10025        3. ooanbz
10026        4. zooanbz
10027        5. ooan
10028        6. oanb
10029
10030        <ooanb|>
10031    "};
10032    let expected = indoc! {"
10033        1. foo_and_barˇ
10034        2. zfoo_and_barˇ
10035        3. foo_and_barˇz
10036        4. zfoo_and_barˇz
10037        5. ooanfoo_and_barˇ
10038        6. oanbfoo_and_barˇ
10039
10040        foo_and_barˇ
10041    "};
10042    cx.set_state(initial_state);
10043    cx.update_editor(|editor, window, cx| {
10044        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10045    });
10046    handle_completion_request_with_insert_and_replace(
10047        &mut cx,
10048        completion_marked_buffer,
10049        vec![completion_text],
10050        Arc::new(AtomicUsize::new(0)),
10051    )
10052    .await;
10053    cx.condition(|editor, _| editor.context_menu_visible())
10054        .await;
10055    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10056        editor
10057            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10058            .unwrap()
10059    });
10060    cx.assert_editor_state(expected);
10061    handle_resolve_completion_request(&mut cx, None).await;
10062    apply_additional_edits.await.unwrap();
10063
10064    // scenario: surrounding text matches surroundings of newest cursor, inserted at the middle
10065    // (expects the same as if it was inserted at the end)
10066    let completion_text = "foo_and_bar";
10067    let initial_state = indoc! {"
10068        1. ooˇanb
10069        2. zooˇanb
10070        3. ooˇanbz
10071        4. zooˇanbz
10072
10073        ooˇanb
10074    "};
10075    let completion_marked_buffer = indoc! {"
10076        1. ooanb
10077        2. zooanb
10078        3. ooanbz
10079        4. zooanbz
10080
10081        <oo|anb>
10082    "};
10083    let expected = indoc! {"
10084        1. foo_and_barˇ
10085        2. zfoo_and_barˇ
10086        3. foo_and_barˇz
10087        4. zfoo_and_barˇz
10088
10089        foo_and_barˇ
10090    "};
10091    cx.set_state(initial_state);
10092    cx.update_editor(|editor, window, cx| {
10093        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10094    });
10095    handle_completion_request_with_insert_and_replace(
10096        &mut cx,
10097        completion_marked_buffer,
10098        vec![completion_text],
10099        Arc::new(AtomicUsize::new(0)),
10100    )
10101    .await;
10102    cx.condition(|editor, _| editor.context_menu_visible())
10103        .await;
10104    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10105        editor
10106            .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10107            .unwrap()
10108    });
10109    cx.assert_editor_state(expected);
10110    handle_resolve_completion_request(&mut cx, None).await;
10111    apply_additional_edits.await.unwrap();
10112}
10113
10114// This used to crash
10115#[gpui::test]
10116async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppContext) {
10117    init_test(cx, |_| {});
10118
10119    let buffer_text = indoc! {"
10120        fn main() {
10121            10.satu;
10122
10123            //
10124            // separate cursors so they open in different excerpts (manually reproducible)
10125            //
10126
10127            10.satu20;
10128        }
10129    "};
10130    let multibuffer_text_with_selections = indoc! {"
10131        fn main() {
10132            10.satuˇ;
10133
10134            //
10135
10136            //
10137
10138            10.satuˇ20;
10139        }
10140    "};
10141    let expected_multibuffer = indoc! {"
10142        fn main() {
10143            10.saturating_sub()ˇ;
10144
10145            //
10146
10147            //
10148
10149            10.saturating_sub()ˇ;
10150        }
10151    "};
10152
10153    let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
10154    let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
10155
10156    let fs = FakeFs::new(cx.executor());
10157    fs.insert_tree(
10158        path!("/a"),
10159        json!({
10160            "main.rs": buffer_text,
10161        }),
10162    )
10163    .await;
10164
10165    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10166    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10167    language_registry.add(rust_lang());
10168    let mut fake_servers = language_registry.register_fake_lsp(
10169        "Rust",
10170        FakeLspAdapter {
10171            capabilities: lsp::ServerCapabilities {
10172                completion_provider: Some(lsp::CompletionOptions {
10173                    resolve_provider: None,
10174                    ..lsp::CompletionOptions::default()
10175                }),
10176                ..lsp::ServerCapabilities::default()
10177            },
10178            ..FakeLspAdapter::default()
10179        },
10180    );
10181    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10182    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10183    let buffer = project
10184        .update(cx, |project, cx| {
10185            project.open_local_buffer(path!("/a/main.rs"), cx)
10186        })
10187        .await
10188        .unwrap();
10189
10190    let multi_buffer = cx.new(|cx| {
10191        let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
10192        multi_buffer.push_excerpts(
10193            buffer.clone(),
10194            [ExcerptRange::new(0..first_excerpt_end)],
10195            cx,
10196        );
10197        multi_buffer.push_excerpts(
10198            buffer.clone(),
10199            [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
10200            cx,
10201        );
10202        multi_buffer
10203    });
10204
10205    let editor = workspace
10206        .update(cx, |_, window, cx| {
10207            cx.new(|cx| {
10208                Editor::new(
10209                    EditorMode::Full {
10210                        scale_ui_elements_with_buffer_font_size: false,
10211                        show_active_line_background: false,
10212                    },
10213                    multi_buffer.clone(),
10214                    Some(project.clone()),
10215                    window,
10216                    cx,
10217                )
10218            })
10219        })
10220        .unwrap();
10221
10222    let pane = workspace
10223        .update(cx, |workspace, _, _| workspace.active_pane().clone())
10224        .unwrap();
10225    pane.update_in(cx, |pane, window, cx| {
10226        pane.add_item(Box::new(editor.clone()), true, true, None, window, cx);
10227    });
10228
10229    let fake_server = fake_servers.next().await.unwrap();
10230
10231    editor.update_in(cx, |editor, window, cx| {
10232        editor.change_selections(None, window, cx, |s| {
10233            s.select_ranges([
10234                Point::new(1, 11)..Point::new(1, 11),
10235                Point::new(7, 11)..Point::new(7, 11),
10236            ])
10237        });
10238
10239        assert_text_with_selections(editor, multibuffer_text_with_selections, cx);
10240    });
10241
10242    editor.update_in(cx, |editor, window, cx| {
10243        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10244    });
10245
10246    fake_server
10247        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
10248            let completion_item = lsp::CompletionItem {
10249                label: "saturating_sub()".into(),
10250                text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
10251                    lsp::InsertReplaceEdit {
10252                        new_text: "saturating_sub()".to_owned(),
10253                        insert: lsp::Range::new(
10254                            lsp::Position::new(7, 7),
10255                            lsp::Position::new(7, 11),
10256                        ),
10257                        replace: lsp::Range::new(
10258                            lsp::Position::new(7, 7),
10259                            lsp::Position::new(7, 13),
10260                        ),
10261                    },
10262                )),
10263                ..lsp::CompletionItem::default()
10264            };
10265
10266            Ok(Some(lsp::CompletionResponse::Array(vec![completion_item])))
10267        })
10268        .next()
10269        .await
10270        .unwrap();
10271
10272    cx.condition(&editor, |editor, _| editor.context_menu_visible())
10273        .await;
10274
10275    editor
10276        .update_in(cx, |editor, window, cx| {
10277            editor
10278                .confirm_completion_replace(&ConfirmCompletionReplace, window, cx)
10279                .unwrap()
10280        })
10281        .await
10282        .unwrap();
10283
10284    editor.update(cx, |editor, cx| {
10285        assert_text_with_selections(editor, expected_multibuffer, cx);
10286    })
10287}
10288
10289#[gpui::test]
10290async fn test_completion(cx: &mut TestAppContext) {
10291    init_test(cx, |_| {});
10292
10293    let mut cx = EditorLspTestContext::new_rust(
10294        lsp::ServerCapabilities {
10295            completion_provider: Some(lsp::CompletionOptions {
10296                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10297                resolve_provider: Some(true),
10298                ..Default::default()
10299            }),
10300            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10301            ..Default::default()
10302        },
10303        cx,
10304    )
10305    .await;
10306    let counter = Arc::new(AtomicUsize::new(0));
10307
10308    cx.set_state(indoc! {"
10309        oneˇ
10310        two
10311        three
10312    "});
10313    cx.simulate_keystroke(".");
10314    handle_completion_request(
10315        &mut cx,
10316        indoc! {"
10317            one.|<>
10318            two
10319            three
10320        "},
10321        vec!["first_completion", "second_completion"],
10322        counter.clone(),
10323    )
10324    .await;
10325    cx.condition(|editor, _| editor.context_menu_visible())
10326        .await;
10327    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
10328
10329    let _handler = handle_signature_help_request(
10330        &mut cx,
10331        lsp::SignatureHelp {
10332            signatures: vec![lsp::SignatureInformation {
10333                label: "test signature".to_string(),
10334                documentation: None,
10335                parameters: Some(vec![lsp::ParameterInformation {
10336                    label: lsp::ParameterLabel::Simple("foo: u8".to_string()),
10337                    documentation: None,
10338                }]),
10339                active_parameter: None,
10340            }],
10341            active_signature: None,
10342            active_parameter: None,
10343        },
10344    );
10345    cx.update_editor(|editor, window, cx| {
10346        assert!(
10347            !editor.signature_help_state.is_shown(),
10348            "No signature help was called for"
10349        );
10350        editor.show_signature_help(&ShowSignatureHelp, window, cx);
10351    });
10352    cx.run_until_parked();
10353    cx.update_editor(|editor, _, _| {
10354        assert!(
10355            !editor.signature_help_state.is_shown(),
10356            "No signature help should be shown when completions menu is open"
10357        );
10358    });
10359
10360    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10361        editor.context_menu_next(&Default::default(), window, cx);
10362        editor
10363            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10364            .unwrap()
10365    });
10366    cx.assert_editor_state(indoc! {"
10367        one.second_completionˇ
10368        two
10369        three
10370    "});
10371
10372    handle_resolve_completion_request(
10373        &mut cx,
10374        Some(vec![
10375            (
10376                //This overlaps with the primary completion edit which is
10377                //misbehavior from the LSP spec, test that we filter it out
10378                indoc! {"
10379                    one.second_ˇcompletion
10380                    two
10381                    threeˇ
10382                "},
10383                "overlapping additional edit",
10384            ),
10385            (
10386                indoc! {"
10387                    one.second_completion
10388                    two
10389                    threeˇ
10390                "},
10391                "\nadditional edit",
10392            ),
10393        ]),
10394    )
10395    .await;
10396    apply_additional_edits.await.unwrap();
10397    cx.assert_editor_state(indoc! {"
10398        one.second_completionˇ
10399        two
10400        three
10401        additional edit
10402    "});
10403
10404    cx.set_state(indoc! {"
10405        one.second_completion
10406        twoˇ
10407        threeˇ
10408        additional edit
10409    "});
10410    cx.simulate_keystroke(" ");
10411    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10412    cx.simulate_keystroke("s");
10413    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10414
10415    cx.assert_editor_state(indoc! {"
10416        one.second_completion
10417        two sˇ
10418        three sˇ
10419        additional edit
10420    "});
10421    handle_completion_request(
10422        &mut cx,
10423        indoc! {"
10424            one.second_completion
10425            two s
10426            three <s|>
10427            additional edit
10428        "},
10429        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10430        counter.clone(),
10431    )
10432    .await;
10433    cx.condition(|editor, _| editor.context_menu_visible())
10434        .await;
10435    assert_eq!(counter.load(atomic::Ordering::Acquire), 2);
10436
10437    cx.simulate_keystroke("i");
10438
10439    handle_completion_request(
10440        &mut cx,
10441        indoc! {"
10442            one.second_completion
10443            two si
10444            three <si|>
10445            additional edit
10446        "},
10447        vec!["fourth_completion", "fifth_completion", "sixth_completion"],
10448        counter.clone(),
10449    )
10450    .await;
10451    cx.condition(|editor, _| editor.context_menu_visible())
10452        .await;
10453    assert_eq!(counter.load(atomic::Ordering::Acquire), 3);
10454
10455    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10456        editor
10457            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10458            .unwrap()
10459    });
10460    cx.assert_editor_state(indoc! {"
10461        one.second_completion
10462        two sixth_completionˇ
10463        three sixth_completionˇ
10464        additional edit
10465    "});
10466
10467    apply_additional_edits.await.unwrap();
10468
10469    update_test_language_settings(&mut cx, |settings| {
10470        settings.defaults.show_completions_on_input = Some(false);
10471    });
10472    cx.set_state("editorˇ");
10473    cx.simulate_keystroke(".");
10474    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10475    cx.simulate_keystrokes("c l o");
10476    cx.assert_editor_state("editor.cloˇ");
10477    assert!(cx.editor(|e, _, _| e.context_menu.borrow_mut().is_none()));
10478    cx.update_editor(|editor, window, cx| {
10479        editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
10480    });
10481    handle_completion_request(
10482        &mut cx,
10483        "editor.<clo|>",
10484        vec!["close", "clobber"],
10485        counter.clone(),
10486    )
10487    .await;
10488    cx.condition(|editor, _| editor.context_menu_visible())
10489        .await;
10490    assert_eq!(counter.load(atomic::Ordering::Acquire), 4);
10491
10492    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
10493        editor
10494            .confirm_completion(&ConfirmCompletion::default(), window, cx)
10495            .unwrap()
10496    });
10497    cx.assert_editor_state("editor.closeˇ");
10498    handle_resolve_completion_request(&mut cx, None).await;
10499    apply_additional_edits.await.unwrap();
10500}
10501
10502#[gpui::test]
10503async fn test_word_completion(cx: &mut TestAppContext) {
10504    let lsp_fetch_timeout_ms = 10;
10505    init_test(cx, |language_settings| {
10506        language_settings.defaults.completions = Some(CompletionSettings {
10507            words: WordsCompletionMode::Fallback,
10508            lsp: true,
10509            lsp_fetch_timeout_ms: 10,
10510            lsp_insert_mode: LspInsertMode::Insert,
10511        });
10512    });
10513
10514    let mut cx = EditorLspTestContext::new_rust(
10515        lsp::ServerCapabilities {
10516            completion_provider: Some(lsp::CompletionOptions {
10517                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10518                ..lsp::CompletionOptions::default()
10519            }),
10520            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10521            ..lsp::ServerCapabilities::default()
10522        },
10523        cx,
10524    )
10525    .await;
10526
10527    let throttle_completions = Arc::new(AtomicBool::new(false));
10528
10529    let lsp_throttle_completions = throttle_completions.clone();
10530    let _completion_requests_handler =
10531        cx.lsp
10532            .server
10533            .on_request::<lsp::request::Completion, _, _>(move |_, cx| {
10534                let lsp_throttle_completions = lsp_throttle_completions.clone();
10535                let cx = cx.clone();
10536                async move {
10537                    if lsp_throttle_completions.load(atomic::Ordering::Acquire) {
10538                        cx.background_executor()
10539                            .timer(Duration::from_millis(lsp_fetch_timeout_ms * 10))
10540                            .await;
10541                    }
10542                    Ok(Some(lsp::CompletionResponse::Array(vec![
10543                        lsp::CompletionItem {
10544                            label: "first".into(),
10545                            ..lsp::CompletionItem::default()
10546                        },
10547                        lsp::CompletionItem {
10548                            label: "last".into(),
10549                            ..lsp::CompletionItem::default()
10550                        },
10551                    ])))
10552                }
10553            });
10554
10555    cx.set_state(indoc! {"
10556        oneˇ
10557        two
10558        three
10559    "});
10560    cx.simulate_keystroke(".");
10561    cx.executor().run_until_parked();
10562    cx.condition(|editor, _| editor.context_menu_visible())
10563        .await;
10564    cx.update_editor(|editor, window, cx| {
10565        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10566        {
10567            assert_eq!(
10568                completion_menu_entries(&menu),
10569                &["first", "last"],
10570                "When LSP server is fast to reply, no fallback word completions are used"
10571            );
10572        } else {
10573            panic!("expected completion menu to be open");
10574        }
10575        editor.cancel(&Cancel, window, cx);
10576    });
10577    cx.executor().run_until_parked();
10578    cx.condition(|editor, _| !editor.context_menu_visible())
10579        .await;
10580
10581    throttle_completions.store(true, atomic::Ordering::Release);
10582    cx.simulate_keystroke(".");
10583    cx.executor()
10584        .advance_clock(Duration::from_millis(lsp_fetch_timeout_ms * 2));
10585    cx.executor().run_until_parked();
10586    cx.condition(|editor, _| editor.context_menu_visible())
10587        .await;
10588    cx.update_editor(|editor, _, _| {
10589        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10590        {
10591            assert_eq!(completion_menu_entries(&menu), &["one", "three", "two"],
10592                "When LSP server is slow, document words can be shown instead, if configured accordingly");
10593        } else {
10594            panic!("expected completion menu to be open");
10595        }
10596    });
10597}
10598
10599#[gpui::test]
10600async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext) {
10601    init_test(cx, |language_settings| {
10602        language_settings.defaults.completions = Some(CompletionSettings {
10603            words: WordsCompletionMode::Enabled,
10604            lsp: true,
10605            lsp_fetch_timeout_ms: 0,
10606            lsp_insert_mode: LspInsertMode::Insert,
10607        });
10608    });
10609
10610    let mut cx = EditorLspTestContext::new_rust(
10611        lsp::ServerCapabilities {
10612            completion_provider: Some(lsp::CompletionOptions {
10613                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10614                ..lsp::CompletionOptions::default()
10615            }),
10616            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10617            ..lsp::ServerCapabilities::default()
10618        },
10619        cx,
10620    )
10621    .await;
10622
10623    let _completion_requests_handler =
10624        cx.lsp
10625            .server
10626            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10627                Ok(Some(lsp::CompletionResponse::Array(vec![
10628                    lsp::CompletionItem {
10629                        label: "first".into(),
10630                        ..lsp::CompletionItem::default()
10631                    },
10632                    lsp::CompletionItem {
10633                        label: "last".into(),
10634                        ..lsp::CompletionItem::default()
10635                    },
10636                ])))
10637            });
10638
10639    cx.set_state(indoc! {"ˇ
10640        first
10641        last
10642        second
10643    "});
10644    cx.simulate_keystroke(".");
10645    cx.executor().run_until_parked();
10646    cx.condition(|editor, _| editor.context_menu_visible())
10647        .await;
10648    cx.update_editor(|editor, _, _| {
10649        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10650        {
10651            assert_eq!(
10652                completion_menu_entries(&menu),
10653                &["first", "last", "second"],
10654                "Word completions that has the same edit as the any of the LSP ones, should not be proposed"
10655            );
10656        } else {
10657            panic!("expected completion menu to be open");
10658        }
10659    });
10660}
10661
10662#[gpui::test]
10663async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
10664    init_test(cx, |language_settings| {
10665        language_settings.defaults.completions = Some(CompletionSettings {
10666            words: WordsCompletionMode::Disabled,
10667            lsp: true,
10668            lsp_fetch_timeout_ms: 0,
10669            lsp_insert_mode: LspInsertMode::Insert,
10670        });
10671    });
10672
10673    let mut cx = EditorLspTestContext::new_rust(
10674        lsp::ServerCapabilities {
10675            completion_provider: Some(lsp::CompletionOptions {
10676                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10677                ..lsp::CompletionOptions::default()
10678            }),
10679            signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10680            ..lsp::ServerCapabilities::default()
10681        },
10682        cx,
10683    )
10684    .await;
10685
10686    let _completion_requests_handler =
10687        cx.lsp
10688            .server
10689            .on_request::<lsp::request::Completion, _, _>(move |_, _| async move {
10690                panic!("LSP completions should not be queried when dealing with word completions")
10691            });
10692
10693    cx.set_state(indoc! {"ˇ
10694        first
10695        last
10696        second
10697    "});
10698    cx.update_editor(|editor, window, cx| {
10699        editor.show_word_completions(&ShowWordCompletions, window, cx);
10700    });
10701    cx.executor().run_until_parked();
10702    cx.condition(|editor, _| editor.context_menu_visible())
10703        .await;
10704    cx.update_editor(|editor, _, _| {
10705        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10706        {
10707            assert_eq!(
10708                completion_menu_entries(&menu),
10709                &["first", "last", "second"],
10710                "`ShowWordCompletions` action should show word completions"
10711            );
10712        } else {
10713            panic!("expected completion menu to be open");
10714        }
10715    });
10716
10717    cx.simulate_keystroke("l");
10718    cx.executor().run_until_parked();
10719    cx.condition(|editor, _| editor.context_menu_visible())
10720        .await;
10721    cx.update_editor(|editor, _, _| {
10722        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10723        {
10724            assert_eq!(
10725                completion_menu_entries(&menu),
10726                &["last"],
10727                "After showing word completions, further editing should filter them and not query the LSP"
10728            );
10729        } else {
10730            panic!("expected completion menu to be open");
10731        }
10732    });
10733}
10734
10735#[gpui::test]
10736async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
10737    init_test(cx, |language_settings| {
10738        language_settings.defaults.completions = Some(CompletionSettings {
10739            words: WordsCompletionMode::Fallback,
10740            lsp: false,
10741            lsp_fetch_timeout_ms: 0,
10742            lsp_insert_mode: LspInsertMode::Insert,
10743        });
10744    });
10745
10746    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
10747
10748    cx.set_state(indoc! {"ˇ
10749        0_usize
10750        let
10751        33
10752        4.5f32
10753    "});
10754    cx.update_editor(|editor, window, cx| {
10755        editor.show_completions(&ShowCompletions::default(), window, cx);
10756    });
10757    cx.executor().run_until_parked();
10758    cx.condition(|editor, _| editor.context_menu_visible())
10759        .await;
10760    cx.update_editor(|editor, window, cx| {
10761        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10762        {
10763            assert_eq!(
10764                completion_menu_entries(&menu),
10765                &["let"],
10766                "With no digits in the completion query, no digits should be in the word completions"
10767            );
10768        } else {
10769            panic!("expected completion menu to be open");
10770        }
10771        editor.cancel(&Cancel, window, cx);
10772    });
10773
10774    cx.set_state(indoc! {"10775        0_usize
10776        let
10777        3
10778        33.35f32
10779    "});
10780    cx.update_editor(|editor, window, cx| {
10781        editor.show_completions(&ShowCompletions::default(), window, cx);
10782    });
10783    cx.executor().run_until_parked();
10784    cx.condition(|editor, _| editor.context_menu_visible())
10785        .await;
10786    cx.update_editor(|editor, _, _| {
10787        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10788        {
10789            assert_eq!(completion_menu_entries(&menu), &["33", "35f32"], "The digit is in the completion query, \
10790                return matching words with digits (`33`, `35f32`) but exclude query duplicates (`3`)");
10791        } else {
10792            panic!("expected completion menu to be open");
10793        }
10794    });
10795}
10796
10797fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
10798    let position = || lsp::Position {
10799        line: params.text_document_position.position.line,
10800        character: params.text_document_position.position.character,
10801    };
10802    Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
10803        range: lsp::Range {
10804            start: position(),
10805            end: position(),
10806        },
10807        new_text: text.to_string(),
10808    }))
10809}
10810
10811#[gpui::test]
10812async fn test_multiline_completion(cx: &mut TestAppContext) {
10813    init_test(cx, |_| {});
10814
10815    let fs = FakeFs::new(cx.executor());
10816    fs.insert_tree(
10817        path!("/a"),
10818        json!({
10819            "main.ts": "a",
10820        }),
10821    )
10822    .await;
10823
10824    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
10825    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
10826    let typescript_language = Arc::new(Language::new(
10827        LanguageConfig {
10828            name: "TypeScript".into(),
10829            matcher: LanguageMatcher {
10830                path_suffixes: vec!["ts".to_string()],
10831                ..LanguageMatcher::default()
10832            },
10833            line_comments: vec!["// ".into()],
10834            ..LanguageConfig::default()
10835        },
10836        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
10837    ));
10838    language_registry.add(typescript_language.clone());
10839    let mut fake_servers = language_registry.register_fake_lsp(
10840        "TypeScript",
10841        FakeLspAdapter {
10842            capabilities: lsp::ServerCapabilities {
10843                completion_provider: Some(lsp::CompletionOptions {
10844                    trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
10845                    ..lsp::CompletionOptions::default()
10846                }),
10847                signature_help_provider: Some(lsp::SignatureHelpOptions::default()),
10848                ..lsp::ServerCapabilities::default()
10849            },
10850            // Emulate vtsls label generation
10851            label_for_completion: Some(Box::new(|item, _| {
10852                let text = if let Some(description) = item
10853                    .label_details
10854                    .as_ref()
10855                    .and_then(|label_details| label_details.description.as_ref())
10856                {
10857                    format!("{} {}", item.label, description)
10858                } else if let Some(detail) = &item.detail {
10859                    format!("{} {}", item.label, detail)
10860                } else {
10861                    item.label.clone()
10862                };
10863                let len = text.len();
10864                Some(language::CodeLabel {
10865                    text,
10866                    runs: Vec::new(),
10867                    filter_range: 0..len,
10868                })
10869            })),
10870            ..FakeLspAdapter::default()
10871        },
10872    );
10873    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
10874    let cx = &mut VisualTestContext::from_window(*workspace, cx);
10875    let worktree_id = workspace
10876        .update(cx, |workspace, _window, cx| {
10877            workspace.project().update(cx, |project, cx| {
10878                project.worktrees(cx).next().unwrap().read(cx).id()
10879            })
10880        })
10881        .unwrap();
10882    let _buffer = project
10883        .update(cx, |project, cx| {
10884            project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx)
10885        })
10886        .await
10887        .unwrap();
10888    let editor = workspace
10889        .update(cx, |workspace, window, cx| {
10890            workspace.open_path((worktree_id, "main.ts"), None, true, window, cx)
10891        })
10892        .unwrap()
10893        .await
10894        .unwrap()
10895        .downcast::<Editor>()
10896        .unwrap();
10897    let fake_server = fake_servers.next().await.unwrap();
10898
10899    let multiline_label = "StickyHeaderExcerpt {\n            excerpt,\n            next_excerpt_controls_present,\n            next_buffer_row,\n        }: StickyHeaderExcerpt<'_>,";
10900    let multiline_label_2 = "a\nb\nc\n";
10901    let multiline_detail = "[]struct {\n\tSignerId\tstruct {\n\t\tIssuer\t\t\tstring\t`json:\"issuer\"`\n\t\tSubjectSerialNumber\"`\n}}";
10902    let multiline_description = "d\ne\nf\n";
10903    let multiline_detail_2 = "g\nh\ni\n";
10904
10905    let mut completion_handle = fake_server.set_request_handler::<lsp::request::Completion, _, _>(
10906        move |params, _| async move {
10907            Ok(Some(lsp::CompletionResponse::Array(vec![
10908                lsp::CompletionItem {
10909                    label: multiline_label.to_string(),
10910                    text_edit: gen_text_edit(&params, "new_text_1"),
10911                    ..lsp::CompletionItem::default()
10912                },
10913                lsp::CompletionItem {
10914                    label: "single line label 1".to_string(),
10915                    detail: Some(multiline_detail.to_string()),
10916                    text_edit: gen_text_edit(&params, "new_text_2"),
10917                    ..lsp::CompletionItem::default()
10918                },
10919                lsp::CompletionItem {
10920                    label: "single line label 2".to_string(),
10921                    label_details: Some(lsp::CompletionItemLabelDetails {
10922                        description: Some(multiline_description.to_string()),
10923                        detail: None,
10924                    }),
10925                    text_edit: gen_text_edit(&params, "new_text_2"),
10926                    ..lsp::CompletionItem::default()
10927                },
10928                lsp::CompletionItem {
10929                    label: multiline_label_2.to_string(),
10930                    detail: Some(multiline_detail_2.to_string()),
10931                    text_edit: gen_text_edit(&params, "new_text_3"),
10932                    ..lsp::CompletionItem::default()
10933                },
10934                lsp::CompletionItem {
10935                    label: "Label with many     spaces and \t but without newlines".to_string(),
10936                    detail: Some(
10937                        "Details with many     spaces and \t but without newlines".to_string(),
10938                    ),
10939                    text_edit: gen_text_edit(&params, "new_text_4"),
10940                    ..lsp::CompletionItem::default()
10941                },
10942            ])))
10943        },
10944    );
10945
10946    editor.update_in(cx, |editor, window, cx| {
10947        cx.focus_self(window);
10948        editor.move_to_end(&MoveToEnd, window, cx);
10949        editor.handle_input(".", window, cx);
10950    });
10951    cx.run_until_parked();
10952    completion_handle.next().await.unwrap();
10953
10954    editor.update(cx, |editor, _| {
10955        assert!(editor.context_menu_visible());
10956        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
10957        {
10958            let completion_labels = menu
10959                .completions
10960                .borrow()
10961                .iter()
10962                .map(|c| c.label.text.clone())
10963                .collect::<Vec<_>>();
10964            assert_eq!(
10965                completion_labels,
10966                &[
10967                    "StickyHeaderExcerpt { excerpt, next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>,",
10968                    "single line label 1 []struct { SignerId struct { Issuer string `json:\"issuer\"` SubjectSerialNumber\"` }}",
10969                    "single line label 2 d e f ",
10970                    "a b c g h i ",
10971                    "Label with many     spaces and \t but without newlines Details with many     spaces and \t but without newlines",
10972                ],
10973                "Completion items should have their labels without newlines, also replacing excessive whitespaces. Completion items without newlines should not be altered.",
10974            );
10975
10976            for completion in menu
10977                .completions
10978                .borrow()
10979                .iter() {
10980                    assert_eq!(
10981                        completion.label.filter_range,
10982                        0..completion.label.text.len(),
10983                        "Adjusted completion items should still keep their filter ranges for the entire label. Item: {completion:?}"
10984                    );
10985                }
10986        } else {
10987            panic!("expected completion menu to be open");
10988        }
10989    });
10990}
10991
10992#[gpui::test]
10993async fn test_completion_page_up_down_keys(cx: &mut TestAppContext) {
10994    init_test(cx, |_| {});
10995    let mut cx = EditorLspTestContext::new_rust(
10996        lsp::ServerCapabilities {
10997            completion_provider: Some(lsp::CompletionOptions {
10998                trigger_characters: Some(vec![".".to_string()]),
10999                ..Default::default()
11000            }),
11001            ..Default::default()
11002        },
11003        cx,
11004    )
11005    .await;
11006    cx.lsp
11007        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11008            Ok(Some(lsp::CompletionResponse::Array(vec![
11009                lsp::CompletionItem {
11010                    label: "first".into(),
11011                    ..Default::default()
11012                },
11013                lsp::CompletionItem {
11014                    label: "last".into(),
11015                    ..Default::default()
11016                },
11017            ])))
11018        });
11019    cx.set_state("variableˇ");
11020    cx.simulate_keystroke(".");
11021    cx.executor().run_until_parked();
11022
11023    cx.update_editor(|editor, _, _| {
11024        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11025        {
11026            assert_eq!(completion_menu_entries(&menu), &["first", "last"]);
11027        } else {
11028            panic!("expected completion menu to be open");
11029        }
11030    });
11031
11032    cx.update_editor(|editor, window, cx| {
11033        editor.move_page_down(&MovePageDown::default(), window, cx);
11034        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11035        {
11036            assert!(
11037                menu.selected_item == 1,
11038                "expected PageDown to select the last item from the context menu"
11039            );
11040        } else {
11041            panic!("expected completion menu to stay open after PageDown");
11042        }
11043    });
11044
11045    cx.update_editor(|editor, window, cx| {
11046        editor.move_page_up(&MovePageUp::default(), window, cx);
11047        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11048        {
11049            assert!(
11050                menu.selected_item == 0,
11051                "expected PageUp to select the first item from the context menu"
11052            );
11053        } else {
11054            panic!("expected completion menu to stay open after PageUp");
11055        }
11056    });
11057}
11058
11059#[gpui::test]
11060async fn test_completion_sort(cx: &mut TestAppContext) {
11061    init_test(cx, |_| {});
11062    let mut cx = EditorLspTestContext::new_rust(
11063        lsp::ServerCapabilities {
11064            completion_provider: Some(lsp::CompletionOptions {
11065                trigger_characters: Some(vec![".".to_string()]),
11066                ..Default::default()
11067            }),
11068            ..Default::default()
11069        },
11070        cx,
11071    )
11072    .await;
11073    cx.lsp
11074        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11075            Ok(Some(lsp::CompletionResponse::Array(vec![
11076                lsp::CompletionItem {
11077                    label: "Range".into(),
11078                    sort_text: Some("a".into()),
11079                    ..Default::default()
11080                },
11081                lsp::CompletionItem {
11082                    label: "r".into(),
11083                    sort_text: Some("b".into()),
11084                    ..Default::default()
11085                },
11086                lsp::CompletionItem {
11087                    label: "ret".into(),
11088                    sort_text: Some("c".into()),
11089                    ..Default::default()
11090                },
11091                lsp::CompletionItem {
11092                    label: "return".into(),
11093                    sort_text: Some("d".into()),
11094                    ..Default::default()
11095                },
11096                lsp::CompletionItem {
11097                    label: "slice".into(),
11098                    sort_text: Some("d".into()),
11099                    ..Default::default()
11100                },
11101            ])))
11102        });
11103    cx.set_state("");
11104    cx.executor().run_until_parked();
11105    cx.update_editor(|editor, window, cx| {
11106        editor.show_completions(
11107            &ShowCompletions {
11108                trigger: Some("r".into()),
11109            },
11110            window,
11111            cx,
11112        );
11113    });
11114    cx.executor().run_until_parked();
11115
11116    cx.update_editor(|editor, _, _| {
11117        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
11118        {
11119            assert_eq!(
11120                completion_menu_entries(&menu),
11121                &["r", "ret", "Range", "return"]
11122            );
11123        } else {
11124            panic!("expected completion menu to be open");
11125        }
11126    });
11127}
11128
11129#[gpui::test]
11130async fn test_as_is_completions(cx: &mut TestAppContext) {
11131    init_test(cx, |_| {});
11132    let mut cx = EditorLspTestContext::new_rust(
11133        lsp::ServerCapabilities {
11134            completion_provider: Some(lsp::CompletionOptions {
11135                ..Default::default()
11136            }),
11137            ..Default::default()
11138        },
11139        cx,
11140    )
11141    .await;
11142    cx.lsp
11143        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
11144            Ok(Some(lsp::CompletionResponse::Array(vec![
11145                lsp::CompletionItem {
11146                    label: "unsafe".into(),
11147                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11148                        range: lsp::Range {
11149                            start: lsp::Position {
11150                                line: 1,
11151                                character: 2,
11152                            },
11153                            end: lsp::Position {
11154                                line: 1,
11155                                character: 3,
11156                            },
11157                        },
11158                        new_text: "unsafe".to_string(),
11159                    })),
11160                    insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
11161                    ..Default::default()
11162                },
11163            ])))
11164        });
11165    cx.set_state("fn a() {}\n");
11166    cx.executor().run_until_parked();
11167    cx.update_editor(|editor, window, cx| {
11168        editor.show_completions(
11169            &ShowCompletions {
11170                trigger: Some("\n".into()),
11171            },
11172            window,
11173            cx,
11174        );
11175    });
11176    cx.executor().run_until_parked();
11177
11178    cx.update_editor(|editor, window, cx| {
11179        editor.confirm_completion(&Default::default(), window, cx)
11180    });
11181    cx.executor().run_until_parked();
11182    cx.assert_editor_state("fn a() {}\n  unsafeˇ");
11183}
11184
11185#[gpui::test]
11186async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
11187    init_test(cx, |_| {});
11188
11189    let mut cx = EditorLspTestContext::new_rust(
11190        lsp::ServerCapabilities {
11191            completion_provider: Some(lsp::CompletionOptions {
11192                trigger_characters: Some(vec![".".to_string()]),
11193                resolve_provider: Some(true),
11194                ..Default::default()
11195            }),
11196            ..Default::default()
11197        },
11198        cx,
11199    )
11200    .await;
11201
11202    cx.set_state("fn main() { let a = 2ˇ; }");
11203    cx.simulate_keystroke(".");
11204    let completion_item = lsp::CompletionItem {
11205        label: "Some".into(),
11206        kind: Some(lsp::CompletionItemKind::SNIPPET),
11207        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
11208        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
11209            kind: lsp::MarkupKind::Markdown,
11210            value: "```rust\nSome(2)\n```".to_string(),
11211        })),
11212        deprecated: Some(false),
11213        sort_text: Some("Some".to_string()),
11214        filter_text: Some("Some".to_string()),
11215        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
11216        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
11217            range: lsp::Range {
11218                start: lsp::Position {
11219                    line: 0,
11220                    character: 22,
11221                },
11222                end: lsp::Position {
11223                    line: 0,
11224                    character: 22,
11225                },
11226            },
11227            new_text: "Some(2)".to_string(),
11228        })),
11229        additional_text_edits: Some(vec![lsp::TextEdit {
11230            range: lsp::Range {
11231                start: lsp::Position {
11232                    line: 0,
11233                    character: 20,
11234                },
11235                end: lsp::Position {
11236                    line: 0,
11237                    character: 22,
11238                },
11239            },
11240            new_text: "".to_string(),
11241        }]),
11242        ..Default::default()
11243    };
11244
11245    let closure_completion_item = completion_item.clone();
11246    let counter = Arc::new(AtomicUsize::new(0));
11247    let counter_clone = counter.clone();
11248    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
11249        let task_completion_item = closure_completion_item.clone();
11250        counter_clone.fetch_add(1, atomic::Ordering::Release);
11251        async move {
11252            Ok(Some(lsp::CompletionResponse::Array(vec![
11253                task_completion_item,
11254            ])))
11255        }
11256    });
11257
11258    cx.condition(|editor, _| editor.context_menu_visible())
11259        .await;
11260    cx.assert_editor_state("fn main() { let a = 2.ˇ; }");
11261    assert!(request.next().await.is_some());
11262    assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
11263
11264    cx.simulate_keystrokes("S o m");
11265    cx.condition(|editor, _| editor.context_menu_visible())
11266        .await;
11267    cx.assert_editor_state("fn main() { let a = 2.Somˇ; }");
11268    assert!(request.next().await.is_some());
11269    assert!(request.next().await.is_some());
11270    assert!(request.next().await.is_some());
11271    request.close();
11272    assert!(request.next().await.is_none());
11273    assert_eq!(
11274        counter.load(atomic::Ordering::Acquire),
11275        4,
11276        "With the completions menu open, only one LSP request should happen per input"
11277    );
11278}
11279
11280#[gpui::test]
11281async fn test_toggle_comment(cx: &mut TestAppContext) {
11282    init_test(cx, |_| {});
11283    let mut cx = EditorTestContext::new(cx).await;
11284    let language = Arc::new(Language::new(
11285        LanguageConfig {
11286            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11287            ..Default::default()
11288        },
11289        Some(tree_sitter_rust::LANGUAGE.into()),
11290    ));
11291    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11292
11293    // If multiple selections intersect a line, the line is only toggled once.
11294    cx.set_state(indoc! {"
11295        fn a() {
11296            «//b();
11297            ˇ»// «c();
11298            //ˇ»  d();
11299        }
11300    "});
11301
11302    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11303
11304    cx.assert_editor_state(indoc! {"
11305        fn a() {
11306            «b();
11307            c();
11308            ˇ» d();
11309        }
11310    "});
11311
11312    // The comment prefix is inserted at the same column for every line in a
11313    // selection.
11314    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11315
11316    cx.assert_editor_state(indoc! {"
11317        fn a() {
11318            // «b();
11319            // c();
11320            ˇ»//  d();
11321        }
11322    "});
11323
11324    // If a selection ends at the beginning of a line, that line is not toggled.
11325    cx.set_selections_state(indoc! {"
11326        fn a() {
11327            // b();
11328            «// c();
11329        ˇ»    //  d();
11330        }
11331    "});
11332
11333    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11334
11335    cx.assert_editor_state(indoc! {"
11336        fn a() {
11337            // b();
11338            «c();
11339        ˇ»    //  d();
11340        }
11341    "});
11342
11343    // If a selection span a single line and is empty, the line is toggled.
11344    cx.set_state(indoc! {"
11345        fn a() {
11346            a();
11347            b();
11348        ˇ
11349        }
11350    "});
11351
11352    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11353
11354    cx.assert_editor_state(indoc! {"
11355        fn a() {
11356            a();
11357            b();
11358        //•ˇ
11359        }
11360    "});
11361
11362    // If a selection span multiple lines, empty lines are not toggled.
11363    cx.set_state(indoc! {"
11364        fn a() {
11365            «a();
11366
11367            c();ˇ»
11368        }
11369    "});
11370
11371    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11372
11373    cx.assert_editor_state(indoc! {"
11374        fn a() {
11375            // «a();
11376
11377            // c();ˇ»
11378        }
11379    "});
11380
11381    // If a selection includes multiple comment prefixes, all lines are uncommented.
11382    cx.set_state(indoc! {"
11383        fn a() {
11384            «// a();
11385            /// b();
11386            //! c();ˇ»
11387        }
11388    "});
11389
11390    cx.update_editor(|e, window, cx| e.toggle_comments(&ToggleComments::default(), window, cx));
11391
11392    cx.assert_editor_state(indoc! {"
11393        fn a() {
11394            «a();
11395            b();
11396            c();ˇ»
11397        }
11398    "});
11399}
11400
11401#[gpui::test]
11402async fn test_toggle_comment_ignore_indent(cx: &mut TestAppContext) {
11403    init_test(cx, |_| {});
11404    let mut cx = EditorTestContext::new(cx).await;
11405    let language = Arc::new(Language::new(
11406        LanguageConfig {
11407            line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
11408            ..Default::default()
11409        },
11410        Some(tree_sitter_rust::LANGUAGE.into()),
11411    ));
11412    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
11413
11414    let toggle_comments = &ToggleComments {
11415        advance_downwards: false,
11416        ignore_indent: true,
11417    };
11418
11419    // If multiple selections intersect a line, the line is only toggled once.
11420    cx.set_state(indoc! {"
11421        fn a() {
11422        //    «b();
11423        //    c();
11424        //    ˇ» d();
11425        }
11426    "});
11427
11428    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11429
11430    cx.assert_editor_state(indoc! {"
11431        fn a() {
11432            «b();
11433            c();
11434            ˇ» d();
11435        }
11436    "});
11437
11438    // The comment prefix is inserted at the beginning of each line
11439    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11440
11441    cx.assert_editor_state(indoc! {"
11442        fn a() {
11443        //    «b();
11444        //    c();
11445        //    ˇ» d();
11446        }
11447    "});
11448
11449    // If a selection ends at the beginning of a line, that line is not toggled.
11450    cx.set_selections_state(indoc! {"
11451        fn a() {
11452        //    b();
11453        //    «c();
11454        ˇ»//     d();
11455        }
11456    "});
11457
11458    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11459
11460    cx.assert_editor_state(indoc! {"
11461        fn a() {
11462        //    b();
11463            «c();
11464        ˇ»//     d();
11465        }
11466    "});
11467
11468    // If a selection span a single line and is empty, the line is toggled.
11469    cx.set_state(indoc! {"
11470        fn a() {
11471            a();
11472            b();
11473        ˇ
11474        }
11475    "});
11476
11477    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11478
11479    cx.assert_editor_state(indoc! {"
11480        fn a() {
11481            a();
11482            b();
11483        //ˇ
11484        }
11485    "});
11486
11487    // If a selection span multiple lines, empty lines are not toggled.
11488    cx.set_state(indoc! {"
11489        fn a() {
11490            «a();
11491
11492            c();ˇ»
11493        }
11494    "});
11495
11496    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11497
11498    cx.assert_editor_state(indoc! {"
11499        fn a() {
11500        //    «a();
11501
11502        //    c();ˇ»
11503        }
11504    "});
11505
11506    // If a selection includes multiple comment prefixes, all lines are uncommented.
11507    cx.set_state(indoc! {"
11508        fn a() {
11509        //    «a();
11510        ///    b();
11511        //!    c();ˇ»
11512        }
11513    "});
11514
11515    cx.update_editor(|e, window, cx| e.toggle_comments(toggle_comments, window, cx));
11516
11517    cx.assert_editor_state(indoc! {"
11518        fn a() {
11519            «a();
11520            b();
11521            c();ˇ»
11522        }
11523    "});
11524}
11525
11526#[gpui::test]
11527async fn test_advance_downward_on_toggle_comment(cx: &mut TestAppContext) {
11528    init_test(cx, |_| {});
11529
11530    let language = Arc::new(Language::new(
11531        LanguageConfig {
11532            line_comments: vec!["// ".into()],
11533            ..Default::default()
11534        },
11535        Some(tree_sitter_rust::LANGUAGE.into()),
11536    ));
11537
11538    let mut cx = EditorTestContext::new(cx).await;
11539
11540    cx.language_registry().add(language.clone());
11541    cx.update_buffer(|buffer, cx| {
11542        buffer.set_language(Some(language), cx);
11543    });
11544
11545    let toggle_comments = &ToggleComments {
11546        advance_downwards: true,
11547        ignore_indent: false,
11548    };
11549
11550    // Single cursor on one line -> advance
11551    // Cursor moves horizontally 3 characters as well on non-blank line
11552    cx.set_state(indoc!(
11553        "fn a() {
11554             ˇdog();
11555             cat();
11556        }"
11557    ));
11558    cx.update_editor(|editor, window, cx| {
11559        editor.toggle_comments(toggle_comments, window, cx);
11560    });
11561    cx.assert_editor_state(indoc!(
11562        "fn a() {
11563             // dog();
11564             catˇ();
11565        }"
11566    ));
11567
11568    // Single selection on one line -> don't advance
11569    cx.set_state(indoc!(
11570        "fn a() {
11571             «dog()ˇ»;
11572             cat();
11573        }"
11574    ));
11575    cx.update_editor(|editor, window, cx| {
11576        editor.toggle_comments(toggle_comments, window, cx);
11577    });
11578    cx.assert_editor_state(indoc!(
11579        "fn a() {
11580             // «dog()ˇ»;
11581             cat();
11582        }"
11583    ));
11584
11585    // Multiple cursors on one line -> advance
11586    cx.set_state(indoc!(
11587        "fn a() {
11588             ˇdˇog();
11589             cat();
11590        }"
11591    ));
11592    cx.update_editor(|editor, window, cx| {
11593        editor.toggle_comments(toggle_comments, window, cx);
11594    });
11595    cx.assert_editor_state(indoc!(
11596        "fn a() {
11597             // dog();
11598             catˇ(ˇ);
11599        }"
11600    ));
11601
11602    // Multiple cursors on one line, with selection -> don't advance
11603    cx.set_state(indoc!(
11604        "fn a() {
11605             ˇdˇog«()ˇ»;
11606             cat();
11607        }"
11608    ));
11609    cx.update_editor(|editor, window, cx| {
11610        editor.toggle_comments(toggle_comments, window, cx);
11611    });
11612    cx.assert_editor_state(indoc!(
11613        "fn a() {
11614             // ˇdˇog«()ˇ»;
11615             cat();
11616        }"
11617    ));
11618
11619    // Single cursor on one line -> advance
11620    // Cursor moves to column 0 on blank line
11621    cx.set_state(indoc!(
11622        "fn a() {
11623             ˇdog();
11624
11625             cat();
11626        }"
11627    ));
11628    cx.update_editor(|editor, window, cx| {
11629        editor.toggle_comments(toggle_comments, window, cx);
11630    });
11631    cx.assert_editor_state(indoc!(
11632        "fn a() {
11633             // dog();
11634        ˇ
11635             cat();
11636        }"
11637    ));
11638
11639    // Single cursor on one line -> advance
11640    // Cursor starts and ends at column 0
11641    cx.set_state(indoc!(
11642        "fn a() {
11643         ˇ    dog();
11644             cat();
11645        }"
11646    ));
11647    cx.update_editor(|editor, window, cx| {
11648        editor.toggle_comments(toggle_comments, window, cx);
11649    });
11650    cx.assert_editor_state(indoc!(
11651        "fn a() {
11652             // dog();
11653         ˇ    cat();
11654        }"
11655    ));
11656}
11657
11658#[gpui::test]
11659async fn test_toggle_block_comment(cx: &mut TestAppContext) {
11660    init_test(cx, |_| {});
11661
11662    let mut cx = EditorTestContext::new(cx).await;
11663
11664    let html_language = Arc::new(
11665        Language::new(
11666            LanguageConfig {
11667                name: "HTML".into(),
11668                block_comment: Some(("<!-- ".into(), " -->".into())),
11669                ..Default::default()
11670            },
11671            Some(tree_sitter_html::LANGUAGE.into()),
11672        )
11673        .with_injection_query(
11674            r#"
11675            (script_element
11676                (raw_text) @injection.content
11677                (#set! injection.language "javascript"))
11678            "#,
11679        )
11680        .unwrap(),
11681    );
11682
11683    let javascript_language = Arc::new(Language::new(
11684        LanguageConfig {
11685            name: "JavaScript".into(),
11686            line_comments: vec!["// ".into()],
11687            ..Default::default()
11688        },
11689        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
11690    ));
11691
11692    cx.language_registry().add(html_language.clone());
11693    cx.language_registry().add(javascript_language.clone());
11694    cx.update_buffer(|buffer, cx| {
11695        buffer.set_language(Some(html_language), cx);
11696    });
11697
11698    // Toggle comments for empty selections
11699    cx.set_state(
11700        &r#"
11701            <p>A</p>ˇ
11702            <p>B</p>ˇ
11703            <p>C</p>ˇ
11704        "#
11705        .unindent(),
11706    );
11707    cx.update_editor(|editor, window, cx| {
11708        editor.toggle_comments(&ToggleComments::default(), window, cx)
11709    });
11710    cx.assert_editor_state(
11711        &r#"
11712            <!-- <p>A</p>ˇ -->
11713            <!-- <p>B</p>ˇ -->
11714            <!-- <p>C</p>ˇ -->
11715        "#
11716        .unindent(),
11717    );
11718    cx.update_editor(|editor, window, cx| {
11719        editor.toggle_comments(&ToggleComments::default(), window, cx)
11720    });
11721    cx.assert_editor_state(
11722        &r#"
11723            <p>A</p>ˇ
11724            <p>B</p>ˇ
11725            <p>C</p>ˇ
11726        "#
11727        .unindent(),
11728    );
11729
11730    // Toggle comments for mixture of empty and non-empty selections, where
11731    // multiple selections occupy a given line.
11732    cx.set_state(
11733        &r#"
11734            <p>A«</p>
11735            <p>ˇ»B</p>ˇ
11736            <p>C«</p>
11737            <p>ˇ»D</p>ˇ
11738        "#
11739        .unindent(),
11740    );
11741
11742    cx.update_editor(|editor, window, cx| {
11743        editor.toggle_comments(&ToggleComments::default(), window, cx)
11744    });
11745    cx.assert_editor_state(
11746        &r#"
11747            <!-- <p>A«</p>
11748            <p>ˇ»B</p>ˇ -->
11749            <!-- <p>C«</p>
11750            <p>ˇ»D</p>ˇ -->
11751        "#
11752        .unindent(),
11753    );
11754    cx.update_editor(|editor, window, cx| {
11755        editor.toggle_comments(&ToggleComments::default(), window, cx)
11756    });
11757    cx.assert_editor_state(
11758        &r#"
11759            <p>A«</p>
11760            <p>ˇ»B</p>ˇ
11761            <p>C«</p>
11762            <p>ˇ»D</p>ˇ
11763        "#
11764        .unindent(),
11765    );
11766
11767    // Toggle comments when different languages are active for different
11768    // selections.
11769    cx.set_state(
11770        &r#"
11771            ˇ<script>
11772                ˇvar x = new Y();
11773            ˇ</script>
11774        "#
11775        .unindent(),
11776    );
11777    cx.executor().run_until_parked();
11778    cx.update_editor(|editor, window, cx| {
11779        editor.toggle_comments(&ToggleComments::default(), window, cx)
11780    });
11781    // TODO this is how it actually worked in Zed Stable, which is not very ergonomic.
11782    // Uncommenting and commenting from this position brings in even more wrong artifacts.
11783    cx.assert_editor_state(
11784        &r#"
11785            <!-- ˇ<script> -->
11786                // ˇvar x = new Y();
11787            <!-- ˇ</script> -->
11788        "#
11789        .unindent(),
11790    );
11791}
11792
11793#[gpui::test]
11794fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
11795    init_test(cx, |_| {});
11796
11797    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11798    let multibuffer = cx.new(|cx| {
11799        let mut multibuffer = MultiBuffer::new(ReadWrite);
11800        multibuffer.push_excerpts(
11801            buffer.clone(),
11802            [
11803                ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
11804                ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
11805            ],
11806            cx,
11807        );
11808        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
11809        multibuffer
11810    });
11811
11812    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11813    editor.update_in(cx, |editor, window, cx| {
11814        assert_eq!(editor.text(cx), "aaaa\nbbbb");
11815        editor.change_selections(None, window, cx, |s| {
11816            s.select_ranges([
11817                Point::new(0, 0)..Point::new(0, 0),
11818                Point::new(1, 0)..Point::new(1, 0),
11819            ])
11820        });
11821
11822        editor.handle_input("X", window, cx);
11823        assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
11824        assert_eq!(
11825            editor.selections.ranges(cx),
11826            [
11827                Point::new(0, 1)..Point::new(0, 1),
11828                Point::new(1, 1)..Point::new(1, 1),
11829            ]
11830        );
11831
11832        // Ensure the cursor's head is respected when deleting across an excerpt boundary.
11833        editor.change_selections(None, window, cx, |s| {
11834            s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
11835        });
11836        editor.backspace(&Default::default(), window, cx);
11837        assert_eq!(editor.text(cx), "Xa\nbbb");
11838        assert_eq!(
11839            editor.selections.ranges(cx),
11840            [Point::new(1, 0)..Point::new(1, 0)]
11841        );
11842
11843        editor.change_selections(None, window, cx, |s| {
11844            s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
11845        });
11846        editor.backspace(&Default::default(), window, cx);
11847        assert_eq!(editor.text(cx), "X\nbb");
11848        assert_eq!(
11849            editor.selections.ranges(cx),
11850            [Point::new(0, 1)..Point::new(0, 1)]
11851        );
11852    });
11853}
11854
11855#[gpui::test]
11856fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
11857    init_test(cx, |_| {});
11858
11859    let markers = vec![('[', ']').into(), ('(', ')').into()];
11860    let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
11861        indoc! {"
11862            [aaaa
11863            (bbbb]
11864            cccc)",
11865        },
11866        markers.clone(),
11867    );
11868    let excerpt_ranges = markers.into_iter().map(|marker| {
11869        let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
11870        ExcerptRange::new(context.clone())
11871    });
11872    let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
11873    let multibuffer = cx.new(|cx| {
11874        let mut multibuffer = MultiBuffer::new(ReadWrite);
11875        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
11876        multibuffer
11877    });
11878
11879    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
11880    editor.update_in(cx, |editor, window, cx| {
11881        let (expected_text, selection_ranges) = marked_text_ranges(
11882            indoc! {"
11883                aaaa
11884                bˇbbb
11885                bˇbbˇb
11886                cccc"
11887            },
11888            true,
11889        );
11890        assert_eq!(editor.text(cx), expected_text);
11891        editor.change_selections(None, window, cx, |s| s.select_ranges(selection_ranges));
11892
11893        editor.handle_input("X", window, cx);
11894
11895        let (expected_text, expected_selections) = marked_text_ranges(
11896            indoc! {"
11897                aaaa
11898                bXˇbbXb
11899                bXˇbbXˇb
11900                cccc"
11901            },
11902            false,
11903        );
11904        assert_eq!(editor.text(cx), expected_text);
11905        assert_eq!(editor.selections.ranges(cx), expected_selections);
11906
11907        editor.newline(&Newline, window, cx);
11908        let (expected_text, expected_selections) = marked_text_ranges(
11909            indoc! {"
11910                aaaa
11911                bX
11912                ˇbbX
11913                b
11914                bX
11915                ˇbbX
11916                ˇb
11917                cccc"
11918            },
11919            false,
11920        );
11921        assert_eq!(editor.text(cx), expected_text);
11922        assert_eq!(editor.selections.ranges(cx), expected_selections);
11923    });
11924}
11925
11926#[gpui::test]
11927fn test_refresh_selections(cx: &mut TestAppContext) {
11928    init_test(cx, |_| {});
11929
11930    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
11931    let mut excerpt1_id = None;
11932    let multibuffer = cx.new(|cx| {
11933        let mut multibuffer = MultiBuffer::new(ReadWrite);
11934        excerpt1_id = multibuffer
11935            .push_excerpts(
11936                buffer.clone(),
11937                [
11938                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
11939                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
11940                ],
11941                cx,
11942            )
11943            .into_iter()
11944            .next();
11945        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
11946        multibuffer
11947    });
11948
11949    let editor = cx.add_window(|window, cx| {
11950        let mut editor = build_editor(multibuffer.clone(), window, cx);
11951        let snapshot = editor.snapshot(window, cx);
11952        editor.change_selections(None, window, cx, |s| {
11953            s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
11954        });
11955        editor.begin_selection(
11956            Point::new(2, 1).to_display_point(&snapshot),
11957            true,
11958            1,
11959            window,
11960            cx,
11961        );
11962        assert_eq!(
11963            editor.selections.ranges(cx),
11964            [
11965                Point::new(1, 3)..Point::new(1, 3),
11966                Point::new(2, 1)..Point::new(2, 1),
11967            ]
11968        );
11969        editor
11970    });
11971
11972    // Refreshing selections is a no-op when excerpts haven't changed.
11973    _ = editor.update(cx, |editor, window, cx| {
11974        editor.change_selections(None, window, cx, |s| s.refresh());
11975        assert_eq!(
11976            editor.selections.ranges(cx),
11977            [
11978                Point::new(1, 3)..Point::new(1, 3),
11979                Point::new(2, 1)..Point::new(2, 1),
11980            ]
11981        );
11982    });
11983
11984    multibuffer.update(cx, |multibuffer, cx| {
11985        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
11986    });
11987    _ = editor.update(cx, |editor, window, cx| {
11988        // Removing an excerpt causes the first selection to become degenerate.
11989        assert_eq!(
11990            editor.selections.ranges(cx),
11991            [
11992                Point::new(0, 0)..Point::new(0, 0),
11993                Point::new(0, 1)..Point::new(0, 1)
11994            ]
11995        );
11996
11997        // Refreshing selections will relocate the first selection to the original buffer
11998        // location.
11999        editor.change_selections(None, window, cx, |s| s.refresh());
12000        assert_eq!(
12001            editor.selections.ranges(cx),
12002            [
12003                Point::new(0, 1)..Point::new(0, 1),
12004                Point::new(0, 3)..Point::new(0, 3)
12005            ]
12006        );
12007        assert!(editor.selections.pending_anchor().is_some());
12008    });
12009}
12010
12011#[gpui::test]
12012fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
12013    init_test(cx, |_| {});
12014
12015    let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
12016    let mut excerpt1_id = None;
12017    let multibuffer = cx.new(|cx| {
12018        let mut multibuffer = MultiBuffer::new(ReadWrite);
12019        excerpt1_id = multibuffer
12020            .push_excerpts(
12021                buffer.clone(),
12022                [
12023                    ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
12024                    ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
12025                ],
12026                cx,
12027            )
12028            .into_iter()
12029            .next();
12030        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
12031        multibuffer
12032    });
12033
12034    let editor = cx.add_window(|window, cx| {
12035        let mut editor = build_editor(multibuffer.clone(), window, cx);
12036        let snapshot = editor.snapshot(window, cx);
12037        editor.begin_selection(
12038            Point::new(1, 3).to_display_point(&snapshot),
12039            false,
12040            1,
12041            window,
12042            cx,
12043        );
12044        assert_eq!(
12045            editor.selections.ranges(cx),
12046            [Point::new(1, 3)..Point::new(1, 3)]
12047        );
12048        editor
12049    });
12050
12051    multibuffer.update(cx, |multibuffer, cx| {
12052        multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
12053    });
12054    _ = editor.update(cx, |editor, window, cx| {
12055        assert_eq!(
12056            editor.selections.ranges(cx),
12057            [Point::new(0, 0)..Point::new(0, 0)]
12058        );
12059
12060        // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
12061        editor.change_selections(None, window, cx, |s| s.refresh());
12062        assert_eq!(
12063            editor.selections.ranges(cx),
12064            [Point::new(0, 3)..Point::new(0, 3)]
12065        );
12066        assert!(editor.selections.pending_anchor().is_some());
12067    });
12068}
12069
12070#[gpui::test]
12071async fn test_extra_newline_insertion(cx: &mut TestAppContext) {
12072    init_test(cx, |_| {});
12073
12074    let language = Arc::new(
12075        Language::new(
12076            LanguageConfig {
12077                brackets: BracketPairConfig {
12078                    pairs: vec![
12079                        BracketPair {
12080                            start: "{".to_string(),
12081                            end: "}".to_string(),
12082                            close: true,
12083                            surround: true,
12084                            newline: true,
12085                        },
12086                        BracketPair {
12087                            start: "/* ".to_string(),
12088                            end: " */".to_string(),
12089                            close: true,
12090                            surround: true,
12091                            newline: true,
12092                        },
12093                    ],
12094                    ..Default::default()
12095                },
12096                ..Default::default()
12097            },
12098            Some(tree_sitter_rust::LANGUAGE.into()),
12099        )
12100        .with_indents_query("")
12101        .unwrap(),
12102    );
12103
12104    let text = concat!(
12105        "{   }\n",     //
12106        "  x\n",       //
12107        "  /*   */\n", //
12108        "x\n",         //
12109        "{{} }\n",     //
12110    );
12111
12112    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
12113    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
12114    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
12115    editor
12116        .condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
12117        .await;
12118
12119    editor.update_in(cx, |editor, window, cx| {
12120        editor.change_selections(None, window, cx, |s| {
12121            s.select_display_ranges([
12122                DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 3),
12123                DisplayPoint::new(DisplayRow(2), 5)..DisplayPoint::new(DisplayRow(2), 5),
12124                DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
12125            ])
12126        });
12127        editor.newline(&Newline, window, cx);
12128
12129        assert_eq!(
12130            editor.buffer().read(cx).read(cx).text(),
12131            concat!(
12132                "{ \n",    // Suppress rustfmt
12133                "\n",      //
12134                "}\n",     //
12135                "  x\n",   //
12136                "  /* \n", //
12137                "  \n",    //
12138                "  */\n",  //
12139                "x\n",     //
12140                "{{} \n",  //
12141                "}\n",     //
12142            )
12143        );
12144    });
12145}
12146
12147#[gpui::test]
12148fn test_highlighted_ranges(cx: &mut TestAppContext) {
12149    init_test(cx, |_| {});
12150
12151    let editor = cx.add_window(|window, cx| {
12152        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
12153        build_editor(buffer.clone(), window, cx)
12154    });
12155
12156    _ = editor.update(cx, |editor, window, cx| {
12157        struct Type1;
12158        struct Type2;
12159
12160        let buffer = editor.buffer.read(cx).snapshot(cx);
12161
12162        let anchor_range =
12163            |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
12164
12165        editor.highlight_background::<Type1>(
12166            &[
12167                anchor_range(Point::new(2, 1)..Point::new(2, 3)),
12168                anchor_range(Point::new(4, 2)..Point::new(4, 4)),
12169                anchor_range(Point::new(6, 3)..Point::new(6, 5)),
12170                anchor_range(Point::new(8, 4)..Point::new(8, 6)),
12171            ],
12172            |_| Hsla::red(),
12173            cx,
12174        );
12175        editor.highlight_background::<Type2>(
12176            &[
12177                anchor_range(Point::new(3, 2)..Point::new(3, 5)),
12178                anchor_range(Point::new(5, 3)..Point::new(5, 6)),
12179                anchor_range(Point::new(7, 4)..Point::new(7, 7)),
12180                anchor_range(Point::new(9, 5)..Point::new(9, 8)),
12181            ],
12182            |_| Hsla::green(),
12183            cx,
12184        );
12185
12186        let snapshot = editor.snapshot(window, cx);
12187        let mut highlighted_ranges = editor.background_highlights_in_range(
12188            anchor_range(Point::new(3, 4)..Point::new(7, 4)),
12189            &snapshot,
12190            cx.theme().colors(),
12191        );
12192        // Enforce a consistent ordering based on color without relying on the ordering of the
12193        // highlight's `TypeId` which is non-executor.
12194        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
12195        assert_eq!(
12196            highlighted_ranges,
12197            &[
12198                (
12199                    DisplayPoint::new(DisplayRow(4), 2)..DisplayPoint::new(DisplayRow(4), 4),
12200                    Hsla::red(),
12201                ),
12202                (
12203                    DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12204                    Hsla::red(),
12205                ),
12206                (
12207                    DisplayPoint::new(DisplayRow(3), 2)..DisplayPoint::new(DisplayRow(3), 5),
12208                    Hsla::green(),
12209                ),
12210                (
12211                    DisplayPoint::new(DisplayRow(5), 3)..DisplayPoint::new(DisplayRow(5), 6),
12212                    Hsla::green(),
12213                ),
12214            ]
12215        );
12216        assert_eq!(
12217            editor.background_highlights_in_range(
12218                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
12219                &snapshot,
12220                cx.theme().colors(),
12221            ),
12222            &[(
12223                DisplayPoint::new(DisplayRow(6), 3)..DisplayPoint::new(DisplayRow(6), 5),
12224                Hsla::red(),
12225            )]
12226        );
12227    });
12228}
12229
12230#[gpui::test]
12231async fn test_following(cx: &mut TestAppContext) {
12232    init_test(cx, |_| {});
12233
12234    let fs = FakeFs::new(cx.executor());
12235    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12236
12237    let buffer = project.update(cx, |project, cx| {
12238        let buffer = project.create_local_buffer(&sample_text(16, 8, 'a'), None, cx);
12239        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
12240    });
12241    let leader = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
12242    let follower = cx.update(|cx| {
12243        cx.open_window(
12244            WindowOptions {
12245                window_bounds: Some(WindowBounds::Windowed(Bounds::from_corners(
12246                    gpui::Point::new(px(0.), px(0.)),
12247                    gpui::Point::new(px(10.), px(80.)),
12248                ))),
12249                ..Default::default()
12250            },
12251            |window, cx| cx.new(|cx| build_editor(buffer.clone(), window, cx)),
12252        )
12253        .unwrap()
12254    });
12255
12256    let is_still_following = Rc::new(RefCell::new(true));
12257    let follower_edit_event_count = Rc::new(RefCell::new(0));
12258    let pending_update = Rc::new(RefCell::new(None));
12259    let leader_entity = leader.root(cx).unwrap();
12260    let follower_entity = follower.root(cx).unwrap();
12261    _ = follower.update(cx, {
12262        let update = pending_update.clone();
12263        let is_still_following = is_still_following.clone();
12264        let follower_edit_event_count = follower_edit_event_count.clone();
12265        |_, window, cx| {
12266            cx.subscribe_in(
12267                &leader_entity,
12268                window,
12269                move |_, leader, event, window, cx| {
12270                    leader.read(cx).add_event_to_update_proto(
12271                        event,
12272                        &mut update.borrow_mut(),
12273                        window,
12274                        cx,
12275                    );
12276                },
12277            )
12278            .detach();
12279
12280            cx.subscribe_in(
12281                &follower_entity,
12282                window,
12283                move |_, _, event: &EditorEvent, _window, _cx| {
12284                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
12285                        *is_still_following.borrow_mut() = false;
12286                    }
12287
12288                    if let EditorEvent::BufferEdited = event {
12289                        *follower_edit_event_count.borrow_mut() += 1;
12290                    }
12291                },
12292            )
12293            .detach();
12294        }
12295    });
12296
12297    // Update the selections only
12298    _ = leader.update(cx, |leader, window, cx| {
12299        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12300    });
12301    follower
12302        .update(cx, |follower, window, cx| {
12303            follower.apply_update_proto(
12304                &project,
12305                pending_update.borrow_mut().take().unwrap(),
12306                window,
12307                cx,
12308            )
12309        })
12310        .unwrap()
12311        .await
12312        .unwrap();
12313    _ = follower.update(cx, |follower, _, cx| {
12314        assert_eq!(follower.selections.ranges(cx), vec![1..1]);
12315    });
12316    assert!(*is_still_following.borrow());
12317    assert_eq!(*follower_edit_event_count.borrow(), 0);
12318
12319    // Update the scroll position only
12320    _ = leader.update(cx, |leader, window, cx| {
12321        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12322    });
12323    follower
12324        .update(cx, |follower, window, cx| {
12325            follower.apply_update_proto(
12326                &project,
12327                pending_update.borrow_mut().take().unwrap(),
12328                window,
12329                cx,
12330            )
12331        })
12332        .unwrap()
12333        .await
12334        .unwrap();
12335    assert_eq!(
12336        follower
12337            .update(cx, |follower, _, cx| follower.scroll_position(cx))
12338            .unwrap(),
12339        gpui::Point::new(1.5, 3.5)
12340    );
12341    assert!(*is_still_following.borrow());
12342    assert_eq!(*follower_edit_event_count.borrow(), 0);
12343
12344    // Update the selections and scroll position. The follower's scroll position is updated
12345    // via autoscroll, not via the leader's exact scroll position.
12346    _ = leader.update(cx, |leader, window, cx| {
12347        leader.change_selections(None, window, cx, |s| s.select_ranges([0..0]));
12348        leader.request_autoscroll(Autoscroll::newest(), cx);
12349        leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx);
12350    });
12351    follower
12352        .update(cx, |follower, window, cx| {
12353            follower.apply_update_proto(
12354                &project,
12355                pending_update.borrow_mut().take().unwrap(),
12356                window,
12357                cx,
12358            )
12359        })
12360        .unwrap()
12361        .await
12362        .unwrap();
12363    _ = follower.update(cx, |follower, _, cx| {
12364        assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0));
12365        assert_eq!(follower.selections.ranges(cx), vec![0..0]);
12366    });
12367    assert!(*is_still_following.borrow());
12368
12369    // Creating a pending selection that precedes another selection
12370    _ = leader.update(cx, |leader, window, cx| {
12371        leader.change_selections(None, window, cx, |s| s.select_ranges([1..1]));
12372        leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx);
12373    });
12374    follower
12375        .update(cx, |follower, window, cx| {
12376            follower.apply_update_proto(
12377                &project,
12378                pending_update.borrow_mut().take().unwrap(),
12379                window,
12380                cx,
12381            )
12382        })
12383        .unwrap()
12384        .await
12385        .unwrap();
12386    _ = follower.update(cx, |follower, _, cx| {
12387        assert_eq!(follower.selections.ranges(cx), vec![0..0, 1..1]);
12388    });
12389    assert!(*is_still_following.borrow());
12390
12391    // Extend the pending selection so that it surrounds another selection
12392    _ = leader.update(cx, |leader, window, cx| {
12393        leader.extend_selection(DisplayPoint::new(DisplayRow(0), 2), 1, window, cx);
12394    });
12395    follower
12396        .update(cx, |follower, window, cx| {
12397            follower.apply_update_proto(
12398                &project,
12399                pending_update.borrow_mut().take().unwrap(),
12400                window,
12401                cx,
12402            )
12403        })
12404        .unwrap()
12405        .await
12406        .unwrap();
12407    _ = follower.update(cx, |follower, _, cx| {
12408        assert_eq!(follower.selections.ranges(cx), vec![0..2]);
12409    });
12410
12411    // Scrolling locally breaks the follow
12412    _ = follower.update(cx, |follower, window, cx| {
12413        let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0);
12414        follower.set_scroll_anchor(
12415            ScrollAnchor {
12416                anchor: top_anchor,
12417                offset: gpui::Point::new(0.0, 0.5),
12418            },
12419            window,
12420            cx,
12421        );
12422    });
12423    assert!(!(*is_still_following.borrow()));
12424}
12425
12426#[gpui::test]
12427async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
12428    init_test(cx, |_| {});
12429
12430    let fs = FakeFs::new(cx.executor());
12431    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
12432    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
12433    let pane = workspace
12434        .update(cx, |workspace, _, _| workspace.active_pane().clone())
12435        .unwrap();
12436
12437    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
12438
12439    let leader = pane.update_in(cx, |_, window, cx| {
12440        let multibuffer = cx.new(|_| MultiBuffer::new(ReadWrite));
12441        cx.new(|cx| build_editor(multibuffer.clone(), window, cx))
12442    });
12443
12444    // Start following the editor when it has no excerpts.
12445    let mut state_message =
12446        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12447    let workspace_entity = workspace.root(cx).unwrap();
12448    let follower_1 = cx
12449        .update_window(*workspace.deref(), |_, window, cx| {
12450            Editor::from_state_proto(
12451                workspace_entity,
12452                ViewId {
12453                    creator: Default::default(),
12454                    id: 0,
12455                },
12456                &mut state_message,
12457                window,
12458                cx,
12459            )
12460        })
12461        .unwrap()
12462        .unwrap()
12463        .await
12464        .unwrap();
12465
12466    let update_message = Rc::new(RefCell::new(None));
12467    follower_1.update_in(cx, {
12468        let update = update_message.clone();
12469        |_, window, cx| {
12470            cx.subscribe_in(&leader, window, move |_, leader, event, window, cx| {
12471                leader.read(cx).add_event_to_update_proto(
12472                    event,
12473                    &mut update.borrow_mut(),
12474                    window,
12475                    cx,
12476                );
12477            })
12478            .detach();
12479        }
12480    });
12481
12482    let (buffer_1, buffer_2) = project.update(cx, |project, cx| {
12483        (
12484            project.create_local_buffer("abc\ndef\nghi\njkl\n", None, cx),
12485            project.create_local_buffer("mno\npqr\nstu\nvwx\n", None, cx),
12486        )
12487    });
12488
12489    // Insert some excerpts.
12490    leader.update(cx, |leader, cx| {
12491        leader.buffer.update(cx, |multibuffer, cx| {
12492            let excerpt_ids = multibuffer.push_excerpts(
12493                buffer_1.clone(),
12494                [
12495                    ExcerptRange::new(1..6),
12496                    ExcerptRange::new(12..15),
12497                    ExcerptRange::new(0..3),
12498                ],
12499                cx,
12500            );
12501            multibuffer.insert_excerpts_after(
12502                excerpt_ids[0],
12503                buffer_2.clone(),
12504                [ExcerptRange::new(8..12), ExcerptRange::new(0..6)],
12505                cx,
12506            );
12507        });
12508    });
12509
12510    // Apply the update of adding the excerpts.
12511    follower_1
12512        .update_in(cx, |follower, window, cx| {
12513            follower.apply_update_proto(
12514                &project,
12515                update_message.borrow().clone().unwrap(),
12516                window,
12517                cx,
12518            )
12519        })
12520        .await
12521        .unwrap();
12522    assert_eq!(
12523        follower_1.update(cx, |editor, cx| editor.text(cx)),
12524        leader.update(cx, |editor, cx| editor.text(cx))
12525    );
12526    update_message.borrow_mut().take();
12527
12528    // Start following separately after it already has excerpts.
12529    let mut state_message =
12530        leader.update_in(cx, |leader, window, cx| leader.to_state_proto(window, cx));
12531    let workspace_entity = workspace.root(cx).unwrap();
12532    let follower_2 = cx
12533        .update_window(*workspace.deref(), |_, window, cx| {
12534            Editor::from_state_proto(
12535                workspace_entity,
12536                ViewId {
12537                    creator: Default::default(),
12538                    id: 0,
12539                },
12540                &mut state_message,
12541                window,
12542                cx,
12543            )
12544        })
12545        .unwrap()
12546        .unwrap()
12547        .await
12548        .unwrap();
12549    assert_eq!(
12550        follower_2.update(cx, |editor, cx| editor.text(cx)),
12551        leader.update(cx, |editor, cx| editor.text(cx))
12552    );
12553
12554    // Remove some excerpts.
12555    leader.update(cx, |leader, cx| {
12556        leader.buffer.update(cx, |multibuffer, cx| {
12557            let excerpt_ids = multibuffer.excerpt_ids();
12558            multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
12559            multibuffer.remove_excerpts([excerpt_ids[0]], cx);
12560        });
12561    });
12562
12563    // Apply the update of removing the excerpts.
12564    follower_1
12565        .update_in(cx, |follower, window, cx| {
12566            follower.apply_update_proto(
12567                &project,
12568                update_message.borrow().clone().unwrap(),
12569                window,
12570                cx,
12571            )
12572        })
12573        .await
12574        .unwrap();
12575    follower_2
12576        .update_in(cx, |follower, window, cx| {
12577            follower.apply_update_proto(
12578                &project,
12579                update_message.borrow().clone().unwrap(),
12580                window,
12581                cx,
12582            )
12583        })
12584        .await
12585        .unwrap();
12586    update_message.borrow_mut().take();
12587    assert_eq!(
12588        follower_1.update(cx, |editor, cx| editor.text(cx)),
12589        leader.update(cx, |editor, cx| editor.text(cx))
12590    );
12591}
12592
12593#[gpui::test]
12594async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12595    init_test(cx, |_| {});
12596
12597    let mut cx = EditorTestContext::new(cx).await;
12598    let lsp_store =
12599        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12600
12601    cx.set_state(indoc! {"
12602        ˇfn func(abc def: i32) -> u32 {
12603        }
12604    "});
12605
12606    cx.update(|_, cx| {
12607        lsp_store.update(cx, |lsp_store, cx| {
12608            lsp_store
12609                .update_diagnostics(
12610                    LanguageServerId(0),
12611                    lsp::PublishDiagnosticsParams {
12612                        uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12613                        version: None,
12614                        diagnostics: vec![
12615                            lsp::Diagnostic {
12616                                range: lsp::Range::new(
12617                                    lsp::Position::new(0, 11),
12618                                    lsp::Position::new(0, 12),
12619                                ),
12620                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12621                                ..Default::default()
12622                            },
12623                            lsp::Diagnostic {
12624                                range: lsp::Range::new(
12625                                    lsp::Position::new(0, 12),
12626                                    lsp::Position::new(0, 15),
12627                                ),
12628                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12629                                ..Default::default()
12630                            },
12631                            lsp::Diagnostic {
12632                                range: lsp::Range::new(
12633                                    lsp::Position::new(0, 25),
12634                                    lsp::Position::new(0, 28),
12635                                ),
12636                                severity: Some(lsp::DiagnosticSeverity::ERROR),
12637                                ..Default::default()
12638                            },
12639                        ],
12640                    },
12641                    &[],
12642                    cx,
12643                )
12644                .unwrap()
12645        });
12646    });
12647
12648    executor.run_until_parked();
12649
12650    cx.update_editor(|editor, window, cx| {
12651        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12652    });
12653
12654    cx.assert_editor_state(indoc! {"
12655        fn func(abc def: i32) -> ˇu32 {
12656        }
12657    "});
12658
12659    cx.update_editor(|editor, window, cx| {
12660        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12661    });
12662
12663    cx.assert_editor_state(indoc! {"
12664        fn func(abc ˇdef: i32) -> u32 {
12665        }
12666    "});
12667
12668    cx.update_editor(|editor, window, cx| {
12669        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12670    });
12671
12672    cx.assert_editor_state(indoc! {"
12673        fn func(abcˇ def: i32) -> u32 {
12674        }
12675    "});
12676
12677    cx.update_editor(|editor, window, cx| {
12678        editor.go_to_prev_diagnostic(&GoToPreviousDiagnostic, window, cx);
12679    });
12680
12681    cx.assert_editor_state(indoc! {"
12682        fn func(abc def: i32) -> ˇu32 {
12683        }
12684    "});
12685}
12686
12687#[gpui::test]
12688async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
12689    init_test(cx, |_| {});
12690
12691    let mut cx = EditorTestContext::new(cx).await;
12692
12693    cx.set_state(indoc! {"
12694        fn func(abˇc def: i32) -> u32 {
12695        }
12696    "});
12697    let lsp_store =
12698        cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
12699
12700    cx.update(|_, cx| {
12701        lsp_store.update(cx, |lsp_store, cx| {
12702            lsp_store.update_diagnostics(
12703                LanguageServerId(0),
12704                lsp::PublishDiagnosticsParams {
12705                    uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(),
12706                    version: None,
12707                    diagnostics: vec![lsp::Diagnostic {
12708                        range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)),
12709                        severity: Some(lsp::DiagnosticSeverity::ERROR),
12710                        message: "we've had problems with <https://link.one>, and <https://link.two> is broken".to_string(),
12711                        ..Default::default()
12712                    }],
12713                },
12714                &[],
12715                cx,
12716            )
12717        })
12718    }).unwrap();
12719    cx.run_until_parked();
12720    cx.update_editor(|editor, window, cx| {
12721        hover_popover::hover(editor, &Default::default(), window, cx)
12722    });
12723    cx.run_until_parked();
12724    cx.update_editor(|editor, _, _| assert!(editor.hover_state.diagnostic_popover.is_some()))
12725}
12726
12727#[gpui::test]
12728async fn test_go_to_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
12729    init_test(cx, |_| {});
12730
12731    let mut cx = EditorTestContext::new(cx).await;
12732
12733    let diff_base = r#"
12734        use some::mod;
12735
12736        const A: u32 = 42;
12737
12738        fn main() {
12739            println!("hello");
12740
12741            println!("world");
12742        }
12743        "#
12744    .unindent();
12745
12746    // Edits are modified, removed, modified, added
12747    cx.set_state(
12748        &r#"
12749        use some::modified;
12750
12751        ˇ
12752        fn main() {
12753            println!("hello there");
12754
12755            println!("around the");
12756            println!("world");
12757        }
12758        "#
12759        .unindent(),
12760    );
12761
12762    cx.set_head_text(&diff_base);
12763    executor.run_until_parked();
12764
12765    cx.update_editor(|editor, window, cx| {
12766        //Wrap around the bottom of the buffer
12767        for _ in 0..3 {
12768            editor.go_to_next_hunk(&GoToHunk, window, cx);
12769        }
12770    });
12771
12772    cx.assert_editor_state(
12773        &r#"
12774        ˇuse some::modified;
12775
12776
12777        fn main() {
12778            println!("hello there");
12779
12780            println!("around the");
12781            println!("world");
12782        }
12783        "#
12784        .unindent(),
12785    );
12786
12787    cx.update_editor(|editor, window, cx| {
12788        //Wrap around the top of the buffer
12789        for _ in 0..2 {
12790            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12791        }
12792    });
12793
12794    cx.assert_editor_state(
12795        &r#"
12796        use some::modified;
12797
12798
12799        fn main() {
12800        ˇ    println!("hello there");
12801
12802            println!("around the");
12803            println!("world");
12804        }
12805        "#
12806        .unindent(),
12807    );
12808
12809    cx.update_editor(|editor, window, cx| {
12810        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12811    });
12812
12813    cx.assert_editor_state(
12814        &r#"
12815        use some::modified;
12816
12817        ˇ
12818        fn main() {
12819            println!("hello there");
12820
12821            println!("around the");
12822            println!("world");
12823        }
12824        "#
12825        .unindent(),
12826    );
12827
12828    cx.update_editor(|editor, window, cx| {
12829        editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12830    });
12831
12832    cx.assert_editor_state(
12833        &r#"
12834        ˇuse some::modified;
12835
12836
12837        fn main() {
12838            println!("hello there");
12839
12840            println!("around the");
12841            println!("world");
12842        }
12843        "#
12844        .unindent(),
12845    );
12846
12847    cx.update_editor(|editor, window, cx| {
12848        for _ in 0..2 {
12849            editor.go_to_prev_hunk(&GoToPreviousHunk, window, cx);
12850        }
12851    });
12852
12853    cx.assert_editor_state(
12854        &r#"
12855        use some::modified;
12856
12857
12858        fn main() {
12859        ˇ    println!("hello there");
12860
12861            println!("around the");
12862            println!("world");
12863        }
12864        "#
12865        .unindent(),
12866    );
12867
12868    cx.update_editor(|editor, window, cx| {
12869        editor.fold(&Fold, window, cx);
12870    });
12871
12872    cx.update_editor(|editor, window, cx| {
12873        editor.go_to_next_hunk(&GoToHunk, window, cx);
12874    });
12875
12876    cx.assert_editor_state(
12877        &r#"
12878        ˇuse some::modified;
12879
12880
12881        fn main() {
12882            println!("hello there");
12883
12884            println!("around the");
12885            println!("world");
12886        }
12887        "#
12888        .unindent(),
12889    );
12890}
12891
12892#[test]
12893fn test_split_words() {
12894    fn split(text: &str) -> Vec<&str> {
12895        split_words(text).collect()
12896    }
12897
12898    assert_eq!(split("HelloWorld"), &["Hello", "World"]);
12899    assert_eq!(split("hello_world"), &["hello_", "world"]);
12900    assert_eq!(split("_hello_world_"), &["_", "hello_", "world_"]);
12901    assert_eq!(split("Hello_World"), &["Hello_", "World"]);
12902    assert_eq!(split("helloWOrld"), &["hello", "WOrld"]);
12903    assert_eq!(split("helloworld"), &["helloworld"]);
12904
12905    assert_eq!(split(":do_the_thing"), &[":", "do_", "the_", "thing"]);
12906}
12907
12908#[gpui::test]
12909async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
12910    init_test(cx, |_| {});
12911
12912    let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
12913    let mut assert = |before, after| {
12914        let _state_context = cx.set_state(before);
12915        cx.run_until_parked();
12916        cx.update_editor(|editor, window, cx| {
12917            editor.move_to_enclosing_bracket(&MoveToEnclosingBracket, window, cx)
12918        });
12919        cx.run_until_parked();
12920        cx.assert_editor_state(after);
12921    };
12922
12923    // Outside bracket jumps to outside of matching bracket
12924    assert("console.logˇ(var);", "console.log(var)ˇ;");
12925    assert("console.log(var)ˇ;", "console.logˇ(var);");
12926
12927    // Inside bracket jumps to inside of matching bracket
12928    assert("console.log(ˇvar);", "console.log(varˇ);");
12929    assert("console.log(varˇ);", "console.log(ˇvar);");
12930
12931    // When outside a bracket and inside, favor jumping to the inside bracket
12932    assert(
12933        "console.log('foo', [1, 2, 3]ˇ);",
12934        "console.log(ˇ'foo', [1, 2, 3]);",
12935    );
12936    assert(
12937        "console.log(ˇ'foo', [1, 2, 3]);",
12938        "console.log('foo', [1, 2, 3]ˇ);",
12939    );
12940
12941    // Bias forward if two options are equally likely
12942    assert(
12943        "let result = curried_fun()ˇ();",
12944        "let result = curried_fun()()ˇ;",
12945    );
12946
12947    // If directly adjacent to a smaller pair but inside a larger (not adjacent), pick the smaller
12948    assert(
12949        indoc! {"
12950            function test() {
12951                console.log('test')ˇ
12952            }"},
12953        indoc! {"
12954            function test() {
12955                console.logˇ('test')
12956            }"},
12957    );
12958}
12959
12960#[gpui::test]
12961async fn test_on_type_formatting_not_triggered(cx: &mut TestAppContext) {
12962    init_test(cx, |_| {});
12963
12964    let fs = FakeFs::new(cx.executor());
12965    fs.insert_tree(
12966        path!("/a"),
12967        json!({
12968            "main.rs": "fn main() { let a = 5; }",
12969            "other.rs": "// Test file",
12970        }),
12971    )
12972    .await;
12973    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
12974
12975    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
12976    language_registry.add(Arc::new(Language::new(
12977        LanguageConfig {
12978            name: "Rust".into(),
12979            matcher: LanguageMatcher {
12980                path_suffixes: vec!["rs".to_string()],
12981                ..Default::default()
12982            },
12983            brackets: BracketPairConfig {
12984                pairs: vec![BracketPair {
12985                    start: "{".to_string(),
12986                    end: "}".to_string(),
12987                    close: true,
12988                    surround: true,
12989                    newline: true,
12990                }],
12991                disabled_scopes_by_bracket_ix: Vec::new(),
12992            },
12993            ..Default::default()
12994        },
12995        Some(tree_sitter_rust::LANGUAGE.into()),
12996    )));
12997    let mut fake_servers = language_registry.register_fake_lsp(
12998        "Rust",
12999        FakeLspAdapter {
13000            capabilities: lsp::ServerCapabilities {
13001                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
13002                    first_trigger_character: "{".to_string(),
13003                    more_trigger_character: None,
13004                }),
13005                ..Default::default()
13006            },
13007            ..Default::default()
13008        },
13009    );
13010
13011    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13012
13013    let cx = &mut VisualTestContext::from_window(*workspace, cx);
13014
13015    let worktree_id = workspace
13016        .update(cx, |workspace, _, cx| {
13017            workspace.project().update(cx, |project, cx| {
13018                project.worktrees(cx).next().unwrap().read(cx).id()
13019            })
13020        })
13021        .unwrap();
13022
13023    let buffer = project
13024        .update(cx, |project, cx| {
13025            project.open_local_buffer(path!("/a/main.rs"), cx)
13026        })
13027        .await
13028        .unwrap();
13029    let editor_handle = workspace
13030        .update(cx, |workspace, window, cx| {
13031            workspace.open_path((worktree_id, "main.rs"), None, true, window, cx)
13032        })
13033        .unwrap()
13034        .await
13035        .unwrap()
13036        .downcast::<Editor>()
13037        .unwrap();
13038
13039    cx.executor().start_waiting();
13040    let fake_server = fake_servers.next().await.unwrap();
13041
13042    fake_server.set_request_handler::<lsp::request::OnTypeFormatting, _, _>(
13043        |params, _| async move {
13044            assert_eq!(
13045                params.text_document_position.text_document.uri,
13046                lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(),
13047            );
13048            assert_eq!(
13049                params.text_document_position.position,
13050                lsp::Position::new(0, 21),
13051            );
13052
13053            Ok(Some(vec![lsp::TextEdit {
13054                new_text: "]".to_string(),
13055                range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13056            }]))
13057        },
13058    );
13059
13060    editor_handle.update_in(cx, |editor, window, cx| {
13061        window.focus(&editor.focus_handle(cx));
13062        editor.change_selections(None, window, cx, |s| {
13063            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
13064        });
13065        editor.handle_input("{", window, cx);
13066    });
13067
13068    cx.executor().run_until_parked();
13069
13070    buffer.update(cx, |buffer, _| {
13071        assert_eq!(
13072            buffer.text(),
13073            "fn main() { let a = {5}; }",
13074            "No extra braces from on type formatting should appear in the buffer"
13075        )
13076    });
13077}
13078
13079#[gpui::test]
13080async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppContext) {
13081    init_test(cx, |_| {});
13082
13083    let fs = FakeFs::new(cx.executor());
13084    fs.insert_tree(
13085        path!("/a"),
13086        json!({
13087            "main.rs": "fn main() { let a = 5; }",
13088            "other.rs": "// Test file",
13089        }),
13090    )
13091    .await;
13092
13093    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
13094
13095    let server_restarts = Arc::new(AtomicUsize::new(0));
13096    let closure_restarts = Arc::clone(&server_restarts);
13097    let language_server_name = "test language server";
13098    let language_name: LanguageName = "Rust".into();
13099
13100    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13101    language_registry.add(Arc::new(Language::new(
13102        LanguageConfig {
13103            name: language_name.clone(),
13104            matcher: LanguageMatcher {
13105                path_suffixes: vec!["rs".to_string()],
13106                ..Default::default()
13107            },
13108            ..Default::default()
13109        },
13110        Some(tree_sitter_rust::LANGUAGE.into()),
13111    )));
13112    let mut fake_servers = language_registry.register_fake_lsp(
13113        "Rust",
13114        FakeLspAdapter {
13115            name: language_server_name,
13116            initialization_options: Some(json!({
13117                "testOptionValue": true
13118            })),
13119            initializer: Some(Box::new(move |fake_server| {
13120                let task_restarts = Arc::clone(&closure_restarts);
13121                fake_server.set_request_handler::<lsp::request::Shutdown, _, _>(move |_, _| {
13122                    task_restarts.fetch_add(1, atomic::Ordering::Release);
13123                    futures::future::ready(Ok(()))
13124                });
13125            })),
13126            ..Default::default()
13127        },
13128    );
13129
13130    let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
13131    let _buffer = project
13132        .update(cx, |project, cx| {
13133            project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx)
13134        })
13135        .await
13136        .unwrap();
13137    let _fake_server = fake_servers.next().await.unwrap();
13138    update_test_language_settings(cx, |language_settings| {
13139        language_settings.languages.insert(
13140            language_name.clone(),
13141            LanguageSettingsContent {
13142                tab_size: NonZeroU32::new(8),
13143                ..Default::default()
13144            },
13145        );
13146    });
13147    cx.executor().run_until_parked();
13148    assert_eq!(
13149        server_restarts.load(atomic::Ordering::Acquire),
13150        0,
13151        "Should not restart LSP server on an unrelated change"
13152    );
13153
13154    update_test_project_settings(cx, |project_settings| {
13155        project_settings.lsp.insert(
13156            "Some other server name".into(),
13157            LspSettings {
13158                binary: None,
13159                settings: None,
13160                initialization_options: Some(json!({
13161                    "some other init value": false
13162                })),
13163                enable_lsp_tasks: false,
13164            },
13165        );
13166    });
13167    cx.executor().run_until_parked();
13168    assert_eq!(
13169        server_restarts.load(atomic::Ordering::Acquire),
13170        0,
13171        "Should not restart LSP server on an unrelated LSP settings change"
13172    );
13173
13174    update_test_project_settings(cx, |project_settings| {
13175        project_settings.lsp.insert(
13176            language_server_name.into(),
13177            LspSettings {
13178                binary: None,
13179                settings: None,
13180                initialization_options: Some(json!({
13181                    "anotherInitValue": false
13182                })),
13183                enable_lsp_tasks: false,
13184            },
13185        );
13186    });
13187    cx.executor().run_until_parked();
13188    assert_eq!(
13189        server_restarts.load(atomic::Ordering::Acquire),
13190        1,
13191        "Should restart LSP server on a related LSP settings change"
13192    );
13193
13194    update_test_project_settings(cx, |project_settings| {
13195        project_settings.lsp.insert(
13196            language_server_name.into(),
13197            LspSettings {
13198                binary: None,
13199                settings: None,
13200                initialization_options: Some(json!({
13201                    "anotherInitValue": false
13202                })),
13203                enable_lsp_tasks: false,
13204            },
13205        );
13206    });
13207    cx.executor().run_until_parked();
13208    assert_eq!(
13209        server_restarts.load(atomic::Ordering::Acquire),
13210        1,
13211        "Should not restart LSP server on a related LSP settings change that is the same"
13212    );
13213
13214    update_test_project_settings(cx, |project_settings| {
13215        project_settings.lsp.insert(
13216            language_server_name.into(),
13217            LspSettings {
13218                binary: None,
13219                settings: None,
13220                initialization_options: None,
13221                enable_lsp_tasks: false,
13222            },
13223        );
13224    });
13225    cx.executor().run_until_parked();
13226    assert_eq!(
13227        server_restarts.load(atomic::Ordering::Acquire),
13228        2,
13229        "Should restart LSP server on another related LSP settings change"
13230    );
13231}
13232
13233#[gpui::test]
13234async fn test_completions_with_additional_edits(cx: &mut TestAppContext) {
13235    init_test(cx, |_| {});
13236
13237    let mut cx = EditorLspTestContext::new_rust(
13238        lsp::ServerCapabilities {
13239            completion_provider: Some(lsp::CompletionOptions {
13240                trigger_characters: Some(vec![".".to_string()]),
13241                resolve_provider: Some(true),
13242                ..Default::default()
13243            }),
13244            ..Default::default()
13245        },
13246        cx,
13247    )
13248    .await;
13249
13250    cx.set_state("fn main() { let a = 2ˇ; }");
13251    cx.simulate_keystroke(".");
13252    let completion_item = lsp::CompletionItem {
13253        label: "some".into(),
13254        kind: Some(lsp::CompletionItemKind::SNIPPET),
13255        detail: Some("Wrap the expression in an `Option::Some`".to_string()),
13256        documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
13257            kind: lsp::MarkupKind::Markdown,
13258            value: "```rust\nSome(2)\n```".to_string(),
13259        })),
13260        deprecated: Some(false),
13261        sort_text: Some("fffffff2".to_string()),
13262        filter_text: Some("some".to_string()),
13263        insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
13264        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13265            range: lsp::Range {
13266                start: lsp::Position {
13267                    line: 0,
13268                    character: 22,
13269                },
13270                end: lsp::Position {
13271                    line: 0,
13272                    character: 22,
13273                },
13274            },
13275            new_text: "Some(2)".to_string(),
13276        })),
13277        additional_text_edits: Some(vec![lsp::TextEdit {
13278            range: lsp::Range {
13279                start: lsp::Position {
13280                    line: 0,
13281                    character: 20,
13282                },
13283                end: lsp::Position {
13284                    line: 0,
13285                    character: 22,
13286                },
13287            },
13288            new_text: "".to_string(),
13289        }]),
13290        ..Default::default()
13291    };
13292
13293    let closure_completion_item = completion_item.clone();
13294    let mut request = cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13295        let task_completion_item = closure_completion_item.clone();
13296        async move {
13297            Ok(Some(lsp::CompletionResponse::Array(vec![
13298                task_completion_item,
13299            ])))
13300        }
13301    });
13302
13303    request.next().await;
13304
13305    cx.condition(|editor, _| editor.context_menu_visible())
13306        .await;
13307    let apply_additional_edits = cx.update_editor(|editor, window, cx| {
13308        editor
13309            .confirm_completion(&ConfirmCompletion::default(), window, cx)
13310            .unwrap()
13311    });
13312    cx.assert_editor_state("fn main() { let a = 2.Some(2)ˇ; }");
13313
13314    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
13315        let task_completion_item = completion_item.clone();
13316        async move { Ok(task_completion_item) }
13317    })
13318    .next()
13319    .await
13320    .unwrap();
13321    apply_additional_edits.await.unwrap();
13322    cx.assert_editor_state("fn main() { let a = Some(2)ˇ; }");
13323}
13324
13325#[gpui::test]
13326async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut TestAppContext) {
13327    init_test(cx, |_| {});
13328
13329    let mut cx = EditorLspTestContext::new_rust(
13330        lsp::ServerCapabilities {
13331            completion_provider: Some(lsp::CompletionOptions {
13332                trigger_characters: Some(vec![".".to_string()]),
13333                resolve_provider: Some(true),
13334                ..Default::default()
13335            }),
13336            ..Default::default()
13337        },
13338        cx,
13339    )
13340    .await;
13341
13342    cx.set_state("fn main() { let a = 2ˇ; }");
13343    cx.simulate_keystroke(".");
13344
13345    let item1 = lsp::CompletionItem {
13346        label: "method id()".to_string(),
13347        filter_text: Some("id".to_string()),
13348        detail: None,
13349        documentation: None,
13350        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13351            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13352            new_text: ".id".to_string(),
13353        })),
13354        ..lsp::CompletionItem::default()
13355    };
13356
13357    let item2 = lsp::CompletionItem {
13358        label: "other".to_string(),
13359        filter_text: Some("other".to_string()),
13360        detail: None,
13361        documentation: None,
13362        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13363            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13364            new_text: ".other".to_string(),
13365        })),
13366        ..lsp::CompletionItem::default()
13367    };
13368
13369    let item1 = item1.clone();
13370    cx.set_request_handler::<lsp::request::Completion, _, _>({
13371        let item1 = item1.clone();
13372        move |_, _, _| {
13373            let item1 = item1.clone();
13374            let item2 = item2.clone();
13375            async move { Ok(Some(lsp::CompletionResponse::Array(vec![item1, item2]))) }
13376        }
13377    })
13378    .next()
13379    .await;
13380
13381    cx.condition(|editor, _| editor.context_menu_visible())
13382        .await;
13383    cx.update_editor(|editor, _, _| {
13384        let context_menu = editor.context_menu.borrow_mut();
13385        let context_menu = context_menu
13386            .as_ref()
13387            .expect("Should have the context menu deployed");
13388        match context_menu {
13389            CodeContextMenu::Completions(completions_menu) => {
13390                let completions = completions_menu.completions.borrow_mut();
13391                assert_eq!(
13392                    completions
13393                        .iter()
13394                        .map(|completion| &completion.label.text)
13395                        .collect::<Vec<_>>(),
13396                    vec!["method id()", "other"]
13397                )
13398            }
13399            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13400        }
13401    });
13402
13403    cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>({
13404        let item1 = item1.clone();
13405        move |_, item_to_resolve, _| {
13406            let item1 = item1.clone();
13407            async move {
13408                if item1 == item_to_resolve {
13409                    Ok(lsp::CompletionItem {
13410                        label: "method id()".to_string(),
13411                        filter_text: Some("id".to_string()),
13412                        detail: Some("Now resolved!".to_string()),
13413                        documentation: Some(lsp::Documentation::String("Docs".to_string())),
13414                        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13415                            range: lsp::Range::new(
13416                                lsp::Position::new(0, 22),
13417                                lsp::Position::new(0, 22),
13418                            ),
13419                            new_text: ".id".to_string(),
13420                        })),
13421                        ..lsp::CompletionItem::default()
13422                    })
13423                } else {
13424                    Ok(item_to_resolve)
13425                }
13426            }
13427        }
13428    })
13429    .next()
13430    .await
13431    .unwrap();
13432    cx.run_until_parked();
13433
13434    cx.update_editor(|editor, window, cx| {
13435        editor.context_menu_next(&Default::default(), window, cx);
13436    });
13437
13438    cx.update_editor(|editor, _, _| {
13439        let context_menu = editor.context_menu.borrow_mut();
13440        let context_menu = context_menu
13441            .as_ref()
13442            .expect("Should have the context menu deployed");
13443        match context_menu {
13444            CodeContextMenu::Completions(completions_menu) => {
13445                let completions = completions_menu.completions.borrow_mut();
13446                assert_eq!(
13447                    completions
13448                        .iter()
13449                        .map(|completion| &completion.label.text)
13450                        .collect::<Vec<_>>(),
13451                    vec!["method id() Now resolved!", "other"],
13452                    "Should update first completion label, but not second as the filter text did not match."
13453                );
13454            }
13455            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13456        }
13457    });
13458}
13459
13460#[gpui::test]
13461async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
13462    init_test(cx, |_| {});
13463
13464    let mut cx = EditorLspTestContext::new_rust(
13465        lsp::ServerCapabilities {
13466            completion_provider: Some(lsp::CompletionOptions {
13467                trigger_characters: Some(vec![".".to_string()]),
13468                resolve_provider: Some(true),
13469                ..Default::default()
13470            }),
13471            ..Default::default()
13472        },
13473        cx,
13474    )
13475    .await;
13476
13477    cx.set_state("fn main() { let a = 2ˇ; }");
13478    cx.simulate_keystroke(".");
13479
13480    let unresolved_item_1 = lsp::CompletionItem {
13481        label: "id".to_string(),
13482        filter_text: Some("id".to_string()),
13483        detail: None,
13484        documentation: None,
13485        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13486            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13487            new_text: ".id".to_string(),
13488        })),
13489        ..lsp::CompletionItem::default()
13490    };
13491    let resolved_item_1 = lsp::CompletionItem {
13492        additional_text_edits: Some(vec![lsp::TextEdit {
13493            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13494            new_text: "!!".to_string(),
13495        }]),
13496        ..unresolved_item_1.clone()
13497    };
13498    let unresolved_item_2 = lsp::CompletionItem {
13499        label: "other".to_string(),
13500        filter_text: Some("other".to_string()),
13501        detail: None,
13502        documentation: None,
13503        text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
13504            range: lsp::Range::new(lsp::Position::new(0, 22), lsp::Position::new(0, 22)),
13505            new_text: ".other".to_string(),
13506        })),
13507        ..lsp::CompletionItem::default()
13508    };
13509    let resolved_item_2 = lsp::CompletionItem {
13510        additional_text_edits: Some(vec![lsp::TextEdit {
13511            range: lsp::Range::new(lsp::Position::new(0, 20), lsp::Position::new(0, 22)),
13512            new_text: "??".to_string(),
13513        }]),
13514        ..unresolved_item_2.clone()
13515    };
13516
13517    let resolve_requests_1 = Arc::new(AtomicUsize::new(0));
13518    let resolve_requests_2 = Arc::new(AtomicUsize::new(0));
13519    cx.lsp
13520        .server
13521        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13522            let unresolved_item_1 = unresolved_item_1.clone();
13523            let resolved_item_1 = resolved_item_1.clone();
13524            let unresolved_item_2 = unresolved_item_2.clone();
13525            let resolved_item_2 = resolved_item_2.clone();
13526            let resolve_requests_1 = resolve_requests_1.clone();
13527            let resolve_requests_2 = resolve_requests_2.clone();
13528            move |unresolved_request, _| {
13529                let unresolved_item_1 = unresolved_item_1.clone();
13530                let resolved_item_1 = resolved_item_1.clone();
13531                let unresolved_item_2 = unresolved_item_2.clone();
13532                let resolved_item_2 = resolved_item_2.clone();
13533                let resolve_requests_1 = resolve_requests_1.clone();
13534                let resolve_requests_2 = resolve_requests_2.clone();
13535                async move {
13536                    if unresolved_request == unresolved_item_1 {
13537                        resolve_requests_1.fetch_add(1, atomic::Ordering::Release);
13538                        Ok(resolved_item_1.clone())
13539                    } else if unresolved_request == unresolved_item_2 {
13540                        resolve_requests_2.fetch_add(1, atomic::Ordering::Release);
13541                        Ok(resolved_item_2.clone())
13542                    } else {
13543                        panic!("Unexpected completion item {unresolved_request:?}")
13544                    }
13545                }
13546            }
13547        })
13548        .detach();
13549
13550    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13551        let unresolved_item_1 = unresolved_item_1.clone();
13552        let unresolved_item_2 = unresolved_item_2.clone();
13553        async move {
13554            Ok(Some(lsp::CompletionResponse::Array(vec![
13555                unresolved_item_1,
13556                unresolved_item_2,
13557            ])))
13558        }
13559    })
13560    .next()
13561    .await;
13562
13563    cx.condition(|editor, _| editor.context_menu_visible())
13564        .await;
13565    cx.update_editor(|editor, _, _| {
13566        let context_menu = editor.context_menu.borrow_mut();
13567        let context_menu = context_menu
13568            .as_ref()
13569            .expect("Should have the context menu deployed");
13570        match context_menu {
13571            CodeContextMenu::Completions(completions_menu) => {
13572                let completions = completions_menu.completions.borrow_mut();
13573                assert_eq!(
13574                    completions
13575                        .iter()
13576                        .map(|completion| &completion.label.text)
13577                        .collect::<Vec<_>>(),
13578                    vec!["id", "other"]
13579                )
13580            }
13581            CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
13582        }
13583    });
13584    cx.run_until_parked();
13585
13586    cx.update_editor(|editor, window, cx| {
13587        editor.context_menu_next(&ContextMenuNext, window, cx);
13588    });
13589    cx.run_until_parked();
13590    cx.update_editor(|editor, window, cx| {
13591        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13592    });
13593    cx.run_until_parked();
13594    cx.update_editor(|editor, window, cx| {
13595        editor.context_menu_next(&ContextMenuNext, window, cx);
13596    });
13597    cx.run_until_parked();
13598    cx.update_editor(|editor, window, cx| {
13599        editor
13600            .compose_completion(&ComposeCompletion::default(), window, cx)
13601            .expect("No task returned")
13602    })
13603    .await
13604    .expect("Completion failed");
13605    cx.run_until_parked();
13606
13607    cx.update_editor(|editor, _, cx| {
13608        assert_eq!(
13609            resolve_requests_1.load(atomic::Ordering::Acquire),
13610            1,
13611            "Should always resolve once despite multiple selections"
13612        );
13613        assert_eq!(
13614            resolve_requests_2.load(atomic::Ordering::Acquire),
13615            1,
13616            "Should always resolve once after multiple selections and applying the completion"
13617        );
13618        assert_eq!(
13619            editor.text(cx),
13620            "fn main() { let a = ??.other; }",
13621            "Should use resolved data when applying the completion"
13622        );
13623    });
13624}
13625
13626#[gpui::test]
13627async fn test_completions_default_resolve_data_handling(cx: &mut TestAppContext) {
13628    init_test(cx, |_| {});
13629
13630    let item_0 = lsp::CompletionItem {
13631        label: "abs".into(),
13632        insert_text: Some("abs".into()),
13633        data: Some(json!({ "very": "special"})),
13634        insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
13635        text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13636            lsp::InsertReplaceEdit {
13637                new_text: "abs".to_string(),
13638                insert: lsp::Range::default(),
13639                replace: lsp::Range::default(),
13640            },
13641        )),
13642        ..lsp::CompletionItem::default()
13643    };
13644    let items = iter::once(item_0.clone())
13645        .chain((11..51).map(|i| lsp::CompletionItem {
13646            label: format!("item_{}", i),
13647            insert_text: Some(format!("item_{}", i)),
13648            insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
13649            ..lsp::CompletionItem::default()
13650        }))
13651        .collect::<Vec<_>>();
13652
13653    let default_commit_characters = vec!["?".to_string()];
13654    let default_data = json!({ "default": "data"});
13655    let default_insert_text_format = lsp::InsertTextFormat::SNIPPET;
13656    let default_insert_text_mode = lsp::InsertTextMode::AS_IS;
13657    let default_edit_range = lsp::Range {
13658        start: lsp::Position {
13659            line: 0,
13660            character: 5,
13661        },
13662        end: lsp::Position {
13663            line: 0,
13664            character: 5,
13665        },
13666    };
13667
13668    let mut cx = EditorLspTestContext::new_rust(
13669        lsp::ServerCapabilities {
13670            completion_provider: Some(lsp::CompletionOptions {
13671                trigger_characters: Some(vec![".".to_string()]),
13672                resolve_provider: Some(true),
13673                ..Default::default()
13674            }),
13675            ..Default::default()
13676        },
13677        cx,
13678    )
13679    .await;
13680
13681    cx.set_state("fn main() { let a = 2ˇ; }");
13682    cx.simulate_keystroke(".");
13683
13684    let completion_data = default_data.clone();
13685    let completion_characters = default_commit_characters.clone();
13686    let completion_items = items.clone();
13687    cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
13688        let default_data = completion_data.clone();
13689        let default_commit_characters = completion_characters.clone();
13690        let items = completion_items.clone();
13691        async move {
13692            Ok(Some(lsp::CompletionResponse::List(lsp::CompletionList {
13693                items,
13694                item_defaults: Some(lsp::CompletionListItemDefaults {
13695                    data: Some(default_data.clone()),
13696                    commit_characters: Some(default_commit_characters.clone()),
13697                    edit_range: Some(lsp::CompletionListItemDefaultsEditRange::Range(
13698                        default_edit_range,
13699                    )),
13700                    insert_text_format: Some(default_insert_text_format),
13701                    insert_text_mode: Some(default_insert_text_mode),
13702                }),
13703                ..lsp::CompletionList::default()
13704            })))
13705        }
13706    })
13707    .next()
13708    .await;
13709
13710    let resolved_items = Arc::new(Mutex::new(Vec::new()));
13711    cx.lsp
13712        .server
13713        .on_request::<lsp::request::ResolveCompletionItem, _, _>({
13714            let closure_resolved_items = resolved_items.clone();
13715            move |item_to_resolve, _| {
13716                let closure_resolved_items = closure_resolved_items.clone();
13717                async move {
13718                    closure_resolved_items.lock().push(item_to_resolve.clone());
13719                    Ok(item_to_resolve)
13720                }
13721            }
13722        })
13723        .detach();
13724
13725    cx.condition(|editor, _| editor.context_menu_visible())
13726        .await;
13727    cx.run_until_parked();
13728    cx.update_editor(|editor, _, _| {
13729        let menu = editor.context_menu.borrow_mut();
13730        match menu.as_ref().expect("should have the completions menu") {
13731            CodeContextMenu::Completions(completions_menu) => {
13732                assert_eq!(
13733                    completions_menu
13734                        .entries
13735                        .borrow()
13736                        .iter()
13737                        .map(|mat| mat.string.clone())
13738                        .collect::<Vec<String>>(),
13739                    items
13740                        .iter()
13741                        .map(|completion| completion.label.clone())
13742                        .collect::<Vec<String>>()
13743                );
13744            }
13745            CodeContextMenu::CodeActions(_) => panic!("Expected to have the completions menu"),
13746        }
13747    });
13748    // Approximate initial displayed interval is 0..12. With extra item padding of 4 this is 0..16
13749    // with 4 from the end.
13750    assert_eq!(
13751        *resolved_items.lock(),
13752        [&items[0..16], &items[items.len() - 4..items.len()]]
13753            .concat()
13754            .iter()
13755            .cloned()
13756            .map(|mut item| {
13757                if item.data.is_none() {
13758                    item.data = Some(default_data.clone());
13759                }
13760                item
13761            })
13762            .collect::<Vec<lsp::CompletionItem>>(),
13763        "Items sent for resolve should be unchanged modulo resolve `data` filled with default if missing"
13764    );
13765    resolved_items.lock().clear();
13766
13767    cx.update_editor(|editor, window, cx| {
13768        editor.context_menu_prev(&ContextMenuPrevious, window, cx);
13769    });
13770    cx.run_until_parked();
13771    // Completions that have already been resolved are skipped.
13772    assert_eq!(
13773        *resolved_items.lock(),
13774        items[items.len() - 16..items.len() - 4]
13775            .iter()
13776            .cloned()
13777            .map(|mut item| {
13778                if item.data.is_none() {
13779                    item.data = Some(default_data.clone());
13780                }
13781                item
13782            })
13783            .collect::<Vec<lsp::CompletionItem>>()
13784    );
13785    resolved_items.lock().clear();
13786}
13787
13788#[gpui::test]
13789async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestAppContext) {
13790    init_test(cx, |_| {});
13791
13792    let mut cx = EditorLspTestContext::new(
13793        Language::new(
13794            LanguageConfig {
13795                matcher: LanguageMatcher {
13796                    path_suffixes: vec!["jsx".into()],
13797                    ..Default::default()
13798                },
13799                overrides: [(
13800                    "element".into(),
13801                    LanguageConfigOverride {
13802                        completion_query_characters: Override::Set(['-'].into_iter().collect()),
13803                        ..Default::default()
13804                    },
13805                )]
13806                .into_iter()
13807                .collect(),
13808                ..Default::default()
13809            },
13810            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
13811        )
13812        .with_override_query("(jsx_self_closing_element) @element")
13813        .unwrap(),
13814        lsp::ServerCapabilities {
13815            completion_provider: Some(lsp::CompletionOptions {
13816                trigger_characters: Some(vec![":".to_string()]),
13817                ..Default::default()
13818            }),
13819            ..Default::default()
13820        },
13821        cx,
13822    )
13823    .await;
13824
13825    cx.lsp
13826        .set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
13827            Ok(Some(lsp::CompletionResponse::Array(vec![
13828                lsp::CompletionItem {
13829                    label: "bg-blue".into(),
13830                    ..Default::default()
13831                },
13832                lsp::CompletionItem {
13833                    label: "bg-red".into(),
13834                    ..Default::default()
13835                },
13836                lsp::CompletionItem {
13837                    label: "bg-yellow".into(),
13838                    ..Default::default()
13839                },
13840            ])))
13841        });
13842
13843    cx.set_state(r#"<p class="bgˇ" />"#);
13844
13845    // Trigger completion when typing a dash, because the dash is an extra
13846    // word character in the 'element' scope, which contains the cursor.
13847    cx.simulate_keystroke("-");
13848    cx.executor().run_until_parked();
13849    cx.update_editor(|editor, _, _| {
13850        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13851        {
13852            assert_eq!(
13853                completion_menu_entries(&menu),
13854                &["bg-red", "bg-blue", "bg-yellow"]
13855            );
13856        } else {
13857            panic!("expected completion menu to be open");
13858        }
13859    });
13860
13861    cx.simulate_keystroke("l");
13862    cx.executor().run_until_parked();
13863    cx.update_editor(|editor, _, _| {
13864        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13865        {
13866            assert_eq!(completion_menu_entries(&menu), &["bg-blue", "bg-yellow"]);
13867        } else {
13868            panic!("expected completion menu to be open");
13869        }
13870    });
13871
13872    // When filtering completions, consider the character after the '-' to
13873    // be the start of a subword.
13874    cx.set_state(r#"<p class="yelˇ" />"#);
13875    cx.simulate_keystroke("l");
13876    cx.executor().run_until_parked();
13877    cx.update_editor(|editor, _, _| {
13878        if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
13879        {
13880            assert_eq!(completion_menu_entries(&menu), &["bg-yellow"]);
13881        } else {
13882            panic!("expected completion menu to be open");
13883        }
13884    });
13885}
13886
13887fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
13888    let entries = menu.entries.borrow();
13889    entries.iter().map(|mat| mat.string.clone()).collect()
13890}
13891
13892#[gpui::test]
13893async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
13894    init_test(cx, |settings| {
13895        settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
13896            FormatterList(vec![Formatter::Prettier].into()),
13897        ))
13898    });
13899
13900    let fs = FakeFs::new(cx.executor());
13901    fs.insert_file(path!("/file.ts"), Default::default()).await;
13902
13903    let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await;
13904    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
13905
13906    language_registry.add(Arc::new(Language::new(
13907        LanguageConfig {
13908            name: "TypeScript".into(),
13909            matcher: LanguageMatcher {
13910                path_suffixes: vec!["ts".to_string()],
13911                ..Default::default()
13912            },
13913            ..Default::default()
13914        },
13915        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
13916    )));
13917    update_test_language_settings(cx, |settings| {
13918        settings.defaults.prettier = Some(PrettierSettings {
13919            allowed: true,
13920            ..PrettierSettings::default()
13921        });
13922    });
13923
13924    let test_plugin = "test_plugin";
13925    let _ = language_registry.register_fake_lsp(
13926        "TypeScript",
13927        FakeLspAdapter {
13928            prettier_plugins: vec![test_plugin],
13929            ..Default::default()
13930        },
13931    );
13932
13933    let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX;
13934    let buffer = project
13935        .update(cx, |project, cx| {
13936            project.open_local_buffer(path!("/file.ts"), cx)
13937        })
13938        .await
13939        .unwrap();
13940
13941    let buffer_text = "one\ntwo\nthree\n";
13942    let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
13943    let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
13944    editor.update_in(cx, |editor, window, cx| {
13945        editor.set_text(buffer_text, window, cx)
13946    });
13947
13948    editor
13949        .update_in(cx, |editor, window, cx| {
13950            editor.perform_format(
13951                project.clone(),
13952                FormatTrigger::Manual,
13953                FormatTarget::Buffers,
13954                window,
13955                cx,
13956            )
13957        })
13958        .unwrap()
13959        .await;
13960    assert_eq!(
13961        editor.update(cx, |editor, cx| editor.text(cx)),
13962        buffer_text.to_string() + prettier_format_suffix,
13963        "Test prettier formatting was not applied to the original buffer text",
13964    );
13965
13966    update_test_language_settings(cx, |settings| {
13967        settings.defaults.formatter = Some(language_settings::SelectedFormatter::Auto)
13968    });
13969    let format = editor.update_in(cx, |editor, window, cx| {
13970        editor.perform_format(
13971            project.clone(),
13972            FormatTrigger::Manual,
13973            FormatTarget::Buffers,
13974            window,
13975            cx,
13976        )
13977    });
13978    format.await.unwrap();
13979    assert_eq!(
13980        editor.update(cx, |editor, cx| editor.text(cx)),
13981        buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix,
13982        "Autoformatting (via test prettier) was not applied to the original buffer text",
13983    );
13984}
13985
13986#[gpui::test]
13987async fn test_addition_reverts(cx: &mut TestAppContext) {
13988    init_test(cx, |_| {});
13989    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
13990    let base_text = indoc! {r#"
13991        struct Row;
13992        struct Row1;
13993        struct Row2;
13994
13995        struct Row4;
13996        struct Row5;
13997        struct Row6;
13998
13999        struct Row8;
14000        struct Row9;
14001        struct Row10;"#};
14002
14003    // When addition hunks are not adjacent to carets, no hunk revert is performed
14004    assert_hunk_revert(
14005        indoc! {r#"struct Row;
14006                   struct Row1;
14007                   struct Row1.1;
14008                   struct Row1.2;
14009                   struct Row2;ˇ
14010
14011                   struct Row4;
14012                   struct Row5;
14013                   struct Row6;
14014
14015                   struct Row8;
14016                   ˇstruct Row9;
14017                   struct Row9.1;
14018                   struct Row9.2;
14019                   struct Row9.3;
14020                   struct Row10;"#},
14021        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14022        indoc! {r#"struct Row;
14023                   struct Row1;
14024                   struct Row1.1;
14025                   struct Row1.2;
14026                   struct Row2;ˇ
14027
14028                   struct Row4;
14029                   struct Row5;
14030                   struct Row6;
14031
14032                   struct Row8;
14033                   ˇstruct Row9;
14034                   struct Row9.1;
14035                   struct Row9.2;
14036                   struct Row9.3;
14037                   struct Row10;"#},
14038        base_text,
14039        &mut cx,
14040    );
14041    // Same for selections
14042    assert_hunk_revert(
14043        indoc! {r#"struct Row;
14044                   struct Row1;
14045                   struct Row2;
14046                   struct Row2.1;
14047                   struct Row2.2;
14048                   «ˇ
14049                   struct Row4;
14050                   struct» Row5;
14051                   «struct Row6;
14052                   ˇ»
14053                   struct Row9.1;
14054                   struct Row9.2;
14055                   struct Row9.3;
14056                   struct Row8;
14057                   struct Row9;
14058                   struct Row10;"#},
14059        vec![DiffHunkStatusKind::Added, DiffHunkStatusKind::Added],
14060        indoc! {r#"struct Row;
14061                   struct Row1;
14062                   struct Row2;
14063                   struct Row2.1;
14064                   struct Row2.2;
14065                   «ˇ
14066                   struct Row4;
14067                   struct» Row5;
14068                   «struct Row6;
14069                   ˇ»
14070                   struct Row9.1;
14071                   struct Row9.2;
14072                   struct Row9.3;
14073                   struct Row8;
14074                   struct Row9;
14075                   struct Row10;"#},
14076        base_text,
14077        &mut cx,
14078    );
14079
14080    // When carets and selections intersect the addition hunks, those are reverted.
14081    // Adjacent carets got merged.
14082    assert_hunk_revert(
14083        indoc! {r#"struct Row;
14084                   ˇ// something on the top
14085                   struct Row1;
14086                   struct Row2;
14087                   struct Roˇw3.1;
14088                   struct Row2.2;
14089                   struct Row2.3;ˇ
14090
14091                   struct Row4;
14092                   struct ˇRow5.1;
14093                   struct Row5.2;
14094                   struct «Rowˇ»5.3;
14095                   struct Row5;
14096                   struct Row6;
14097                   ˇ
14098                   struct Row9.1;
14099                   struct «Rowˇ»9.2;
14100                   struct «ˇRow»9.3;
14101                   struct Row8;
14102                   struct Row9;
14103                   «ˇ// something on bottom»
14104                   struct Row10;"#},
14105        vec![
14106            DiffHunkStatusKind::Added,
14107            DiffHunkStatusKind::Added,
14108            DiffHunkStatusKind::Added,
14109            DiffHunkStatusKind::Added,
14110            DiffHunkStatusKind::Added,
14111        ],
14112        indoc! {r#"struct Row;
14113                   ˇstruct Row1;
14114                   struct Row2;
14115                   ˇ
14116                   struct Row4;
14117                   ˇstruct Row5;
14118                   struct Row6;
14119                   ˇ
14120                   ˇstruct Row8;
14121                   struct Row9;
14122                   ˇstruct Row10;"#},
14123        base_text,
14124        &mut cx,
14125    );
14126}
14127
14128#[gpui::test]
14129async fn test_modification_reverts(cx: &mut TestAppContext) {
14130    init_test(cx, |_| {});
14131    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14132    let base_text = indoc! {r#"
14133        struct Row;
14134        struct Row1;
14135        struct Row2;
14136
14137        struct Row4;
14138        struct Row5;
14139        struct Row6;
14140
14141        struct Row8;
14142        struct Row9;
14143        struct Row10;"#};
14144
14145    // Modification hunks behave the same as the addition ones.
14146    assert_hunk_revert(
14147        indoc! {r#"struct Row;
14148                   struct Row1;
14149                   struct Row33;
14150                   ˇ
14151                   struct Row4;
14152                   struct Row5;
14153                   struct Row6;
14154                   ˇ
14155                   struct Row99;
14156                   struct Row9;
14157                   struct Row10;"#},
14158        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14159        indoc! {r#"struct Row;
14160                   struct Row1;
14161                   struct Row33;
14162                   ˇ
14163                   struct Row4;
14164                   struct Row5;
14165                   struct Row6;
14166                   ˇ
14167                   struct Row99;
14168                   struct Row9;
14169                   struct Row10;"#},
14170        base_text,
14171        &mut cx,
14172    );
14173    assert_hunk_revert(
14174        indoc! {r#"struct Row;
14175                   struct Row1;
14176                   struct Row33;
14177                   «ˇ
14178                   struct Row4;
14179                   struct» Row5;
14180                   «struct Row6;
14181                   ˇ»
14182                   struct Row99;
14183                   struct Row9;
14184                   struct Row10;"#},
14185        vec![DiffHunkStatusKind::Modified, DiffHunkStatusKind::Modified],
14186        indoc! {r#"struct Row;
14187                   struct Row1;
14188                   struct Row33;
14189                   «ˇ
14190                   struct Row4;
14191                   struct» Row5;
14192                   «struct Row6;
14193                   ˇ»
14194                   struct Row99;
14195                   struct Row9;
14196                   struct Row10;"#},
14197        base_text,
14198        &mut cx,
14199    );
14200
14201    assert_hunk_revert(
14202        indoc! {r#"ˇstruct Row1.1;
14203                   struct Row1;
14204                   «ˇstr»uct Row22;
14205
14206                   struct ˇRow44;
14207                   struct Row5;
14208                   struct «Rˇ»ow66;ˇ
14209
14210                   «struˇ»ct Row88;
14211                   struct Row9;
14212                   struct Row1011;ˇ"#},
14213        vec![
14214            DiffHunkStatusKind::Modified,
14215            DiffHunkStatusKind::Modified,
14216            DiffHunkStatusKind::Modified,
14217            DiffHunkStatusKind::Modified,
14218            DiffHunkStatusKind::Modified,
14219            DiffHunkStatusKind::Modified,
14220        ],
14221        indoc! {r#"struct Row;
14222                   ˇstruct Row1;
14223                   struct Row2;
14224                   ˇ
14225                   struct Row4;
14226                   ˇstruct Row5;
14227                   struct Row6;
14228                   ˇ
14229                   struct Row8;
14230                   ˇstruct Row9;
14231                   struct Row10;ˇ"#},
14232        base_text,
14233        &mut cx,
14234    );
14235}
14236
14237#[gpui::test]
14238async fn test_deleting_over_diff_hunk(cx: &mut TestAppContext) {
14239    init_test(cx, |_| {});
14240    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14241    let base_text = indoc! {r#"
14242        one
14243
14244        two
14245        three
14246        "#};
14247
14248    cx.set_head_text(base_text);
14249    cx.set_state("\nˇ\n");
14250    cx.executor().run_until_parked();
14251    cx.update_editor(|editor, _window, cx| {
14252        editor.expand_selected_diff_hunks(cx);
14253    });
14254    cx.executor().run_until_parked();
14255    cx.update_editor(|editor, window, cx| {
14256        editor.backspace(&Default::default(), window, cx);
14257    });
14258    cx.run_until_parked();
14259    cx.assert_state_with_diff(
14260        indoc! {r#"
14261
14262        - two
14263        - threeˇ
14264        +
14265        "#}
14266        .to_string(),
14267    );
14268}
14269
14270#[gpui::test]
14271async fn test_deletion_reverts(cx: &mut TestAppContext) {
14272    init_test(cx, |_| {});
14273    let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
14274    let base_text = indoc! {r#"struct Row;
14275struct Row1;
14276struct Row2;
14277
14278struct Row4;
14279struct Row5;
14280struct Row6;
14281
14282struct Row8;
14283struct Row9;
14284struct Row10;"#};
14285
14286    // Deletion hunks trigger with carets on adjacent rows, so carets and selections have to stay farther to avoid the revert
14287    assert_hunk_revert(
14288        indoc! {r#"struct Row;
14289                   struct Row2;
14290
14291                   ˇstruct Row4;
14292                   struct Row5;
14293                   struct Row6;
14294                   ˇ
14295                   struct Row8;
14296                   struct Row10;"#},
14297        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14298        indoc! {r#"struct Row;
14299                   struct Row2;
14300
14301                   ˇstruct Row4;
14302                   struct Row5;
14303                   struct Row6;
14304                   ˇ
14305                   struct Row8;
14306                   struct Row10;"#},
14307        base_text,
14308        &mut cx,
14309    );
14310    assert_hunk_revert(
14311        indoc! {r#"struct Row;
14312                   struct Row2;
14313
14314                   «ˇstruct Row4;
14315                   struct» Row5;
14316                   «struct Row6;
14317                   ˇ»
14318                   struct Row8;
14319                   struct Row10;"#},
14320        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14321        indoc! {r#"struct Row;
14322                   struct Row2;
14323
14324                   «ˇstruct Row4;
14325                   struct» Row5;
14326                   «struct Row6;
14327                   ˇ»
14328                   struct Row8;
14329                   struct Row10;"#},
14330        base_text,
14331        &mut cx,
14332    );
14333
14334    // Deletion hunks are ephemeral, so it's impossible to place the caret into them — Zed triggers reverts for lines, adjacent to carets and selections.
14335    assert_hunk_revert(
14336        indoc! {r#"struct Row;
14337                   ˇstruct Row2;
14338
14339                   struct Row4;
14340                   struct Row5;
14341                   struct Row6;
14342
14343                   struct Row8;ˇ
14344                   struct Row10;"#},
14345        vec![DiffHunkStatusKind::Deleted, DiffHunkStatusKind::Deleted],
14346        indoc! {r#"struct Row;
14347                   struct Row1;
14348                   ˇstruct Row2;
14349
14350                   struct Row4;
14351                   struct Row5;
14352                   struct Row6;
14353
14354                   struct Row8;ˇ
14355                   struct Row9;
14356                   struct Row10;"#},
14357        base_text,
14358        &mut cx,
14359    );
14360    assert_hunk_revert(
14361        indoc! {r#"struct Row;
14362                   struct Row2«ˇ;
14363                   struct Row4;
14364                   struct» Row5;
14365                   «struct Row6;
14366
14367                   struct Row8;ˇ»
14368                   struct Row10;"#},
14369        vec![
14370            DiffHunkStatusKind::Deleted,
14371            DiffHunkStatusKind::Deleted,
14372            DiffHunkStatusKind::Deleted,
14373        ],
14374        indoc! {r#"struct Row;
14375                   struct Row1;
14376                   struct Row2«ˇ;
14377
14378                   struct Row4;
14379                   struct» Row5;
14380                   «struct Row6;
14381
14382                   struct Row8;ˇ»
14383                   struct Row9;
14384                   struct Row10;"#},
14385        base_text,
14386        &mut cx,
14387    );
14388}
14389
14390#[gpui::test]
14391async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
14392    init_test(cx, |_| {});
14393
14394    let base_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj";
14395    let base_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu";
14396    let base_text_3 =
14397        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}";
14398
14399    let text_1 = edit_first_char_of_every_line(base_text_1);
14400    let text_2 = edit_first_char_of_every_line(base_text_2);
14401    let text_3 = edit_first_char_of_every_line(base_text_3);
14402
14403    let buffer_1 = cx.new(|cx| Buffer::local(text_1.clone(), cx));
14404    let buffer_2 = cx.new(|cx| Buffer::local(text_2.clone(), cx));
14405    let buffer_3 = cx.new(|cx| Buffer::local(text_3.clone(), cx));
14406
14407    let multibuffer = cx.new(|cx| {
14408        let mut multibuffer = MultiBuffer::new(ReadWrite);
14409        multibuffer.push_excerpts(
14410            buffer_1.clone(),
14411            [
14412                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14413                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14414                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14415            ],
14416            cx,
14417        );
14418        multibuffer.push_excerpts(
14419            buffer_2.clone(),
14420            [
14421                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14422                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14423                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14424            ],
14425            cx,
14426        );
14427        multibuffer.push_excerpts(
14428            buffer_3.clone(),
14429            [
14430                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14431                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14432                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14433            ],
14434            cx,
14435        );
14436        multibuffer
14437    });
14438
14439    let fs = FakeFs::new(cx.executor());
14440    let project = Project::test(fs, [path!("/").as_ref()], cx).await;
14441    let (editor, cx) = cx
14442        .add_window_view(|window, cx| build_editor_with_project(project, multibuffer, window, cx));
14443    editor.update_in(cx, |editor, _window, cx| {
14444        for (buffer, diff_base) in [
14445            (buffer_1.clone(), base_text_1),
14446            (buffer_2.clone(), base_text_2),
14447            (buffer_3.clone(), base_text_3),
14448        ] {
14449            let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
14450            editor
14451                .buffer
14452                .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
14453        }
14454    });
14455    cx.executor().run_until_parked();
14456
14457    editor.update_in(cx, |editor, window, cx| {
14458        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}");
14459        editor.select_all(&SelectAll, window, cx);
14460        editor.git_restore(&Default::default(), window, cx);
14461    });
14462    cx.executor().run_until_parked();
14463
14464    // When all ranges are selected, all buffer hunks are reverted.
14465    editor.update(cx, |editor, cx| {
14466        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");
14467    });
14468    buffer_1.update(cx, |buffer, _| {
14469        assert_eq!(buffer.text(), base_text_1);
14470    });
14471    buffer_2.update(cx, |buffer, _| {
14472        assert_eq!(buffer.text(), base_text_2);
14473    });
14474    buffer_3.update(cx, |buffer, _| {
14475        assert_eq!(buffer.text(), base_text_3);
14476    });
14477
14478    editor.update_in(cx, |editor, window, cx| {
14479        editor.undo(&Default::default(), window, cx);
14480    });
14481
14482    editor.update_in(cx, |editor, window, cx| {
14483        editor.change_selections(None, window, cx, |s| {
14484            s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
14485        });
14486        editor.git_restore(&Default::default(), window, cx);
14487    });
14488
14489    // Now, when all ranges selected belong to buffer_1, the revert should succeed,
14490    // but not affect buffer_2 and its related excerpts.
14491    editor.update(cx, |editor, cx| {
14492        assert_eq!(
14493            editor.text(cx),
14494            "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}"
14495        );
14496    });
14497    buffer_1.update(cx, |buffer, _| {
14498        assert_eq!(buffer.text(), base_text_1);
14499    });
14500    buffer_2.update(cx, |buffer, _| {
14501        assert_eq!(
14502            buffer.text(),
14503            "Xlll\nXmmm\nXnnn\nXooo\nXppp\nXqqq\nXrrr\nXsss\nXttt\nXuuu"
14504        );
14505    });
14506    buffer_3.update(cx, |buffer, _| {
14507        assert_eq!(
14508            buffer.text(),
14509            "Xvvv\nXwww\nXxxx\nXyyy\nXzzz\nX{{{\nX|||\nX}}}\nX~~~\nX\u{7f}\u{7f}\u{7f}"
14510        );
14511    });
14512
14513    fn edit_first_char_of_every_line(text: &str) -> String {
14514        text.split('\n')
14515            .map(|line| format!("X{}", &line[1..]))
14516            .collect::<Vec<_>>()
14517            .join("\n")
14518    }
14519}
14520
14521#[gpui::test]
14522async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) {
14523    init_test(cx, |_| {});
14524
14525    let cols = 4;
14526    let rows = 10;
14527    let sample_text_1 = sample_text(rows, cols, 'a');
14528    assert_eq!(
14529        sample_text_1,
14530        "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj"
14531    );
14532    let sample_text_2 = sample_text(rows, cols, 'l');
14533    assert_eq!(
14534        sample_text_2,
14535        "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
14536    );
14537    let sample_text_3 = sample_text(rows, cols, 'v');
14538    assert_eq!(
14539        sample_text_3,
14540        "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
14541    );
14542
14543    let buffer_1 = cx.new(|cx| Buffer::local(sample_text_1.clone(), cx));
14544    let buffer_2 = cx.new(|cx| Buffer::local(sample_text_2.clone(), cx));
14545    let buffer_3 = cx.new(|cx| Buffer::local(sample_text_3.clone(), cx));
14546
14547    let multi_buffer = cx.new(|cx| {
14548        let mut multibuffer = MultiBuffer::new(ReadWrite);
14549        multibuffer.push_excerpts(
14550            buffer_1.clone(),
14551            [
14552                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14553                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14554                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14555            ],
14556            cx,
14557        );
14558        multibuffer.push_excerpts(
14559            buffer_2.clone(),
14560            [
14561                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14562                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14563                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14564            ],
14565            cx,
14566        );
14567        multibuffer.push_excerpts(
14568            buffer_3.clone(),
14569            [
14570                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
14571                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
14572                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
14573            ],
14574            cx,
14575        );
14576        multibuffer
14577    });
14578
14579    let fs = FakeFs::new(cx.executor());
14580    fs.insert_tree(
14581        "/a",
14582        json!({
14583            "main.rs": sample_text_1,
14584            "other.rs": sample_text_2,
14585            "lib.rs": sample_text_3,
14586        }),
14587    )
14588    .await;
14589    let project = Project::test(fs, ["/a".as_ref()], cx).await;
14590    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
14591    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
14592    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
14593        Editor::new(
14594            EditorMode::full(),
14595            multi_buffer,
14596            Some(project.clone()),
14597            window,
14598            cx,
14599        )
14600    });
14601    let multibuffer_item_id = workspace
14602        .update(cx, |workspace, window, cx| {
14603            assert!(
14604                workspace.active_item(cx).is_none(),
14605                "active item should be None before the first item is added"
14606            );
14607            workspace.add_item_to_active_pane(
14608                Box::new(multi_buffer_editor.clone()),
14609                None,
14610                true,
14611                window,
14612                cx,
14613            );
14614            let active_item = workspace
14615                .active_item(cx)
14616                .expect("should have an active item after adding the multi buffer");
14617            assert!(
14618                !active_item.is_singleton(cx),
14619                "A multi buffer was expected to active after adding"
14620            );
14621            active_item.item_id()
14622        })
14623        .unwrap();
14624    cx.executor().run_until_parked();
14625
14626    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14627        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14628            s.select_ranges(Some(1..2))
14629        });
14630        editor.open_excerpts(&OpenExcerpts, window, cx);
14631    });
14632    cx.executor().run_until_parked();
14633    let first_item_id = workspace
14634        .update(cx, |workspace, window, cx| {
14635            let active_item = workspace
14636                .active_item(cx)
14637                .expect("should have an active item after navigating into the 1st buffer");
14638            let first_item_id = active_item.item_id();
14639            assert_ne!(
14640                first_item_id, multibuffer_item_id,
14641                "Should navigate into the 1st buffer and activate it"
14642            );
14643            assert!(
14644                active_item.is_singleton(cx),
14645                "New active item should be a singleton buffer"
14646            );
14647            assert_eq!(
14648                active_item
14649                    .act_as::<Editor>(cx)
14650                    .expect("should have navigated into an editor for the 1st buffer")
14651                    .read(cx)
14652                    .text(cx),
14653                sample_text_1
14654            );
14655
14656            workspace
14657                .go_back(workspace.active_pane().downgrade(), window, cx)
14658                .detach_and_log_err(cx);
14659
14660            first_item_id
14661        })
14662        .unwrap();
14663    cx.executor().run_until_parked();
14664    workspace
14665        .update(cx, |workspace, _, cx| {
14666            let active_item = workspace
14667                .active_item(cx)
14668                .expect("should have an active item after navigating back");
14669            assert_eq!(
14670                active_item.item_id(),
14671                multibuffer_item_id,
14672                "Should navigate back to the multi buffer"
14673            );
14674            assert!(!active_item.is_singleton(cx));
14675        })
14676        .unwrap();
14677
14678    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14679        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14680            s.select_ranges(Some(39..40))
14681        });
14682        editor.open_excerpts(&OpenExcerpts, window, cx);
14683    });
14684    cx.executor().run_until_parked();
14685    let second_item_id = workspace
14686        .update(cx, |workspace, window, cx| {
14687            let active_item = workspace
14688                .active_item(cx)
14689                .expect("should have an active item after navigating into the 2nd buffer");
14690            let second_item_id = active_item.item_id();
14691            assert_ne!(
14692                second_item_id, multibuffer_item_id,
14693                "Should navigate away from the multibuffer"
14694            );
14695            assert_ne!(
14696                second_item_id, first_item_id,
14697                "Should navigate into the 2nd buffer and activate it"
14698            );
14699            assert!(
14700                active_item.is_singleton(cx),
14701                "New active item should be a singleton buffer"
14702            );
14703            assert_eq!(
14704                active_item
14705                    .act_as::<Editor>(cx)
14706                    .expect("should have navigated into an editor")
14707                    .read(cx)
14708                    .text(cx),
14709                sample_text_2
14710            );
14711
14712            workspace
14713                .go_back(workspace.active_pane().downgrade(), window, cx)
14714                .detach_and_log_err(cx);
14715
14716            second_item_id
14717        })
14718        .unwrap();
14719    cx.executor().run_until_parked();
14720    workspace
14721        .update(cx, |workspace, _, cx| {
14722            let active_item = workspace
14723                .active_item(cx)
14724                .expect("should have an active item after navigating back from the 2nd buffer");
14725            assert_eq!(
14726                active_item.item_id(),
14727                multibuffer_item_id,
14728                "Should navigate back from the 2nd buffer to the multi buffer"
14729            );
14730            assert!(!active_item.is_singleton(cx));
14731        })
14732        .unwrap();
14733
14734    multi_buffer_editor.update_in(cx, |editor, window, cx| {
14735        editor.change_selections(Some(Autoscroll::Next), window, cx, |s| {
14736            s.select_ranges(Some(70..70))
14737        });
14738        editor.open_excerpts(&OpenExcerpts, window, cx);
14739    });
14740    cx.executor().run_until_parked();
14741    workspace
14742        .update(cx, |workspace, window, cx| {
14743            let active_item = workspace
14744                .active_item(cx)
14745                .expect("should have an active item after navigating into the 3rd buffer");
14746            let third_item_id = active_item.item_id();
14747            assert_ne!(
14748                third_item_id, multibuffer_item_id,
14749                "Should navigate into the 3rd buffer and activate it"
14750            );
14751            assert_ne!(third_item_id, first_item_id);
14752            assert_ne!(third_item_id, second_item_id);
14753            assert!(
14754                active_item.is_singleton(cx),
14755                "New active item should be a singleton buffer"
14756            );
14757            assert_eq!(
14758                active_item
14759                    .act_as::<Editor>(cx)
14760                    .expect("should have navigated into an editor")
14761                    .read(cx)
14762                    .text(cx),
14763                sample_text_3
14764            );
14765
14766            workspace
14767                .go_back(workspace.active_pane().downgrade(), window, cx)
14768                .detach_and_log_err(cx);
14769        })
14770        .unwrap();
14771    cx.executor().run_until_parked();
14772    workspace
14773        .update(cx, |workspace, _, cx| {
14774            let active_item = workspace
14775                .active_item(cx)
14776                .expect("should have an active item after navigating back from the 3rd buffer");
14777            assert_eq!(
14778                active_item.item_id(),
14779                multibuffer_item_id,
14780                "Should navigate back from the 3rd buffer to the multi buffer"
14781            );
14782            assert!(!active_item.is_singleton(cx));
14783        })
14784        .unwrap();
14785}
14786
14787#[gpui::test]
14788async fn test_toggle_selected_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
14789    init_test(cx, |_| {});
14790
14791    let mut cx = EditorTestContext::new(cx).await;
14792
14793    let diff_base = r#"
14794        use some::mod;
14795
14796        const A: u32 = 42;
14797
14798        fn main() {
14799            println!("hello");
14800
14801            println!("world");
14802        }
14803        "#
14804    .unindent();
14805
14806    cx.set_state(
14807        &r#"
14808        use some::modified;
14809
14810        ˇ
14811        fn main() {
14812            println!("hello there");
14813
14814            println!("around the");
14815            println!("world");
14816        }
14817        "#
14818        .unindent(),
14819    );
14820
14821    cx.set_head_text(&diff_base);
14822    executor.run_until_parked();
14823
14824    cx.update_editor(|editor, window, cx| {
14825        editor.go_to_next_hunk(&GoToHunk, window, cx);
14826        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14827    });
14828    executor.run_until_parked();
14829    cx.assert_state_with_diff(
14830        r#"
14831          use some::modified;
14832
14833
14834          fn main() {
14835        -     println!("hello");
14836        + ˇ    println!("hello there");
14837
14838              println!("around the");
14839              println!("world");
14840          }
14841        "#
14842        .unindent(),
14843    );
14844
14845    cx.update_editor(|editor, window, cx| {
14846        for _ in 0..2 {
14847            editor.go_to_next_hunk(&GoToHunk, window, cx);
14848            editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14849        }
14850    });
14851    executor.run_until_parked();
14852    cx.assert_state_with_diff(
14853        r#"
14854        - use some::mod;
14855        + ˇuse some::modified;
14856
14857
14858          fn main() {
14859        -     println!("hello");
14860        +     println!("hello there");
14861
14862        +     println!("around the");
14863              println!("world");
14864          }
14865        "#
14866        .unindent(),
14867    );
14868
14869    cx.update_editor(|editor, window, cx| {
14870        editor.go_to_next_hunk(&GoToHunk, window, cx);
14871        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
14872    });
14873    executor.run_until_parked();
14874    cx.assert_state_with_diff(
14875        r#"
14876        - use some::mod;
14877        + use some::modified;
14878
14879        - const A: u32 = 42;
14880          ˇ
14881          fn main() {
14882        -     println!("hello");
14883        +     println!("hello there");
14884
14885        +     println!("around the");
14886              println!("world");
14887          }
14888        "#
14889        .unindent(),
14890    );
14891
14892    cx.update_editor(|editor, window, cx| {
14893        editor.cancel(&Cancel, window, cx);
14894    });
14895
14896    cx.assert_state_with_diff(
14897        r#"
14898          use some::modified;
14899
14900          ˇ
14901          fn main() {
14902              println!("hello there");
14903
14904              println!("around the");
14905              println!("world");
14906          }
14907        "#
14908        .unindent(),
14909    );
14910}
14911
14912#[gpui::test]
14913async fn test_diff_base_change_with_expanded_diff_hunks(
14914    executor: BackgroundExecutor,
14915    cx: &mut TestAppContext,
14916) {
14917    init_test(cx, |_| {});
14918
14919    let mut cx = EditorTestContext::new(cx).await;
14920
14921    let diff_base = r#"
14922        use some::mod1;
14923        use some::mod2;
14924
14925        const A: u32 = 42;
14926        const B: u32 = 42;
14927        const C: u32 = 42;
14928
14929        fn main() {
14930            println!("hello");
14931
14932            println!("world");
14933        }
14934        "#
14935    .unindent();
14936
14937    cx.set_state(
14938        &r#"
14939        use some::mod2;
14940
14941        const A: u32 = 42;
14942        const C: u32 = 42;
14943
14944        fn main(ˇ) {
14945            //println!("hello");
14946
14947            println!("world");
14948            //
14949            //
14950        }
14951        "#
14952        .unindent(),
14953    );
14954
14955    cx.set_head_text(&diff_base);
14956    executor.run_until_parked();
14957
14958    cx.update_editor(|editor, window, cx| {
14959        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
14960    });
14961    executor.run_until_parked();
14962    cx.assert_state_with_diff(
14963        r#"
14964        - use some::mod1;
14965          use some::mod2;
14966
14967          const A: u32 = 42;
14968        - const B: u32 = 42;
14969          const C: u32 = 42;
14970
14971          fn main(ˇ) {
14972        -     println!("hello");
14973        +     //println!("hello");
14974
14975              println!("world");
14976        +     //
14977        +     //
14978          }
14979        "#
14980        .unindent(),
14981    );
14982
14983    cx.set_head_text("new diff base!");
14984    executor.run_until_parked();
14985    cx.assert_state_with_diff(
14986        r#"
14987        - new diff base!
14988        + use some::mod2;
14989        +
14990        + const A: u32 = 42;
14991        + const C: u32 = 42;
14992        +
14993        + fn main(ˇ) {
14994        +     //println!("hello");
14995        +
14996        +     println!("world");
14997        +     //
14998        +     //
14999        + }
15000        "#
15001        .unindent(),
15002    );
15003}
15004
15005#[gpui::test]
15006async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
15007    init_test(cx, |_| {});
15008
15009    let file_1_old = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15010    let file_1_new = "aaa\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj";
15011    let file_2_old = "lll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15012    let file_2_new = "lll\nmmm\nNNN\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu";
15013    let file_3_old = "111\n222\n333\n444\n555\n777\n888\n999\n000\n!!!";
15014    let file_3_new = "111\n222\n333\n444\n555\n666\n777\n888\n999\n000\n!!!";
15015
15016    let buffer_1 = cx.new(|cx| Buffer::local(file_1_new.to_string(), cx));
15017    let buffer_2 = cx.new(|cx| Buffer::local(file_2_new.to_string(), cx));
15018    let buffer_3 = cx.new(|cx| Buffer::local(file_3_new.to_string(), cx));
15019
15020    let multi_buffer = cx.new(|cx| {
15021        let mut multibuffer = MultiBuffer::new(ReadWrite);
15022        multibuffer.push_excerpts(
15023            buffer_1.clone(),
15024            [
15025                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15026                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15027                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15028            ],
15029            cx,
15030        );
15031        multibuffer.push_excerpts(
15032            buffer_2.clone(),
15033            [
15034                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15035                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15036                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15037            ],
15038            cx,
15039        );
15040        multibuffer.push_excerpts(
15041            buffer_3.clone(),
15042            [
15043                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
15044                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
15045                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
15046            ],
15047            cx,
15048        );
15049        multibuffer
15050    });
15051
15052    let editor =
15053        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15054    editor
15055        .update(cx, |editor, _window, cx| {
15056            for (buffer, diff_base) in [
15057                (buffer_1.clone(), file_1_old),
15058                (buffer_2.clone(), file_2_old),
15059                (buffer_3.clone(), file_3_old),
15060            ] {
15061                let diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
15062                editor
15063                    .buffer
15064                    .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
15065            }
15066        })
15067        .unwrap();
15068
15069    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15070    cx.run_until_parked();
15071
15072    cx.assert_editor_state(
15073        &"
15074            ˇaaa
15075            ccc
15076            ddd
15077
15078            ggg
15079            hhh
15080
15081
15082            lll
15083            mmm
15084            NNN
15085
15086            qqq
15087            rrr
15088
15089            uuu
15090            111
15091            222
15092            333
15093
15094            666
15095            777
15096
15097            000
15098            !!!"
15099        .unindent(),
15100    );
15101
15102    cx.update_editor(|editor, window, cx| {
15103        editor.select_all(&SelectAll, window, cx);
15104        editor.toggle_selected_diff_hunks(&ToggleSelectedDiffHunks, window, cx);
15105    });
15106    cx.executor().run_until_parked();
15107
15108    cx.assert_state_with_diff(
15109        "
15110            «aaa
15111          - bbb
15112            ccc
15113            ddd
15114
15115            ggg
15116            hhh
15117
15118
15119            lll
15120            mmm
15121          - nnn
15122          + NNN
15123
15124            qqq
15125            rrr
15126
15127            uuu
15128            111
15129            222
15130            333
15131
15132          + 666
15133            777
15134
15135            000
15136            !!!ˇ»"
15137            .unindent(),
15138    );
15139}
15140
15141#[gpui::test]
15142async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
15143    init_test(cx, |_| {});
15144
15145    let base = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\n";
15146    let text = "aaa\nBBB\nBB2\nccc\nDDD\nEEE\nfff\nggg\nhhh\niii\n";
15147
15148    let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx));
15149    let multi_buffer = cx.new(|cx| {
15150        let mut multibuffer = MultiBuffer::new(ReadWrite);
15151        multibuffer.push_excerpts(
15152            buffer.clone(),
15153            [
15154                ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
15155                ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)),
15156                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)),
15157            ],
15158            cx,
15159        );
15160        multibuffer
15161    });
15162
15163    let editor =
15164        cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
15165    editor
15166        .update(cx, |editor, _window, cx| {
15167            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
15168            editor
15169                .buffer
15170                .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
15171        })
15172        .unwrap();
15173
15174    let mut cx = EditorTestContext::for_editor(editor, cx).await;
15175    cx.run_until_parked();
15176
15177    cx.update_editor(|editor, window, cx| {
15178        editor.expand_all_diff_hunks(&Default::default(), window, cx)
15179    });
15180    cx.executor().run_until_parked();
15181
15182    // When the start of a hunk coincides with the start of its excerpt,
15183    // the hunk is expanded. When the start of a a hunk is earlier than
15184    // the start of its excerpt, the hunk is not expanded.
15185    cx.assert_state_with_diff(
15186        "
15187            ˇaaa
15188          - bbb
15189          + BBB
15190
15191          - ddd
15192          - eee
15193          + DDD
15194          + EEE
15195            fff
15196
15197            iii
15198        "
15199        .unindent(),
15200    );
15201}
15202
15203#[gpui::test]
15204async fn test_edits_around_expanded_insertion_hunks(
15205    executor: BackgroundExecutor,
15206    cx: &mut TestAppContext,
15207) {
15208    init_test(cx, |_| {});
15209
15210    let mut cx = EditorTestContext::new(cx).await;
15211
15212    let diff_base = r#"
15213        use some::mod1;
15214        use some::mod2;
15215
15216        const A: u32 = 42;
15217
15218        fn main() {
15219            println!("hello");
15220
15221            println!("world");
15222        }
15223        "#
15224    .unindent();
15225    executor.run_until_parked();
15226    cx.set_state(
15227        &r#"
15228        use some::mod1;
15229        use some::mod2;
15230
15231        const A: u32 = 42;
15232        const B: u32 = 42;
15233        const C: u32 = 42;
15234        ˇ
15235
15236        fn main() {
15237            println!("hello");
15238
15239            println!("world");
15240        }
15241        "#
15242        .unindent(),
15243    );
15244
15245    cx.set_head_text(&diff_base);
15246    executor.run_until_parked();
15247
15248    cx.update_editor(|editor, window, cx| {
15249        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15250    });
15251    executor.run_until_parked();
15252
15253    cx.assert_state_with_diff(
15254        r#"
15255        use some::mod1;
15256        use some::mod2;
15257
15258        const A: u32 = 42;
15259      + const B: u32 = 42;
15260      + const C: u32 = 42;
15261      + ˇ
15262
15263        fn main() {
15264            println!("hello");
15265
15266            println!("world");
15267        }
15268      "#
15269        .unindent(),
15270    );
15271
15272    cx.update_editor(|editor, window, cx| editor.handle_input("const D: u32 = 42;\n", window, cx));
15273    executor.run_until_parked();
15274
15275    cx.assert_state_with_diff(
15276        r#"
15277        use some::mod1;
15278        use some::mod2;
15279
15280        const A: u32 = 42;
15281      + const B: u32 = 42;
15282      + const C: u32 = 42;
15283      + const D: u32 = 42;
15284      + ˇ
15285
15286        fn main() {
15287            println!("hello");
15288
15289            println!("world");
15290        }
15291      "#
15292        .unindent(),
15293    );
15294
15295    cx.update_editor(|editor, window, cx| editor.handle_input("const E: u32 = 42;\n", window, cx));
15296    executor.run_until_parked();
15297
15298    cx.assert_state_with_diff(
15299        r#"
15300        use some::mod1;
15301        use some::mod2;
15302
15303        const A: u32 = 42;
15304      + const B: u32 = 42;
15305      + const C: u32 = 42;
15306      + const D: u32 = 42;
15307      + const E: u32 = 42;
15308      + ˇ
15309
15310        fn main() {
15311            println!("hello");
15312
15313            println!("world");
15314        }
15315      "#
15316        .unindent(),
15317    );
15318
15319    cx.update_editor(|editor, window, cx| {
15320        editor.delete_line(&DeleteLine, window, cx);
15321    });
15322    executor.run_until_parked();
15323
15324    cx.assert_state_with_diff(
15325        r#"
15326        use some::mod1;
15327        use some::mod2;
15328
15329        const A: u32 = 42;
15330      + const B: u32 = 42;
15331      + const C: u32 = 42;
15332      + const D: u32 = 42;
15333      + const E: u32 = 42;
15334        ˇ
15335        fn main() {
15336            println!("hello");
15337
15338            println!("world");
15339        }
15340      "#
15341        .unindent(),
15342    );
15343
15344    cx.update_editor(|editor, window, cx| {
15345        editor.move_up(&MoveUp, window, cx);
15346        editor.delete_line(&DeleteLine, window, cx);
15347        editor.move_up(&MoveUp, window, cx);
15348        editor.delete_line(&DeleteLine, window, cx);
15349        editor.move_up(&MoveUp, window, cx);
15350        editor.delete_line(&DeleteLine, window, cx);
15351    });
15352    executor.run_until_parked();
15353    cx.assert_state_with_diff(
15354        r#"
15355        use some::mod1;
15356        use some::mod2;
15357
15358        const A: u32 = 42;
15359      + const B: u32 = 42;
15360        ˇ
15361        fn main() {
15362            println!("hello");
15363
15364            println!("world");
15365        }
15366      "#
15367        .unindent(),
15368    );
15369
15370    cx.update_editor(|editor, window, cx| {
15371        editor.select_up_by_lines(&SelectUpByLines { lines: 5 }, window, cx);
15372        editor.delete_line(&DeleteLine, window, cx);
15373    });
15374    executor.run_until_parked();
15375    cx.assert_state_with_diff(
15376        r#"
15377        ˇ
15378        fn main() {
15379            println!("hello");
15380
15381            println!("world");
15382        }
15383      "#
15384        .unindent(),
15385    );
15386}
15387
15388#[gpui::test]
15389async fn test_toggling_adjacent_diff_hunks(cx: &mut TestAppContext) {
15390    init_test(cx, |_| {});
15391
15392    let mut cx = EditorTestContext::new(cx).await;
15393    cx.set_head_text(indoc! { "
15394        one
15395        two
15396        three
15397        four
15398        five
15399        "
15400    });
15401    cx.set_state(indoc! { "
15402        one
15403        ˇthree
15404        five
15405    "});
15406    cx.run_until_parked();
15407    cx.update_editor(|editor, window, cx| {
15408        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15409    });
15410    cx.assert_state_with_diff(
15411        indoc! { "
15412        one
15413      - two
15414        ˇthree
15415      - four
15416        five
15417    "}
15418        .to_string(),
15419    );
15420    cx.update_editor(|editor, window, cx| {
15421        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15422    });
15423
15424    cx.assert_state_with_diff(
15425        indoc! { "
15426        one
15427        ˇthree
15428        five
15429    "}
15430        .to_string(),
15431    );
15432
15433    cx.set_state(indoc! { "
15434        one
15435        ˇTWO
15436        three
15437        four
15438        five
15439    "});
15440    cx.run_until_parked();
15441    cx.update_editor(|editor, window, cx| {
15442        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15443    });
15444
15445    cx.assert_state_with_diff(
15446        indoc! { "
15447            one
15448          - two
15449          + ˇTWO
15450            three
15451            four
15452            five
15453        "}
15454        .to_string(),
15455    );
15456    cx.update_editor(|editor, window, cx| {
15457        editor.move_up(&Default::default(), window, cx);
15458        editor.toggle_selected_diff_hunks(&Default::default(), window, cx);
15459    });
15460    cx.assert_state_with_diff(
15461        indoc! { "
15462            one
15463            ˇTWO
15464            three
15465            four
15466            five
15467        "}
15468        .to_string(),
15469    );
15470}
15471
15472#[gpui::test]
15473async fn test_edits_around_expanded_deletion_hunks(
15474    executor: BackgroundExecutor,
15475    cx: &mut TestAppContext,
15476) {
15477    init_test(cx, |_| {});
15478
15479    let mut cx = EditorTestContext::new(cx).await;
15480
15481    let diff_base = r#"
15482        use some::mod1;
15483        use some::mod2;
15484
15485        const A: u32 = 42;
15486        const B: u32 = 42;
15487        const C: u32 = 42;
15488
15489
15490        fn main() {
15491            println!("hello");
15492
15493            println!("world");
15494        }
15495    "#
15496    .unindent();
15497    executor.run_until_parked();
15498    cx.set_state(
15499        &r#"
15500        use some::mod1;
15501        use some::mod2;
15502
15503        ˇconst B: u32 = 42;
15504        const C: u32 = 42;
15505
15506
15507        fn main() {
15508            println!("hello");
15509
15510            println!("world");
15511        }
15512        "#
15513        .unindent(),
15514    );
15515
15516    cx.set_head_text(&diff_base);
15517    executor.run_until_parked();
15518
15519    cx.update_editor(|editor, window, cx| {
15520        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15521    });
15522    executor.run_until_parked();
15523
15524    cx.assert_state_with_diff(
15525        r#"
15526        use some::mod1;
15527        use some::mod2;
15528
15529      - const A: u32 = 42;
15530        ˇconst B: u32 = 42;
15531        const C: u32 = 42;
15532
15533
15534        fn main() {
15535            println!("hello");
15536
15537            println!("world");
15538        }
15539      "#
15540        .unindent(),
15541    );
15542
15543    cx.update_editor(|editor, window, cx| {
15544        editor.delete_line(&DeleteLine, window, cx);
15545    });
15546    executor.run_until_parked();
15547    cx.assert_state_with_diff(
15548        r#"
15549        use some::mod1;
15550        use some::mod2;
15551
15552      - const A: u32 = 42;
15553      - const B: u32 = 42;
15554        ˇconst C: u32 = 42;
15555
15556
15557        fn main() {
15558            println!("hello");
15559
15560            println!("world");
15561        }
15562      "#
15563        .unindent(),
15564    );
15565
15566    cx.update_editor(|editor, window, cx| {
15567        editor.delete_line(&DeleteLine, window, cx);
15568    });
15569    executor.run_until_parked();
15570    cx.assert_state_with_diff(
15571        r#"
15572        use some::mod1;
15573        use some::mod2;
15574
15575      - const A: u32 = 42;
15576      - const B: u32 = 42;
15577      - const C: u32 = 42;
15578        ˇ
15579
15580        fn main() {
15581            println!("hello");
15582
15583            println!("world");
15584        }
15585      "#
15586        .unindent(),
15587    );
15588
15589    cx.update_editor(|editor, window, cx| {
15590        editor.handle_input("replacement", window, cx);
15591    });
15592    executor.run_until_parked();
15593    cx.assert_state_with_diff(
15594        r#"
15595        use some::mod1;
15596        use some::mod2;
15597
15598      - const A: u32 = 42;
15599      - const B: u32 = 42;
15600      - const C: u32 = 42;
15601      -
15602      + replacementˇ
15603
15604        fn main() {
15605            println!("hello");
15606
15607            println!("world");
15608        }
15609      "#
15610        .unindent(),
15611    );
15612}
15613
15614#[gpui::test]
15615async fn test_backspace_after_deletion_hunk(executor: BackgroundExecutor, cx: &mut TestAppContext) {
15616    init_test(cx, |_| {});
15617
15618    let mut cx = EditorTestContext::new(cx).await;
15619
15620    let base_text = r#"
15621        one
15622        two
15623        three
15624        four
15625        five
15626    "#
15627    .unindent();
15628    executor.run_until_parked();
15629    cx.set_state(
15630        &r#"
15631        one
15632        two
15633        fˇour
15634        five
15635        "#
15636        .unindent(),
15637    );
15638
15639    cx.set_head_text(&base_text);
15640    executor.run_until_parked();
15641
15642    cx.update_editor(|editor, window, cx| {
15643        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15644    });
15645    executor.run_until_parked();
15646
15647    cx.assert_state_with_diff(
15648        r#"
15649          one
15650          two
15651        - three
15652          fˇour
15653          five
15654        "#
15655        .unindent(),
15656    );
15657
15658    cx.update_editor(|editor, window, cx| {
15659        editor.backspace(&Backspace, window, cx);
15660        editor.backspace(&Backspace, window, cx);
15661    });
15662    executor.run_until_parked();
15663    cx.assert_state_with_diff(
15664        r#"
15665          one
15666          two
15667        - threeˇ
15668        - four
15669        + our
15670          five
15671        "#
15672        .unindent(),
15673    );
15674}
15675
15676#[gpui::test]
15677async fn test_edit_after_expanded_modification_hunk(
15678    executor: BackgroundExecutor,
15679    cx: &mut TestAppContext,
15680) {
15681    init_test(cx, |_| {});
15682
15683    let mut cx = EditorTestContext::new(cx).await;
15684
15685    let diff_base = r#"
15686        use some::mod1;
15687        use some::mod2;
15688
15689        const A: u32 = 42;
15690        const B: u32 = 42;
15691        const C: u32 = 42;
15692        const D: u32 = 42;
15693
15694
15695        fn main() {
15696            println!("hello");
15697
15698            println!("world");
15699        }"#
15700    .unindent();
15701
15702    cx.set_state(
15703        &r#"
15704        use some::mod1;
15705        use some::mod2;
15706
15707        const A: u32 = 42;
15708        const B: u32 = 42;
15709        const C: u32 = 43ˇ
15710        const D: u32 = 42;
15711
15712
15713        fn main() {
15714            println!("hello");
15715
15716            println!("world");
15717        }"#
15718        .unindent(),
15719    );
15720
15721    cx.set_head_text(&diff_base);
15722    executor.run_until_parked();
15723    cx.update_editor(|editor, window, cx| {
15724        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
15725    });
15726    executor.run_until_parked();
15727
15728    cx.assert_state_with_diff(
15729        r#"
15730        use some::mod1;
15731        use some::mod2;
15732
15733        const A: u32 = 42;
15734        const B: u32 = 42;
15735      - const C: u32 = 42;
15736      + const C: u32 = 43ˇ
15737        const D: u32 = 42;
15738
15739
15740        fn main() {
15741            println!("hello");
15742
15743            println!("world");
15744        }"#
15745        .unindent(),
15746    );
15747
15748    cx.update_editor(|editor, window, cx| {
15749        editor.handle_input("\nnew_line\n", window, cx);
15750    });
15751    executor.run_until_parked();
15752
15753    cx.assert_state_with_diff(
15754        r#"
15755        use some::mod1;
15756        use some::mod2;
15757
15758        const A: u32 = 42;
15759        const B: u32 = 42;
15760      - const C: u32 = 42;
15761      + const C: u32 = 43
15762      + new_line
15763      + ˇ
15764        const D: u32 = 42;
15765
15766
15767        fn main() {
15768            println!("hello");
15769
15770            println!("world");
15771        }"#
15772        .unindent(),
15773    );
15774}
15775
15776#[gpui::test]
15777async fn test_stage_and_unstage_added_file_hunk(
15778    executor: BackgroundExecutor,
15779    cx: &mut TestAppContext,
15780) {
15781    init_test(cx, |_| {});
15782
15783    let mut cx = EditorTestContext::new(cx).await;
15784    cx.update_editor(|editor, _, cx| {
15785        editor.set_expand_all_diff_hunks(cx);
15786    });
15787
15788    let working_copy = r#"
15789            ˇfn main() {
15790                println!("hello, world!");
15791            }
15792        "#
15793    .unindent();
15794
15795    cx.set_state(&working_copy);
15796    executor.run_until_parked();
15797
15798    cx.assert_state_with_diff(
15799        r#"
15800            + ˇfn main() {
15801            +     println!("hello, world!");
15802            + }
15803        "#
15804        .unindent(),
15805    );
15806    cx.assert_index_text(None);
15807
15808    cx.update_editor(|editor, window, cx| {
15809        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15810    });
15811    executor.run_until_parked();
15812    cx.assert_index_text(Some(&working_copy.replace("ˇ", "")));
15813    cx.assert_state_with_diff(
15814        r#"
15815            + ˇfn main() {
15816            +     println!("hello, world!");
15817            + }
15818        "#
15819        .unindent(),
15820    );
15821
15822    cx.update_editor(|editor, window, cx| {
15823        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
15824    });
15825    executor.run_until_parked();
15826    cx.assert_index_text(None);
15827}
15828
15829async fn setup_indent_guides_editor(
15830    text: &str,
15831    cx: &mut TestAppContext,
15832) -> (BufferId, EditorTestContext) {
15833    init_test(cx, |_| {});
15834
15835    let mut cx = EditorTestContext::new(cx).await;
15836
15837    let buffer_id = cx.update_editor(|editor, window, cx| {
15838        editor.set_text(text, window, cx);
15839        let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
15840
15841        buffer_ids[0]
15842    });
15843
15844    (buffer_id, cx)
15845}
15846
15847fn assert_indent_guides(
15848    range: Range<u32>,
15849    expected: Vec<IndentGuide>,
15850    active_indices: Option<Vec<usize>>,
15851    cx: &mut EditorTestContext,
15852) {
15853    let indent_guides = cx.update_editor(|editor, window, cx| {
15854        let snapshot = editor.snapshot(window, cx).display_snapshot;
15855        let mut indent_guides: Vec<_> = crate::indent_guides::indent_guides_in_range(
15856            editor,
15857            MultiBufferRow(range.start)..MultiBufferRow(range.end),
15858            true,
15859            &snapshot,
15860            cx,
15861        );
15862
15863        indent_guides.sort_by(|a, b| {
15864            a.depth.cmp(&b.depth).then(
15865                a.start_row
15866                    .cmp(&b.start_row)
15867                    .then(a.end_row.cmp(&b.end_row)),
15868            )
15869        });
15870        indent_guides
15871    });
15872
15873    if let Some(expected) = active_indices {
15874        let active_indices = cx.update_editor(|editor, window, cx| {
15875            let snapshot = editor.snapshot(window, cx).display_snapshot;
15876            editor.find_active_indent_guide_indices(&indent_guides, &snapshot, window, cx)
15877        });
15878
15879        assert_eq!(
15880            active_indices.unwrap().into_iter().collect::<Vec<_>>(),
15881            expected,
15882            "Active indent guide indices do not match"
15883        );
15884    }
15885
15886    assert_eq!(indent_guides, expected, "Indent guides do not match");
15887}
15888
15889fn indent_guide(buffer_id: BufferId, start_row: u32, end_row: u32, depth: u32) -> IndentGuide {
15890    IndentGuide {
15891        buffer_id,
15892        start_row: MultiBufferRow(start_row),
15893        end_row: MultiBufferRow(end_row),
15894        depth,
15895        tab_size: 4,
15896        settings: IndentGuideSettings {
15897            enabled: true,
15898            line_width: 1,
15899            active_line_width: 1,
15900            ..Default::default()
15901        },
15902    }
15903}
15904
15905#[gpui::test]
15906async fn test_indent_guide_single_line(cx: &mut TestAppContext) {
15907    let (buffer_id, mut cx) = setup_indent_guides_editor(
15908        &"
15909    fn main() {
15910        let a = 1;
15911    }"
15912        .unindent(),
15913        cx,
15914    )
15915    .await;
15916
15917    assert_indent_guides(0..3, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
15918}
15919
15920#[gpui::test]
15921async fn test_indent_guide_simple_block(cx: &mut TestAppContext) {
15922    let (buffer_id, mut cx) = setup_indent_guides_editor(
15923        &"
15924    fn main() {
15925        let a = 1;
15926        let b = 2;
15927    }"
15928        .unindent(),
15929        cx,
15930    )
15931    .await;
15932
15933    assert_indent_guides(0..4, vec![indent_guide(buffer_id, 1, 2, 0)], None, &mut cx);
15934}
15935
15936#[gpui::test]
15937async fn test_indent_guide_nested(cx: &mut TestAppContext) {
15938    let (buffer_id, mut cx) = setup_indent_guides_editor(
15939        &"
15940    fn main() {
15941        let a = 1;
15942        if a == 3 {
15943            let b = 2;
15944        } else {
15945            let c = 3;
15946        }
15947    }"
15948        .unindent(),
15949        cx,
15950    )
15951    .await;
15952
15953    assert_indent_guides(
15954        0..8,
15955        vec![
15956            indent_guide(buffer_id, 1, 6, 0),
15957            indent_guide(buffer_id, 3, 3, 1),
15958            indent_guide(buffer_id, 5, 5, 1),
15959        ],
15960        None,
15961        &mut cx,
15962    );
15963}
15964
15965#[gpui::test]
15966async fn test_indent_guide_tab(cx: &mut TestAppContext) {
15967    let (buffer_id, mut cx) = setup_indent_guides_editor(
15968        &"
15969    fn main() {
15970        let a = 1;
15971            let b = 2;
15972        let c = 3;
15973    }"
15974        .unindent(),
15975        cx,
15976    )
15977    .await;
15978
15979    assert_indent_guides(
15980        0..5,
15981        vec![
15982            indent_guide(buffer_id, 1, 3, 0),
15983            indent_guide(buffer_id, 2, 2, 1),
15984        ],
15985        None,
15986        &mut cx,
15987    );
15988}
15989
15990#[gpui::test]
15991async fn test_indent_guide_continues_on_empty_line(cx: &mut TestAppContext) {
15992    let (buffer_id, mut cx) = setup_indent_guides_editor(
15993        &"
15994        fn main() {
15995            let a = 1;
15996
15997            let c = 3;
15998        }"
15999        .unindent(),
16000        cx,
16001    )
16002    .await;
16003
16004    assert_indent_guides(0..5, vec![indent_guide(buffer_id, 1, 3, 0)], None, &mut cx);
16005}
16006
16007#[gpui::test]
16008async fn test_indent_guide_complex(cx: &mut TestAppContext) {
16009    let (buffer_id, mut cx) = setup_indent_guides_editor(
16010        &"
16011        fn main() {
16012            let a = 1;
16013
16014            let c = 3;
16015
16016            if a == 3 {
16017                let b = 2;
16018            } else {
16019                let c = 3;
16020            }
16021        }"
16022        .unindent(),
16023        cx,
16024    )
16025    .await;
16026
16027    assert_indent_guides(
16028        0..11,
16029        vec![
16030            indent_guide(buffer_id, 1, 9, 0),
16031            indent_guide(buffer_id, 6, 6, 1),
16032            indent_guide(buffer_id, 8, 8, 1),
16033        ],
16034        None,
16035        &mut cx,
16036    );
16037}
16038
16039#[gpui::test]
16040async fn test_indent_guide_starts_off_screen(cx: &mut TestAppContext) {
16041    let (buffer_id, mut cx) = setup_indent_guides_editor(
16042        &"
16043        fn main() {
16044            let a = 1;
16045
16046            let c = 3;
16047
16048            if a == 3 {
16049                let b = 2;
16050            } else {
16051                let c = 3;
16052            }
16053        }"
16054        .unindent(),
16055        cx,
16056    )
16057    .await;
16058
16059    assert_indent_guides(
16060        1..11,
16061        vec![
16062            indent_guide(buffer_id, 1, 9, 0),
16063            indent_guide(buffer_id, 6, 6, 1),
16064            indent_guide(buffer_id, 8, 8, 1),
16065        ],
16066        None,
16067        &mut cx,
16068    );
16069}
16070
16071#[gpui::test]
16072async fn test_indent_guide_ends_off_screen(cx: &mut TestAppContext) {
16073    let (buffer_id, mut cx) = setup_indent_guides_editor(
16074        &"
16075        fn main() {
16076            let a = 1;
16077
16078            let c = 3;
16079
16080            if a == 3 {
16081                let b = 2;
16082            } else {
16083                let c = 3;
16084            }
16085        }"
16086        .unindent(),
16087        cx,
16088    )
16089    .await;
16090
16091    assert_indent_guides(
16092        1..10,
16093        vec![
16094            indent_guide(buffer_id, 1, 9, 0),
16095            indent_guide(buffer_id, 6, 6, 1),
16096            indent_guide(buffer_id, 8, 8, 1),
16097        ],
16098        None,
16099        &mut cx,
16100    );
16101}
16102
16103#[gpui::test]
16104async fn test_indent_guide_without_brackets(cx: &mut TestAppContext) {
16105    let (buffer_id, mut cx) = setup_indent_guides_editor(
16106        &"
16107        block1
16108            block2
16109                block3
16110                    block4
16111            block2
16112        block1
16113        block1"
16114            .unindent(),
16115        cx,
16116    )
16117    .await;
16118
16119    assert_indent_guides(
16120        1..10,
16121        vec![
16122            indent_guide(buffer_id, 1, 4, 0),
16123            indent_guide(buffer_id, 2, 3, 1),
16124            indent_guide(buffer_id, 3, 3, 2),
16125        ],
16126        None,
16127        &mut cx,
16128    );
16129}
16130
16131#[gpui::test]
16132async fn test_indent_guide_ends_before_empty_line(cx: &mut TestAppContext) {
16133    let (buffer_id, mut cx) = setup_indent_guides_editor(
16134        &"
16135        block1
16136            block2
16137                block3
16138
16139        block1
16140        block1"
16141            .unindent(),
16142        cx,
16143    )
16144    .await;
16145
16146    assert_indent_guides(
16147        0..6,
16148        vec![
16149            indent_guide(buffer_id, 1, 2, 0),
16150            indent_guide(buffer_id, 2, 2, 1),
16151        ],
16152        None,
16153        &mut cx,
16154    );
16155}
16156
16157#[gpui::test]
16158async fn test_indent_guide_continuing_off_screen(cx: &mut TestAppContext) {
16159    let (buffer_id, mut cx) = setup_indent_guides_editor(
16160        &"
16161        block1
16162
16163
16164
16165            block2
16166        "
16167        .unindent(),
16168        cx,
16169    )
16170    .await;
16171
16172    assert_indent_guides(0..1, vec![indent_guide(buffer_id, 1, 1, 0)], None, &mut cx);
16173}
16174
16175#[gpui::test]
16176async fn test_indent_guide_tabs(cx: &mut TestAppContext) {
16177    let (buffer_id, mut cx) = setup_indent_guides_editor(
16178        &"
16179        def a:
16180        \tb = 3
16181        \tif True:
16182        \t\tc = 4
16183        \t\td = 5
16184        \tprint(b)
16185        "
16186        .unindent(),
16187        cx,
16188    )
16189    .await;
16190
16191    assert_indent_guides(
16192        0..6,
16193        vec![
16194            indent_guide(buffer_id, 1, 6, 0),
16195            indent_guide(buffer_id, 3, 4, 1),
16196        ],
16197        None,
16198        &mut cx,
16199    );
16200}
16201
16202#[gpui::test]
16203async fn test_active_indent_guide_single_line(cx: &mut TestAppContext) {
16204    let (buffer_id, mut cx) = setup_indent_guides_editor(
16205        &"
16206    fn main() {
16207        let a = 1;
16208    }"
16209        .unindent(),
16210        cx,
16211    )
16212    .await;
16213
16214    cx.update_editor(|editor, window, cx| {
16215        editor.change_selections(None, window, cx, |s| {
16216            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16217        });
16218    });
16219
16220    assert_indent_guides(
16221        0..3,
16222        vec![indent_guide(buffer_id, 1, 1, 0)],
16223        Some(vec![0]),
16224        &mut cx,
16225    );
16226}
16227
16228#[gpui::test]
16229async fn test_active_indent_guide_respect_indented_range(cx: &mut TestAppContext) {
16230    let (buffer_id, mut cx) = setup_indent_guides_editor(
16231        &"
16232    fn main() {
16233        if 1 == 2 {
16234            let a = 1;
16235        }
16236    }"
16237        .unindent(),
16238        cx,
16239    )
16240    .await;
16241
16242    cx.update_editor(|editor, window, cx| {
16243        editor.change_selections(None, window, cx, |s| {
16244            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16245        });
16246    });
16247
16248    assert_indent_guides(
16249        0..4,
16250        vec![
16251            indent_guide(buffer_id, 1, 3, 0),
16252            indent_guide(buffer_id, 2, 2, 1),
16253        ],
16254        Some(vec![1]),
16255        &mut cx,
16256    );
16257
16258    cx.update_editor(|editor, window, cx| {
16259        editor.change_selections(None, window, cx, |s| {
16260            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16261        });
16262    });
16263
16264    assert_indent_guides(
16265        0..4,
16266        vec![
16267            indent_guide(buffer_id, 1, 3, 0),
16268            indent_guide(buffer_id, 2, 2, 1),
16269        ],
16270        Some(vec![1]),
16271        &mut cx,
16272    );
16273
16274    cx.update_editor(|editor, window, cx| {
16275        editor.change_selections(None, window, cx, |s| {
16276            s.select_ranges([Point::new(3, 0)..Point::new(3, 0)])
16277        });
16278    });
16279
16280    assert_indent_guides(
16281        0..4,
16282        vec![
16283            indent_guide(buffer_id, 1, 3, 0),
16284            indent_guide(buffer_id, 2, 2, 1),
16285        ],
16286        Some(vec![0]),
16287        &mut cx,
16288    );
16289}
16290
16291#[gpui::test]
16292async fn test_active_indent_guide_empty_line(cx: &mut TestAppContext) {
16293    let (buffer_id, mut cx) = setup_indent_guides_editor(
16294        &"
16295    fn main() {
16296        let a = 1;
16297
16298        let b = 2;
16299    }"
16300        .unindent(),
16301        cx,
16302    )
16303    .await;
16304
16305    cx.update_editor(|editor, window, cx| {
16306        editor.change_selections(None, window, cx, |s| {
16307            s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
16308        });
16309    });
16310
16311    assert_indent_guides(
16312        0..5,
16313        vec![indent_guide(buffer_id, 1, 3, 0)],
16314        Some(vec![0]),
16315        &mut cx,
16316    );
16317}
16318
16319#[gpui::test]
16320async fn test_active_indent_guide_non_matching_indent(cx: &mut TestAppContext) {
16321    let (buffer_id, mut cx) = setup_indent_guides_editor(
16322        &"
16323    def m:
16324        a = 1
16325        pass"
16326            .unindent(),
16327        cx,
16328    )
16329    .await;
16330
16331    cx.update_editor(|editor, window, cx| {
16332        editor.change_selections(None, window, cx, |s| {
16333            s.select_ranges([Point::new(1, 0)..Point::new(1, 0)])
16334        });
16335    });
16336
16337    assert_indent_guides(
16338        0..3,
16339        vec![indent_guide(buffer_id, 1, 2, 0)],
16340        Some(vec![0]),
16341        &mut cx,
16342    );
16343}
16344
16345#[gpui::test]
16346async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
16347    init_test(cx, |_| {});
16348    let mut cx = EditorTestContext::new(cx).await;
16349    let text = indoc! {
16350        "
16351        impl A {
16352            fn b() {
16353                0;
16354                3;
16355                5;
16356                6;
16357                7;
16358            }
16359        }
16360        "
16361    };
16362    let base_text = indoc! {
16363        "
16364        impl A {
16365            fn b() {
16366                0;
16367                1;
16368                2;
16369                3;
16370                4;
16371            }
16372            fn c() {
16373                5;
16374                6;
16375                7;
16376            }
16377        }
16378        "
16379    };
16380
16381    cx.update_editor(|editor, window, cx| {
16382        editor.set_text(text, window, cx);
16383
16384        editor.buffer().update(cx, |multibuffer, cx| {
16385            let buffer = multibuffer.as_singleton().unwrap();
16386            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
16387
16388            multibuffer.set_all_diff_hunks_expanded(cx);
16389            multibuffer.add_diff(diff, cx);
16390
16391            buffer.read(cx).remote_id()
16392        })
16393    });
16394    cx.run_until_parked();
16395
16396    cx.assert_state_with_diff(
16397        indoc! { "
16398          impl A {
16399              fn b() {
16400                  0;
16401        -         1;
16402        -         2;
16403                  3;
16404        -         4;
16405        -     }
16406        -     fn c() {
16407                  5;
16408                  6;
16409                  7;
16410              }
16411          }
16412          ˇ"
16413        }
16414        .to_string(),
16415    );
16416
16417    let mut actual_guides = cx.update_editor(|editor, window, cx| {
16418        editor
16419            .snapshot(window, cx)
16420            .buffer_snapshot
16421            .indent_guides_in_range(Anchor::min()..Anchor::max(), false, cx)
16422            .map(|guide| (guide.start_row..=guide.end_row, guide.depth))
16423            .collect::<Vec<_>>()
16424    });
16425    actual_guides.sort_by_key(|item| (*item.0.start(), item.1));
16426    assert_eq!(
16427        actual_guides,
16428        vec![
16429            (MultiBufferRow(1)..=MultiBufferRow(12), 0),
16430            (MultiBufferRow(2)..=MultiBufferRow(6), 1),
16431            (MultiBufferRow(9)..=MultiBufferRow(11), 1),
16432        ]
16433    );
16434}
16435
16436#[gpui::test]
16437async fn test_adjacent_diff_hunks(executor: BackgroundExecutor, cx: &mut TestAppContext) {
16438    init_test(cx, |_| {});
16439    let mut cx = EditorTestContext::new(cx).await;
16440
16441    let diff_base = r#"
16442        a
16443        b
16444        c
16445        "#
16446    .unindent();
16447
16448    cx.set_state(
16449        &r#"
16450        ˇA
16451        b
16452        C
16453        "#
16454        .unindent(),
16455    );
16456    cx.set_head_text(&diff_base);
16457    cx.update_editor(|editor, window, cx| {
16458        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16459    });
16460    executor.run_until_parked();
16461
16462    let both_hunks_expanded = r#"
16463        - a
16464        + ˇA
16465          b
16466        - c
16467        + C
16468        "#
16469    .unindent();
16470
16471    cx.assert_state_with_diff(both_hunks_expanded.clone());
16472
16473    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16474        let snapshot = editor.snapshot(window, cx);
16475        let hunks = editor
16476            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16477            .collect::<Vec<_>>();
16478        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16479        let buffer_id = hunks[0].buffer_id;
16480        hunks
16481            .into_iter()
16482            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16483            .collect::<Vec<_>>()
16484    });
16485    assert_eq!(hunk_ranges.len(), 2);
16486
16487    cx.update_editor(|editor, _, cx| {
16488        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16489    });
16490    executor.run_until_parked();
16491
16492    let second_hunk_expanded = r#"
16493          ˇA
16494          b
16495        - c
16496        + C
16497        "#
16498    .unindent();
16499
16500    cx.assert_state_with_diff(second_hunk_expanded);
16501
16502    cx.update_editor(|editor, _, cx| {
16503        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16504    });
16505    executor.run_until_parked();
16506
16507    cx.assert_state_with_diff(both_hunks_expanded.clone());
16508
16509    cx.update_editor(|editor, _, cx| {
16510        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16511    });
16512    executor.run_until_parked();
16513
16514    let first_hunk_expanded = r#"
16515        - a
16516        + ˇA
16517          b
16518          C
16519        "#
16520    .unindent();
16521
16522    cx.assert_state_with_diff(first_hunk_expanded);
16523
16524    cx.update_editor(|editor, _, cx| {
16525        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16526    });
16527    executor.run_until_parked();
16528
16529    cx.assert_state_with_diff(both_hunks_expanded);
16530
16531    cx.set_state(
16532        &r#"
16533        ˇA
16534        b
16535        "#
16536        .unindent(),
16537    );
16538    cx.run_until_parked();
16539
16540    // TODO this cursor position seems bad
16541    cx.assert_state_with_diff(
16542        r#"
16543        - ˇa
16544        + A
16545          b
16546        "#
16547        .unindent(),
16548    );
16549
16550    cx.update_editor(|editor, window, cx| {
16551        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16552    });
16553
16554    cx.assert_state_with_diff(
16555        r#"
16556            - ˇa
16557            + A
16558              b
16559            - c
16560            "#
16561        .unindent(),
16562    );
16563
16564    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16565        let snapshot = editor.snapshot(window, cx);
16566        let hunks = editor
16567            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16568            .collect::<Vec<_>>();
16569        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16570        let buffer_id = hunks[0].buffer_id;
16571        hunks
16572            .into_iter()
16573            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16574            .collect::<Vec<_>>()
16575    });
16576    assert_eq!(hunk_ranges.len(), 2);
16577
16578    cx.update_editor(|editor, _, cx| {
16579        editor.toggle_single_diff_hunk(hunk_ranges[1].clone(), cx);
16580    });
16581    executor.run_until_parked();
16582
16583    cx.assert_state_with_diff(
16584        r#"
16585        - ˇa
16586        + A
16587          b
16588        "#
16589        .unindent(),
16590    );
16591}
16592
16593#[gpui::test]
16594async fn test_toggle_deletion_hunk_at_start_of_file(
16595    executor: BackgroundExecutor,
16596    cx: &mut TestAppContext,
16597) {
16598    init_test(cx, |_| {});
16599    let mut cx = EditorTestContext::new(cx).await;
16600
16601    let diff_base = r#"
16602        a
16603        b
16604        c
16605        "#
16606    .unindent();
16607
16608    cx.set_state(
16609        &r#"
16610        ˇb
16611        c
16612        "#
16613        .unindent(),
16614    );
16615    cx.set_head_text(&diff_base);
16616    cx.update_editor(|editor, window, cx| {
16617        editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
16618    });
16619    executor.run_until_parked();
16620
16621    let hunk_expanded = r#"
16622        - a
16623          ˇb
16624          c
16625        "#
16626    .unindent();
16627
16628    cx.assert_state_with_diff(hunk_expanded.clone());
16629
16630    let hunk_ranges = cx.update_editor(|editor, window, cx| {
16631        let snapshot = editor.snapshot(window, cx);
16632        let hunks = editor
16633            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16634            .collect::<Vec<_>>();
16635        let excerpt_id = editor.buffer.read(cx).excerpt_ids()[0];
16636        let buffer_id = hunks[0].buffer_id;
16637        hunks
16638            .into_iter()
16639            .map(|hunk| Anchor::range_in_buffer(excerpt_id, buffer_id, hunk.buffer_range.clone()))
16640            .collect::<Vec<_>>()
16641    });
16642    assert_eq!(hunk_ranges.len(), 1);
16643
16644    cx.update_editor(|editor, _, cx| {
16645        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16646    });
16647    executor.run_until_parked();
16648
16649    let hunk_collapsed = r#"
16650          ˇb
16651          c
16652        "#
16653    .unindent();
16654
16655    cx.assert_state_with_diff(hunk_collapsed);
16656
16657    cx.update_editor(|editor, _, cx| {
16658        editor.toggle_single_diff_hunk(hunk_ranges[0].clone(), cx);
16659    });
16660    executor.run_until_parked();
16661
16662    cx.assert_state_with_diff(hunk_expanded.clone());
16663}
16664
16665#[gpui::test]
16666async fn test_display_diff_hunks(cx: &mut TestAppContext) {
16667    init_test(cx, |_| {});
16668
16669    let fs = FakeFs::new(cx.executor());
16670    fs.insert_tree(
16671        path!("/test"),
16672        json!({
16673            ".git": {},
16674            "file-1": "ONE\n",
16675            "file-2": "TWO\n",
16676            "file-3": "THREE\n",
16677        }),
16678    )
16679    .await;
16680
16681    fs.set_head_for_repo(
16682        path!("/test/.git").as_ref(),
16683        &[
16684            ("file-1".into(), "one\n".into()),
16685            ("file-2".into(), "two\n".into()),
16686            ("file-3".into(), "three\n".into()),
16687        ],
16688    );
16689
16690    let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
16691    let mut buffers = vec![];
16692    for i in 1..=3 {
16693        let buffer = project
16694            .update(cx, |project, cx| {
16695                let path = format!(path!("/test/file-{}"), i);
16696                project.open_local_buffer(path, cx)
16697            })
16698            .await
16699            .unwrap();
16700        buffers.push(buffer);
16701    }
16702
16703    let multibuffer = cx.new(|cx| {
16704        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
16705        multibuffer.set_all_diff_hunks_expanded(cx);
16706        for buffer in &buffers {
16707            let snapshot = buffer.read(cx).snapshot();
16708            multibuffer.set_excerpts_for_path(
16709                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
16710                buffer.clone(),
16711                vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
16712                DEFAULT_MULTIBUFFER_CONTEXT,
16713                cx,
16714            );
16715        }
16716        multibuffer
16717    });
16718
16719    let editor = cx.add_window(|window, cx| {
16720        Editor::new(EditorMode::full(), multibuffer, Some(project), window, cx)
16721    });
16722    cx.run_until_parked();
16723
16724    let snapshot = editor
16725        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16726        .unwrap();
16727    let hunks = snapshot
16728        .display_diff_hunks_for_rows(DisplayRow(0)..DisplayRow(u32::MAX), &Default::default())
16729        .map(|hunk| match hunk {
16730            DisplayDiffHunk::Unfolded {
16731                display_row_range, ..
16732            } => display_row_range,
16733            DisplayDiffHunk::Folded { .. } => unreachable!(),
16734        })
16735        .collect::<Vec<_>>();
16736    assert_eq!(
16737        hunks,
16738        [
16739            DisplayRow(2)..DisplayRow(4),
16740            DisplayRow(7)..DisplayRow(9),
16741            DisplayRow(12)..DisplayRow(14),
16742        ]
16743    );
16744}
16745
16746#[gpui::test]
16747async fn test_partially_staged_hunk(cx: &mut TestAppContext) {
16748    init_test(cx, |_| {});
16749
16750    let mut cx = EditorTestContext::new(cx).await;
16751    cx.set_head_text(indoc! { "
16752        one
16753        two
16754        three
16755        four
16756        five
16757        "
16758    });
16759    cx.set_index_text(indoc! { "
16760        one
16761        two
16762        three
16763        four
16764        five
16765        "
16766    });
16767    cx.set_state(indoc! {"
16768        one
16769        TWO
16770        ˇTHREE
16771        FOUR
16772        five
16773    "});
16774    cx.run_until_parked();
16775    cx.update_editor(|editor, window, cx| {
16776        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16777    });
16778    cx.run_until_parked();
16779    cx.assert_index_text(Some(indoc! {"
16780        one
16781        TWO
16782        THREE
16783        FOUR
16784        five
16785    "}));
16786    cx.set_state(indoc! { "
16787        one
16788        TWO
16789        ˇTHREE-HUNDRED
16790        FOUR
16791        five
16792    "});
16793    cx.run_until_parked();
16794    cx.update_editor(|editor, window, cx| {
16795        let snapshot = editor.snapshot(window, cx);
16796        let hunks = editor
16797            .diff_hunks_in_ranges(&[Anchor::min()..Anchor::max()], &snapshot.buffer_snapshot)
16798            .collect::<Vec<_>>();
16799        assert_eq!(hunks.len(), 1);
16800        assert_eq!(
16801            hunks[0].status(),
16802            DiffHunkStatus {
16803                kind: DiffHunkStatusKind::Modified,
16804                secondary: DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk
16805            }
16806        );
16807
16808        editor.toggle_staged_selected_diff_hunks(&Default::default(), window, cx);
16809    });
16810    cx.run_until_parked();
16811    cx.assert_index_text(Some(indoc! {"
16812        one
16813        TWO
16814        THREE-HUNDRED
16815        FOUR
16816        five
16817    "}));
16818}
16819
16820#[gpui::test]
16821fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
16822    init_test(cx, |_| {});
16823
16824    let editor = cx.add_window(|window, cx| {
16825        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
16826        build_editor(buffer, window, cx)
16827    });
16828
16829    let render_args = Arc::new(Mutex::new(None));
16830    let snapshot = editor
16831        .update(cx, |editor, window, cx| {
16832            let snapshot = editor.buffer().read(cx).snapshot(cx);
16833            let range =
16834                snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(2, 6));
16835
16836            struct RenderArgs {
16837                row: MultiBufferRow,
16838                folded: bool,
16839                callback: Arc<dyn Fn(bool, &mut Window, &mut App) + Send + Sync>,
16840            }
16841
16842            let crease = Crease::inline(
16843                range,
16844                FoldPlaceholder::test(),
16845                {
16846                    let toggle_callback = render_args.clone();
16847                    move |row, folded, callback, _window, _cx| {
16848                        *toggle_callback.lock() = Some(RenderArgs {
16849                            row,
16850                            folded,
16851                            callback,
16852                        });
16853                        div()
16854                    }
16855                },
16856                |_row, _folded, _window, _cx| div(),
16857            );
16858
16859            editor.insert_creases(Some(crease), cx);
16860            let snapshot = editor.snapshot(window, cx);
16861            let _div = snapshot.render_crease_toggle(
16862                MultiBufferRow(1),
16863                false,
16864                cx.entity().clone(),
16865                window,
16866                cx,
16867            );
16868            snapshot
16869        })
16870        .unwrap();
16871
16872    let render_args = render_args.lock().take().unwrap();
16873    assert_eq!(render_args.row, MultiBufferRow(1));
16874    assert!(!render_args.folded);
16875    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16876
16877    cx.update_window(*editor, |_, window, cx| {
16878        (render_args.callback)(true, window, cx)
16879    })
16880    .unwrap();
16881    let snapshot = editor
16882        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16883        .unwrap();
16884    assert!(snapshot.is_line_folded(MultiBufferRow(1)));
16885
16886    cx.update_window(*editor, |_, window, cx| {
16887        (render_args.callback)(false, window, cx)
16888    })
16889    .unwrap();
16890    let snapshot = editor
16891        .update(cx, |editor, window, cx| editor.snapshot(window, cx))
16892        .unwrap();
16893    assert!(!snapshot.is_line_folded(MultiBufferRow(1)));
16894}
16895
16896#[gpui::test]
16897async fn test_input_text(cx: &mut TestAppContext) {
16898    init_test(cx, |_| {});
16899    let mut cx = EditorTestContext::new(cx).await;
16900
16901    cx.set_state(
16902        &r#"ˇone
16903        two
16904
16905        three
16906        fourˇ
16907        five
16908
16909        siˇx"#
16910            .unindent(),
16911    );
16912
16913    cx.dispatch_action(HandleInput(String::new()));
16914    cx.assert_editor_state(
16915        &r#"ˇone
16916        two
16917
16918        three
16919        fourˇ
16920        five
16921
16922        siˇx"#
16923            .unindent(),
16924    );
16925
16926    cx.dispatch_action(HandleInput("AAAA".to_string()));
16927    cx.assert_editor_state(
16928        &r#"AAAAˇone
16929        two
16930
16931        three
16932        fourAAAAˇ
16933        five
16934
16935        siAAAAˇx"#
16936            .unindent(),
16937    );
16938}
16939
16940#[gpui::test]
16941async fn test_scroll_cursor_center_top_bottom(cx: &mut TestAppContext) {
16942    init_test(cx, |_| {});
16943
16944    let mut cx = EditorTestContext::new(cx).await;
16945    cx.set_state(
16946        r#"let foo = 1;
16947let foo = 2;
16948let foo = 3;
16949let fooˇ = 4;
16950let foo = 5;
16951let foo = 6;
16952let foo = 7;
16953let foo = 8;
16954let foo = 9;
16955let foo = 10;
16956let foo = 11;
16957let foo = 12;
16958let foo = 13;
16959let foo = 14;
16960let foo = 15;"#,
16961    );
16962
16963    cx.update_editor(|e, window, cx| {
16964        assert_eq!(
16965            e.next_scroll_position,
16966            NextScrollCursorCenterTopBottom::Center,
16967            "Default next scroll direction is center",
16968        );
16969
16970        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16971        assert_eq!(
16972            e.next_scroll_position,
16973            NextScrollCursorCenterTopBottom::Top,
16974            "After center, next scroll direction should be top",
16975        );
16976
16977        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16978        assert_eq!(
16979            e.next_scroll_position,
16980            NextScrollCursorCenterTopBottom::Bottom,
16981            "After top, next scroll direction should be bottom",
16982        );
16983
16984        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16985        assert_eq!(
16986            e.next_scroll_position,
16987            NextScrollCursorCenterTopBottom::Center,
16988            "After bottom, scrolling should start over",
16989        );
16990
16991        e.scroll_cursor_center_top_bottom(&ScrollCursorCenterTopBottom, window, cx);
16992        assert_eq!(
16993            e.next_scroll_position,
16994            NextScrollCursorCenterTopBottom::Top,
16995            "Scrolling continues if retriggered fast enough"
16996        );
16997    });
16998
16999    cx.executor()
17000        .advance_clock(SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT + Duration::from_millis(200));
17001    cx.executor().run_until_parked();
17002    cx.update_editor(|e, _, _| {
17003        assert_eq!(
17004            e.next_scroll_position,
17005            NextScrollCursorCenterTopBottom::Center,
17006            "If scrolling is not triggered fast enough, it should reset"
17007        );
17008    });
17009}
17010
17011#[gpui::test]
17012async fn test_goto_definition_with_find_all_references_fallback(cx: &mut TestAppContext) {
17013    init_test(cx, |_| {});
17014    let mut cx = EditorLspTestContext::new_rust(
17015        lsp::ServerCapabilities {
17016            definition_provider: Some(lsp::OneOf::Left(true)),
17017            references_provider: Some(lsp::OneOf::Left(true)),
17018            ..lsp::ServerCapabilities::default()
17019        },
17020        cx,
17021    )
17022    .await;
17023
17024    let set_up_lsp_handlers = |empty_go_to_definition: bool, cx: &mut EditorLspTestContext| {
17025        let go_to_definition = cx
17026            .lsp
17027            .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17028                move |params, _| async move {
17029                    if empty_go_to_definition {
17030                        Ok(None)
17031                    } else {
17032                        Ok(Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location {
17033                            uri: params.text_document_position_params.text_document.uri,
17034                            range: lsp::Range::new(
17035                                lsp::Position::new(4, 3),
17036                                lsp::Position::new(4, 6),
17037                            ),
17038                        })))
17039                    }
17040                },
17041            );
17042        let references = cx
17043            .lsp
17044            .set_request_handler::<lsp::request::References, _, _>(move |params, _| async move {
17045                Ok(Some(vec![lsp::Location {
17046                    uri: params.text_document_position.text_document.uri,
17047                    range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 11)),
17048                }]))
17049            });
17050        (go_to_definition, references)
17051    };
17052
17053    cx.set_state(
17054        &r#"fn one() {
17055            let mut a = ˇtwo();
17056        }
17057
17058        fn two() {}"#
17059            .unindent(),
17060    );
17061    set_up_lsp_handlers(false, &mut cx);
17062    let navigated = cx
17063        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17064        .await
17065        .expect("Failed to navigate to definition");
17066    assert_eq!(
17067        navigated,
17068        Navigated::Yes,
17069        "Should have navigated to definition from the GetDefinition response"
17070    );
17071    cx.assert_editor_state(
17072        &r#"fn one() {
17073            let mut a = two();
17074        }
17075
17076        fn «twoˇ»() {}"#
17077            .unindent(),
17078    );
17079
17080    let editors = cx.update_workspace(|workspace, _, cx| {
17081        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17082    });
17083    cx.update_editor(|_, _, test_editor_cx| {
17084        assert_eq!(
17085            editors.len(),
17086            1,
17087            "Initially, only one, test, editor should be open in the workspace"
17088        );
17089        assert_eq!(
17090            test_editor_cx.entity(),
17091            editors.last().expect("Asserted len is 1").clone()
17092        );
17093    });
17094
17095    set_up_lsp_handlers(true, &mut cx);
17096    let navigated = cx
17097        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17098        .await
17099        .expect("Failed to navigate to lookup references");
17100    assert_eq!(
17101        navigated,
17102        Navigated::Yes,
17103        "Should have navigated to references as a fallback after empty GoToDefinition response"
17104    );
17105    // We should not change the selections in the existing file,
17106    // if opening another milti buffer with the references
17107    cx.assert_editor_state(
17108        &r#"fn one() {
17109            let mut a = two();
17110        }
17111
17112        fn «twoˇ»() {}"#
17113            .unindent(),
17114    );
17115    let editors = cx.update_workspace(|workspace, _, cx| {
17116        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17117    });
17118    cx.update_editor(|_, _, test_editor_cx| {
17119        assert_eq!(
17120            editors.len(),
17121            2,
17122            "After falling back to references search, we open a new editor with the results"
17123        );
17124        let references_fallback_text = editors
17125            .into_iter()
17126            .find(|new_editor| *new_editor != test_editor_cx.entity())
17127            .expect("Should have one non-test editor now")
17128            .read(test_editor_cx)
17129            .text(test_editor_cx);
17130        assert_eq!(
17131            references_fallback_text, "fn one() {\n    let mut a = two();\n}",
17132            "Should use the range from the references response and not the GoToDefinition one"
17133        );
17134    });
17135}
17136
17137#[gpui::test]
17138async fn test_goto_definition_no_fallback(cx: &mut TestAppContext) {
17139    init_test(cx, |_| {});
17140    cx.update(|cx| {
17141        let mut editor_settings = EditorSettings::get_global(cx).clone();
17142        editor_settings.go_to_definition_fallback = GoToDefinitionFallback::None;
17143        EditorSettings::override_global(editor_settings, cx);
17144    });
17145    let mut cx = EditorLspTestContext::new_rust(
17146        lsp::ServerCapabilities {
17147            definition_provider: Some(lsp::OneOf::Left(true)),
17148            references_provider: Some(lsp::OneOf::Left(true)),
17149            ..lsp::ServerCapabilities::default()
17150        },
17151        cx,
17152    )
17153    .await;
17154    let original_state = r#"fn one() {
17155        let mut a = ˇtwo();
17156    }
17157
17158    fn two() {}"#
17159        .unindent();
17160    cx.set_state(&original_state);
17161
17162    let mut go_to_definition = cx
17163        .lsp
17164        .set_request_handler::<lsp::request::GotoDefinition, _, _>(
17165            move |_, _| async move { Ok(None) },
17166        );
17167    let _references = cx
17168        .lsp
17169        .set_request_handler::<lsp::request::References, _, _>(move |_, _| async move {
17170            panic!("Should not call for references with no go to definition fallback")
17171        });
17172
17173    let navigated = cx
17174        .update_editor(|editor, window, cx| editor.go_to_definition(&GoToDefinition, window, cx))
17175        .await
17176        .expect("Failed to navigate to lookup references");
17177    go_to_definition
17178        .next()
17179        .await
17180        .expect("Should have called the go_to_definition handler");
17181
17182    assert_eq!(
17183        navigated,
17184        Navigated::No,
17185        "Should have navigated to references as a fallback after empty GoToDefinition response"
17186    );
17187    cx.assert_editor_state(&original_state);
17188    let editors = cx.update_workspace(|workspace, _, cx| {
17189        workspace.items_of_type::<Editor>(cx).collect::<Vec<_>>()
17190    });
17191    cx.update_editor(|_, _, _| {
17192        assert_eq!(
17193            editors.len(),
17194            1,
17195            "After unsuccessful fallback, no other editor should have been opened"
17196        );
17197    });
17198}
17199
17200#[gpui::test]
17201async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) {
17202    init_test(cx, |_| {});
17203
17204    let language = Arc::new(Language::new(
17205        LanguageConfig::default(),
17206        Some(tree_sitter_rust::LANGUAGE.into()),
17207    ));
17208
17209    let text = r#"
17210        #[cfg(test)]
17211        mod tests() {
17212            #[test]
17213            fn runnable_1() {
17214                let a = 1;
17215            }
17216
17217            #[test]
17218            fn runnable_2() {
17219                let a = 1;
17220                let b = 2;
17221            }
17222        }
17223    "#
17224    .unindent();
17225
17226    let fs = FakeFs::new(cx.executor());
17227    fs.insert_file("/file.rs", Default::default()).await;
17228
17229    let project = Project::test(fs, ["/a".as_ref()], cx).await;
17230    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17231    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17232    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
17233    let multi_buffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
17234
17235    let editor = cx.new_window_entity(|window, cx| {
17236        Editor::new(
17237            EditorMode::full(),
17238            multi_buffer,
17239            Some(project.clone()),
17240            window,
17241            cx,
17242        )
17243    });
17244
17245    editor.update_in(cx, |editor, window, cx| {
17246        let snapshot = editor.buffer().read(cx).snapshot(cx);
17247        editor.tasks.insert(
17248            (buffer.read(cx).remote_id(), 3),
17249            RunnableTasks {
17250                templates: vec![],
17251                offset: snapshot.anchor_before(43),
17252                column: 0,
17253                extra_variables: HashMap::default(),
17254                context_range: BufferOffset(43)..BufferOffset(85),
17255            },
17256        );
17257        editor.tasks.insert(
17258            (buffer.read(cx).remote_id(), 8),
17259            RunnableTasks {
17260                templates: vec![],
17261                offset: snapshot.anchor_before(86),
17262                column: 0,
17263                extra_variables: HashMap::default(),
17264                context_range: BufferOffset(86)..BufferOffset(191),
17265            },
17266        );
17267
17268        // Test finding task when cursor is inside function body
17269        editor.change_selections(None, window, cx, |s| {
17270            s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
17271        });
17272        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17273        assert_eq!(row, 3, "Should find task for cursor inside runnable_1");
17274
17275        // Test finding task when cursor is on function name
17276        editor.change_selections(None, window, cx, |s| {
17277            s.select_ranges([Point::new(8, 4)..Point::new(8, 4)])
17278        });
17279        let (_, row, _) = editor.find_enclosing_node_task(cx).unwrap();
17280        assert_eq!(row, 8, "Should find task when cursor is on function name");
17281    });
17282}
17283
17284#[gpui::test]
17285async fn test_folding_buffers(cx: &mut TestAppContext) {
17286    init_test(cx, |_| {});
17287
17288    let sample_text_1 = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17289    let sample_text_2 = "llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu".to_string();
17290    let sample_text_3 = "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n1111\n2222\n3333\n4444\n5555".to_string();
17291
17292    let fs = FakeFs::new(cx.executor());
17293    fs.insert_tree(
17294        path!("/a"),
17295        json!({
17296            "first.rs": sample_text_1,
17297            "second.rs": sample_text_2,
17298            "third.rs": sample_text_3,
17299        }),
17300    )
17301    .await;
17302    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17303    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17304    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17305    let worktree = project.update(cx, |project, cx| {
17306        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17307        assert_eq!(worktrees.len(), 1);
17308        worktrees.pop().unwrap()
17309    });
17310    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17311
17312    let buffer_1 = project
17313        .update(cx, |project, cx| {
17314            project.open_buffer((worktree_id, "first.rs"), cx)
17315        })
17316        .await
17317        .unwrap();
17318    let buffer_2 = project
17319        .update(cx, |project, cx| {
17320            project.open_buffer((worktree_id, "second.rs"), cx)
17321        })
17322        .await
17323        .unwrap();
17324    let buffer_3 = project
17325        .update(cx, |project, cx| {
17326            project.open_buffer((worktree_id, "third.rs"), cx)
17327        })
17328        .await
17329        .unwrap();
17330
17331    let multi_buffer = cx.new(|cx| {
17332        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17333        multi_buffer.push_excerpts(
17334            buffer_1.clone(),
17335            [
17336                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17337                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17338                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17339            ],
17340            cx,
17341        );
17342        multi_buffer.push_excerpts(
17343            buffer_2.clone(),
17344            [
17345                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17346                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17347                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17348            ],
17349            cx,
17350        );
17351        multi_buffer.push_excerpts(
17352            buffer_3.clone(),
17353            [
17354                ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
17355                ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
17356                ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
17357            ],
17358            cx,
17359        );
17360        multi_buffer
17361    });
17362    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17363        Editor::new(
17364            EditorMode::full(),
17365            multi_buffer.clone(),
17366            Some(project.clone()),
17367            window,
17368            cx,
17369        )
17370    });
17371
17372    assert_eq!(
17373        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17374        "\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",
17375    );
17376
17377    multi_buffer_editor.update(cx, |editor, cx| {
17378        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17379    });
17380    assert_eq!(
17381        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17382        "\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",
17383        "After folding the first buffer, its text should not be displayed"
17384    );
17385
17386    multi_buffer_editor.update(cx, |editor, cx| {
17387        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17388    });
17389    assert_eq!(
17390        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17391        "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555",
17392        "After folding the second buffer, its text should not be displayed"
17393    );
17394
17395    multi_buffer_editor.update(cx, |editor, cx| {
17396        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17397    });
17398    assert_eq!(
17399        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17400        "\n\n\n\n\n",
17401        "After folding the third buffer, its text should not be displayed"
17402    );
17403
17404    // Emulate selection inside the fold logic, that should work
17405    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17406        editor
17407            .snapshot(window, cx)
17408            .next_line_boundary(Point::new(0, 4));
17409    });
17410
17411    multi_buffer_editor.update(cx, |editor, cx| {
17412        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17413    });
17414    assert_eq!(
17415        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17416        "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17417        "After unfolding the second buffer, its text should be displayed"
17418    );
17419
17420    // Typing inside of buffer 1 causes that buffer to be unfolded.
17421    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17422        assert_eq!(
17423            multi_buffer
17424                .read(cx)
17425                .snapshot(cx)
17426                .text_for_range(Point::new(1, 0)..Point::new(1, 4))
17427                .collect::<String>(),
17428            "bbbb"
17429        );
17430        editor.change_selections(None, window, cx, |selections| {
17431            selections.select_ranges(vec![Point::new(1, 0)..Point::new(1, 0)]);
17432        });
17433        editor.handle_input("B", window, cx);
17434    });
17435
17436    assert_eq!(
17437        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17438        "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n",
17439        "After unfolding the first buffer, its and 2nd buffer's text should be displayed"
17440    );
17441
17442    multi_buffer_editor.update(cx, |editor, cx| {
17443        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17444    });
17445    assert_eq!(
17446        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17447        "\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",
17448        "After unfolding the all buffers, all original text should be displayed"
17449    );
17450}
17451
17452#[gpui::test]
17453async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) {
17454    init_test(cx, |_| {});
17455
17456    let sample_text_1 = "1111\n2222\n3333".to_string();
17457    let sample_text_2 = "4444\n5555\n6666".to_string();
17458    let sample_text_3 = "7777\n8888\n9999".to_string();
17459
17460    let fs = FakeFs::new(cx.executor());
17461    fs.insert_tree(
17462        path!("/a"),
17463        json!({
17464            "first.rs": sample_text_1,
17465            "second.rs": sample_text_2,
17466            "third.rs": sample_text_3,
17467        }),
17468    )
17469    .await;
17470    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17471    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17472    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17473    let worktree = project.update(cx, |project, cx| {
17474        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17475        assert_eq!(worktrees.len(), 1);
17476        worktrees.pop().unwrap()
17477    });
17478    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17479
17480    let buffer_1 = project
17481        .update(cx, |project, cx| {
17482            project.open_buffer((worktree_id, "first.rs"), cx)
17483        })
17484        .await
17485        .unwrap();
17486    let buffer_2 = project
17487        .update(cx, |project, cx| {
17488            project.open_buffer((worktree_id, "second.rs"), cx)
17489        })
17490        .await
17491        .unwrap();
17492    let buffer_3 = project
17493        .update(cx, |project, cx| {
17494            project.open_buffer((worktree_id, "third.rs"), cx)
17495        })
17496        .await
17497        .unwrap();
17498
17499    let multi_buffer = cx.new(|cx| {
17500        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17501        multi_buffer.push_excerpts(
17502            buffer_1.clone(),
17503            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17504            cx,
17505        );
17506        multi_buffer.push_excerpts(
17507            buffer_2.clone(),
17508            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17509            cx,
17510        );
17511        multi_buffer.push_excerpts(
17512            buffer_3.clone(),
17513            [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
17514            cx,
17515        );
17516        multi_buffer
17517    });
17518
17519    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17520        Editor::new(
17521            EditorMode::full(),
17522            multi_buffer,
17523            Some(project.clone()),
17524            window,
17525            cx,
17526        )
17527    });
17528
17529    let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999";
17530    assert_eq!(
17531        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17532        full_text,
17533    );
17534
17535    multi_buffer_editor.update(cx, |editor, cx| {
17536        editor.fold_buffer(buffer_1.read(cx).remote_id(), cx)
17537    });
17538    assert_eq!(
17539        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17540        "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999",
17541        "After folding the first buffer, its text should not be displayed"
17542    );
17543
17544    multi_buffer_editor.update(cx, |editor, cx| {
17545        editor.fold_buffer(buffer_2.read(cx).remote_id(), cx)
17546    });
17547
17548    assert_eq!(
17549        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17550        "\n\n\n\n\n\n7777\n8888\n9999",
17551        "After folding the second buffer, its text should not be displayed"
17552    );
17553
17554    multi_buffer_editor.update(cx, |editor, cx| {
17555        editor.fold_buffer(buffer_3.read(cx).remote_id(), cx)
17556    });
17557    assert_eq!(
17558        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17559        "\n\n\n\n\n",
17560        "After folding the third buffer, its text should not be displayed"
17561    );
17562
17563    multi_buffer_editor.update(cx, |editor, cx| {
17564        editor.unfold_buffer(buffer_2.read(cx).remote_id(), cx)
17565    });
17566    assert_eq!(
17567        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17568        "\n\n\n\n4444\n5555\n6666\n\n",
17569        "After unfolding the second buffer, its text should be displayed"
17570    );
17571
17572    multi_buffer_editor.update(cx, |editor, cx| {
17573        editor.unfold_buffer(buffer_1.read(cx).remote_id(), cx)
17574    });
17575    assert_eq!(
17576        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17577        "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n",
17578        "After unfolding the first buffer, its text should be displayed"
17579    );
17580
17581    multi_buffer_editor.update(cx, |editor, cx| {
17582        editor.unfold_buffer(buffer_3.read(cx).remote_id(), cx)
17583    });
17584    assert_eq!(
17585        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17586        full_text,
17587        "After unfolding all buffers, all original text should be displayed"
17588    );
17589}
17590
17591#[gpui::test]
17592async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut TestAppContext) {
17593    init_test(cx, |_| {});
17594
17595    let sample_text = "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj".to_string();
17596
17597    let fs = FakeFs::new(cx.executor());
17598    fs.insert_tree(
17599        path!("/a"),
17600        json!({
17601            "main.rs": sample_text,
17602        }),
17603    )
17604    .await;
17605    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
17606    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
17607    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
17608    let worktree = project.update(cx, |project, cx| {
17609        let mut worktrees = project.worktrees(cx).collect::<Vec<_>>();
17610        assert_eq!(worktrees.len(), 1);
17611        worktrees.pop().unwrap()
17612    });
17613    let worktree_id = worktree.update(cx, |worktree, _| worktree.id());
17614
17615    let buffer_1 = project
17616        .update(cx, |project, cx| {
17617            project.open_buffer((worktree_id, "main.rs"), cx)
17618        })
17619        .await
17620        .unwrap();
17621
17622    let multi_buffer = cx.new(|cx| {
17623        let mut multi_buffer = MultiBuffer::new(ReadWrite);
17624        multi_buffer.push_excerpts(
17625            buffer_1.clone(),
17626            [ExcerptRange::new(
17627                Point::new(0, 0)
17628                    ..Point::new(
17629                        sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1,
17630                        0,
17631                    ),
17632            )],
17633            cx,
17634        );
17635        multi_buffer
17636    });
17637    let multi_buffer_editor = cx.new_window_entity(|window, cx| {
17638        Editor::new(
17639            EditorMode::full(),
17640            multi_buffer,
17641            Some(project.clone()),
17642            window,
17643            cx,
17644        )
17645    });
17646
17647    let selection_range = Point::new(1, 0)..Point::new(2, 0);
17648    multi_buffer_editor.update_in(cx, |editor, window, cx| {
17649        enum TestHighlight {}
17650        let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
17651        let highlight_range = selection_range.clone().to_anchors(&multi_buffer_snapshot);
17652        editor.highlight_text::<TestHighlight>(
17653            vec![highlight_range.clone()],
17654            HighlightStyle::color(Hsla::green()),
17655            cx,
17656        );
17657        editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range)));
17658    });
17659
17660    let full_text = format!("\n\n{sample_text}");
17661    assert_eq!(
17662        multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)),
17663        full_text,
17664    );
17665}
17666
17667#[gpui::test]
17668async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContext) {
17669    init_test(cx, |_| {});
17670    cx.update(|cx| {
17671        let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
17672            "keymaps/default-linux.json",
17673            cx,
17674        )
17675        .unwrap();
17676        cx.bind_keys(default_key_bindings);
17677    });
17678
17679    let (editor, cx) = cx.add_window_view(|window, cx| {
17680        let multi_buffer = MultiBuffer::build_multi(
17681            [
17682                ("a0\nb0\nc0\nd0\ne0\n", vec![Point::row_range(0..2)]),
17683                ("a1\nb1\nc1\nd1\ne1\n", vec![Point::row_range(0..2)]),
17684                ("a2\nb2\nc2\nd2\ne2\n", vec![Point::row_range(0..2)]),
17685                ("a3\nb3\nc3\nd3\ne3\n", vec![Point::row_range(0..2)]),
17686            ],
17687            cx,
17688        );
17689        let mut editor = Editor::new(EditorMode::full(), multi_buffer.clone(), None, window, cx);
17690
17691        let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
17692        // fold all but the second buffer, so that we test navigating between two
17693        // adjacent folded buffers, as well as folded buffers at the start and
17694        // end the multibuffer
17695        editor.fold_buffer(buffer_ids[0], cx);
17696        editor.fold_buffer(buffer_ids[2], cx);
17697        editor.fold_buffer(buffer_ids[3], cx);
17698
17699        editor
17700    });
17701    cx.simulate_resize(size(px(1000.), px(1000.)));
17702
17703    let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
17704    cx.assert_excerpts_with_selections(indoc! {"
17705        [EXCERPT]
17706        ˇ[FOLDED]
17707        [EXCERPT]
17708        a1
17709        b1
17710        [EXCERPT]
17711        [FOLDED]
17712        [EXCERPT]
17713        [FOLDED]
17714        "
17715    });
17716    cx.simulate_keystroke("down");
17717    cx.assert_excerpts_with_selections(indoc! {"
17718        [EXCERPT]
17719        [FOLDED]
17720        [EXCERPT]
17721        ˇa1
17722        b1
17723        [EXCERPT]
17724        [FOLDED]
17725        [EXCERPT]
17726        [FOLDED]
17727        "
17728    });
17729    cx.simulate_keystroke("down");
17730    cx.assert_excerpts_with_selections(indoc! {"
17731        [EXCERPT]
17732        [FOLDED]
17733        [EXCERPT]
17734        a1
17735        ˇb1
17736        [EXCERPT]
17737        [FOLDED]
17738        [EXCERPT]
17739        [FOLDED]
17740        "
17741    });
17742    cx.simulate_keystroke("down");
17743    cx.assert_excerpts_with_selections(indoc! {"
17744        [EXCERPT]
17745        [FOLDED]
17746        [EXCERPT]
17747        a1
17748        b1
17749        ˇ[EXCERPT]
17750        [FOLDED]
17751        [EXCERPT]
17752        [FOLDED]
17753        "
17754    });
17755    cx.simulate_keystroke("down");
17756    cx.assert_excerpts_with_selections(indoc! {"
17757        [EXCERPT]
17758        [FOLDED]
17759        [EXCERPT]
17760        a1
17761        b1
17762        [EXCERPT]
17763        ˇ[FOLDED]
17764        [EXCERPT]
17765        [FOLDED]
17766        "
17767    });
17768    for _ in 0..5 {
17769        cx.simulate_keystroke("down");
17770        cx.assert_excerpts_with_selections(indoc! {"
17771            [EXCERPT]
17772            [FOLDED]
17773            [EXCERPT]
17774            a1
17775            b1
17776            [EXCERPT]
17777            [FOLDED]
17778            [EXCERPT]
17779            ˇ[FOLDED]
17780            "
17781        });
17782    }
17783
17784    cx.simulate_keystroke("up");
17785    cx.assert_excerpts_with_selections(indoc! {"
17786        [EXCERPT]
17787        [FOLDED]
17788        [EXCERPT]
17789        a1
17790        b1
17791        [EXCERPT]
17792        ˇ[FOLDED]
17793        [EXCERPT]
17794        [FOLDED]
17795        "
17796    });
17797    cx.simulate_keystroke("up");
17798    cx.assert_excerpts_with_selections(indoc! {"
17799        [EXCERPT]
17800        [FOLDED]
17801        [EXCERPT]
17802        a1
17803        b1
17804        ˇ[EXCERPT]
17805        [FOLDED]
17806        [EXCERPT]
17807        [FOLDED]
17808        "
17809    });
17810    cx.simulate_keystroke("up");
17811    cx.assert_excerpts_with_selections(indoc! {"
17812        [EXCERPT]
17813        [FOLDED]
17814        [EXCERPT]
17815        a1
17816        ˇb1
17817        [EXCERPT]
17818        [FOLDED]
17819        [EXCERPT]
17820        [FOLDED]
17821        "
17822    });
17823    cx.simulate_keystroke("up");
17824    cx.assert_excerpts_with_selections(indoc! {"
17825        [EXCERPT]
17826        [FOLDED]
17827        [EXCERPT]
17828        ˇa1
17829        b1
17830        [EXCERPT]
17831        [FOLDED]
17832        [EXCERPT]
17833        [FOLDED]
17834        "
17835    });
17836    for _ in 0..5 {
17837        cx.simulate_keystroke("up");
17838        cx.assert_excerpts_with_selections(indoc! {"
17839            [EXCERPT]
17840            ˇ[FOLDED]
17841            [EXCERPT]
17842            a1
17843            b1
17844            [EXCERPT]
17845            [FOLDED]
17846            [EXCERPT]
17847            [FOLDED]
17848            "
17849        });
17850    }
17851}
17852
17853#[gpui::test]
17854async fn test_inline_completion_text(cx: &mut TestAppContext) {
17855    init_test(cx, |_| {});
17856
17857    // Simple insertion
17858    assert_highlighted_edits(
17859        "Hello, world!",
17860        vec![(Point::new(0, 6)..Point::new(0, 6), " beautiful".into())],
17861        true,
17862        cx,
17863        |highlighted_edits, cx| {
17864            assert_eq!(highlighted_edits.text, "Hello, beautiful world!");
17865            assert_eq!(highlighted_edits.highlights.len(), 1);
17866            assert_eq!(highlighted_edits.highlights[0].0, 6..16);
17867            assert_eq!(
17868                highlighted_edits.highlights[0].1.background_color,
17869                Some(cx.theme().status().created_background)
17870            );
17871        },
17872    )
17873    .await;
17874
17875    // Replacement
17876    assert_highlighted_edits(
17877        "This is a test.",
17878        vec![(Point::new(0, 0)..Point::new(0, 4), "That".into())],
17879        false,
17880        cx,
17881        |highlighted_edits, cx| {
17882            assert_eq!(highlighted_edits.text, "That is a test.");
17883            assert_eq!(highlighted_edits.highlights.len(), 1);
17884            assert_eq!(highlighted_edits.highlights[0].0, 0..4);
17885            assert_eq!(
17886                highlighted_edits.highlights[0].1.background_color,
17887                Some(cx.theme().status().created_background)
17888            );
17889        },
17890    )
17891    .await;
17892
17893    // Multiple edits
17894    assert_highlighted_edits(
17895        "Hello, world!",
17896        vec![
17897            (Point::new(0, 0)..Point::new(0, 5), "Greetings".into()),
17898            (Point::new(0, 12)..Point::new(0, 12), " and universe".into()),
17899        ],
17900        false,
17901        cx,
17902        |highlighted_edits, cx| {
17903            assert_eq!(highlighted_edits.text, "Greetings, world and universe!");
17904            assert_eq!(highlighted_edits.highlights.len(), 2);
17905            assert_eq!(highlighted_edits.highlights[0].0, 0..9);
17906            assert_eq!(highlighted_edits.highlights[1].0, 16..29);
17907            assert_eq!(
17908                highlighted_edits.highlights[0].1.background_color,
17909                Some(cx.theme().status().created_background)
17910            );
17911            assert_eq!(
17912                highlighted_edits.highlights[1].1.background_color,
17913                Some(cx.theme().status().created_background)
17914            );
17915        },
17916    )
17917    .await;
17918
17919    // Multiple lines with edits
17920    assert_highlighted_edits(
17921        "First line\nSecond line\nThird line\nFourth line",
17922        vec![
17923            (Point::new(1, 7)..Point::new(1, 11), "modified".to_string()),
17924            (
17925                Point::new(2, 0)..Point::new(2, 10),
17926                "New third line".to_string(),
17927            ),
17928            (Point::new(3, 6)..Point::new(3, 6), " updated".to_string()),
17929        ],
17930        false,
17931        cx,
17932        |highlighted_edits, cx| {
17933            assert_eq!(
17934                highlighted_edits.text,
17935                "Second modified\nNew third line\nFourth updated line"
17936            );
17937            assert_eq!(highlighted_edits.highlights.len(), 3);
17938            assert_eq!(highlighted_edits.highlights[0].0, 7..15); // "modified"
17939            assert_eq!(highlighted_edits.highlights[1].0, 16..30); // "New third line"
17940            assert_eq!(highlighted_edits.highlights[2].0, 37..45); // " updated"
17941            for highlight in &highlighted_edits.highlights {
17942                assert_eq!(
17943                    highlight.1.background_color,
17944                    Some(cx.theme().status().created_background)
17945                );
17946            }
17947        },
17948    )
17949    .await;
17950}
17951
17952#[gpui::test]
17953async fn test_inline_completion_text_with_deletions(cx: &mut TestAppContext) {
17954    init_test(cx, |_| {});
17955
17956    // Deletion
17957    assert_highlighted_edits(
17958        "Hello, world!",
17959        vec![(Point::new(0, 5)..Point::new(0, 11), "".to_string())],
17960        true,
17961        cx,
17962        |highlighted_edits, cx| {
17963            assert_eq!(highlighted_edits.text, "Hello, world!");
17964            assert_eq!(highlighted_edits.highlights.len(), 1);
17965            assert_eq!(highlighted_edits.highlights[0].0, 5..11);
17966            assert_eq!(
17967                highlighted_edits.highlights[0].1.background_color,
17968                Some(cx.theme().status().deleted_background)
17969            );
17970        },
17971    )
17972    .await;
17973
17974    // Insertion
17975    assert_highlighted_edits(
17976        "Hello, world!",
17977        vec![(Point::new(0, 6)..Point::new(0, 6), " digital".to_string())],
17978        true,
17979        cx,
17980        |highlighted_edits, cx| {
17981            assert_eq!(highlighted_edits.highlights.len(), 1);
17982            assert_eq!(highlighted_edits.highlights[0].0, 6..14);
17983            assert_eq!(
17984                highlighted_edits.highlights[0].1.background_color,
17985                Some(cx.theme().status().created_background)
17986            );
17987        },
17988    )
17989    .await;
17990}
17991
17992async fn assert_highlighted_edits(
17993    text: &str,
17994    edits: Vec<(Range<Point>, String)>,
17995    include_deletions: bool,
17996    cx: &mut TestAppContext,
17997    assertion_fn: impl Fn(HighlightedText, &App),
17998) {
17999    let window = cx.add_window(|window, cx| {
18000        let buffer = MultiBuffer::build_simple(text, cx);
18001        Editor::new(EditorMode::full(), buffer, None, window, cx)
18002    });
18003    let cx = &mut VisualTestContext::from_window(*window, cx);
18004
18005    let (buffer, snapshot) = window
18006        .update(cx, |editor, _window, cx| {
18007            (
18008                editor.buffer().clone(),
18009                editor.buffer().read(cx).snapshot(cx),
18010            )
18011        })
18012        .unwrap();
18013
18014    let edits = edits
18015        .into_iter()
18016        .map(|(range, edit)| {
18017            (
18018                snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end),
18019                edit,
18020            )
18021        })
18022        .collect::<Vec<_>>();
18023
18024    let text_anchor_edits = edits
18025        .clone()
18026        .into_iter()
18027        .map(|(range, edit)| (range.start.text_anchor..range.end.text_anchor, edit))
18028        .collect::<Vec<_>>();
18029
18030    let edit_preview = window
18031        .update(cx, |_, _window, cx| {
18032            buffer
18033                .read(cx)
18034                .as_singleton()
18035                .unwrap()
18036                .read(cx)
18037                .preview_edits(text_anchor_edits.into(), cx)
18038        })
18039        .unwrap()
18040        .await;
18041
18042    cx.update(|_window, cx| {
18043        let highlighted_edits = inline_completion_edit_text(
18044            &snapshot.as_singleton().unwrap().2,
18045            &edits,
18046            &edit_preview,
18047            include_deletions,
18048            cx,
18049        );
18050        assertion_fn(highlighted_edits, cx)
18051    });
18052}
18053
18054#[track_caller]
18055fn assert_breakpoint(
18056    breakpoints: &BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
18057    path: &Arc<Path>,
18058    expected: Vec<(u32, Breakpoint)>,
18059) {
18060    if expected.len() == 0usize {
18061        assert!(!breakpoints.contains_key(path), "{}", path.display());
18062    } else {
18063        let mut breakpoint = breakpoints
18064            .get(path)
18065            .unwrap()
18066            .into_iter()
18067            .map(|breakpoint| {
18068                (
18069                    breakpoint.row,
18070                    Breakpoint {
18071                        message: breakpoint.message.clone(),
18072                        state: breakpoint.state,
18073                        condition: breakpoint.condition.clone(),
18074                        hit_condition: breakpoint.hit_condition.clone(),
18075                    },
18076                )
18077            })
18078            .collect::<Vec<_>>();
18079
18080        breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
18081
18082        assert_eq!(expected, breakpoint);
18083    }
18084}
18085
18086fn add_log_breakpoint_at_cursor(
18087    editor: &mut Editor,
18088    log_message: &str,
18089    window: &mut Window,
18090    cx: &mut Context<Editor>,
18091) {
18092    let (anchor, bp) = editor
18093        .breakpoints_at_cursors(window, cx)
18094        .first()
18095        .and_then(|(anchor, bp)| {
18096            if let Some(bp) = bp {
18097                Some((*anchor, bp.clone()))
18098            } else {
18099                None
18100            }
18101        })
18102        .unwrap_or_else(|| {
18103            let cursor_position: Point = editor.selections.newest(cx).head();
18104
18105            let breakpoint_position = editor
18106                .snapshot(window, cx)
18107                .display_snapshot
18108                .buffer_snapshot
18109                .anchor_before(Point::new(cursor_position.row, 0));
18110
18111            (breakpoint_position, Breakpoint::new_log(&log_message))
18112        });
18113
18114    editor.edit_breakpoint_at_anchor(
18115        anchor,
18116        bp,
18117        BreakpointEditAction::EditLogMessage(log_message.into()),
18118        cx,
18119    );
18120}
18121
18122#[gpui::test]
18123async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
18124    init_test(cx, |_| {});
18125
18126    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18127    let fs = FakeFs::new(cx.executor());
18128    fs.insert_tree(
18129        path!("/a"),
18130        json!({
18131            "main.rs": sample_text,
18132        }),
18133    )
18134    .await;
18135    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18136    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18137    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18138
18139    let fs = FakeFs::new(cx.executor());
18140    fs.insert_tree(
18141        path!("/a"),
18142        json!({
18143            "main.rs": sample_text,
18144        }),
18145    )
18146    .await;
18147    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18148    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18149    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18150    let worktree_id = workspace
18151        .update(cx, |workspace, _window, cx| {
18152            workspace.project().update(cx, |project, cx| {
18153                project.worktrees(cx).next().unwrap().read(cx).id()
18154            })
18155        })
18156        .unwrap();
18157
18158    let buffer = project
18159        .update(cx, |project, cx| {
18160            project.open_buffer((worktree_id, "main.rs"), cx)
18161        })
18162        .await
18163        .unwrap();
18164
18165    let (editor, cx) = cx.add_window_view(|window, cx| {
18166        Editor::new(
18167            EditorMode::full(),
18168            MultiBuffer::build_from_buffer(buffer, cx),
18169            Some(project.clone()),
18170            window,
18171            cx,
18172        )
18173    });
18174
18175    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18176    let abs_path = project.read_with(cx, |project, cx| {
18177        project
18178            .absolute_path(&project_path, cx)
18179            .map(|path_buf| Arc::from(path_buf.to_owned()))
18180            .unwrap()
18181    });
18182
18183    // assert we can add breakpoint on the first line
18184    editor.update_in(cx, |editor, window, cx| {
18185        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18186        editor.move_to_end(&MoveToEnd, window, cx);
18187        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18188    });
18189
18190    let breakpoints = editor.update(cx, |editor, cx| {
18191        editor
18192            .breakpoint_store()
18193            .as_ref()
18194            .unwrap()
18195            .read(cx)
18196            .all_breakpoints(cx)
18197            .clone()
18198    });
18199
18200    assert_eq!(1, breakpoints.len());
18201    assert_breakpoint(
18202        &breakpoints,
18203        &abs_path,
18204        vec![
18205            (0, Breakpoint::new_standard()),
18206            (3, Breakpoint::new_standard()),
18207        ],
18208    );
18209
18210    editor.update_in(cx, |editor, window, cx| {
18211        editor.move_to_beginning(&MoveToBeginning, window, cx);
18212        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18213    });
18214
18215    let breakpoints = editor.update(cx, |editor, cx| {
18216        editor
18217            .breakpoint_store()
18218            .as_ref()
18219            .unwrap()
18220            .read(cx)
18221            .all_breakpoints(cx)
18222            .clone()
18223    });
18224
18225    assert_eq!(1, breakpoints.len());
18226    assert_breakpoint(
18227        &breakpoints,
18228        &abs_path,
18229        vec![(3, Breakpoint::new_standard())],
18230    );
18231
18232    editor.update_in(cx, |editor, window, cx| {
18233        editor.move_to_end(&MoveToEnd, window, cx);
18234        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18235    });
18236
18237    let breakpoints = editor.update(cx, |editor, cx| {
18238        editor
18239            .breakpoint_store()
18240            .as_ref()
18241            .unwrap()
18242            .read(cx)
18243            .all_breakpoints(cx)
18244            .clone()
18245    });
18246
18247    assert_eq!(0, breakpoints.len());
18248    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18249}
18250
18251#[gpui::test]
18252async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
18253    init_test(cx, |_| {});
18254
18255    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18256
18257    let fs = FakeFs::new(cx.executor());
18258    fs.insert_tree(
18259        path!("/a"),
18260        json!({
18261            "main.rs": sample_text,
18262        }),
18263    )
18264    .await;
18265    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18266    let (workspace, cx) =
18267        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18268
18269    let worktree_id = workspace.update(cx, |workspace, cx| {
18270        workspace.project().update(cx, |project, cx| {
18271            project.worktrees(cx).next().unwrap().read(cx).id()
18272        })
18273    });
18274
18275    let buffer = project
18276        .update(cx, |project, cx| {
18277            project.open_buffer((worktree_id, "main.rs"), cx)
18278        })
18279        .await
18280        .unwrap();
18281
18282    let (editor, cx) = cx.add_window_view(|window, cx| {
18283        Editor::new(
18284            EditorMode::full(),
18285            MultiBuffer::build_from_buffer(buffer, cx),
18286            Some(project.clone()),
18287            window,
18288            cx,
18289        )
18290    });
18291
18292    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18293    let abs_path = project.read_with(cx, |project, cx| {
18294        project
18295            .absolute_path(&project_path, cx)
18296            .map(|path_buf| Arc::from(path_buf.to_owned()))
18297            .unwrap()
18298    });
18299
18300    editor.update_in(cx, |editor, window, cx| {
18301        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18302    });
18303
18304    let breakpoints = editor.update(cx, |editor, cx| {
18305        editor
18306            .breakpoint_store()
18307            .as_ref()
18308            .unwrap()
18309            .read(cx)
18310            .all_breakpoints(cx)
18311            .clone()
18312    });
18313
18314    assert_breakpoint(
18315        &breakpoints,
18316        &abs_path,
18317        vec![(0, Breakpoint::new_log("hello world"))],
18318    );
18319
18320    // Removing a log message from a log breakpoint should remove it
18321    editor.update_in(cx, |editor, window, cx| {
18322        add_log_breakpoint_at_cursor(editor, "", window, cx);
18323    });
18324
18325    let breakpoints = editor.update(cx, |editor, cx| {
18326        editor
18327            .breakpoint_store()
18328            .as_ref()
18329            .unwrap()
18330            .read(cx)
18331            .all_breakpoints(cx)
18332            .clone()
18333    });
18334
18335    assert_breakpoint(&breakpoints, &abs_path, vec![]);
18336
18337    editor.update_in(cx, |editor, window, cx| {
18338        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18339        editor.move_to_end(&MoveToEnd, window, cx);
18340        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18341        // Not adding a log message to a standard breakpoint shouldn't remove it
18342        add_log_breakpoint_at_cursor(editor, "", window, cx);
18343    });
18344
18345    let breakpoints = editor.update(cx, |editor, cx| {
18346        editor
18347            .breakpoint_store()
18348            .as_ref()
18349            .unwrap()
18350            .read(cx)
18351            .all_breakpoints(cx)
18352            .clone()
18353    });
18354
18355    assert_breakpoint(
18356        &breakpoints,
18357        &abs_path,
18358        vec![
18359            (0, Breakpoint::new_standard()),
18360            (3, Breakpoint::new_standard()),
18361        ],
18362    );
18363
18364    editor.update_in(cx, |editor, window, cx| {
18365        add_log_breakpoint_at_cursor(editor, "hello world", window, cx);
18366    });
18367
18368    let breakpoints = editor.update(cx, |editor, cx| {
18369        editor
18370            .breakpoint_store()
18371            .as_ref()
18372            .unwrap()
18373            .read(cx)
18374            .all_breakpoints(cx)
18375            .clone()
18376    });
18377
18378    assert_breakpoint(
18379        &breakpoints,
18380        &abs_path,
18381        vec![
18382            (0, Breakpoint::new_standard()),
18383            (3, Breakpoint::new_log("hello world")),
18384        ],
18385    );
18386
18387    editor.update_in(cx, |editor, window, cx| {
18388        add_log_breakpoint_at_cursor(editor, "hello Earth!!", window, cx);
18389    });
18390
18391    let breakpoints = editor.update(cx, |editor, cx| {
18392        editor
18393            .breakpoint_store()
18394            .as_ref()
18395            .unwrap()
18396            .read(cx)
18397            .all_breakpoints(cx)
18398            .clone()
18399    });
18400
18401    assert_breakpoint(
18402        &breakpoints,
18403        &abs_path,
18404        vec![
18405            (0, Breakpoint::new_standard()),
18406            (3, Breakpoint::new_log("hello Earth!!")),
18407        ],
18408    );
18409}
18410
18411/// This also tests that Editor::breakpoint_at_cursor_head is working properly
18412/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
18413/// or when breakpoints were placed out of order. This tests for a regression too
18414#[gpui::test]
18415async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
18416    init_test(cx, |_| {});
18417
18418    let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
18419    let fs = FakeFs::new(cx.executor());
18420    fs.insert_tree(
18421        path!("/a"),
18422        json!({
18423            "main.rs": sample_text,
18424        }),
18425    )
18426    .await;
18427    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18428    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18429    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18430
18431    let fs = FakeFs::new(cx.executor());
18432    fs.insert_tree(
18433        path!("/a"),
18434        json!({
18435            "main.rs": sample_text,
18436        }),
18437    )
18438    .await;
18439    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18440    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18441    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18442    let worktree_id = workspace
18443        .update(cx, |workspace, _window, cx| {
18444            workspace.project().update(cx, |project, cx| {
18445                project.worktrees(cx).next().unwrap().read(cx).id()
18446            })
18447        })
18448        .unwrap();
18449
18450    let buffer = project
18451        .update(cx, |project, cx| {
18452            project.open_buffer((worktree_id, "main.rs"), cx)
18453        })
18454        .await
18455        .unwrap();
18456
18457    let (editor, cx) = cx.add_window_view(|window, cx| {
18458        Editor::new(
18459            EditorMode::full(),
18460            MultiBuffer::build_from_buffer(buffer, cx),
18461            Some(project.clone()),
18462            window,
18463            cx,
18464        )
18465    });
18466
18467    let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
18468    let abs_path = project.read_with(cx, |project, cx| {
18469        project
18470            .absolute_path(&project_path, cx)
18471            .map(|path_buf| Arc::from(path_buf.to_owned()))
18472            .unwrap()
18473    });
18474
18475    // assert we can add breakpoint on the first line
18476    editor.update_in(cx, |editor, window, cx| {
18477        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18478        editor.move_to_end(&MoveToEnd, window, cx);
18479        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18480        editor.move_up(&MoveUp, window, cx);
18481        editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
18482    });
18483
18484    let breakpoints = editor.update(cx, |editor, cx| {
18485        editor
18486            .breakpoint_store()
18487            .as_ref()
18488            .unwrap()
18489            .read(cx)
18490            .all_breakpoints(cx)
18491            .clone()
18492    });
18493
18494    assert_eq!(1, breakpoints.len());
18495    assert_breakpoint(
18496        &breakpoints,
18497        &abs_path,
18498        vec![
18499            (0, Breakpoint::new_standard()),
18500            (2, Breakpoint::new_standard()),
18501            (3, Breakpoint::new_standard()),
18502        ],
18503    );
18504
18505    editor.update_in(cx, |editor, window, cx| {
18506        editor.move_to_beginning(&MoveToBeginning, window, cx);
18507        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18508        editor.move_to_end(&MoveToEnd, window, cx);
18509        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18510        // Disabling a breakpoint that doesn't exist should do nothing
18511        editor.move_up(&MoveUp, window, cx);
18512        editor.move_up(&MoveUp, window, cx);
18513        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18514    });
18515
18516    let breakpoints = editor.update(cx, |editor, cx| {
18517        editor
18518            .breakpoint_store()
18519            .as_ref()
18520            .unwrap()
18521            .read(cx)
18522            .all_breakpoints(cx)
18523            .clone()
18524    });
18525
18526    let disable_breakpoint = {
18527        let mut bp = Breakpoint::new_standard();
18528        bp.state = BreakpointState::Disabled;
18529        bp
18530    };
18531
18532    assert_eq!(1, breakpoints.len());
18533    assert_breakpoint(
18534        &breakpoints,
18535        &abs_path,
18536        vec![
18537            (0, disable_breakpoint.clone()),
18538            (2, Breakpoint::new_standard()),
18539            (3, disable_breakpoint.clone()),
18540        ],
18541    );
18542
18543    editor.update_in(cx, |editor, window, cx| {
18544        editor.move_to_beginning(&MoveToBeginning, window, cx);
18545        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18546        editor.move_to_end(&MoveToEnd, window, cx);
18547        editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
18548        editor.move_up(&MoveUp, window, cx);
18549        editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
18550    });
18551
18552    let breakpoints = editor.update(cx, |editor, cx| {
18553        editor
18554            .breakpoint_store()
18555            .as_ref()
18556            .unwrap()
18557            .read(cx)
18558            .all_breakpoints(cx)
18559            .clone()
18560    });
18561
18562    assert_eq!(1, breakpoints.len());
18563    assert_breakpoint(
18564        &breakpoints,
18565        &abs_path,
18566        vec![
18567            (0, Breakpoint::new_standard()),
18568            (2, disable_breakpoint),
18569            (3, Breakpoint::new_standard()),
18570        ],
18571    );
18572}
18573
18574#[gpui::test]
18575async fn test_rename_with_duplicate_edits(cx: &mut TestAppContext) {
18576    init_test(cx, |_| {});
18577    let capabilities = lsp::ServerCapabilities {
18578        rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
18579            prepare_provider: Some(true),
18580            work_done_progress_options: Default::default(),
18581        })),
18582        ..Default::default()
18583    };
18584    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18585
18586    cx.set_state(indoc! {"
18587        struct Fˇoo {}
18588    "});
18589
18590    cx.update_editor(|editor, _, cx| {
18591        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18592        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18593        editor.highlight_background::<DocumentHighlightRead>(
18594            &[highlight_range],
18595            |c| c.editor_document_highlight_read_background,
18596            cx,
18597        );
18598    });
18599
18600    let mut prepare_rename_handler = cx
18601        .set_request_handler::<lsp::request::PrepareRenameRequest, _, _>(
18602            move |_, _, _| async move {
18603                Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range {
18604                    start: lsp::Position {
18605                        line: 0,
18606                        character: 7,
18607                    },
18608                    end: lsp::Position {
18609                        line: 0,
18610                        character: 10,
18611                    },
18612                })))
18613            },
18614        );
18615    let prepare_rename_task = cx
18616        .update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18617        .expect("Prepare rename was not started");
18618    prepare_rename_handler.next().await.unwrap();
18619    prepare_rename_task.await.expect("Prepare rename failed");
18620
18621    let mut rename_handler =
18622        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18623            let edit = lsp::TextEdit {
18624                range: lsp::Range {
18625                    start: lsp::Position {
18626                        line: 0,
18627                        character: 7,
18628                    },
18629                    end: lsp::Position {
18630                        line: 0,
18631                        character: 10,
18632                    },
18633                },
18634                new_text: "FooRenamed".to_string(),
18635            };
18636            Ok(Some(lsp::WorkspaceEdit::new(
18637                // Specify the same edit twice
18638                std::collections::HashMap::from_iter(Some((url, vec![edit.clone(), edit]))),
18639            )))
18640        });
18641    let rename_task = cx
18642        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18643        .expect("Confirm rename was not started");
18644    rename_handler.next().await.unwrap();
18645    rename_task.await.expect("Confirm rename failed");
18646    cx.run_until_parked();
18647
18648    // Despite two edits, only one is actually applied as those are identical
18649    cx.assert_editor_state(indoc! {"
18650        struct FooRenamedˇ {}
18651    "});
18652}
18653
18654#[gpui::test]
18655async fn test_rename_without_prepare(cx: &mut TestAppContext) {
18656    init_test(cx, |_| {});
18657    // These capabilities indicate that the server does not support prepare rename.
18658    let capabilities = lsp::ServerCapabilities {
18659        rename_provider: Some(lsp::OneOf::Left(true)),
18660        ..Default::default()
18661    };
18662    let mut cx = EditorLspTestContext::new_rust(capabilities, cx).await;
18663
18664    cx.set_state(indoc! {"
18665        struct Fˇoo {}
18666    "});
18667
18668    cx.update_editor(|editor, _window, cx| {
18669        let highlight_range = Point::new(0, 7)..Point::new(0, 10);
18670        let highlight_range = highlight_range.to_anchors(&editor.buffer().read(cx).snapshot(cx));
18671        editor.highlight_background::<DocumentHighlightRead>(
18672            &[highlight_range],
18673            |c| c.editor_document_highlight_read_background,
18674            cx,
18675        );
18676    });
18677
18678    cx.update_editor(|e, window, cx| e.rename(&Rename, window, cx))
18679        .expect("Prepare rename was not started")
18680        .await
18681        .expect("Prepare rename failed");
18682
18683    let mut rename_handler =
18684        cx.set_request_handler::<lsp::request::Rename, _, _>(move |url, _, _| async move {
18685            let edit = lsp::TextEdit {
18686                range: lsp::Range {
18687                    start: lsp::Position {
18688                        line: 0,
18689                        character: 7,
18690                    },
18691                    end: lsp::Position {
18692                        line: 0,
18693                        character: 10,
18694                    },
18695                },
18696                new_text: "FooRenamed".to_string(),
18697            };
18698            Ok(Some(lsp::WorkspaceEdit::new(
18699                std::collections::HashMap::from_iter(Some((url, vec![edit]))),
18700            )))
18701        });
18702    let rename_task = cx
18703        .update_editor(|e, window, cx| e.confirm_rename(&ConfirmRename, window, cx))
18704        .expect("Confirm rename was not started");
18705    rename_handler.next().await.unwrap();
18706    rename_task.await.expect("Confirm rename failed");
18707    cx.run_until_parked();
18708
18709    // Correct range is renamed, as `surrounding_word` is used to find it.
18710    cx.assert_editor_state(indoc! {"
18711        struct FooRenamedˇ {}
18712    "});
18713}
18714
18715#[gpui::test]
18716async fn test_tree_sitter_brackets_newline_insertion(cx: &mut TestAppContext) {
18717    init_test(cx, |_| {});
18718    let mut cx = EditorTestContext::new(cx).await;
18719
18720    let language = Arc::new(
18721        Language::new(
18722            LanguageConfig::default(),
18723            Some(tree_sitter_html::LANGUAGE.into()),
18724        )
18725        .with_brackets_query(
18726            r#"
18727            ("<" @open "/>" @close)
18728            ("</" @open ">" @close)
18729            ("<" @open ">" @close)
18730            ("\"" @open "\"" @close)
18731            ((element (start_tag) @open (end_tag) @close) (#set! newline.only))
18732        "#,
18733        )
18734        .unwrap(),
18735    );
18736    cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
18737
18738    cx.set_state(indoc! {"
18739        <span>ˇ</span>
18740    "});
18741    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18742    cx.assert_editor_state(indoc! {"
18743        <span>
18744        ˇ
18745        </span>
18746    "});
18747
18748    cx.set_state(indoc! {"
18749        <span><span></span>ˇ</span>
18750    "});
18751    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18752    cx.assert_editor_state(indoc! {"
18753        <span><span></span>
18754        ˇ</span>
18755    "});
18756
18757    cx.set_state(indoc! {"
18758        <span>ˇ
18759        </span>
18760    "});
18761    cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx));
18762    cx.assert_editor_state(indoc! {"
18763        <span>
18764        ˇ
18765        </span>
18766    "});
18767}
18768
18769#[gpui::test(iterations = 10)]
18770async fn test_apply_code_lens_actions_with_commands(cx: &mut gpui::TestAppContext) {
18771    init_test(cx, |_| {});
18772
18773    let fs = FakeFs::new(cx.executor());
18774    fs.insert_tree(
18775        path!("/dir"),
18776        json!({
18777            "a.ts": "a",
18778        }),
18779    )
18780    .await;
18781
18782    let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
18783    let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
18784    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
18785
18786    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
18787    language_registry.add(Arc::new(Language::new(
18788        LanguageConfig {
18789            name: "TypeScript".into(),
18790            matcher: LanguageMatcher {
18791                path_suffixes: vec!["ts".to_string()],
18792                ..Default::default()
18793            },
18794            ..Default::default()
18795        },
18796        Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
18797    )));
18798    let mut fake_language_servers = language_registry.register_fake_lsp(
18799        "TypeScript",
18800        FakeLspAdapter {
18801            capabilities: lsp::ServerCapabilities {
18802                code_lens_provider: Some(lsp::CodeLensOptions {
18803                    resolve_provider: Some(true),
18804                }),
18805                execute_command_provider: Some(lsp::ExecuteCommandOptions {
18806                    commands: vec!["_the/command".to_string()],
18807                    ..lsp::ExecuteCommandOptions::default()
18808                }),
18809                ..lsp::ServerCapabilities::default()
18810            },
18811            ..FakeLspAdapter::default()
18812        },
18813    );
18814
18815    let (buffer, _handle) = project
18816        .update(cx, |p, cx| {
18817            p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx)
18818        })
18819        .await
18820        .unwrap();
18821    cx.executor().run_until_parked();
18822
18823    let fake_server = fake_language_servers.next().await.unwrap();
18824
18825    let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
18826    let anchor = buffer_snapshot.anchor_at(0, text::Bias::Left);
18827    drop(buffer_snapshot);
18828    let actions = cx
18829        .update_window(*workspace, |_, window, cx| {
18830            project.code_actions(&buffer, anchor..anchor, window, cx)
18831        })
18832        .unwrap();
18833
18834    fake_server
18835        .set_request_handler::<lsp::request::CodeLensRequest, _, _>(|_, _| async move {
18836            Ok(Some(vec![
18837                lsp::CodeLens {
18838                    range: lsp::Range::default(),
18839                    command: Some(lsp::Command {
18840                        title: "Code lens command".to_owned(),
18841                        command: "_the/command".to_owned(),
18842                        arguments: None,
18843                    }),
18844                    data: None,
18845                },
18846                lsp::CodeLens {
18847                    range: lsp::Range::default(),
18848                    command: Some(lsp::Command {
18849                        title: "Command not in capabilities".to_owned(),
18850                        command: "not in capabilities".to_owned(),
18851                        arguments: None,
18852                    }),
18853                    data: None,
18854                },
18855                lsp::CodeLens {
18856                    range: lsp::Range {
18857                        start: lsp::Position {
18858                            line: 1,
18859                            character: 1,
18860                        },
18861                        end: lsp::Position {
18862                            line: 1,
18863                            character: 1,
18864                        },
18865                    },
18866                    command: Some(lsp::Command {
18867                        title: "Command not in range".to_owned(),
18868                        command: "_the/command".to_owned(),
18869                        arguments: None,
18870                    }),
18871                    data: None,
18872                },
18873            ]))
18874        })
18875        .next()
18876        .await;
18877
18878    let actions = actions.await.unwrap();
18879    assert_eq!(
18880        actions.len(),
18881        1,
18882        "Should have only one valid action for the 0..0 range"
18883    );
18884    let action = actions[0].clone();
18885    let apply = project.update(cx, |project, cx| {
18886        project.apply_code_action(buffer.clone(), action, true, cx)
18887    });
18888
18889    // Resolving the code action does not populate its edits. In absence of
18890    // edits, we must execute the given command.
18891    fake_server.set_request_handler::<lsp::request::CodeLensResolve, _, _>(
18892        |mut lens, _| async move {
18893            let lens_command = lens.command.as_mut().expect("should have a command");
18894            assert_eq!(lens_command.title, "Code lens command");
18895            lens_command.arguments = Some(vec![json!("the-argument")]);
18896            Ok(lens)
18897        },
18898    );
18899
18900    // While executing the command, the language server sends the editor
18901    // a `workspaceEdit` request.
18902    fake_server
18903        .set_request_handler::<lsp::request::ExecuteCommand, _, _>({
18904            let fake = fake_server.clone();
18905            move |params, _| {
18906                assert_eq!(params.command, "_the/command");
18907                let fake = fake.clone();
18908                async move {
18909                    fake.server
18910                        .request::<lsp::request::ApplyWorkspaceEdit>(
18911                            lsp::ApplyWorkspaceEditParams {
18912                                label: None,
18913                                edit: lsp::WorkspaceEdit {
18914                                    changes: Some(
18915                                        [(
18916                                            lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(),
18917                                            vec![lsp::TextEdit {
18918                                                range: lsp::Range::new(
18919                                                    lsp::Position::new(0, 0),
18920                                                    lsp::Position::new(0, 0),
18921                                                ),
18922                                                new_text: "X".into(),
18923                                            }],
18924                                        )]
18925                                        .into_iter()
18926                                        .collect(),
18927                                    ),
18928                                    ..Default::default()
18929                                },
18930                            },
18931                        )
18932                        .await
18933                        .unwrap();
18934                    Ok(Some(json!(null)))
18935                }
18936            }
18937        })
18938        .next()
18939        .await;
18940
18941    // Applying the code lens command returns a project transaction containing the edits
18942    // sent by the language server in its `workspaceEdit` request.
18943    let transaction = apply.await.unwrap();
18944    assert!(transaction.0.contains_key(&buffer));
18945    buffer.update(cx, |buffer, cx| {
18946        assert_eq!(buffer.text(), "Xa");
18947        buffer.undo(cx);
18948        assert_eq!(buffer.text(), "a");
18949    });
18950}
18951
18952#[gpui::test]
18953async fn test_editor_restore_data_different_in_panes(cx: &mut TestAppContext) {
18954    init_test(cx, |_| {});
18955
18956    let fs = FakeFs::new(cx.executor());
18957    let main_text = r#"fn main() {
18958println!("1");
18959println!("2");
18960println!("3");
18961println!("4");
18962println!("5");
18963}"#;
18964    let lib_text = "mod foo {}";
18965    fs.insert_tree(
18966        path!("/a"),
18967        json!({
18968            "lib.rs": lib_text,
18969            "main.rs": main_text,
18970        }),
18971    )
18972    .await;
18973
18974    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
18975    let (workspace, cx) =
18976        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
18977    let worktree_id = workspace.update(cx, |workspace, cx| {
18978        workspace.project().update(cx, |project, cx| {
18979            project.worktrees(cx).next().unwrap().read(cx).id()
18980        })
18981    });
18982
18983    let expected_ranges = vec![
18984        Point::new(0, 0)..Point::new(0, 0),
18985        Point::new(1, 0)..Point::new(1, 1),
18986        Point::new(2, 0)..Point::new(2, 2),
18987        Point::new(3, 0)..Point::new(3, 3),
18988    ];
18989
18990    let pane_1 = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
18991    let editor_1 = workspace
18992        .update_in(cx, |workspace, window, cx| {
18993            workspace.open_path(
18994                (worktree_id, "main.rs"),
18995                Some(pane_1.downgrade()),
18996                true,
18997                window,
18998                cx,
18999            )
19000        })
19001        .unwrap()
19002        .await
19003        .downcast::<Editor>()
19004        .unwrap();
19005    pane_1.update(cx, |pane, cx| {
19006        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19007        open_editor.update(cx, |editor, cx| {
19008            assert_eq!(
19009                editor.display_text(cx),
19010                main_text,
19011                "Original main.rs text on initial open",
19012            );
19013            assert_eq!(
19014                editor
19015                    .selections
19016                    .all::<Point>(cx)
19017                    .into_iter()
19018                    .map(|s| s.range())
19019                    .collect::<Vec<_>>(),
19020                vec![Point::zero()..Point::zero()],
19021                "Default selections on initial open",
19022            );
19023        })
19024    });
19025    editor_1.update_in(cx, |editor, window, cx| {
19026        editor.change_selections(None, window, cx, |s| {
19027            s.select_ranges(expected_ranges.clone());
19028        });
19029    });
19030
19031    let pane_2 = workspace.update_in(cx, |workspace, window, cx| {
19032        workspace.split_pane(pane_1.clone(), SplitDirection::Right, window, cx)
19033    });
19034    let editor_2 = workspace
19035        .update_in(cx, |workspace, window, cx| {
19036            workspace.open_path(
19037                (worktree_id, "main.rs"),
19038                Some(pane_2.downgrade()),
19039                true,
19040                window,
19041                cx,
19042            )
19043        })
19044        .unwrap()
19045        .await
19046        .downcast::<Editor>()
19047        .unwrap();
19048    pane_2.update(cx, |pane, cx| {
19049        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19050        open_editor.update(cx, |editor, cx| {
19051            assert_eq!(
19052                editor.display_text(cx),
19053                main_text,
19054                "Original main.rs text on initial open in another panel",
19055            );
19056            assert_eq!(
19057                editor
19058                    .selections
19059                    .all::<Point>(cx)
19060                    .into_iter()
19061                    .map(|s| s.range())
19062                    .collect::<Vec<_>>(),
19063                vec![Point::zero()..Point::zero()],
19064                "Default selections on initial open in another panel",
19065            );
19066        })
19067    });
19068
19069    editor_2.update_in(cx, |editor, window, cx| {
19070        editor.fold_ranges(expected_ranges.clone(), false, window, cx);
19071    });
19072
19073    let _other_editor_1 = workspace
19074        .update_in(cx, |workspace, window, cx| {
19075            workspace.open_path(
19076                (worktree_id, "lib.rs"),
19077                Some(pane_1.downgrade()),
19078                true,
19079                window,
19080                cx,
19081            )
19082        })
19083        .unwrap()
19084        .await
19085        .downcast::<Editor>()
19086        .unwrap();
19087    pane_1
19088        .update_in(cx, |pane, window, cx| {
19089            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19090                .unwrap()
19091        })
19092        .await
19093        .unwrap();
19094    drop(editor_1);
19095    pane_1.update(cx, |pane, cx| {
19096        pane.active_item()
19097            .unwrap()
19098            .downcast::<Editor>()
19099            .unwrap()
19100            .update(cx, |editor, cx| {
19101                assert_eq!(
19102                    editor.display_text(cx),
19103                    lib_text,
19104                    "Other file should be open and active",
19105                );
19106            });
19107        assert_eq!(pane.items().count(), 1, "No other editors should be open");
19108    });
19109
19110    let _other_editor_2 = workspace
19111        .update_in(cx, |workspace, window, cx| {
19112            workspace.open_path(
19113                (worktree_id, "lib.rs"),
19114                Some(pane_2.downgrade()),
19115                true,
19116                window,
19117                cx,
19118            )
19119        })
19120        .unwrap()
19121        .await
19122        .downcast::<Editor>()
19123        .unwrap();
19124    pane_2
19125        .update_in(cx, |pane, window, cx| {
19126            pane.close_inactive_items(&CloseInactiveItems::default(), window, cx)
19127                .unwrap()
19128        })
19129        .await
19130        .unwrap();
19131    drop(editor_2);
19132    pane_2.update(cx, |pane, cx| {
19133        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19134        open_editor.update(cx, |editor, cx| {
19135            assert_eq!(
19136                editor.display_text(cx),
19137                lib_text,
19138                "Other file should be open and active in another panel too",
19139            );
19140        });
19141        assert_eq!(
19142            pane.items().count(),
19143            1,
19144            "No other editors should be open in another pane",
19145        );
19146    });
19147
19148    let _editor_1_reopened = workspace
19149        .update_in(cx, |workspace, window, cx| {
19150            workspace.open_path(
19151                (worktree_id, "main.rs"),
19152                Some(pane_1.downgrade()),
19153                true,
19154                window,
19155                cx,
19156            )
19157        })
19158        .unwrap()
19159        .await
19160        .downcast::<Editor>()
19161        .unwrap();
19162    let _editor_2_reopened = workspace
19163        .update_in(cx, |workspace, window, cx| {
19164            workspace.open_path(
19165                (worktree_id, "main.rs"),
19166                Some(pane_2.downgrade()),
19167                true,
19168                window,
19169                cx,
19170            )
19171        })
19172        .unwrap()
19173        .await
19174        .downcast::<Editor>()
19175        .unwrap();
19176    pane_1.update(cx, |pane, cx| {
19177        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19178        open_editor.update(cx, |editor, cx| {
19179            assert_eq!(
19180                editor.display_text(cx),
19181                main_text,
19182                "Previous editor in the 1st panel had no extra text manipulations and should get none on reopen",
19183            );
19184            assert_eq!(
19185                editor
19186                    .selections
19187                    .all::<Point>(cx)
19188                    .into_iter()
19189                    .map(|s| s.range())
19190                    .collect::<Vec<_>>(),
19191                expected_ranges,
19192                "Previous editor in the 1st panel had selections and should get them restored on reopen",
19193            );
19194        })
19195    });
19196    pane_2.update(cx, |pane, cx| {
19197        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19198        open_editor.update(cx, |editor, cx| {
19199            assert_eq!(
19200                editor.display_text(cx),
19201                r#"fn main() {
19202⋯rintln!("1");
19203⋯intln!("2");
19204⋯ntln!("3");
19205println!("4");
19206println!("5");
19207}"#,
19208                "Previous editor in the 2nd pane had folds and should restore those on reopen in the same pane",
19209            );
19210            assert_eq!(
19211                editor
19212                    .selections
19213                    .all::<Point>(cx)
19214                    .into_iter()
19215                    .map(|s| s.range())
19216                    .collect::<Vec<_>>(),
19217                vec![Point::zero()..Point::zero()],
19218                "Previous editor in the 2nd pane had no selections changed hence should restore none",
19219            );
19220        })
19221    });
19222}
19223
19224#[gpui::test]
19225async fn test_editor_does_not_restore_data_when_turned_off(cx: &mut TestAppContext) {
19226    init_test(cx, |_| {});
19227
19228    let fs = FakeFs::new(cx.executor());
19229    let main_text = r#"fn main() {
19230println!("1");
19231println!("2");
19232println!("3");
19233println!("4");
19234println!("5");
19235}"#;
19236    let lib_text = "mod foo {}";
19237    fs.insert_tree(
19238        path!("/a"),
19239        json!({
19240            "lib.rs": lib_text,
19241            "main.rs": main_text,
19242        }),
19243    )
19244    .await;
19245
19246    let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
19247    let (workspace, cx) =
19248        cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
19249    let worktree_id = workspace.update(cx, |workspace, cx| {
19250        workspace.project().update(cx, |project, cx| {
19251            project.worktrees(cx).next().unwrap().read(cx).id()
19252        })
19253    });
19254
19255    let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone());
19256    let editor = workspace
19257        .update_in(cx, |workspace, window, cx| {
19258            workspace.open_path(
19259                (worktree_id, "main.rs"),
19260                Some(pane.downgrade()),
19261                true,
19262                window,
19263                cx,
19264            )
19265        })
19266        .unwrap()
19267        .await
19268        .downcast::<Editor>()
19269        .unwrap();
19270    pane.update(cx, |pane, cx| {
19271        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19272        open_editor.update(cx, |editor, cx| {
19273            assert_eq!(
19274                editor.display_text(cx),
19275                main_text,
19276                "Original main.rs text on initial open",
19277            );
19278        })
19279    });
19280    editor.update_in(cx, |editor, window, cx| {
19281        editor.fold_ranges(vec![Point::new(0, 0)..Point::new(0, 0)], false, window, cx);
19282    });
19283
19284    cx.update_global(|store: &mut SettingsStore, cx| {
19285        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19286            s.restore_on_file_reopen = Some(false);
19287        });
19288    });
19289    editor.update_in(cx, |editor, window, cx| {
19290        editor.fold_ranges(
19291            vec![
19292                Point::new(1, 0)..Point::new(1, 1),
19293                Point::new(2, 0)..Point::new(2, 2),
19294                Point::new(3, 0)..Point::new(3, 3),
19295            ],
19296            false,
19297            window,
19298            cx,
19299        );
19300    });
19301    pane.update_in(cx, |pane, window, cx| {
19302        pane.close_all_items(&CloseAllItems::default(), window, cx)
19303            .unwrap()
19304    })
19305    .await
19306    .unwrap();
19307    pane.update(cx, |pane, _| {
19308        assert!(pane.active_item().is_none());
19309    });
19310    cx.update_global(|store: &mut SettingsStore, cx| {
19311        store.update_user_settings::<WorkspaceSettings>(cx, |s| {
19312            s.restore_on_file_reopen = Some(true);
19313        });
19314    });
19315
19316    let _editor_reopened = workspace
19317        .update_in(cx, |workspace, window, cx| {
19318            workspace.open_path(
19319                (worktree_id, "main.rs"),
19320                Some(pane.downgrade()),
19321                true,
19322                window,
19323                cx,
19324            )
19325        })
19326        .unwrap()
19327        .await
19328        .downcast::<Editor>()
19329        .unwrap();
19330    pane.update(cx, |pane, cx| {
19331        let open_editor = pane.active_item().unwrap().downcast::<Editor>().unwrap();
19332        open_editor.update(cx, |editor, cx| {
19333            assert_eq!(
19334                editor.display_text(cx),
19335                main_text,
19336                "No folds: even after enabling the restoration, previous editor's data should not be saved to be used for the restoration"
19337            );
19338        })
19339    });
19340}
19341
19342fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
19343    let point = DisplayPoint::new(DisplayRow(row as u32), column as u32);
19344    point..point
19345}
19346
19347fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Context<Editor>) {
19348    let (text, ranges) = marked_text_ranges(marked_text, true);
19349    assert_eq!(editor.text(cx), text);
19350    assert_eq!(
19351        editor.selections.ranges(cx),
19352        ranges,
19353        "Assert selections are {}",
19354        marked_text
19355    );
19356}
19357
19358pub fn handle_signature_help_request(
19359    cx: &mut EditorLspTestContext,
19360    mocked_response: lsp::SignatureHelp,
19361) -> impl Future<Output = ()> + use<> {
19362    let mut request =
19363        cx.set_request_handler::<lsp::request::SignatureHelpRequest, _, _>(move |_, _, _| {
19364            let mocked_response = mocked_response.clone();
19365            async move { Ok(Some(mocked_response)) }
19366        });
19367
19368    async move {
19369        request.next().await;
19370    }
19371}
19372
19373/// Handle completion request passing a marked string specifying where the completion
19374/// should be triggered from using '|' character, what range should be replaced, and what completions
19375/// should be returned using '<' and '>' to delimit the range.
19376///
19377/// Also see `handle_completion_request_with_insert_and_replace`.
19378#[track_caller]
19379pub fn handle_completion_request(
19380    cx: &mut EditorLspTestContext,
19381    marked_string: &str,
19382    completions: Vec<&'static str>,
19383    counter: Arc<AtomicUsize>,
19384) -> impl Future<Output = ()> {
19385    let complete_from_marker: TextRangeMarker = '|'.into();
19386    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19387    let (_, mut marked_ranges) = marked_text_ranges_by(
19388        marked_string,
19389        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19390    );
19391
19392    let complete_from_position =
19393        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19394    let replace_range =
19395        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19396
19397    let mut request =
19398        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19399            let completions = completions.clone();
19400            counter.fetch_add(1, atomic::Ordering::Release);
19401            async move {
19402                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19403                assert_eq!(
19404                    params.text_document_position.position,
19405                    complete_from_position
19406                );
19407                Ok(Some(lsp::CompletionResponse::Array(
19408                    completions
19409                        .iter()
19410                        .map(|completion_text| lsp::CompletionItem {
19411                            label: completion_text.to_string(),
19412                            text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
19413                                range: replace_range,
19414                                new_text: completion_text.to_string(),
19415                            })),
19416                            ..Default::default()
19417                        })
19418                        .collect(),
19419                )))
19420            }
19421        });
19422
19423    async move {
19424        request.next().await;
19425    }
19426}
19427
19428/// Similar to `handle_completion_request`, but a [`CompletionTextEdit::InsertAndReplace`] will be
19429/// given instead, which also contains an `insert` range.
19430///
19431/// This function uses the cursor position to mimic what Rust-Analyzer provides as the `insert` range,
19432/// that is, `replace_range.start..cursor_pos`.
19433pub fn handle_completion_request_with_insert_and_replace(
19434    cx: &mut EditorLspTestContext,
19435    marked_string: &str,
19436    completions: Vec<&'static str>,
19437    counter: Arc<AtomicUsize>,
19438) -> impl Future<Output = ()> {
19439    let complete_from_marker: TextRangeMarker = '|'.into();
19440    let replace_range_marker: TextRangeMarker = ('<', '>').into();
19441    let (_, mut marked_ranges) = marked_text_ranges_by(
19442        marked_string,
19443        vec![complete_from_marker.clone(), replace_range_marker.clone()],
19444    );
19445
19446    let complete_from_position =
19447        cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start);
19448    let replace_range =
19449        cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone());
19450
19451    let mut request =
19452        cx.set_request_handler::<lsp::request::Completion, _, _>(move |url, params, _| {
19453            let completions = completions.clone();
19454            counter.fetch_add(1, atomic::Ordering::Release);
19455            async move {
19456                assert_eq!(params.text_document_position.text_document.uri, url.clone());
19457                assert_eq!(
19458                    params.text_document_position.position, complete_from_position,
19459                    "marker `|` position doesn't match",
19460                );
19461                Ok(Some(lsp::CompletionResponse::Array(
19462                    completions
19463                        .iter()
19464                        .map(|completion_text| lsp::CompletionItem {
19465                            label: completion_text.to_string(),
19466                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
19467                                lsp::InsertReplaceEdit {
19468                                    insert: lsp::Range {
19469                                        start: replace_range.start,
19470                                        end: complete_from_position,
19471                                    },
19472                                    replace: replace_range,
19473                                    new_text: completion_text.to_string(),
19474                                },
19475                            )),
19476                            ..Default::default()
19477                        })
19478                        .collect(),
19479                )))
19480            }
19481        });
19482
19483    async move {
19484        request.next().await;
19485    }
19486}
19487
19488fn handle_resolve_completion_request(
19489    cx: &mut EditorLspTestContext,
19490    edits: Option<Vec<(&'static str, &'static str)>>,
19491) -> impl Future<Output = ()> {
19492    let edits = edits.map(|edits| {
19493        edits
19494            .iter()
19495            .map(|(marked_string, new_text)| {
19496                let (_, marked_ranges) = marked_text_ranges(marked_string, false);
19497                let replace_range = cx.to_lsp_range(marked_ranges[0].clone());
19498                lsp::TextEdit::new(replace_range, new_text.to_string())
19499            })
19500            .collect::<Vec<_>>()
19501    });
19502
19503    let mut request =
19504        cx.set_request_handler::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
19505            let edits = edits.clone();
19506            async move {
19507                Ok(lsp::CompletionItem {
19508                    additional_text_edits: edits,
19509                    ..Default::default()
19510                })
19511            }
19512        });
19513
19514    async move {
19515        request.next().await;
19516    }
19517}
19518
19519pub(crate) fn update_test_language_settings(
19520    cx: &mut TestAppContext,
19521    f: impl Fn(&mut AllLanguageSettingsContent),
19522) {
19523    cx.update(|cx| {
19524        SettingsStore::update_global(cx, |store, cx| {
19525            store.update_user_settings::<AllLanguageSettings>(cx, f);
19526        });
19527    });
19528}
19529
19530pub(crate) fn update_test_project_settings(
19531    cx: &mut TestAppContext,
19532    f: impl Fn(&mut ProjectSettings),
19533) {
19534    cx.update(|cx| {
19535        SettingsStore::update_global(cx, |store, cx| {
19536            store.update_user_settings::<ProjectSettings>(cx, f);
19537        });
19538    });
19539}
19540
19541pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
19542    cx.update(|cx| {
19543        assets::Assets.load_test_fonts(cx);
19544        let store = SettingsStore::test(cx);
19545        cx.set_global(store);
19546        theme::init(theme::LoadThemes::JustBase, cx);
19547        release_channel::init(SemanticVersion::default(), cx);
19548        client::init_settings(cx);
19549        language::init(cx);
19550        Project::init_settings(cx);
19551        workspace::init_settings(cx);
19552        crate::init(cx);
19553    });
19554
19555    update_test_language_settings(cx, f);
19556}
19557
19558#[track_caller]
19559fn assert_hunk_revert(
19560    not_reverted_text_with_selections: &str,
19561    expected_hunk_statuses_before: Vec<DiffHunkStatusKind>,
19562    expected_reverted_text_with_selections: &str,
19563    base_text: &str,
19564    cx: &mut EditorLspTestContext,
19565) {
19566    cx.set_state(not_reverted_text_with_selections);
19567    cx.set_head_text(base_text);
19568    cx.executor().run_until_parked();
19569
19570    let actual_hunk_statuses_before = cx.update_editor(|editor, window, cx| {
19571        let snapshot = editor.snapshot(window, cx);
19572        let reverted_hunk_statuses = snapshot
19573            .buffer_snapshot
19574            .diff_hunks_in_range(0..snapshot.buffer_snapshot.len())
19575            .map(|hunk| hunk.status().kind)
19576            .collect::<Vec<_>>();
19577
19578        editor.git_restore(&Default::default(), window, cx);
19579        reverted_hunk_statuses
19580    });
19581    cx.executor().run_until_parked();
19582    cx.assert_editor_state(expected_reverted_text_with_selections);
19583    assert_eq!(actual_hunk_statuses_before, expected_hunk_statuses_before);
19584}